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