xref: /netbsd-src/sbin/nvmectl/humanize_bignum.c (revision 7f9c3b1be0b3a8ec8fdd9eed1f5219d65b874088)
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