humanize_number.c revision 335891
1101776Stjr/*	$NetBSD: humanize_number.c,v 1.14 2008/04/28 20:22:59 martin Exp $	*/
2132498Stjr
3101776Stjr/*
4101776Stjr * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
5227753Stheraven * Copyright 2013 John-Mark Gurney <jmg@FreeBSD.org>
6227753Stheraven * All rights reserved.
7227753Stheraven *
8227753Stheraven * This code is derived from software contributed to The NetBSD Foundation
9227753Stheraven * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
10101776Stjr * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
11101776Stjr *
12101776Stjr * Redistribution and use in source and binary forms, with or without
13101776Stjr * modification, are permitted provided that the following conditions
14101776Stjr * are met:
15101776Stjr * 1. Redistributions of source code must retain the above copyright
16101776Stjr *    notice, this list of conditions and the following disclaimer.
17101776Stjr * 2. Redistributions in binary form must reproduce the above copyright
18101776Stjr *    notice, this list of conditions and the following disclaimer in the
19101776Stjr *    documentation and/or other materials provided with the distribution.
20101776Stjr *
21101776Stjr * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22101776Stjr * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23101776Stjr * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24101776Stjr * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25101776Stjr * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26101776Stjr * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27101776Stjr * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28101776Stjr * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29101776Stjr * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30101776Stjr * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31101776Stjr * POSSIBILITY OF SUCH DAMAGE.
32101776Stjr */
33101776Stjr
34101776Stjr#include <sys/cdefs.h>
35101776Stjr__FBSDID("$FreeBSD: stable/10/lib/libutil/humanize_number.c 335891 2018-07-03 14:57:11Z robak $");
36101776Stjr
37101776Stjr#include <sys/types.h>
38132498Stjr#include <assert.h>
39101776Stjr#include <inttypes.h>
40101776Stjr#include <stdio.h>
41101776Stjr#include <stdlib.h>
42101776Stjr#include <string.h>
43132498Stjr#include <locale.h>
44101776Stjr#include <libutil.h>
45101776Stjr
46227753Stheravenstatic const int maxscale = 6;
47101776Stjr
48305577Sacheint
49321074Skibhumanize_number(char *buf, size_t len, int64_t quotient,
50132498Stjr    const char *suffix, int scale, int flags)
51132498Stjr{
52132498Stjr	const char *prefixes, *sep;
53227753Stheraven	int	i, r, remainder, s1, s2, sign;
54227753Stheraven	int	divisordeccut;
55101776Stjr	int64_t	divisor, max;
56321074Skib	size_t	baselen;
57103678Stjr
58101776Stjr	/* Since so many callers don't check -1, NUL terminate the buffer */
59103678Stjr	if (len > 0)
60305577Sache		buf[0] = '\0';
61103678Stjr
62103678Stjr	/* validate args */
63103678Stjr	if (buf == NULL || suffix == NULL)
64101776Stjr		return (-1);
65305577Sache	if (scale < 0)
66305577Sache		return (-1);
67305577Sache	else if (scale > maxscale &&
68305577Sache	    ((scale & ~(HN_AUTOSCALE|HN_GETSCALE)) != 0))
69132498Stjr		return (-1);
70305577Sache	if ((flags & HN_DIVISOR_1000) && (flags & HN_IEC_PREFIXES))
71132498Stjr		return (-1);
72305577Sache
73305577Sache	/* setup parameters */
74132498Stjr	remainder = 0;
75132498Stjr
76132498Stjr	if (flags & HN_IEC_PREFIXES) {
77227753Stheraven		baselen = 2;
78132498Stjr		/*
79178287Sjhb		 * Use the prefixes for power of two recommended by
80305577Sache		 * the International Electrotechnical Commission
81132498Stjr		 * (IEC) in IEC 80000-3 (i.e. Ki, Mi, Gi...).
82305577Sache		 *
83103678Stjr		 * HN_IEC_PREFIXES implies a divisor of 1024 here
84305577Sache		 * (use of HN_DIVISOR_1000 would have triggered
85132498Stjr		 * an assertion earlier).
86132498Stjr		 */
87132498Stjr		divisor = 1024;
88132498Stjr		divisordeccut = 973;	/* ceil(.95 * 1024) */
89132498Stjr		if (flags & HN_B)
90132498Stjr			prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
91132498Stjr		else
92132498Stjr			prefixes = "\0\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
93136092Sstefanf	} else {
94136092Sstefanf		baselen = 1;
95101776Stjr		if (flags & HN_DIVISOR_1000) {
96132498Stjr			divisor = 1000;
97132498Stjr			divisordeccut = 950;
98132498Stjr			if (flags & HN_B)
99132498Stjr				prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
100305577Sache			else
101305577Sache				prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
102305577Sache		} else {
103305577Sache			divisor = 1024;
104305577Sache			divisordeccut = 973;	/* ceil(.95 * 1024) */
105305577Sache			if (flags & HN_B)
106305577Sache				prefixes = "B\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
107305577Sache			else
108305577Sache				prefixes = "\0\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
109305577Sache		}
110305577Sache	}
111132498Stjr
112132498Stjr#define	SCALE2PREFIX(scale)	(&prefixes[(scale) * 3])
113132498Stjr
114305577Sache	if (quotient < 0) {
115199784Swollman		sign = -1;
116321074Skib		quotient = -quotient;
117321074Skib		baselen += 2;		/* sign, digit */
118321074Skib	} else {
119321074Skib		sign = 1;
120103678Stjr		baselen += 1;		/* digit */
121103678Stjr	}
122321074Skib	if (flags & HN_NOSPACE)
123321074Skib		sep = "";
124101776Stjr	else {
125305577Sache		sep = " ";
126227753Stheraven		baselen++;
127227753Stheraven	}
128227753Stheraven	baselen += strlen(suffix);
129227753Stheraven
130227753Stheraven	/* Check if enough room for `x y' + suffix + `\0' */
131	if (len < baselen + 1)
132		return (-1);
133
134	if (scale & (HN_AUTOSCALE | HN_GETSCALE)) {
135		/* See if there is additional columns can be used. */
136		for (max = 1, i = len - baselen; i-- > 0;)
137			max *= 10;
138
139		/*
140		 * Divide the number until it fits the given column.
141		 * If there will be an overflow by the rounding below,
142		 * divide once more.
143		 */
144		for (i = 0;
145		    (quotient >= max || (quotient == max - 1 &&
146		    (remainder >= divisordeccut || remainder >=
147		    divisor / 2))) && i < maxscale; i++) {
148			remainder = quotient % divisor;
149			quotient /= divisor;
150		}
151
152		if (scale & HN_GETSCALE)
153			return (i);
154	} else {
155		for (i = 0; i < scale && i < maxscale; i++) {
156			remainder = quotient % divisor;
157			quotient /= divisor;
158		}
159	}
160
161	/* If a value <= 9.9 after rounding and ... */
162	/*
163	 * XXX - should we make sure there is enough space for the decimal
164	 * place and if not, don't do HN_DECIMAL?
165	 */
166	if (((quotient == 9 && remainder < divisordeccut) || quotient < 9) &&
167	    i > 0 && flags & HN_DECIMAL) {
168		s1 = (int)quotient + ((remainder * 10 + divisor / 2) /
169		    divisor / 10);
170		s2 = ((remainder * 10 + divisor / 2) / divisor) % 10;
171		r = snprintf(buf, len, "%d%s%d%s%s%s",
172		    sign * s1, localeconv()->decimal_point, s2,
173		    sep, SCALE2PREFIX(i), suffix);
174	} else
175		r = snprintf(buf, len, "%" PRId64 "%s%s%s",
176		    sign * (quotient + (remainder + divisor / 2) / divisor),
177		    sep, SCALE2PREFIX(i), suffix);
178
179	return (r);
180}
181