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 */ 26 27 #include <stdio.h> 28 #include <string.h> 29 #include <sys/types.h> 30 #include <sys/debug.h> 31 #include "libcmdutils.h" 32 33 /* The largest suffix that can fit, aka an exabyte (2^60 / 10^18) */ 34 #define INDEX_MAX (6) 35 36 /* Verify INDEX_MAX fits */ 37 CTASSERT(INDEX_MAX * 10 < sizeof (uint64_t) * 8); 38 39 void 40 nicenum_scale(uint64_t n, size_t units, char *buf, size_t buflen, 41 uint32_t flags) 42 { 43 uint64_t divamt = 1024; 44 uint64_t divisor = 1; 45 int index = 0; 46 int rc = 0; 47 char u; 48 49 if (units == 0) 50 units = 1; 51 52 if (n > 0) { 53 n *= units; 54 if (n < units) 55 goto overflow; 56 } 57 58 if (flags & NN_DIVISOR_1000) 59 divamt = 1000; 60 61 /* 62 * This tries to find the suffix S(n) such that 63 * S(n) <= n < S(n+1), where S(n) = 2^(n*10) | 10^(3*n) 64 * (i.e. 1024/1000, 1,048,576/1,000,000, etc). Stop once S(n) 65 * is the largest prefix supported (i.e. don't bother computing 66 * and checking S(n+1). Since INDEX_MAX should be the largest 67 * suffix that fits (currently an exabyte), S(INDEX_MAX + 1) is 68 * never checked as it would overflow. 69 */ 70 while (index < INDEX_MAX) { 71 uint64_t newdiv = divisor * divamt; 72 73 /* CTASSERT() guarantee these never trip */ 74 VERIFY3U(newdiv, >=, divamt); 75 VERIFY3U(newdiv, >=, divisor); 76 77 if (n < newdiv) 78 break; 79 80 divisor = newdiv; 81 index++; 82 } 83 84 u = " KMGTPE"[index]; 85 86 if (index == 0) { 87 rc = snprintf(buf, buflen, "%llu", n); 88 } else if (n % divisor == 0) { 89 /* 90 * If this is an even multiple of the base, always display 91 * without any decimal precision. 92 */ 93 rc = snprintf(buf, buflen, "%llu%c", n / divisor, u); 94 } else { 95 /* 96 * We want to choose a precision that reflects the best choice 97 * for fitting in 5 characters. This can get rather tricky 98 * when we have numbers that are very close to an order of 99 * magnitude. For example, when displaying 10239 (which is 100 * really 9.999K), we want only a single place of precision 101 * for 10.0K. We could develop some complex heuristics for 102 * this, but it's much easier just to try each combination 103 * in turn. 104 */ 105 int i; 106 for (i = 2; i >= 0; i--) { 107 if ((rc = snprintf(buf, buflen, "%.*f%c", i, 108 (double)n / divisor, u)) <= 5) 109 break; 110 } 111 } 112 113 if (rc + 1 > buflen || rc < 0) 114 goto overflow; 115 116 return; 117 118 overflow: 119 /* prefer a more verbose message if possible */ 120 if (buflen > 10) 121 (void) strlcpy(buf, "<overflow>", buflen); 122 else 123 (void) strlcpy(buf, "??", buflen); 124 } 125 126 void 127 nicenum(uint64_t num, char *buf, size_t buflen) 128 { 129 nicenum_scale(num, 1, buf, buflen, 0); 130 } 131