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