/* $NetBSD: humanize_bignum.c,v 1.1 2017/02/13 11:16:46 nonaka Exp $ */ /* NetBSD: humanize_number.c,v 1.16 2012/03/17 20:01:14 christos Exp */ /* * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include "bn.h" static const BIGNUM * BN_value_5(void) { static mp_digit digit = 5UL; static const BIGNUM bn = { &digit, 1, 1, 0 }; return &bn; } static const BIGNUM * BN_value_10(void) { static mp_digit digit = 10UL; static const BIGNUM bn = { &digit, 1, 1, 0 }; return &bn; } static const BIGNUM * BN_value_50(void) { static mp_digit digit = 50UL; static const BIGNUM bn = { &digit, 1, 1, 0 }; return &bn; } static const BIGNUM * BN_value_100(void) { static mp_digit digit = 100UL; static const BIGNUM bn = { &digit, 1, 1, 0 }; return &bn; } static const BIGNUM * BN_value_995(void) { static mp_digit digit = 995UL; static const BIGNUM bn = { &digit, 1, 1, 0 }; return &bn; } static const BIGNUM * BN_value_1000(void) { static mp_digit digit = 1000UL; static const BIGNUM bn = { &digit, 1, 1, 0 }; return &bn; } static const BIGNUM * BN_value_1024(void) { static mp_digit digit = 1024UL; static const BIGNUM bn = { &digit, 1, 1, 0 }; return &bn; } int humanize_bignum(char *buf, size_t len, const BIGNUM *bytes, const char *suffix, int scale, int flags) { const char *prefixes, *sep; const BIGNUM *divisor, *post; BIGNUM *nbytes = NULL, *max = NULL; BIGNUM *t1 = NULL, *t2 = NULL; int r, sign; size_t i, baselen, maxscale; char *p1, *p2; if ((nbytes = BN_dup(bytes)) == NULL) goto error; post = BN_value_one(); if (flags & HN_DIVISOR_1000) { /* SI for decimal multiplies */ divisor = BN_value_1000(); if (flags & HN_B) prefixes = "B\0k\0M\0G\0T\0P\0E\0Z\0Y"; else prefixes = "\0\0k\0M\0G\0T\0P\0E\0Z\0Y"; } else { /* * binary multiplies * XXX IEC 60027-2 recommends Ki, Mi, Gi... */ divisor = BN_value_1024(); if (flags & HN_B) prefixes = "B\0K\0M\0G\0T\0P\0E\0Z\0Y"; else prefixes = "\0\0K\0M\0G\0T\0P\0E\0Z\0Y"; } #define SCALE2PREFIX(scale) (&prefixes[(scale) << 1]) maxscale = 9; if ((size_t)scale >= maxscale && (scale & (HN_AUTOSCALE | HN_GETSCALE)) == 0) goto error; if (buf == NULL || suffix == NULL) goto error; if (len > 0) buf[0] = '\0'; if (BN_is_negative(nbytes)) { sign = -1; baselen = 3; /* sign, digit, prefix */ BN_set_negative(nbytes, 0); } else { sign = 1; baselen = 2; /* digit, prefix */ } if ((t1 = BN_new()) == NULL) goto error; BN_mul(t1, nbytes, BN_value_100(), NULL); BN_swap(nbytes, t1); if (flags & HN_NOSPACE) sep = ""; else { sep = " "; baselen++; } baselen += strlen(suffix); /* Check if enough room for `x y' + suffix + `\0' */ if (len < baselen + 1) goto error; if ((t2 = BN_new()) == NULL) goto error; if (scale & (HN_AUTOSCALE | HN_GETSCALE)) { /* See if there is additional columns can be used. */ if ((max = BN_new()) == NULL) goto error; BN_copy(max, BN_value_100()); for (i = len - baselen; i-- > 0;) { BN_mul(t1, max, BN_value_10(), NULL); BN_swap(max, t1); } /* * Divide the number until it fits the given column. * If there will be an overflow by the rounding below, * divide once more. */ if (BN_sub(t1, max, BN_value_50()) == 0) goto error; BN_swap(max, t1); for (i = 0; BN_cmp(nbytes, max) >= 0 && i < maxscale; i++) { if (BN_div(t1, t2, nbytes, divisor, NULL) == 0) goto error; BN_swap(nbytes, t1); if (i == maxscale - 1) break; } if (scale & HN_GETSCALE) { r = (int)i; goto out; } } else { for (i = 0; i < (size_t)scale && i < maxscale; i++) { if (BN_div(t1, t2, nbytes, divisor, NULL) == 0) goto error; BN_swap(nbytes, t1); if (i == maxscale - 1) break; } } if (BN_mul(t1, nbytes, post, NULL) == 0) goto error; BN_swap(nbytes, t1); /* If a value <= 9.9 after rounding and ... */ if (BN_cmp(nbytes, __UNCONST(BN_value_995())) < 0 && i > 0 && (flags & HN_DECIMAL)) { /* baselen + \0 + .N */ if (len < baselen + 1 + 2) return -1; if (BN_add(t1, nbytes, BN_value_5()) == 0) goto error; BN_swap(nbytes, t1); if (BN_div(t1, t2, nbytes, BN_value_10(), NULL) == 0) goto error; BN_swap(nbytes, t1); if (BN_div(t1, t2, nbytes, BN_value_10(), NULL) == 0) goto error; if (sign == -1) BN_set_negative(t1, 1); p1 = BN_bn2dec(t1); p2 = BN_bn2dec(t2); if (p1 == NULL || p2 == NULL) { free(p2); free(p1); goto error; } r = snprintf(buf, len, "%s%s%s%s%s%s", p1, localeconv()->decimal_point, p2, sep, SCALE2PREFIX(i), suffix); free(p2); free(p1); } else { if (BN_add(t1, nbytes, BN_value_50()) == 0) goto error; BN_swap(nbytes, t1); if (BN_div(t1, t2, nbytes, BN_value_100(), NULL) == 0) goto error; BN_swap(nbytes, t1); if (sign == -1) BN_set_negative(nbytes, 1); p1 = BN_bn2dec(nbytes); if (p1 == NULL) goto error; r = snprintf(buf, len, "%s%s%s%s", p1, sep, SCALE2PREFIX(i), suffix); free(p1); } out: BN_free(t2); BN_free(t1); BN_free(max); BN_free(nbytes); return r; error: r = -1; goto out; }