1/* $NetBSD: humanize_bignum.c,v 1.1 2017/02/13 11:16:46 nonaka Exp $ */ 2/* NetBSD: humanize_number.c,v 1.16 2012/03/17 20:01:14 christos Exp */ 3 4/* 5 * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 10 * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34#include <sys/cdefs.h> 35 36#include <assert.h> 37#include <inttypes.h> 38#include <stdio.h> 39#include <stdlib.h> 40#include <string.h> 41#include <locale.h> 42 43#include "bn.h" 44 45static const BIGNUM * 46BN_value_5(void) 47{ 48 static mp_digit digit = 5UL; 49 static const BIGNUM bn = { &digit, 1, 1, 0 }; 50 return &bn; 51} 52 53static const BIGNUM * 54BN_value_10(void) 55{ 56 static mp_digit digit = 10UL; 57 static const BIGNUM bn = { &digit, 1, 1, 0 }; 58 return &bn; 59} 60 61static const BIGNUM * 62BN_value_50(void) 63{ 64 static mp_digit digit = 50UL; 65 static const BIGNUM bn = { &digit, 1, 1, 0 }; 66 return &bn; 67} 68 69static const BIGNUM * 70BN_value_100(void) 71{ 72 static mp_digit digit = 100UL; 73 static const BIGNUM bn = { &digit, 1, 1, 0 }; 74 return &bn; 75} 76 77static const BIGNUM * 78BN_value_995(void) 79{ 80 static mp_digit digit = 995UL; 81 static const BIGNUM bn = { &digit, 1, 1, 0 }; 82 return &bn; 83} 84 85static const BIGNUM * 86BN_value_1000(void) 87{ 88 static mp_digit digit = 1000UL; 89 static const BIGNUM bn = { &digit, 1, 1, 0 }; 90 return &bn; 91} 92 93static const BIGNUM * 94BN_value_1024(void) 95{ 96 static mp_digit digit = 1024UL; 97 static const BIGNUM bn = { &digit, 1, 1, 0 }; 98 return &bn; 99} 100 101int 102humanize_bignum(char *buf, size_t len, const BIGNUM *bytes, const char *suffix, 103 int scale, int flags) 104{ 105 const char *prefixes, *sep; 106 const BIGNUM *divisor, *post; 107 BIGNUM *nbytes = NULL, *max = NULL; 108 BIGNUM *t1 = NULL, *t2 = NULL; 109 int r, sign; 110 size_t i, baselen, maxscale; 111 char *p1, *p2; 112 113 if ((nbytes = BN_dup(bytes)) == NULL) 114 goto error; 115 116 post = BN_value_one(); 117 118 if (flags & HN_DIVISOR_1000) { 119 /* SI for decimal multiplies */ 120 divisor = BN_value_1000(); 121 if (flags & HN_B) 122 prefixes = "B\0k\0M\0G\0T\0P\0E\0Z\0Y"; 123 else 124 prefixes = "\0\0k\0M\0G\0T\0P\0E\0Z\0Y"; 125 } else { 126 /* 127 * binary multiplies 128 * XXX IEC 60027-2 recommends Ki, Mi, Gi... 129 */ 130 divisor = BN_value_1024(); 131 if (flags & HN_B) 132 prefixes = "B\0K\0M\0G\0T\0P\0E\0Z\0Y"; 133 else 134 prefixes = "\0\0K\0M\0G\0T\0P\0E\0Z\0Y"; 135 } 136 137#define SCALE2PREFIX(scale) (&prefixes[(scale) << 1]) 138 maxscale = 9; 139 140 if ((size_t)scale >= maxscale && 141 (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0) 142 goto error; 143 144 if (buf == NULL || suffix == NULL) 145 goto error; 146 147 if (len > 0) 148 buf[0] = '\0'; 149 if (BN_is_negative(nbytes)) { 150 sign = -1; 151 baselen = 3; /* sign, digit, prefix */ 152 BN_set_negative(nbytes, 0); 153 } else { 154 sign = 1; 155 baselen = 2; /* digit, prefix */ 156 } 157 if ((t1 = BN_new()) == NULL) 158 goto error; 159 BN_mul(t1, nbytes, BN_value_100(), NULL); 160 BN_swap(nbytes, t1); 161 162 if (flags & HN_NOSPACE) 163 sep = ""; 164 else { 165 sep = " "; 166 baselen++; 167 } 168 baselen += strlen(suffix); 169 170 /* Check if enough room for `x y' + suffix + `\0' */ 171 if (len < baselen + 1) 172 goto error; 173 174 if ((t2 = BN_new()) == NULL) 175 goto error; 176 177 if (scale & (HN_AUTOSCALE | HN_GETSCALE)) { 178 /* See if there is additional columns can be used. */ 179 if ((max = BN_new()) == NULL) 180 goto error; 181 BN_copy(max, BN_value_100()); 182 for (i = len - baselen; i-- > 0;) { 183 BN_mul(t1, max, BN_value_10(), NULL); 184 BN_swap(max, t1); 185 } 186 187 /* 188 * Divide the number until it fits the given column. 189 * If there will be an overflow by the rounding below, 190 * divide once more. 191 */ 192 if (BN_sub(t1, max, BN_value_50()) == 0) 193 goto error; 194 BN_swap(max, t1); 195 for (i = 0; BN_cmp(nbytes, max) >= 0 && i < maxscale; i++) { 196 if (BN_div(t1, t2, nbytes, divisor, NULL) == 0) 197 goto error; 198 BN_swap(nbytes, t1); 199 if (i == maxscale - 1) 200 break; 201 } 202 203 if (scale & HN_GETSCALE) { 204 r = (int)i; 205 goto out; 206 } 207 } else { 208 for (i = 0; i < (size_t)scale && i < maxscale; i++) { 209 if (BN_div(t1, t2, nbytes, divisor, NULL) == 0) 210 goto error; 211 BN_swap(nbytes, t1); 212 if (i == maxscale - 1) 213 break; 214 } 215 } 216 if (BN_mul(t1, nbytes, post, NULL) == 0) 217 goto error; 218 BN_swap(nbytes, t1); 219 220 /* If a value <= 9.9 after rounding and ... */ 221 if (BN_cmp(nbytes, __UNCONST(BN_value_995())) < 0 && 222 i > 0 && 223 (flags & HN_DECIMAL)) { 224 /* baselen + \0 + .N */ 225 if (len < baselen + 1 + 2) 226 return -1; 227 228 if (BN_add(t1, nbytes, BN_value_5()) == 0) 229 goto error; 230 BN_swap(nbytes, t1); 231 if (BN_div(t1, t2, nbytes, BN_value_10(), NULL) == 0) 232 goto error; 233 BN_swap(nbytes, t1); 234 if (BN_div(t1, t2, nbytes, BN_value_10(), NULL) == 0) 235 goto error; 236 237 if (sign == -1) 238 BN_set_negative(t1, 1); 239 p1 = BN_bn2dec(t1); 240 p2 = BN_bn2dec(t2); 241 if (p1 == NULL || p2 == NULL) { 242 free(p2); 243 free(p1); 244 goto error; 245 } 246 r = snprintf(buf, len, "%s%s%s%s%s%s", 247 p1, localeconv()->decimal_point, p2, 248 sep, SCALE2PREFIX(i), suffix); 249 free(p2); 250 free(p1); 251 } else { 252 if (BN_add(t1, nbytes, BN_value_50()) == 0) 253 goto error; 254 BN_swap(nbytes, t1); 255 if (BN_div(t1, t2, nbytes, BN_value_100(), NULL) == 0) 256 goto error; 257 BN_swap(nbytes, t1); 258 if (sign == -1) 259 BN_set_negative(nbytes, 1); 260 p1 = BN_bn2dec(nbytes); 261 if (p1 == NULL) 262 goto error; 263 r = snprintf(buf, len, "%s%s%s%s", 264 p1, sep, SCALE2PREFIX(i), suffix); 265 free(p1); 266 } 267 268out: 269 BN_free(t2); 270 BN_free(t1); 271 BN_free(max); 272 BN_free(nbytes); 273 return r; 274 275error: 276 r = -1; 277 goto out; 278} 279