Adaptive Framework  0.9.0
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
afw_number.c
Go to the documentation of this file.
1 // See the 'COPYING' file in the project root for licensing information.
2 /*
3  * Adaptive Framework Number
4  *
5  * Copyright (c) 2010-2023 Clemson University
6  *
7  */
8 
14 #include "afw_internal.h"
15 #include <float.h>
16 
17 /* Convert a double to utf8 in specified pool. */
18 AFW_DEFINE(const afw_utf8_t *)
20  afw_double_t d,
21  const afw_pool_t *p, afw_xctx_t *xctx)
22 {
23  /* IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT
24  *
25  * Make sure s is one longer than precision plus 1 for \0 plus a
26  * little padding because of implementation differences that may
27  * not count leading sign in precision, etc. For %.16g, 32 is used
28  * just to be safe.
29  */
30  char s[32];
31 
32  /*
33  * %.16E will be able to print minus, plus 16 digits, plus period,
34  * plus 'e', plus sign, plus 3 digits.
35  *
36  * JSON only supports finite numbers. If not finite, return a string
37  * with "NaN", "-INF", or "INF".
38  *
39  * For the E conversion style [-]d.dddE±dd is used.
40  */
41  /*
42  * Note:
43  *
44  * The exponent contains at least two digits, more digits are used only
45  * if necessary. If the value is ​0​, the exponent is also ​0​.
46  *
47  * Considered using %.15G since last digit may be wrong with %.16G, but
48  * that would be a problem as well because part of the time %.16G is
49  * correct.
50  *
51  * 9,007,199,254,740,992 (the largest integer 2^53) is correct for %.16G,
52  * 9,007,199,254,740,993 is 9,007,199,254,740,992 for %.16G.
53  *
54  * Note:
55  *
56  * # says decimal point character is written even if no digits follow it.
57  *
58  */
59  afw_size_t i;
60  char dst[32];
61  char *pdst = &dst[0];
62  char *epos;
63 
64  if (afw_number_is_finite(d)) {
65  sprintf(s, "%#.16E", d);
66 
67  /* remove '+' and leading zero if any after 'E' inserted by sprintf */
68  strcpy(pdst, s);
69  epos = strchr(pdst, 'E');
70 
71  for (i = epos - dst; dst[i] != '\0'; i++) {
72  if (dst[i] == '0' || dst[i] == '+') continue;
73  else pdst[0] = dst[i];
74  pdst++;
75  }
76 
77  /* make sure it is null terminated */
78  pdst[0] = '\0';
79 
80  if (strlen(dst)==1)
81  strcpy(dst, AFW_NUMBER_Q_EXPONENT_ZERO);
82 
83  /* Find most significant digits without trailing zeroes before 'E' */
84  epos = strchr(s, 'E');
85 
86  for (i = epos - s - 1; i > 0; i--) {
87  if (s[i] == '0') continue;
88 
89  /* there must be at least one digit after the '.' */
90  else if (s[i] == '.') {
91  s[i + 1] = '0';
92  s[i + 2] = '\0';
93  break;
94  }
95  else {
96  s[i + 1] = '\0';
97  break;
98  }
99  }
100 
101  /* put the 2 pieces back together minus unnecessary zeroes and + */
102  strcat(s, dst);
103 
104  } else if (afw_number_is_NaN(d)) {
105  strcpy(s, AFW_NUMBER_Q_NAN);
106  } else if (d <= DBL_MAX) {
107  strcpy(s, AFW_NUMBER_Q_NEGATIVE_INFINITY);
108  } else {
109  strcpy(s, AFW_NUMBER_Q_INFINITY);
110  }
111 
112  return afw_utf8_create_copy(s, AFW_UTF8_Z_LEN, p, xctx);
113 }
114 
115 
116 /* Calculate buffer needed for integer as utf8. */
117 AFW_DEFINE_STATIC_INLINE(afw_size_t)
118 impl_integer_len_as_u8(
119  afw_integer_t i)
120 {
121  afw_size_t len;
122 
123  if (i == AFW_INTEGER_MIN) {
124  return strlen(AFW_INTEGER_Q_MIN);
125  }
126 
127  len = 0;
128  if (i < 0) {
129  len++;
130  i = -i;
131  }
132 
133  do {
134  len++;
135  i = i / 10;
136  } while (i);
137 
138  return len;
139 }
140 
141 
142 /* Convert an integer to u8 in supplied buffer that is correct size. */
143 AFW_DEFINE_STATIC_INLINE(void)
144 impl_number_integer_set_u8(
146 {
147  if (i == AFW_INTEGER_MIN) {
148  memcpy(s, AFW_INTEGER_Q_MIN, needed);
149  }
150 
151  else {
152  if (i < 0) {
153  *s = '-';
154  i = -i;
155  }
156  s += needed;
157  do {
158  *--s = '0' + (i % 10);
159  i /= 10;
160  } while (i);
161  }
162 }
163 
164 
165 
166 /* Convert an integer to utf8 in specified pool. */
167 AFW_DEFINE(const afw_utf8_t *)
169  afw_integer_t i,
170  const afw_pool_t *p, afw_xctx_t *xctx)
171 {
172  afw_utf8_t *result;
173  afw_utf8_octet_t *s;
174 
175  result = afw_pool_calloc_type(p, afw_utf8_t, xctx);
176  result->len = impl_integer_len_as_u8(i);
177  s = afw_pool_calloc(p, result->len, xctx);
178  impl_number_integer_set_u8(i, s, result->len, xctx);
179  result->s = s;
180 
181  return result;
182 }
183 
184 
185 /* Convert an integer to utf8 in supplied buffer. */
189  afw_xctx_t *xctx)
190 {
191  afw_size_t needed;
192 
193  needed = impl_integer_len_as_u8(i);
194  impl_number_integer_set_u8(i, s, needed, xctx);
195 
196  return needed;
197 }
198 
199 
200 
201 /* Parse an integer or double. */
204  const afw_utf8_octet_t *cursor, afw_size_t len,
205  afw_integer_t *i, afw_double_t *d, afw_boolean_t *is_double,
206  const afw_pool_t *p, afw_xctx_t *xctx)
207 {
208  const afw_utf8_octet_t *c;
209  const afw_utf8_octet_t *s;
210  afw_boolean_t zero;
211  afw_boolean_t is_integer;
212  afw_boolean_t is_negative;
213  afw_integer_t negative;
214  double number;
215  double d0;
216 
217  c = cursor;
218  if (is_double) *is_double = false;
219 
220  /* If parse double, allow Infinity, -Infinity, and NaN. */
221  if (d) {
222  d0 = 0;
223  if (afw_utf8_len_starts_with_z(c, len, AFW_NUMBER_Q_INF)) {
224  *d = 1 / d0;
225  if (is_double) *is_double = true;
226  return strlen(AFW_NUMBER_Q_INF);
227  }
228 
229  else if (afw_utf8_len_starts_with_z(c, len, AFW_NUMBER_Q_INFINITY)) {
230  *d = 1 / d0;
231  if (is_double) *is_double = true;
232  return strlen(AFW_NUMBER_Q_INFINITY);
233  }
234 
235  else if (afw_utf8_len_starts_with_z(c, len, AFW_NUMBER_Q_NEGATIVE_INF)) {
236  *d = -1 / d0;
237  if (is_double) *is_double = true;
238  return strlen(AFW_NUMBER_Q_NEGATIVE_INF);
239  }
240 
241  else if (afw_utf8_len_starts_with_z(c, len,
242  AFW_NUMBER_Q_NEGATIVE_INFINITY))
243  {
244  *d = -1 / d0;
245  if (is_double) *is_double = true;
246  return strlen(AFW_NUMBER_Q_NEGATIVE_INFINITY);
247  }
248 
249  else if (afw_utf8_len_starts_with_z(c, len, AFW_NUMBER_Q_NAN)) {
250  *d = sqrt(-1);
251  if (is_double) *is_double = true;
252  return strlen(AFW_NUMBER_Q_NAN);
253  }
254 
255  else if (afw_utf8_len_starts_with_z(c, len, AFW_NUMBER_Q_NEGATIVE_NAN)) {
256  *d = sqrt(-1);
257  if (is_double) *is_double = true;
258  return strlen(AFW_NUMBER_Q_NEGATIVE_NAN);
259  }
260  }
261 
262 
263  /* Parse integer part and prescan floating point part. */
264  zero = 1;
265  is_integer = true;
266  is_negative = false;
267  do {
268 
269  /* Number can't end here. */
270  if (len <= 0) {
271  return -1;
272  }
273 
274  /* Optional leading '-' */
275  if (*c == '-') {
276  c++;
277  (len)--;
278  is_negative = true;
279  }
280 
281  /*
282  * Ignore '+'. If caller doesn't allow, it can check first char for
283  * '+' before calling.
284  */
285  if (*c == '+') {
286  c++;
287  (len)--;
288  }
289 
290  /* Number can't end here. */
291  if (len <= 0) {
292  return -1;
293  }
294 
295  /* Can be '0' or digits'1'-'9' followed by additional digits '0'-'9'. */
296  if (*c >= '1' && *c <= '9') {
297  zero = 0;
298  /* Sum digits as a negative number. Negative is one larger. */
299  negative = -((*c) - '0');
300  c++;
301  (len)--;
302  while (len > 0 && *c >= '0' && *c <= '9') {
303  negative = (negative * 10) - ((*c) - '0');
304  if (negative > 0) {
305  is_integer = false;
306  }
307  c++;
308  (len)--;
309  }
310  }
311  else if (*c == '0') {
312  negative = 0;
313  c++;
314  (len)--;
315  }
316  else {
317  return -1;
318  }
319 
320  /* Number is an integer if at end of buffer. */
321  if (len <= 0) {
322  break;
323  }
324 
325  /* If not checking for double, don't check further. */
326  if (!d) break;
327 
328  /* Next can be an optional period followed by digits '0'-'9'. */
329  if (*c == '.') {
330  c++;
331  (len)--;
332  is_integer = false;
333  /* Number can't end here. */
334  if (len <= 0 || *c < '0' || *c > '9') {
335  return -1;
336  }
337  while (len > 0 && *c >= '0' && *c <= '9') {
338  if (*c != '0') {
339  zero = 0;
340  }
341  c++;
342  (len)--;
343  }
344  }
345 
346  /* Number can end here. */
347  if (len <= 0) {
348  break;
349  }
350 
351 
352  /*
353  * Next can be an 'e' or 'E' followed by optional '-' or '+' followed
354  * by digits '0' - '9'.
355  */
356  if (*c == 'e' || *c == 'E') {
357  is_integer = false;
358  /* Skip 'e' or 'E'. */
359  c++;
360  (len)--;
361  /* Number can't end here. */
362  if (len <= 0) {
363  return -1;
364  }
365  /* Skip '+' or '-'. */
366  if (*c == '+' || *c == '-') {
367  c++;
368  (len)--;
369  }
370  /* Number can't end here. */
371  if (len <= 0 || *c < '0' || *c > '9') {
372  return -1;
373  }
374  /* Skip digits. */
375  while (len > 0 && *c >= '0' && *c <= '9') {
376  c++;
377  (len)--;
378  }
379  }
380  } while (0);
381 
382  /*
383  * If is_integer (no '.', 'e', or 'E'), return integer.
384  *
385  * Note: javascript treats all numbers as double, but adaptive
386  * frameworks treats integers as afw_integer_t.
387  */
388  if (is_integer) {
389  if (is_double) *is_double = false;
390  if (!i) return -1;
391  if (is_negative) {
392  *i = negative;
393  }
394  else if (negative != AFW_INTEGER_MIN) {
395  *i = -negative;
396  }
397  else {
398  return -1;
399  }
400  return (afw_size_t)(c - cursor);
401  }
402 
403 
404  /*
405  * Not integer, create a double value. Use atof to convert number if it
406  * is not zero.
407  */
408  if (is_double) *is_double = true;
409  if (!d) return -1;
410  number = 0;
411  if (!zero) {
412  s = apr_pstrndup(afw_pool_get_apr_pool(p), cursor, c - cursor);
413  errno = 0;
414  number = strtod(s, NULL);
415  if (errno != 0) {
416  return -1;
417  }
418  }
419 
420  *d = number;
421  return (afw_size_t)(c - cursor);
422 }
423 
424 AFW_DEFINE(int)
426 {
427  int result;
428 
429  for (result = 1; i > 9; i /= 10) {
430  result++;
431  }
432 
433  return result;
434 }
435 
436 AFW_DEFINE(int)
438 {
439  int result;
440 
441  for (result = 1; i > 9; i /= 10) {
442  result++;
443  }
444 
445  return result;
446 }
AFW_DEFINE(const afw_object_t *)
Adaptive Framework Core Internal.
#define AFW_UTF8_Z_LEN
String is NUL (0) terminate.
Definition: afw_common.h:266
double afw_double_t
Normal AFW number is double.
Definition: afw_common.h:202
#define AFW_INTEGER_Q_MIN
smallest afw_integer_t quoted
Definition: afw_common.h:296
_Bool afw_boolean_t
Definition: afw_common.h:373
char afw_utf8_octet_t
8 bits of utf-8 codepoint.
Definition: afw_common.h:236
apr_size_t afw_size_t
size_t.
Definition: afw_common.h:151
#define AFW_INTEGER_MIN
smallest afw_integer_t
Definition: afw_common.h:291
apr_int64_t afw_integer_t
typedef for big signed int.
Definition: afw_common.h:321
afw_number_bytes_needed_size_t(afw_size_t i)
Determine bytes needed to hold printable size_t.
Definition: afw_number.c:425
afw_boolean_t afw_number_is_NaN(double d)
Determine if double is not a number.
Definition: afw_number.h:84
afw_boolean_t afw_number_is_finite(double d)
Determine if double is finite.
Definition: afw_number.h:45
afw_number_parse(const afw_utf8_octet_t *cursor, afw_size_t len, afw_integer_t *i, afw_double_t *d, afw_boolean_t *is_double, const afw_pool_t *p, afw_xctx_t *xctx)
Parse an integer or double using specified pool for work area.
Definition: afw_number.c:203
afw_number_integer_set_u8(afw_integer_t i, afw_utf8_octet_t *s, afw_size_t len, afw_xctx_t *xctx)
Convert an integer to u8 in supplied buffer.
Definition: afw_number.c:187
afw_number_double_to_utf8(afw_double_t d, const afw_pool_t *p, afw_xctx_t *xctx)
Convert a double to utf8 in specified pool.
Definition: afw_number.c:19
afw_number_bytes_needed_integer(afw_integer_t i)
Determine bytes needed to hold printable afw_integer_t.
Definition: afw_number.c:437
afw_number_integer_to_utf8(afw_integer_t i, const afw_pool_t *p, afw_xctx_t *xctx)
Convert an integer to utf8 in specified pool.
Definition: afw_number.c:168
#define afw_pool_calloc(instance, size, xctx)
Call method calloc of interface afw_pool.
#define afw_pool_get_apr_pool(instance)
Call method get_apr_pool of interface afw_pool.
#define afw_pool_calloc_type(instance, type, xctx)
Macro to allocate cleared memory to hold type in pool.
Definition: afw_pool.h:167
#define afw_utf8_create_copy(s, len, p, xctx)
Make a utf-8 sting from chars in pool specified.
Definition: afw_utf8.h:369
afw_boolean_t afw_utf8_len_starts_with_z(const afw_utf8_octet_t *s1, afw_size_t len1, const afw_utf8_z_t *s2_z)
Returns true if series of bytes for len s1 starts with zero terminated string s2.
Definition: afw_utf8.h:732
Interface afw_pool public struct.
NFC normalized UTF-8 string.
Definition: afw_common.h:545
Interface afw_xctx public struct.