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