xref: /titanic_44/usr/src/lib/libcmdutils/common/nicenum.c (revision a66b28a398b570821116edfa8ced7b2bd4aeeab8)
1*a66b28a3SJason King /*
2*a66b28a3SJason King  * CDDL HEADER START
3*a66b28a3SJason King  *
4*a66b28a3SJason King  * The contents of this file are subject to the terms of the
5*a66b28a3SJason King  * Common Development and Distribution License (the "License").
6*a66b28a3SJason King  * You may not use this file except in compliance with the License.
7*a66b28a3SJason King  *
8*a66b28a3SJason King  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*a66b28a3SJason King  * or http://www.opensolaris.org/os/licensing.
10*a66b28a3SJason King  * See the License for the specific language governing permissions
11*a66b28a3SJason King  * and limitations under the License.
12*a66b28a3SJason King  *
13*a66b28a3SJason King  * When distributing Covered Code, include this CDDL HEADER in each
14*a66b28a3SJason King  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*a66b28a3SJason King  * If applicable, add the following below this CDDL HEADER, with the
16*a66b28a3SJason King  * fields enclosed by brackets "[]" replaced with your own identifying
17*a66b28a3SJason King  * information: Portions Copyright [yyyy] [name of copyright owner]
18*a66b28a3SJason King  *
19*a66b28a3SJason King  * CDDL HEADER END
20*a66b28a3SJason King  */
21*a66b28a3SJason King 
22*a66b28a3SJason King /*
23*a66b28a3SJason King  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24*a66b28a3SJason King  * Copyright 2017 Jason king
25*a66b28a3SJason King  */
26*a66b28a3SJason King 
27*a66b28a3SJason King #include <stdio.h>
28*a66b28a3SJason King #include <string.h>
29*a66b28a3SJason King #include <sys/types.h>
30*a66b28a3SJason King #include <sys/debug.h>
31*a66b28a3SJason King #include "libcmdutils.h"
32*a66b28a3SJason King 
33*a66b28a3SJason King /* The largest suffix that can fit, aka an exabyte (2^60 / 10^18) */
34*a66b28a3SJason King #define	INDEX_MAX	(6)
35*a66b28a3SJason King 
36*a66b28a3SJason King /* Verify INDEX_MAX fits */
37*a66b28a3SJason King CTASSERT(INDEX_MAX * 10 < sizeof (uint64_t) * 8);
38*a66b28a3SJason King 
39*a66b28a3SJason King void
nicenum_scale(uint64_t n,size_t units,char * buf,size_t buflen,uint32_t flags)40*a66b28a3SJason King nicenum_scale(uint64_t n, size_t units, char *buf, size_t buflen,
41*a66b28a3SJason King     uint32_t flags)
42*a66b28a3SJason King {
43*a66b28a3SJason King 	uint64_t divamt = 1024;
44*a66b28a3SJason King 	uint64_t divisor = 1;
45*a66b28a3SJason King 	int index = 0;
46*a66b28a3SJason King 	int rc = 0;
47*a66b28a3SJason King 	char u;
48*a66b28a3SJason King 
49*a66b28a3SJason King 	if (units == 0)
50*a66b28a3SJason King 		units = 1;
51*a66b28a3SJason King 
52*a66b28a3SJason King 	if (n > 0) {
53*a66b28a3SJason King 		n *= units;
54*a66b28a3SJason King 		if (n < units)
55*a66b28a3SJason King 			goto overflow;
56*a66b28a3SJason King 	}
57*a66b28a3SJason King 
58*a66b28a3SJason King 	if (flags & NN_DIVISOR_1000)
59*a66b28a3SJason King 		divamt = 1000;
60*a66b28a3SJason King 
61*a66b28a3SJason King 	/*
62*a66b28a3SJason King 	 * This tries to find the suffix S(n) such that
63*a66b28a3SJason King 	 * S(n) <= n < S(n+1), where S(n) = 2^(n*10) | 10^(3*n)
64*a66b28a3SJason King 	 * (i.e. 1024/1000, 1,048,576/1,000,000, etc).  Stop once S(n)
65*a66b28a3SJason King 	 * is the largest prefix supported (i.e. don't bother computing
66*a66b28a3SJason King 	 * and checking S(n+1).  Since INDEX_MAX should be the largest
67*a66b28a3SJason King 	 * suffix that fits (currently an exabyte), S(INDEX_MAX + 1) is
68*a66b28a3SJason King 	 * never checked as it would overflow.
69*a66b28a3SJason King 	 */
70*a66b28a3SJason King 	while (index < INDEX_MAX) {
71*a66b28a3SJason King 		uint64_t newdiv = divisor * divamt;
72*a66b28a3SJason King 
73*a66b28a3SJason King 		/* CTASSERT() guarantee these never trip */
74*a66b28a3SJason King 		VERIFY3U(newdiv, >=, divamt);
75*a66b28a3SJason King 		VERIFY3U(newdiv, >=, divisor);
76*a66b28a3SJason King 
77*a66b28a3SJason King 		if (n < newdiv)
78*a66b28a3SJason King 			break;
79*a66b28a3SJason King 
80*a66b28a3SJason King 		divisor = newdiv;
81*a66b28a3SJason King 		index++;
82*a66b28a3SJason King 	}
83*a66b28a3SJason King 
84*a66b28a3SJason King 	u = " KMGTPE"[index];
85*a66b28a3SJason King 
86*a66b28a3SJason King 	if (index == 0) {
87*a66b28a3SJason King 		rc = snprintf(buf, buflen, "%llu", n);
88*a66b28a3SJason King 	} else if (n % divisor == 0) {
89*a66b28a3SJason King 		/*
90*a66b28a3SJason King 		 * If this is an even multiple of the base, always display
91*a66b28a3SJason King 		 * without any decimal precision.
92*a66b28a3SJason King 		 */
93*a66b28a3SJason King 		rc = snprintf(buf, buflen, "%llu%c", n / divisor, u);
94*a66b28a3SJason King 	} else {
95*a66b28a3SJason King 		/*
96*a66b28a3SJason King 		 * We want to choose a precision that reflects the best choice
97*a66b28a3SJason King 		 * for fitting in 5 characters.  This can get rather tricky
98*a66b28a3SJason King 		 * when we have numbers that are very close to an order of
99*a66b28a3SJason King 		 * magnitude.  For example, when displaying 10239 (which is
100*a66b28a3SJason King 		 * really 9.999K), we want only a single place of precision
101*a66b28a3SJason King 		 * for 10.0K.  We could develop some complex heuristics for
102*a66b28a3SJason King 		 * this, but it's much easier just to try each combination
103*a66b28a3SJason King 		 * in turn.
104*a66b28a3SJason King 		 */
105*a66b28a3SJason King 		int i;
106*a66b28a3SJason King 		for (i = 2; i >= 0; i--) {
107*a66b28a3SJason King 			if ((rc = snprintf(buf, buflen, "%.*f%c", i,
108*a66b28a3SJason King 			    (double)n / divisor, u)) <= 5)
109*a66b28a3SJason King 				break;
110*a66b28a3SJason King 		}
111*a66b28a3SJason King 	}
112*a66b28a3SJason King 
113*a66b28a3SJason King 	if (rc + 1 > buflen || rc < 0)
114*a66b28a3SJason King 		goto overflow;
115*a66b28a3SJason King 
116*a66b28a3SJason King 	return;
117*a66b28a3SJason King 
118*a66b28a3SJason King overflow:
119*a66b28a3SJason King 	/* prefer a more verbose message if possible */
120*a66b28a3SJason King 	if (buflen > 10)
121*a66b28a3SJason King 		(void) strlcpy(buf, "<overflow>", buflen);
122*a66b28a3SJason King 	else
123*a66b28a3SJason King 		(void) strlcpy(buf, "??", buflen);
124*a66b28a3SJason King }
125*a66b28a3SJason King 
126*a66b28a3SJason King void
nicenum(uint64_t num,char * buf,size_t buflen)127*a66b28a3SJason King nicenum(uint64_t num, char *buf, size_t buflen)
128*a66b28a3SJason King {
129*a66b28a3SJason King 	nicenum_scale(num, 1, buf, buflen, 0);
130*a66b28a3SJason King }
131