Path: blob/main/sys/contrib/openzfs/lib/libzutil/zutil_nicenum.c
48378 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/2122/*23* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.24*/2526#include <ctype.h>27#include <math.h>28#include <stdio.h>29#include <libzutil.h>30#include <string.h>3132/*33* Return B_TRUE if "str" is a number string, B_FALSE otherwise.34* Works for integer and floating point numbers.35*/36boolean_t37zfs_isnumber(const char *str)38{39if (!*str)40return (B_FALSE);4142for (; *str; str++)43if (!(isdigit(*str) || (*str == '.')))44return (B_FALSE);4546/*47* Numbers should not end with a period ("." ".." or "5." are48* not valid)49*/50if (str[strlen(str) - 1] == '.') {51return (B_FALSE);52}5354return (B_TRUE);55}5657/*58* Convert a number to an appropriately human-readable output.59*/60void61zfs_nicenum_format(uint64_t num, char *buf, size_t buflen,62enum zfs_nicenum_format format)63{64uint64_t n = num;65int index = 0;66const char *u;67const char *units[3][7] = {68[ZFS_NICENUM_1024] = {"", "K", "M", "G", "T", "P", "E"},69[ZFS_NICENUM_BYTES] = {"B", "K", "M", "G", "T", "P", "E"},70[ZFS_NICENUM_TIME] = {"ns", "us", "ms", "s", "?", "?", "?"}71};7273const int units_len[] = {[ZFS_NICENUM_1024] = 6,74[ZFS_NICENUM_BYTES] = 6,75[ZFS_NICENUM_TIME] = 4};7677const int k_unit[] = { [ZFS_NICENUM_1024] = 1024,78[ZFS_NICENUM_BYTES] = 1024,79[ZFS_NICENUM_TIME] = 1000};8081double val;8283if (format == ZFS_NICENUM_RAW) {84snprintf(buf, buflen, "%llu", (u_longlong_t)num);85return;86} else if (format == ZFS_NICENUM_RAWTIME && num > 0) {87snprintf(buf, buflen, "%llu", (u_longlong_t)num);88return;89} else if (format == ZFS_NICENUM_RAWTIME && num == 0) {90snprintf(buf, buflen, "%s", "-");91return;92}9394while (n >= k_unit[format] && index < units_len[format]) {95n /= k_unit[format];96index++;97}9899u = units[format][index];100101/* Don't print zero latencies since they're invalid */102if ((format == ZFS_NICENUM_TIME) && (num == 0)) {103(void) snprintf(buf, buflen, "-");104} else if ((index == 0) || ((num %105(uint64_t)powl(k_unit[format], index)) == 0)) {106/*107* If this is an even multiple of the base, always display108* without any decimal precision.109*/110(void) snprintf(buf, buflen, "%llu%s", (u_longlong_t)n, u);111112} else {113/*114* We want to choose a precision that reflects the best choice115* for fitting in 5 characters. This can get rather tricky when116* we have numbers that are very close to an order of magnitude.117* For example, when displaying 10239 (which is really 9.999K),118* we want only a single place of precision for 10.0K. We could119* develop some complex heuristics for this, but it's much120* easier just to try each combination in turn.121*/122int i;123for (i = 2; i >= 0; i--) {124val = (double)num /125(uint64_t)powl(k_unit[format], index);126127/*128* Don't print floating point values for time. Note,129* we use floor() instead of round() here, since130* round can result in undesirable results. For131* example, if "num" is in the range of132* 999500-999999, it will print out "1000us". This133* doesn't happen if we use floor().134*/135if (format == ZFS_NICENUM_TIME) {136if (snprintf(buf, buflen, "%d%s",137(unsigned int) floor(val), u) <= 5)138break;139140} else {141if (snprintf(buf, buflen, "%.*f%s", i,142val, u) <= 5)143break;144}145}146}147}148149/*150* Convert a number to an appropriately human-readable output.151*/152void153zfs_nicenum(uint64_t num, char *buf, size_t buflen)154{155zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_1024);156}157158/*159* Convert a time to an appropriately human-readable output.160* @num: Time in nanoseconds161*/162void163zfs_nicetime(uint64_t num, char *buf, size_t buflen)164{165zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_TIME);166}167168/*169* Print out a raw number with correct column spacing170*/171void172zfs_niceraw(uint64_t num, char *buf, size_t buflen)173{174zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_RAW);175}176177/*178* Convert a number of bytes to an appropriately human-readable output.179*/180void181zfs_nicebytes(uint64_t num, char *buf, size_t buflen)182{183zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_BYTES);184}185186187