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