Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/compat/humanize_number.c
2065 views
1
/* $NetBSD: humanize_number.c,v 1.14 2008/04/28 20:22:59 martin Exp $ */
2
3
/*-
4
* SPDX-License-Identifier: BSD-2-Clause
5
*
6
* Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
7
* Copyright 2013 John-Mark Gurney <[email protected]>
8
* All rights reserved.
9
*
10
* This code is derived from software contributed to The NetBSD Foundation
11
* by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
12
* NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
13
*
14
* Redistribution and use in source and binary forms, with or without
15
* modification, are permitted provided that the following conditions
16
* are met:
17
* 1. Redistributions of source code must retain the above copyright
18
* notice, this list of conditions and the following disclaimer.
19
* 2. Redistributions in binary form must reproduce the above copyright
20
* notice, this list of conditions and the following disclaimer in the
21
* documentation and/or other materials provided with the distribution.
22
*
23
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
* POSSIBILITY OF SUCH DAMAGE.
34
*/
35
36
#include "bsd_compat.h"
37
38
#if !HAVE_HUMANIZE_NUMBER
39
40
#include <sys/types.h>
41
#include <assert.h>
42
#include <inttypes.h>
43
#include <stdio.h>
44
#include <stdlib.h>
45
#include <string.h>
46
#include <locale.h>
47
48
#include "humanize_number.h"
49
50
static const int maxscale = 6;
51
52
int
53
humanize_number(char *buf, size_t len, int64_t quotient,
54
const char *suffix, int scale, int flags)
55
{
56
const char *prefixes, *sep;
57
int i, r, remainder, s1, s2, sign;
58
int divisordeccut;
59
int64_t divisor, max;
60
size_t baselen;
61
62
/* Since so many callers don't check -1, NUL terminate the buffer */
63
if (len > 0)
64
buf[0] = '\0';
65
66
/* validate args */
67
if (buf == NULL || suffix == NULL)
68
return (-1);
69
if (scale < 0)
70
return (-1);
71
else if (scale > maxscale &&
72
((scale & ~(HN_AUTOSCALE|HN_GETSCALE)) != 0))
73
return (-1);
74
if ((flags & HN_DIVISOR_1000) && (flags & HN_IEC_PREFIXES))
75
return (-1);
76
77
/* setup parameters */
78
remainder = 0;
79
80
if (flags & HN_IEC_PREFIXES) {
81
baselen = 2;
82
/*
83
* Use the prefixes for power of two recommended by
84
* the International Electrotechnical Commission
85
* (IEC) in IEC 80000-3 (i.e. Ki, Mi, Gi...).
86
*
87
* HN_IEC_PREFIXES implies a divisor of 1024 here
88
* (use of HN_DIVISOR_1000 would have triggered
89
* an assertion earlier).
90
*/
91
divisor = 1024;
92
divisordeccut = 973; /* ceil(.95 * 1024) */
93
if (flags & HN_B)
94
prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
95
else
96
prefixes = "\0\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
97
} else {
98
baselen = 1;
99
if (flags & HN_DIVISOR_1000) {
100
divisor = 1000;
101
divisordeccut = 950;
102
if (flags & HN_B)
103
prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
104
else
105
prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
106
} else {
107
divisor = 1024;
108
divisordeccut = 973; /* ceil(.95 * 1024) */
109
if (flags & HN_B)
110
prefixes = "B\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
111
else
112
prefixes = "\0\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
113
}
114
}
115
116
#define SCALE2PREFIX(scale) (&prefixes[(scale) * 3])
117
118
if (quotient < 0) {
119
sign = -1;
120
quotient = -quotient;
121
baselen += 2; /* sign, digit */
122
} else {
123
sign = 1;
124
baselen += 1; /* digit */
125
}
126
if (flags & HN_NOSPACE)
127
sep = "";
128
else {
129
sep = " ";
130
baselen++;
131
}
132
baselen += strlen(suffix);
133
134
/* Check if enough room for `x y' + suffix + `\0' */
135
if (len < baselen + 1)
136
return (-1);
137
138
if (scale & (HN_AUTOSCALE | HN_GETSCALE)) {
139
/* See if there is additional columns can be used. */
140
for (max = 1, i = len - baselen; i-- > 0;)
141
max *= 10;
142
143
/*
144
* Divide the number until it fits the given column.
145
* If there will be an overflow by the rounding below,
146
* divide once more.
147
*/
148
for (i = 0;
149
(quotient >= max || (quotient == max - 1 &&
150
(remainder >= divisordeccut || remainder >=
151
divisor / 2))) && i < maxscale; i++) {
152
remainder = quotient % divisor;
153
quotient /= divisor;
154
}
155
156
if (scale & HN_GETSCALE)
157
return (i);
158
} else {
159
for (i = 0; i < scale && i < maxscale; i++) {
160
remainder = quotient % divisor;
161
quotient /= divisor;
162
}
163
}
164
165
/* If a value <= 9.9 after rounding and ... */
166
/*
167
* XXX - should we make sure there is enough space for the decimal
168
* place and if not, don't do HN_DECIMAL?
169
*/
170
if (((quotient == 9 && remainder < divisordeccut) || quotient < 9) &&
171
i > 0 && flags & HN_DECIMAL) {
172
s1 = (int)quotient + ((remainder * 10 + divisor / 2) /
173
divisor / 10);
174
s2 = ((remainder * 10 + divisor / 2) / divisor) % 10;
175
r = snprintf(buf, len, "%d%s%d%s%s%s",
176
sign * s1, localeconv()->decimal_point, s2,
177
sep, SCALE2PREFIX(i), suffix);
178
} else
179
r = snprintf(buf, len, "%" PRId64 "%s%s%s",
180
sign * (quotient + (remainder + divisor / 2) / divisor),
181
sep, SCALE2PREFIX(i), suffix);
182
183
return (r);
184
}
185
#endif
186
187