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
45 static const BIGNUM *
BN_value_5(void)46 BN_value_5(void)
47 {
48 static mp_digit digit = 5UL;
49 static const BIGNUM bn = { &digit, 1, 1, 0 };
50 return &bn;
51 }
52
53 static const BIGNUM *
BN_value_10(void)54 BN_value_10(void)
55 {
56 static mp_digit digit = 10UL;
57 static const BIGNUM bn = { &digit, 1, 1, 0 };
58 return &bn;
59 }
60
61 static const BIGNUM *
BN_value_50(void)62 BN_value_50(void)
63 {
64 static mp_digit digit = 50UL;
65 static const BIGNUM bn = { &digit, 1, 1, 0 };
66 return &bn;
67 }
68
69 static const BIGNUM *
BN_value_100(void)70 BN_value_100(void)
71 {
72 static mp_digit digit = 100UL;
73 static const BIGNUM bn = { &digit, 1, 1, 0 };
74 return &bn;
75 }
76
77 static const BIGNUM *
BN_value_995(void)78 BN_value_995(void)
79 {
80 static mp_digit digit = 995UL;
81 static const BIGNUM bn = { &digit, 1, 1, 0 };
82 return &bn;
83 }
84
85 static const BIGNUM *
BN_value_1000(void)86 BN_value_1000(void)
87 {
88 static mp_digit digit = 1000UL;
89 static const BIGNUM bn = { &digit, 1, 1, 0 };
90 return &bn;
91 }
92
93 static const BIGNUM *
BN_value_1024(void)94 BN_value_1024(void)
95 {
96 static mp_digit digit = 1024UL;
97 static const BIGNUM bn = { &digit, 1, 1, 0 };
98 return &bn;
99 }
100
101 int
humanize_bignum(char * buf,size_t len,const BIGNUM * bytes,const char * suffix,int scale,int flags)102 humanize_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
268 out:
269 BN_free(t2);
270 BN_free(t1);
271 BN_free(max);
272 BN_free(nbytes);
273 return r;
274
275 error:
276 r = -1;
277 goto out;
278 }
279