Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/contrib/openzfs/lib/libzutil/zutil_nicenum.c
48378 views
1
// SPDX-License-Identifier: CDDL-1.0
2
/*
3
* CDDL HEADER START
4
*
5
* The contents of this file are subject to the terms of the
6
* Common Development and Distribution License (the "License").
7
* You may not use this file except in compliance with the License.
8
*
9
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10
* or https://opensource.org/licenses/CDDL-1.0.
11
* See the License for the specific language governing permissions
12
* and limitations under the License.
13
*
14
* When distributing Covered Code, include this CDDL HEADER in each
15
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16
* If applicable, add the following below this CDDL HEADER, with the
17
* fields enclosed by brackets "[]" replaced with your own identifying
18
* information: Portions Copyright [yyyy] [name of copyright owner]
19
*
20
* CDDL HEADER END
21
*/
22
23
/*
24
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
25
*/
26
27
#include <ctype.h>
28
#include <math.h>
29
#include <stdio.h>
30
#include <libzutil.h>
31
#include <string.h>
32
33
/*
34
* Return B_TRUE if "str" is a number string, B_FALSE otherwise.
35
* Works for integer and floating point numbers.
36
*/
37
boolean_t
38
zfs_isnumber(const char *str)
39
{
40
if (!*str)
41
return (B_FALSE);
42
43
for (; *str; str++)
44
if (!(isdigit(*str) || (*str == '.')))
45
return (B_FALSE);
46
47
/*
48
* Numbers should not end with a period ("." ".." or "5." are
49
* not valid)
50
*/
51
if (str[strlen(str) - 1] == '.') {
52
return (B_FALSE);
53
}
54
55
return (B_TRUE);
56
}
57
58
/*
59
* Convert a number to an appropriately human-readable output.
60
*/
61
void
62
zfs_nicenum_format(uint64_t num, char *buf, size_t buflen,
63
enum zfs_nicenum_format format)
64
{
65
uint64_t n = num;
66
int index = 0;
67
const char *u;
68
const char *units[3][7] = {
69
[ZFS_NICENUM_1024] = {"", "K", "M", "G", "T", "P", "E"},
70
[ZFS_NICENUM_BYTES] = {"B", "K", "M", "G", "T", "P", "E"},
71
[ZFS_NICENUM_TIME] = {"ns", "us", "ms", "s", "?", "?", "?"}
72
};
73
74
const int units_len[] = {[ZFS_NICENUM_1024] = 6,
75
[ZFS_NICENUM_BYTES] = 6,
76
[ZFS_NICENUM_TIME] = 4};
77
78
const int k_unit[] = { [ZFS_NICENUM_1024] = 1024,
79
[ZFS_NICENUM_BYTES] = 1024,
80
[ZFS_NICENUM_TIME] = 1000};
81
82
double val;
83
84
if (format == ZFS_NICENUM_RAW) {
85
snprintf(buf, buflen, "%llu", (u_longlong_t)num);
86
return;
87
} else if (format == ZFS_NICENUM_RAWTIME && num > 0) {
88
snprintf(buf, buflen, "%llu", (u_longlong_t)num);
89
return;
90
} else if (format == ZFS_NICENUM_RAWTIME && num == 0) {
91
snprintf(buf, buflen, "%s", "-");
92
return;
93
}
94
95
while (n >= k_unit[format] && index < units_len[format]) {
96
n /= k_unit[format];
97
index++;
98
}
99
100
u = units[format][index];
101
102
/* Don't print zero latencies since they're invalid */
103
if ((format == ZFS_NICENUM_TIME) && (num == 0)) {
104
(void) snprintf(buf, buflen, "-");
105
} else if ((index == 0) || ((num %
106
(uint64_t)powl(k_unit[format], index)) == 0)) {
107
/*
108
* If this is an even multiple of the base, always display
109
* without any decimal precision.
110
*/
111
(void) snprintf(buf, buflen, "%llu%s", (u_longlong_t)n, u);
112
113
} else {
114
/*
115
* We want to choose a precision that reflects the best choice
116
* for fitting in 5 characters. This can get rather tricky when
117
* we have numbers that are very close to an order of magnitude.
118
* For example, when displaying 10239 (which is really 9.999K),
119
* we want only a single place of precision for 10.0K. We could
120
* develop some complex heuristics for this, but it's much
121
* easier just to try each combination in turn.
122
*/
123
int i;
124
for (i = 2; i >= 0; i--) {
125
val = (double)num /
126
(uint64_t)powl(k_unit[format], index);
127
128
/*
129
* Don't print floating point values for time. Note,
130
* we use floor() instead of round() here, since
131
* round can result in undesirable results. For
132
* example, if "num" is in the range of
133
* 999500-999999, it will print out "1000us". This
134
* doesn't happen if we use floor().
135
*/
136
if (format == ZFS_NICENUM_TIME) {
137
if (snprintf(buf, buflen, "%d%s",
138
(unsigned int) floor(val), u) <= 5)
139
break;
140
141
} else {
142
if (snprintf(buf, buflen, "%.*f%s", i,
143
val, u) <= 5)
144
break;
145
}
146
}
147
}
148
}
149
150
/*
151
* Convert a number to an appropriately human-readable output.
152
*/
153
void
154
zfs_nicenum(uint64_t num, char *buf, size_t buflen)
155
{
156
zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_1024);
157
}
158
159
/*
160
* Convert a time to an appropriately human-readable output.
161
* @num: Time in nanoseconds
162
*/
163
void
164
zfs_nicetime(uint64_t num, char *buf, size_t buflen)
165
{
166
zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_TIME);
167
}
168
169
/*
170
* Print out a raw number with correct column spacing
171
*/
172
void
173
zfs_niceraw(uint64_t num, char *buf, size_t buflen)
174
{
175
zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_RAW);
176
}
177
178
/*
179
* Convert a number of bytes to an appropriately human-readable output.
180
*/
181
void
182
zfs_nicebytes(uint64_t num, char *buf, size_t buflen)
183
{
184
zfs_nicenum_format(num, buf, buflen, ZFS_NICENUM_BYTES);
185
}
186
187