Track scaling factors of layer2 data elements.
[oonf.git] / src / libcommon / isonumber.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon version 2 (olsrd2)
4  * Copyright (c) 2004-2015, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /**
43  * @file
44  */
45
46 #include <errno.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49
50 #include <oonf/libcommon/isonumber.h>
51 #include <oonf/libcommon/string.h>
52
53 static const char *_isonumber_u64_to_string(
54   char *out, size_t out_len, uint64_t number, const char *unit, uint64_t scaling, bool raw);
55
56 /**
57  * Converts an unsigned 64 bit integer into a human readable number
58  * in string representation.
59  *
60  * '120000' will become '120 k' for example.
61  *
62  * @param out pointer to output buffer
63  * @param number number to convert.
64  * @param unit unit to be appended at the end, can be NULL
65  * @param scaling fixed point integer arithmetics scaling factor
66  * @param raw true if the whole text conversion should be bypassed
67  *   and only the raw number shall be written, false otherwise
68  * @return pointer to converted string
69  */
70 const char *
71 isonumber_from_u64(struct isonumber_str *out, uint64_t number, const char *unit, uint64_t scaling, bool raw) {
72   return _isonumber_u64_to_string(out->buf, sizeof(*out), number, unit, scaling, raw);
73 }
74
75 /**
76  * Converts a signed 64 bit integer into a human readable number
77  * in string representation.
78  *
79  * '-120000' will become '-120 k' for example.
80  *
81  * @param out pointer to output buffer
82  * @param number number to convert.
83  * @param unit unit to be appended at the end, can be NULL
84  * @param scaling fixed point integer arithmetics scaling factor
85  * @param raw true if the whole text conversion should be bypassed
86  *   and only the raw number shall be written, false otherwise
87  * @return pointer to converted string
88  */
89 const char *
90 isonumber_from_s64(struct isonumber_str *out, int64_t number, const char *unit, uint64_t scaling, bool raw) {
91   char *outbuf = out->buf;
92   uint64_t num;
93   size_t len;
94
95   len = sizeof(*out);
96   if (number == INT64_MIN) {
97     *outbuf++ = '-';
98     len--;
99     num = 1ull << 63;
100   }
101   else if (number < 0) {
102     *outbuf++ = '-';
103     len--;
104     num = (uint64_t)(-number);
105   }
106   else {
107     num = (uint64_t)number;
108   }
109
110   if (_isonumber_u64_to_string(outbuf, len, num, unit, scaling, raw)) {
111     return out->buf;
112   }
113   return NULL;
114 }
115
116 /**
117  * Converts a string representation of a (fractional) number with iso-prefix
118  * to a signed 64bit integer.
119  * @param dst pointer to destination variable
120  * @param iso pointer to string source
121  * @param scaling fixed point integer arithmetics scaling factor
122  * @return -1 if an error happened, 0 otherwise
123  */
124 int
125 isonumber_to_s64(int64_t *dst, const char *iso, uint64_t scaling) {
126   const char *ptr;
127   int result;
128   uint64_t u64;
129
130   ptr = iso;
131   if (*iso == '-') {
132     ptr++;
133   }
134
135   result = isonumber_to_u64(&u64, ptr, scaling);
136   if (!result) {
137     if (*iso == '-') {
138       *dst = -((int64_t)u64);
139     }
140     else {
141       *dst = (int64_t)u64;
142     }
143   }
144   return result;
145 }
146
147 /**
148  * Converts a string representation of a (fractional) number with iso-prefix
149  * to an unsigned 64bit integer.
150  * @param dst pointer to destination variable
151  * @param iso pointer to string source
152  * @param scaling fixed point integer arithmetics scaling factor
153  * @return -1 if an error happened, 0 otherwise
154  */
155 int
156 isonumber_to_u64(uint64_t *dst, const char *iso, uint64_t scaling) {
157   static const char symbol_large[] = " kMGTPE";
158
159   uint64_t num, fraction_scale, factor;
160   char *next = NULL, *prefix;
161
162   errno = 0;
163   num = strtoull(iso, &next, 10);
164   if (errno) {
165     return -1;
166   }
167
168   if (*next == 0) {
169     if (num > UINT64_MAX / scaling) {
170       /* this would be an integer overflow */
171       return -1;
172     }
173
174     *dst = num * scaling;
175     return 0;
176   }
177
178   /* Handle fractional part */
179   fraction_scale = 1;
180   if (*next == '.') {
181     next++;
182     while (*next >= '0' && *next <= '9') {
183       num *= 10;
184       num += (*next - '0');
185       fraction_scale *= 10;
186       next++;
187     }
188   }
189   while (fraction_scale < scaling && (scaling % 10) == 0) {
190     num *= 10;
191     fraction_scale *= 10;
192     scaling /= 10;
193   }
194
195   /* handle spaces */
196   while (*next == ' ') {
197     next++;
198   }
199
200   factor = 1;
201   if (*next) {
202     /* handle iso-prefix */
203     if (next[1] != 0) {
204       return -1;
205     }
206
207     prefix = strchr(symbol_large, next[0]);
208     if (!prefix) {
209       return -1;
210     }
211
212     while (prefix > symbol_large) {
213       factor *= 1000;
214       prefix--;
215     }
216   }
217
218   while (fraction_scale > scaling) {
219     fraction_scale /= 1000;
220     if (factor > 1) {
221       factor /= 1000;
222     }
223     else {
224       num /= 1000;
225     }
226   }
227
228   if (num > UINT64_MAX / (factor*scaling)) {
229     /* this would be an integer overflow */
230     return -1;
231   }
232
233   *dst = num * factor * scaling;
234   return 0;
235 }
236
237 /**
238  * Helper function to convert an unsigned 64bit integer
239  * into a string representation with fractional digits,
240  * an optional unit and iso-prefixes
241  * @param out pointer to output buffer
242  * @param out_len length of output buffer
243  * @param number number to convert
244  * @param unit unit that should be appended on result
245  * @param scaling fixed point integer arithmetics scaling factor
246  * @param raw true to suppress iso prefixes and unit, false otherwise
247  * @return pointer to output buffer, NULL if an error happened
248  */
249 static const char *
250 _isonumber_u64_to_string(char *out, size_t out_len, uint64_t number, const char *unit, uint64_t scaling, bool raw) {
251   static const char symbol_large[] = " kMGTPE";
252   static const char symbol_small[] = " munpfa";
253   uint64_t print, n;
254   const char *unit_modifier;
255   size_t idx, len;
256   int result, fraction;
257
258   if (number >= scaling) {
259     unit_modifier = symbol_large;
260     while (!raw && *unit_modifier != 0 && number / 1000 >= scaling) {
261       scaling *= 1000;
262       unit_modifier++;
263     }
264   }
265   else {
266     unit_modifier = symbol_small;
267     while (!raw && *unit_modifier != 0 && number < scaling && scaling >= 1000) {
268       scaling /= 1000;
269       unit_modifier++;
270     }
271   }
272
273   /* print whole */
274   if ((result = snprintf(out, out_len, "%" PRIu64, number / scaling)) < 0) {
275     return NULL;
276   }
277
278   idx = result;
279   len = result;
280
281   out[len++] = '.';
282   n = number;
283
284   /* show three fractional digits */
285   fraction = 3;
286   while (true) {
287     n = n % scaling;
288     if (n == 0 || fraction == 0) {
289       break;
290     }
291     fraction--;
292     n *= 10;
293     print = n / scaling;
294
295     if (print >= 10) {
296       return NULL;
297     }
298
299     out[len++] = (char)'0' + (char)(print);
300     if (print) {
301       idx = len;
302     }
303   }
304
305   if (!raw && *unit_modifier != ' ') {
306     out[idx++] = *unit_modifier;
307   }
308   out[idx++] = 0;
309
310   if (!raw && unit != NULL) {
311     strscat(out, unit, out_len);
312   }
313
314   return str_trim(out);
315 }