1 /*-
2 * Copyright 2012 Clifton Royston
3 * Copyright 2013 John-Mark Gurney
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 */
28
29 #include <sys/param.h>
30 #include <inttypes.h>
31 #include <libutil.h>
32 #include <limits.h>
33 #include <math.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 #define MAX_STR_FLAGS_RESULT 80
40 #define MAX_INT_STR_DIGITS 12
41
42 static const int64_t halfExabyte = (int64_t)500*1000*1000*1000*1000*1000L;
43
44 static struct {
45 int retval;
46 const char *res;
47 int64_t num;
48 int flags;
49 int scale;
50 size_t buflen;
51 } test_args[] = {
52 /* tests 0-13 test 1000 suffixes */
53 { 2, "0 ", (int64_t)0L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
54 { 3, "1 k", (int64_t)500L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
55 { 3, "1 M", (int64_t)500*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
56 { 3, "1 G", (int64_t)500*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
57 { 3, "1 T", (int64_t)500*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
58 { 3, "1 P", (int64_t)500*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
59 { 3, "1 E", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
60 { 2, "1 ", (int64_t)1L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
61 { 3, "2 k", (int64_t)1500L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
62 { 3, "2 M", (int64_t)1500*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
63 { 3, "2 G", (int64_t)1500*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
64 { 3, "2 T", (int64_t)1500*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
65 { 3, "2 P", (int64_t)1500*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
66 { 3, "2 E", (int64_t)1500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
67
68 /* tests 14-27 test 1024 suffixes */
69 { 2, "0 ", (int64_t)0L, 0, HN_AUTOSCALE, 4 },
70 { 3, "1 K", (int64_t)512L, 0, HN_AUTOSCALE, 4 },
71 { 3, "1 M", (int64_t)512*1024L, 0, HN_AUTOSCALE, 4 },
72 { 3, "1 G", (int64_t)512*1024*1024L, 0, HN_AUTOSCALE, 4 },
73 { 3, "1 T", (int64_t)512*1024*1024*1024L, 0, HN_AUTOSCALE, 4 },
74 { 3, "1 P", (int64_t)512*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 4 },
75 { 3, "1 E", (int64_t)512*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 4 },
76 { 2, "1 ", (int64_t)1L, 0, HN_AUTOSCALE, 4 },
77 { 3, "2 K", (int64_t)1536L, 0, HN_AUTOSCALE, 4 },
78 { 3, "2 M", (int64_t)1536*1024L, 0, HN_AUTOSCALE, 4 },
79 { 3, "2 G", (int64_t)1536*1024*1024L, 0, HN_AUTOSCALE, 4 },
80 { 3, "2 T", (int64_t)1536*1024*1024*1024L, 0, HN_AUTOSCALE, 4 },
81 { 3, "2 P", (int64_t)1536*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 4 },
82 { 3, "2 E", (int64_t)1536*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 4 },
83
84 /* tests 28-37 test rounding */
85 { 3, "0 M", (int64_t)500*1000L-1, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
86 { 3, "1 M", (int64_t)500*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
87 { 3, "1 M", (int64_t)1000*1000L + 500*1000L-1, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
88 { 3, "2 M", (int64_t)1000*1000L + 500*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 4 },
89 { 3, "0 K", (int64_t)512L-1, 0, HN_AUTOSCALE, 4 },
90 { 3, "1 K", (int64_t)512L, 0, HN_AUTOSCALE, 4 },
91 { 3, "0 M", (int64_t)512*1024L-1, 0, HN_AUTOSCALE, 4 },
92 { 3, "1 M", (int64_t)512*1024L, 0, HN_AUTOSCALE, 4 },
93 { 3, "1 M", (int64_t)1024*1024L + 512*1024L-1, 0, HN_AUTOSCALE, 4 },
94 { 3, "2 M", (int64_t)1024*1024L + 512*1024L, 0, HN_AUTOSCALE, 4 },
95
96 /* tests 38-61 test specific scale factors with 1000 divisor */
97 { 3, "0 k", (int64_t)0L, HN_DIVISOR_1000, 1, 4 },
98 { 3, "1 k", (int64_t)500L, HN_DIVISOR_1000, 1, 4 },
99 { 3, "0 M", (int64_t)500L, HN_DIVISOR_1000, 2, 4 },
100 { 3, "1 M", (int64_t)500*1000L, HN_DIVISOR_1000, 2, 4 },
101 { 3, "0 G", (int64_t)500*1000L, HN_DIVISOR_1000, 3, 4 },
102 { 3, "1 G", (int64_t)500*1000*1000L, HN_DIVISOR_1000, 3, 4 },
103 { 3, "0 T", (int64_t)500*1000*1000L, HN_DIVISOR_1000, 4, 4 },
104 { 3, "1 T", (int64_t)500*1000*1000*1000L, HN_DIVISOR_1000, 4, 4 },
105 { 3, "0 P", (int64_t)500*1000*1000*1000L, HN_DIVISOR_1000, 5, 4 },
106 { 3, "1 P", (int64_t)500*1000*1000*1000*1000L, HN_DIVISOR_1000, 5, 4 },
107 { 3, "0 E", (int64_t)500*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 4 },
108 { 3, "1 E", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 4 },
109 { 3, "0 k", (int64_t)1L, HN_DIVISOR_1000, 1, 4 },
110 { 3, "2 k", (int64_t)1500L, HN_DIVISOR_1000, 1, 4 },
111 { 3, "0 M", (int64_t)1500L, HN_DIVISOR_1000, 2, 4 },
112 { 3, "2 M", (int64_t)1500*1000L, HN_DIVISOR_1000, 2, 4 },
113 { 3, "0 G", (int64_t)1500*1000L, HN_DIVISOR_1000, 3, 4 },
114 { 3, "2 G", (int64_t)1500*1000*1000L, HN_DIVISOR_1000, 3, 4 },
115 { 3, "0 T", (int64_t)1500*1000*1000L, HN_DIVISOR_1000, 4, 4 },
116 { 3, "2 T", (int64_t)1500*1000*1000*1000L, HN_DIVISOR_1000, 4, 4 },
117 { 3, "0 P", (int64_t)1500*1000*1000*1000L, HN_DIVISOR_1000, 5, 4 },
118 { 3, "2 P", (int64_t)1500*1000*1000*1000*1000L, HN_DIVISOR_1000, 5, 4 },
119 { 3, "0 E", (int64_t)1500*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 4 },
120 { 3, "2 E", (int64_t)1500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 4 },
121
122 /* tests 62-85 test specific scale factors with 1024 divisor */
123 { 3, "0 K", (int64_t)0L, 0, 1, 4 },
124 { 3, "1 K", (int64_t)512L, 0, 1, 4 },
125 { 3, "0 M", (int64_t)512L, 0, 2, 4 },
126 { 3, "1 M", (int64_t)512*1024L, 0, 2, 4 },
127 { 3, "0 G", (int64_t)512*1024L, 0, 3, 4 },
128 { 3, "1 G", (int64_t)512*1024*1024L, 0, 3, 4 },
129 { 3, "0 T", (int64_t)512*1024*1024L, 0, 4, 4 },
130 { 3, "1 T", (int64_t)512*1024*1024*1024L, 0, 4, 4 },
131 { 3, "0 P", (int64_t)512*1024*1024*1024L, 0, 5, 4 },
132 { 3, "1 P", (int64_t)512*1024*1024*1024*1024L, 0, 5, 4 },
133 { 3, "0 E", (int64_t)512*1024*1024*1024*1024L, 0, 6, 4 },
134 { 3, "1 E", (int64_t)512*1024*1024*1024*1024*1024L, 0, 6, 4 },
135 { 3, "0 K", (int64_t)1L, 0, 1, 4 },
136 { 3, "2 K", (int64_t)1536L, 0, 1, 4 },
137 { 3, "0 M", (int64_t)1536L, 0, 2, 4 },
138 { 3, "2 M", (int64_t)1536*1024L, 0, 2, 4 },
139 { 3, "0 G", (int64_t)1536*1024L, 0, 3, 4 },
140 { 3, "2 G", (int64_t)1536*1024*1024L, 0, 3, 4 },
141 { 3, "0 T", (int64_t)1536*1024*1024L, 0, 4, 4 },
142 { 3, "2 T", (int64_t)1536*1024*1024*1024L, 0, 4, 4 },
143 { 3, "0 P", (int64_t)1536*1024*1024*1024L, 0, 5, 4 },
144 { 3, "2 P", (int64_t)1536*1024*1024*1024*1024L, 0, 5, 4 },
145 { 3, "0 E", (int64_t)1536*1024*1024*1024*1024L, 0, 6, 4 },
146 { 3, "2 E", (int64_t)1536*1024*1024*1024*1024*1024L, 0, 6, 4 },
147
148 /* tests 86-99 test invalid specific scale values of < 0 or >= 7 with
149 and without HN_DIVISOR_1000 set */
150 /* all should return errors with new code; with old, the latter 3
151 are instead processed as if having AUTOSCALE and/or GETSCALE set */
152 { -1, "", (int64_t)1L, 0, 7, 4 },
153 { -1, "", (int64_t)1L, HN_DIVISOR_1000, 7, 4 },
154 { -1, "", (int64_t)1L, 0, 1000, 4 },
155 { -1, "", (int64_t)1L, HN_DIVISOR_1000, 1000, 4 },
156 { -1, "", (int64_t)0L, 0, 1000*1000, 4 },
157 { -1, "", (int64_t)0L, HN_DIVISOR_1000, 1000*1000, 4 },
158 { -1, "", (int64_t)0L, 0, INT_MAX, 4 },
159 { -1, "", (int64_t)0L, HN_DIVISOR_1000, INT_MAX, 4 },
160
161 /* Negative scale values are not handled well
162 by the existing library routine - should report as error */
163 /* all should return errors with new code, fail assertion with old */
164
165 { -1, "", (int64_t)1L, 0, -1, 4 },
166 { -1, "", (int64_t)1L, HN_DIVISOR_1000, -1, 4 },
167 { -1, "", (int64_t)1L, 0, -1000, 4 },
168 { -1, "", (int64_t)1L, HN_DIVISOR_1000, -1000, 4 },
169
170 /* __INT_MIN doesn't print properly, skipped. */
171
172 { -1, "", (int64_t)1L, 0, -__INT_MAX, 4 },
173 { -1, "", (int64_t)1L, HN_DIVISOR_1000, -__INT_MAX, 4 },
174
175
176 /* tests for scale == 0, without autoscale */
177 /* tests 100-114 test scale 0 with 1000 divisor - print first N digits */
178 { 2, "0 ", (int64_t)0L, HN_DIVISOR_1000, 0, 4 },
179 { 2, "1 ", (int64_t)1L, HN_DIVISOR_1000, 0, 4 },
180 { 3, "10 ", (int64_t)10L, HN_DIVISOR_1000, 0, 4 },
181 { 3, "0 M", (int64_t)150L, HN_DIVISOR_1000, HN_NOSPACE, 4 },
182 { 3, "0 M", (int64_t)500L, HN_DIVISOR_1000, HN_NOSPACE, 4 },
183 { 3, "0 M", (int64_t)999L, HN_DIVISOR_1000, HN_NOSPACE, 4 },
184 { 4, "150", (int64_t)150L, HN_DIVISOR_1000, 0, 4 },
185 { 4, "500", (int64_t)500L, HN_DIVISOR_1000, 0, 4 },
186 { 4, "999", (int64_t)999L, HN_DIVISOR_1000, 0, 4 },
187 { 5, "100", (int64_t)1000L, HN_DIVISOR_1000, 0, 4 },
188 { 5, "150", (int64_t)1500L, HN_DIVISOR_1000, 0, 4 },
189 { 7, "500", (int64_t)500*1000L, HN_DIVISOR_1000, 0, 4 },
190 { 8, "150", (int64_t)1500*1000L, HN_DIVISOR_1000, 0, 4 },
191 { 10, "500", (int64_t)500*1000*1000L, HN_DIVISOR_1000, 0, 4 },
192 { 11, "150", (int64_t)1500*1000*1000L, HN_DIVISOR_1000, 0, 4 },
193
194 /* tests 115-126 test scale 0 with 1024 divisor - print first N digits */
195 { 2, "0 ", (int64_t)0L, 0, 0, 4 },
196 { 2, "1 ", (int64_t)1L, 0, 0, 4 },
197 { 3, "10 ", (int64_t)10L, 0, 0, 4 },
198 { 4, "150", (int64_t)150L, 0, 0, 4 },
199 { 4, "500", (int64_t)500L, 0, 0, 4 },
200 { 4, "999", (int64_t)999L, 0, 0, 4 },
201 { 5, "100", (int64_t)1000L, 0, 0, 4 },
202 { 5, "150", (int64_t)1500L, 0, 0, 4 },
203 { 7, "500", (int64_t)500*1000L, 0, 0, 4 },
204 { 8, "150", (int64_t)1500*1000L, 0, 0, 4 },
205 { 10, "500", (int64_t)500*1000*1000L, 0, 0, 4 },
206 { 11, "150", (int64_t)1500*1000*1000L, 0, 0, 4 },
207
208 /* Test case for rounding of edge numbers around 999.5+, see PR224498.
209 * Require buflen >= 5 */
210 { 4, "1.0M", (int64_t)1023500, HN_DECIMAL|HN_B|HN_NOSPACE, HN_AUTOSCALE, 5 },
211
212 /* Test boundary cases for very large positive/negative number formatting */
213 /* Explicit scale, divisor 1024 */
214
215 /* Requires buflen >= 6 */
216 { 3, "8 E", INT64_MAX, 0, 6, 6 },
217 { 4, "-8 E", -INT64_MAX, 0, 6, 6 },
218 { 3, "0 E", (int64_t)92*1024*1024*1024*1024*1024L, 0, 6, 6 },
219 { 3, "0 E", -(int64_t)92*1024*1024*1024*1024*1024L, 0, 6, 6 },
220 { 3, "0 E", (int64_t)82*1024*1024*1024*1024*1024L, 0, 6, 6 },
221 { 3, "0 E", -(int64_t)82*1024*1024*1024*1024*1024L, 0, 6, 6 },
222 { 3, "0 E", (int64_t)81*1024*1024*1024*1024*1024L, 0, 6, 6 },
223 { 3, "0 E", -(int64_t)81*1024*1024*1024*1024*1024L, 0, 6, 6 },
224 { 4, "92 P", (int64_t)92*1024*1024*1024*1024*1024L, 0, 5, 6 },
225 { 5, "-92 P", -(int64_t)92*1024*1024*1024*1024*1024L, 0, 5, 6 },
226 { 4, "82 P", (int64_t)82*1024*1024*1024*1024*1024L, 0, 5, 6 },
227 { 5, "-82 P", -(int64_t)82*1024*1024*1024*1024*1024L, 0, 5, 6 },
228 { 4, "81 P", (int64_t)81*1024*1024*1024*1024*1024L, 0, 5, 6 },
229 { 5, "-81 P", -(int64_t)81*1024*1024*1024*1024*1024L, 0, 5, 6 },
230
231 /* Explicit scale, divisor 1000 */
232 { 3, "9 E", INT64_MAX, HN_DIVISOR_1000, 6, 6 },
233 { 4, "-9 E", -INT64_MAX, HN_DIVISOR_1000, 6, 6 },
234 { 3, "0 E", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 6, 6 },
235 { 3, "0 E", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 6, 6 },
236 { 3, "0 E", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 6, 6 },
237 { 3, "0 E", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 6, 6 },
238 { 4, "92 P", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 5, 6 },
239 { 5, "-92 P", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 5, 6 },
240 { 4, "91 P", (int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 5, 6 },
241 { 5, "-91 P", -(int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 5, 6 },
242
243 /* Autoscale, divisor 1024 */
244 { 3, "8 E", INT64_MAX, 0, HN_AUTOSCALE, 6 },
245 { 4, "-8 E", -INT64_MAX, 0, HN_AUTOSCALE, 6 },
246 { 4, "92 P", (int64_t)92*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 6 },
247 { 5, "-92 P", -(int64_t)92*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 6 },
248 { 4, "82 P", (int64_t)82*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 6 },
249 { 5, "-82 P", -(int64_t)82*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 6 },
250 { 4, "81 P", (int64_t)81*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 6 },
251 { 5, "-81 P", -(int64_t)81*1024*1024*1024*1024*1024L, 0, HN_AUTOSCALE, 6 },
252 /* Autoscale, divisor 1000 */
253 { 3, "9 E", INT64_MAX, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
254 { 4, "-9 E", -INT64_MAX, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
255 { 4, "92 P", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
256 { 5, "-92 P", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
257 { 4, "91 P", (int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
258 { 5, "-91 P", -(int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
259
260 /* 0 scale, divisor 1024 */
261 { 12, "skdj", INT64_MAX, 0, 0, 6 },
262 { 21, "-9223", -INT64_MAX, 0, 0, 6 },
263 { 19, "10358", (int64_t)92*1024*1024*1024*1024*1024L, 0, 0, 6 },
264 { 20, "-1035", -(int64_t)92*1024*1024*1024*1024*1024L, 0, 0, 6 },
265 { 18, "92323", (int64_t)82*1024*1024*1024*1024*1024L, 0, 0, 6 },
266 { 19, "-9232", -(int64_t)82*1024*1024*1024*1024*1024L, 0, 0, 6 },
267 { 18, "91197", (int64_t)81*1024*1024*1024*1024*1024L, 0, 0, 6 },
268 { 19, "-9119", -(int64_t)81*1024*1024*1024*1024*1024L, 0, 0, 6 },
269
270 /* 0 scale, divisor 1000 */
271 /* XXX - why does this fail? */
272 { -1, "", INT64_MAX, HN_DIVISOR_1000, 0, 6 },
273 { 21, "-9223", -INT64_MAX, HN_DIVISOR_1000, 0, 6 },
274 { 19, "10358", (int64_t)92*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 0, 6 },
275 { 20, "-1035", -(int64_t)92*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 0, 6 },
276 { 18, "92323", (int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 0, 6 },
277 { 19, "-9232", -(int64_t)82*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 0, 6 },
278 /* Expected to pass */
279 { 18, "91197", (int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 0, 6 },
280 { 19, "-9119", -(int64_t)81*1024*1024*1024*1024*1024L, HN_DIVISOR_1000, 0, 6 },
281
282
283
284 /* Need to implement tests for GETSCALE */
285 /* { ?, "", (int64_t)0L, HN_DIVISOR_1000, HN_GETSCALE, 6 },
286 ...
287 */
288 /* Tests for HN_DECIMAL */
289 /* Positive, Autoscale */
290 { 5, "500 k", (int64_t)500*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
291 { 5, "994 k", (int64_t)994*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
292 { 5, "995 k", (int64_t)995*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
293 { 5, "999 k", (int64_t)999*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
294 { 5, "1.0 M", (int64_t)1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
295 { 5, "1.5 M", (int64_t)1500*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
296 { 5, "1.9 M", (int64_t)1949*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
297 { 5, "2.0 M", (int64_t)1950*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
298 { 5, "9.9 M", (int64_t)9949*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
299 { 4, "10 M", (int64_t)9950*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
300 { 5, "500 M", (int64_t)500*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
301 { 5, "994 M", (int64_t)994*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
302 { 5, "995 M", (int64_t)995*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
303 { 5, "999 M", (int64_t)999*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
304
305 { 5, "500 K", (int64_t)500*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
306 { 5, "994 K", (int64_t)994*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
307 { 5, "995 K", (int64_t)995*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
308 { 5, "999 K", (int64_t)999*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
309 { 5, "1.0 M", (int64_t)1000*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
310 { 5, "1.0 M", (int64_t)1018*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
311 { 5, "1.0 M", (int64_t)1019*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
312 { 5, "1.5 M", (int64_t)1536*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
313 { 5, "1.9 M", (int64_t)1996*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
314 { 5, "2.0 M", (int64_t)1997*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
315 { 5, "2.0 M", (int64_t)2047*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
316 { 5, "2.0 M", (int64_t)2048*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
317 { 5, "2.0 M", (int64_t)2099*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
318 { 5, "2.1 M", (int64_t)2100*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
319 { 5, "9.9 M", (int64_t)10188*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
320 /* XXX - shouldn't the following two be "10. M"? */
321 { 4, "10 M", (int64_t)10189*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
322 { 4, "10 M", (int64_t)10240*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
323 { 5, "500 M", (int64_t)500*1024*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
324 { 5, "994 M", (int64_t)994*1024*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
325 { 5, "995 M", (int64_t)995*1024*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
326 { 5, "999 M", (int64_t)999*1024*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
327 { 5, "1.0 G", (int64_t)1000*1024*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
328 { 5, "1.0 G", (int64_t)1023*1024*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
329
330 /* Negative, Autoscale - should pass */
331 { 6, "-1.5 ", -(int64_t)1500*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
332 { 6, "-1.9 ", -(int64_t)1949*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
333 { 6, "-9.9 ", -(int64_t)9949*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
334
335 { 6, "-1.5 ", -(int64_t)1536*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
336 { 6, "-1.9 ", -(int64_t)1949*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
337 { 6, "-9.7 ", -(int64_t)9949*1024L, HN_DECIMAL, HN_AUTOSCALE, 6 },
338
339 /* Positive/negative, at maximum scale */
340 { 5, "500 P", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
341 { 5, "1.9 E", (int64_t)1949*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
342 { 5, "8.9 E", (int64_t)8949*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
343 { 5, "9.2 E", INT64_MAX, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
344 /* Negatives work with latest rev only: */
345 { 6, "-9.2 ", -INT64_MAX, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
346 { 6, "-8.9 ", -(int64_t)8949*1000*1000*1000*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, HN_AUTOSCALE, 6 },
347
348 { 5, "8.0 E", INT64_MAX, HN_DECIMAL, HN_AUTOSCALE, 6 },
349 { 5, "7.9 E", INT64_MAX-(int64_t)100*1024*1024*1024*1024*1024LL, HN_DECIMAL, HN_AUTOSCALE, 6 },
350 { 6, "-8.0 ", -INT64_MAX, HN_DECIMAL, HN_AUTOSCALE, 6 },
351 { 6, "-7.9 ", -INT64_MAX+(int64_t)100*1024*1024*1024*1024*1024LL, HN_DECIMAL, HN_AUTOSCALE, 6 },
352
353 /* Positive, Fixed scales */
354 { 5, "500 k", (int64_t)500*1000L, HN_DECIMAL|HN_DIVISOR_1000, 1, 6 },
355 { 5, "0.5 M", (int64_t)500*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
356 { 5, "949 k", (int64_t)949*1000L, HN_DECIMAL|HN_DIVISOR_1000, 1, 6 },
357 { 5, "0.9 M", (int64_t)949*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
358 { 5, "950 k", (int64_t)950*1000L, HN_DECIMAL|HN_DIVISOR_1000, 1, 6 },
359 { 5, "1.0 M", (int64_t)950*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
360 { 5, "999 k", (int64_t)999*1000L, HN_DECIMAL|HN_DIVISOR_1000, 1, 6 },
361 { 5, "1.0 M", (int64_t)999*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
362 { 5, "1.5 M", (int64_t)1500*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
363 { 5, "1.9 M", (int64_t)1949*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
364 { 5, "2.0 M", (int64_t)1950*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
365 { 5, "9.9 M", (int64_t)9949*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
366 { 4, "10 M", (int64_t)9950*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
367 { 5, "500 M", (int64_t)500*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
368 { 5, "0.5 G", (int64_t)500*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, 3, 6 },
369 { 5, "999 M", (int64_t)999*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, 2, 6 },
370 { 5, "1.0 G", (int64_t)999*1000*1000L, HN_DECIMAL|HN_DIVISOR_1000, 3, 6 },
371 /* Positive/negative, at maximum scale */
372 { 5, "500 P", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 5, 6 },
373 { 5, "1.0 E", (int64_t)500*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 6 },
374 { 5, "1.9 E", (int64_t)1949*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 6 },
375 { 5, "8.9 E", (int64_t)8949*1000*1000*1000*1000*1000L, HN_DIVISOR_1000, 6, 6 },
376 { 5, "9.2 E", INT64_MAX, HN_DECIMAL|HN_DIVISOR_1000, 6, 6 },
377
378 /* HN_DECIMAL + binary + fixed scale cases not completed */
379 { 5, "512 K", (int64_t)512*1024L, HN_DECIMAL, 1, 6 },
380 { 5, "0.5 M", (int64_t)512*1024L, HN_DECIMAL, 2, 6 },
381
382 /* Negative, Fixed scales */
383 /* Not yet added, but should work with latest rev */
384
385 };
386
387
388 /* Command line options usage */
389 static void
usage(char * progname)390 usage(char * progname) {
391 printf("%s: tests libutil humanize_number function\n", progname);
392 printf("Usage: %s [-nE] [-l num] [-v]\n\n", progname);
393 printf("Options:\n");
394 printf("\t-l num\tSet max length for result; buflen = num + 1\n");
395 printf("\t\t (NOTE: does not change expected result strings.)\n");
396 printf("\t-n\tInclude negative scale tests, which cause older libutil\n");
397 printf("\t\t version of function to coredump with assertion failure\n");
398 printf("\t-E\tInclude numbers > 1/2 Exa[byte] which currently fail\n");
399 printf("\t-v\tVerbose - always print summary results\n");
400 printf("\t-h, -?\tShow options\n");
401 }
402
403 /* Parse command line options */
404 static void
read_options(int argc,char * const argv[],size_t * bufLength,int * includeNegativeScale,int * includeExabytes,int * verbose)405 read_options(int argc, char * const argv[], size_t *bufLength,
406 int *includeNegativeScale, int *includeExabytes, int *verbose) {
407 int ch;
408 size_t temp;
409
410 while ((ch = getopt(argc, argv, "nEh?vl:")) != -1) {
411 switch (ch) {
412 default:
413 usage(argv[0]);
414 exit(1);
415 break; /* UNREACHABLE */
416 case 'h' :
417 case '?' :
418 usage(argv[0]);
419 exit(0);
420 break; /* UNREACHABLE */
421 case 'l' :
422 sscanf(optarg, "%zu", &temp);
423 *bufLength = temp + 1;
424 break;
425 case 'n' :
426 *includeNegativeScale = 1;
427 break;
428 case 'E' :
429 *includeExabytes = 1;
430 break;
431 case 'v' :
432 *verbose = 1;
433 break;
434 }
435 }
436 }
437
438 static struct {
439 int value;
440 const char *name;
441 } flags[] = {
442 { HN_AUTOSCALE, "HN_AUTOSCALE" },
443 { HN_GETSCALE, "HN_GETSCALE" },
444 { HN_DIVISOR_1000, "HN_DIVISOR_1000" },
445 { HN_B, "HN_B" },
446 { HN_DECIMAL, "HN_DECIMAL" },
447 };
448
449 static const char *separator = "|";
450
451 /* Format flags parameter for meaningful display */
452 static char *
str_flags(int hn_flags,char * noFlags)453 str_flags(int hn_flags, char *noFlags) {
454 size_t i;
455 char * result;
456
457 result = malloc(MAX_STR_FLAGS_RESULT);
458 result[0] = '\0';
459
460 for (i = 0; i < sizeof flags / sizeof *flags; i++) {
461 if (hn_flags & flags[i].value) {
462 if (*result != 0)
463 strlcat(result, separator,
464 MAX_STR_FLAGS_RESULT);
465 strlcat(result, flags[i].name, MAX_STR_FLAGS_RESULT);
466 }
467 }
468
469 if (strlen(result) == 0)
470 strlcat(result, noFlags, MAX_STR_FLAGS_RESULT);
471 return result;
472 }
473
474
475 /* Format scale parameter for meaningful display */
476 static char *
str_scale(int scale)477 str_scale(int scale) {
478 char *result;
479
480 if (scale == HN_AUTOSCALE || scale == HN_GETSCALE)
481 return str_flags(scale, "");
482
483 result = malloc(MAX_INT_STR_DIGITS);
484 result[0] = '\0';
485 snprintf(result, MAX_INT_STR_DIGITS, "%d", scale);
486 return result;
487 }
488
489 static void
testskipped(size_t i)490 testskipped(size_t i)
491 {
492
493 printf("ok %zu # skip - not turned on\n", i);
494 }
495
496 int
main(int argc,char * const argv[])497 main(int argc, char * const argv[])
498 {
499 char *buf;
500 char *flag_str, *scale_str;
501 size_t blen, buflen, errcnt, i, skipped, tested;
502 int r;
503 int includeNegScale;
504 int includeExabyteTests;
505 int verbose;
506
507 buf = NULL;
508 buflen = 0;
509 includeNegScale = 0;
510 includeExabyteTests = 0;
511 verbose = 0;
512
513 read_options(argc, argv, &buflen, &includeNegScale,
514 &includeExabyteTests, &verbose);
515
516 errcnt = 0;
517 tested = 0;
518 skipped = 0;
519
520 if (buflen != 4)
521 printf("Warning: buffer size %zu != 4, expect some results to differ.\n", buflen);
522
523 printf("1..%zu\n", nitems(test_args));
524 for (i = 0; i < nitems(test_args); i++) {
525 blen = (buflen > 0) ? buflen : test_args[i].buflen;
526 buf = realloc(buf, blen);
527
528 if (test_args[i].scale < 0 && ! includeNegScale) {
529 skipped++;
530 testskipped(i + 1);
531 continue;
532 }
533 if (test_args[i].num >= halfExabyte && ! includeExabyteTests) {
534 skipped++;
535 testskipped(i + 1);
536 continue;
537 }
538
539 r = humanize_number(buf, blen, test_args[i].num, "",
540 test_args[i].scale, test_args[i].flags);
541 flag_str = str_flags(test_args[i].flags, "[no flags]");
542 scale_str = str_scale(test_args[i].scale);
543
544 if (r != test_args[i].retval) {
545 if (verbose)
546 printf("wrong return value on index %zu, "
547 "buflen: %zu, got: %d + \"%s\", "
548 "expected %d + \"%s\"; num = %jd, "
549 "scale = %s, flags= %s.\n",
550 i, blen, r, buf, test_args[i].retval,
551 test_args[i].res,
552 (intmax_t)test_args[i].num,
553 scale_str, flag_str);
554 else
555 printf("not ok %zu # return %d != %d\n",
556 i + 1, r, test_args[i].retval);
557 errcnt++;
558 } else if (strcmp(buf, test_args[i].res) != 0) {
559 if (verbose)
560 printf("result mismatch on index %zu, got: "
561 "\"%s\", expected \"%s\"; num = %jd, "
562 "buflen: %zu, scale = %s, flags= %s.\n",
563 i, buf, test_args[i].res,
564 (intmax_t)test_args[i].num,
565 blen, scale_str, flag_str);
566 else
567 printf("not ok %zu # buf \"%s\" != \"%s\"\n",
568 i + 1, buf, test_args[i].res);
569 errcnt++;
570 } else {
571 if (verbose)
572 printf("successful result on index %zu, "
573 "returned %d, got: \"%s\"; num = %jd, "
574 "buflen = %zd, scale = %s, flags= %s.\n",
575 i, r, buf, (intmax_t)test_args[i].num,
576 blen, scale_str, flag_str);
577 else
578 printf("ok %zu\n", i + 1);
579 }
580 tested++;
581 }
582 free(buf);
583
584 if (verbose)
585 printf("total errors: %zu/%zu tests, %zu skipped\n", errcnt,
586 tested, skipped);
587
588 if (errcnt)
589 return 1;
590
591 return 0;
592 }
593