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 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 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