1 /* $NetBSD: humanize_number.c,v 1.14 2008/04/28 20:22:59 martin Exp $ */ 2 3 /*- 4 * SPDX-License-Identifier: BSD-2-Clause 5 * 6 * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc. 7 * Copyright 2013 John-Mark Gurney <jmg@FreeBSD.org> 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to The NetBSD Foundation 11 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 12 * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions 16 * are met: 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 2. Redistributions in binary form must reproduce the above copyright 20 * notice, this list of conditions and the following disclaimer in the 21 * documentation and/or other materials provided with the distribution. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 24 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 26 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 27 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 * POSSIBILITY OF SUCH DAMAGE. 34 */ 35 36 #include <sys/types.h> 37 #include <assert.h> 38 #include <inttypes.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <locale.h> 43 #include <libutil.h> 44 45 static const int maxscale = 6; 46 47 int 48 humanize_number(char *buf, size_t len, int64_t quotient, 49 const char *suffix, int scale, int flags) 50 { 51 const char *prefixes, *sep; 52 int i, r, remainder, s1, s2, sign; 53 int divisordeccut; 54 int64_t divisor, max; 55 size_t baselen; 56 57 /* Since so many callers don't check -1, NUL terminate the buffer */ 58 if (len > 0) 59 buf[0] = '\0'; 60 61 /* validate args */ 62 if (buf == NULL || suffix == NULL) 63 return (-1); 64 if (scale < 0) 65 return (-1); 66 else if (scale > maxscale && 67 ((scale & ~(HN_AUTOSCALE|HN_GETSCALE)) != 0)) 68 return (-1); 69 if ((flags & HN_DIVISOR_1000) && (flags & HN_IEC_PREFIXES)) 70 return (-1); 71 72 /* setup parameters */ 73 remainder = 0; 74 75 if (flags & HN_IEC_PREFIXES) { 76 baselen = 2; 77 /* 78 * Use the prefixes for power of two recommended by 79 * the International Electrotechnical Commission 80 * (IEC) in IEC 80000-3 (i.e. Ki, Mi, Gi...). 81 * 82 * HN_IEC_PREFIXES implies a divisor of 1024 here 83 * (use of HN_DIVISOR_1000 would have triggered 84 * an assertion earlier). 85 */ 86 divisor = 1024; 87 divisordeccut = 973; /* ceil(.95 * 1024) */ 88 if (flags & HN_B) 89 prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei"; 90 else 91 prefixes = "\0\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei"; 92 } else { 93 baselen = 1; 94 if (flags & HN_DIVISOR_1000) { 95 divisor = 1000; 96 divisordeccut = 950; 97 if (flags & HN_B) 98 prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E"; 99 else 100 prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E"; 101 } else { 102 divisor = 1024; 103 divisordeccut = 973; /* ceil(.95 * 1024) */ 104 if (flags & HN_B) 105 prefixes = "B\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E"; 106 else 107 prefixes = "\0\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E"; 108 } 109 } 110 111 #define SCALE2PREFIX(scale) (&prefixes[(scale) * 3]) 112 113 if (quotient < 0) { 114 sign = -1; 115 quotient = -quotient; 116 baselen += 2; /* sign, digit */ 117 } else { 118 sign = 1; 119 baselen += 1; /* digit */ 120 } 121 if (flags & HN_NOSPACE) 122 sep = ""; 123 else { 124 sep = " "; 125 baselen++; 126 } 127 baselen += strlen(suffix); 128 129 /* Check if enough room for `x y' + suffix + `\0' */ 130 if (len < baselen + 1) 131 return (-1); 132 133 if (scale & (HN_AUTOSCALE | HN_GETSCALE)) { 134 /* See if there is additional columns can be used. */ 135 for (max = 1, i = len - baselen; i-- > 0;) 136 max *= 10; 137 138 /* 139 * Divide the number until it fits the given column. 140 * If there will be an overflow by the rounding below, 141 * divide once more. 142 */ 143 for (i = 0; 144 (quotient >= max || (quotient == max - 1 && 145 (remainder >= divisordeccut || remainder >= 146 divisor / 2))) && i < maxscale; i++) { 147 remainder = quotient % divisor; 148 quotient /= divisor; 149 } 150 151 if (scale & HN_GETSCALE) 152 return (i); 153 } else { 154 for (i = 0; i < scale && i < maxscale; i++) { 155 remainder = quotient % divisor; 156 quotient /= divisor; 157 } 158 } 159 160 /* If a value <= 9.9 after rounding and ... */ 161 /* 162 * XXX - should we make sure there is enough space for the decimal 163 * place and if not, don't do HN_DECIMAL? 164 */ 165 if (((quotient == 9 && remainder < divisordeccut) || quotient < 9) && 166 i > 0 && flags & HN_DECIMAL) { 167 s1 = (int)quotient + ((remainder * 10 + divisor / 2) / 168 divisor / 10); 169 s2 = ((remainder * 10 + divisor / 2) / divisor) % 10; 170 r = snprintf(buf, len, "%d%s%d%s%s%s", 171 sign * s1, localeconv()->decimal_point, s2, 172 sep, SCALE2PREFIX(i), suffix); 173 } else 174 r = snprintf(buf, len, "%" PRId64 "%s%s%s", 175 sign * (quotient + (remainder + divisor / 2) / divisor), 176 sep, SCALE2PREFIX(i), suffix); 177 178 return (r); 179 } 180