iodine 0.4.19 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of iodine might be problematic. Click here for more details.

Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/CHANGELOG.md +22 -0
  4. data/LIMITS.md +19 -9
  5. data/README.md +92 -77
  6. data/SPEC-PubSub-Draft.md +113 -0
  7. data/SPEC-Websocket-Draft.md +127 -143
  8. data/bin/http-hello +0 -1
  9. data/bin/raw-rbhttp +1 -1
  10. data/bin/raw_broadcast +8 -10
  11. data/bin/updated api +2 -2
  12. data/bin/ws-broadcast +2 -4
  13. data/bin/ws-echo +2 -2
  14. data/examples/config.ru +13 -13
  15. data/examples/echo.ru +5 -6
  16. data/examples/hello.ru +2 -3
  17. data/examples/info.md +316 -0
  18. data/examples/pubsub_engine.ru +81 -0
  19. data/examples/redis.ru +9 -9
  20. data/examples/shootout.ru +45 -11
  21. data/ext/iodine/defer.c +194 -297
  22. data/ext/iodine/defer.h +61 -53
  23. data/ext/iodine/evio.c +0 -260
  24. data/ext/iodine/evio.h +50 -22
  25. data/ext/iodine/evio_callbacks.c +26 -0
  26. data/ext/iodine/evio_epoll.c +251 -0
  27. data/ext/iodine/evio_kqueue.c +193 -0
  28. data/ext/iodine/extconf.rb +1 -1
  29. data/ext/iodine/facil.c +1420 -542
  30. data/ext/iodine/facil.h +151 -64
  31. data/ext/iodine/fio_ary.h +418 -0
  32. data/ext/iodine/{base64.c → fio_base64.c} +33 -24
  33. data/ext/iodine/{base64.h → fio_base64.h} +6 -7
  34. data/ext/iodine/{fio_cli_helper.c → fio_cli.c} +77 -58
  35. data/ext/iodine/{fio_cli_helper.h → fio_cli.h} +9 -4
  36. data/ext/iodine/fio_hashmap.h +759 -0
  37. data/ext/iodine/fio_json_parser.h +651 -0
  38. data/ext/iodine/fio_llist.h +257 -0
  39. data/ext/iodine/fio_mem.c +672 -0
  40. data/ext/iodine/fio_mem.h +140 -0
  41. data/ext/iodine/fio_random.c +248 -0
  42. data/ext/iodine/{random.h → fio_random.h} +11 -14
  43. data/ext/iodine/{sha1.c → fio_sha1.c} +28 -24
  44. data/ext/iodine/{sha1.h → fio_sha1.h} +38 -16
  45. data/ext/iodine/{sha2.c → fio_sha2.c} +66 -49
  46. data/ext/iodine/{sha2.h → fio_sha2.h} +57 -26
  47. data/ext/iodine/{fiobj_internal.c → fio_siphash.c} +9 -90
  48. data/ext/iodine/fio_siphash.h +18 -0
  49. data/ext/iodine/fio_tmpfile.h +38 -0
  50. data/ext/iodine/fiobj.h +24 -7
  51. data/ext/iodine/fiobj4sock.h +23 -0
  52. data/ext/iodine/fiobj_ary.c +143 -226
  53. data/ext/iodine/fiobj_ary.h +17 -16
  54. data/ext/iodine/fiobj_data.c +1160 -0
  55. data/ext/iodine/fiobj_data.h +164 -0
  56. data/ext/iodine/fiobj_hash.c +298 -406
  57. data/ext/iodine/fiobj_hash.h +101 -54
  58. data/ext/iodine/fiobj_json.c +478 -601
  59. data/ext/iodine/fiobj_json.h +34 -9
  60. data/ext/iodine/fiobj_numbers.c +383 -51
  61. data/ext/iodine/fiobj_numbers.h +87 -11
  62. data/ext/iodine/fiobj_str.c +423 -184
  63. data/ext/iodine/fiobj_str.h +81 -32
  64. data/ext/iodine/fiobject.c +273 -522
  65. data/ext/iodine/fiobject.h +477 -112
  66. data/ext/iodine/http.c +2243 -83
  67. data/ext/iodine/http.h +842 -121
  68. data/ext/iodine/http1.c +810 -385
  69. data/ext/iodine/http1.h +16 -39
  70. data/ext/iodine/http1_parser.c +146 -74
  71. data/ext/iodine/http1_parser.h +15 -4
  72. data/ext/iodine/http_internal.c +1258 -0
  73. data/ext/iodine/http_internal.h +226 -0
  74. data/ext/iodine/http_mime_parser.h +341 -0
  75. data/ext/iodine/iodine.c +86 -68
  76. data/ext/iodine/iodine.h +26 -11
  77. data/ext/iodine/iodine_helpers.c +8 -7
  78. data/ext/iodine/iodine_http.c +487 -324
  79. data/ext/iodine/iodine_json.c +304 -0
  80. data/ext/iodine/iodine_json.h +6 -0
  81. data/ext/iodine/iodine_protocol.c +107 -45
  82. data/ext/iodine/iodine_pubsub.c +526 -225
  83. data/ext/iodine/iodine_pubsub.h +10 -0
  84. data/ext/iodine/iodine_websockets.c +268 -510
  85. data/ext/iodine/iodine_websockets.h +2 -4
  86. data/ext/iodine/pubsub.c +726 -432
  87. data/ext/iodine/pubsub.h +85 -103
  88. data/ext/iodine/rb-call.c +4 -4
  89. data/ext/iodine/rb-defer.c +46 -22
  90. data/ext/iodine/rb-fiobj2rb.h +117 -0
  91. data/ext/iodine/rb-rack-io.c +73 -238
  92. data/ext/iodine/rb-rack-io.h +2 -2
  93. data/ext/iodine/rb-registry.c +35 -93
  94. data/ext/iodine/rb-registry.h +1 -0
  95. data/ext/iodine/redis_engine.c +742 -304
  96. data/ext/iodine/redis_engine.h +42 -39
  97. data/ext/iodine/resp_parser.h +311 -0
  98. data/ext/iodine/sock.c +627 -490
  99. data/ext/iodine/sock.h +345 -297
  100. data/ext/iodine/spnlock.inc +15 -4
  101. data/ext/iodine/websocket_parser.h +16 -20
  102. data/ext/iodine/websockets.c +188 -257
  103. data/ext/iodine/websockets.h +24 -133
  104. data/lib/iodine.rb +52 -7
  105. data/lib/iodine/cli.rb +6 -24
  106. data/lib/iodine/json.rb +40 -0
  107. data/lib/iodine/version.rb +1 -1
  108. data/lib/iodine/websocket.rb +5 -3
  109. data/lib/rack/handler/iodine.rb +58 -13
  110. metadata +38 -48
  111. data/bin/ws-shootout +0 -107
  112. data/examples/broadcast.ru +0 -56
  113. data/ext/iodine/bscrypt-common.h +0 -116
  114. data/ext/iodine/bscrypt.h +0 -49
  115. data/ext/iodine/fio2resp.c +0 -60
  116. data/ext/iodine/fio2resp.h +0 -51
  117. data/ext/iodine/fio_dict.c +0 -446
  118. data/ext/iodine/fio_dict.h +0 -99
  119. data/ext/iodine/fio_hash_table.h +0 -370
  120. data/ext/iodine/fio_list.h +0 -111
  121. data/ext/iodine/fiobj_internal.h +0 -280
  122. data/ext/iodine/fiobj_primitives.c +0 -131
  123. data/ext/iodine/fiobj_primitives.h +0 -55
  124. data/ext/iodine/fiobj_sym.c +0 -135
  125. data/ext/iodine/fiobj_sym.h +0 -60
  126. data/ext/iodine/hex.c +0 -124
  127. data/ext/iodine/hex.h +0 -70
  128. data/ext/iodine/http1_request.c +0 -81
  129. data/ext/iodine/http1_request.h +0 -58
  130. data/ext/iodine/http1_response.c +0 -417
  131. data/ext/iodine/http1_response.h +0 -95
  132. data/ext/iodine/http_request.c +0 -111
  133. data/ext/iodine/http_request.h +0 -102
  134. data/ext/iodine/http_response.c +0 -1703
  135. data/ext/iodine/http_response.h +0 -250
  136. data/ext/iodine/misc.c +0 -182
  137. data/ext/iodine/misc.h +0 -74
  138. data/ext/iodine/random.c +0 -208
  139. data/ext/iodine/redis_connection.c +0 -278
  140. data/ext/iodine/redis_connection.h +0 -86
  141. data/ext/iodine/resp.c +0 -842
  142. data/ext/iodine/resp.h +0 -261
  143. data/ext/iodine/siphash.c +0 -154
  144. data/ext/iodine/siphash.h +0 -22
  145. data/ext/iodine/xor-crypt.c +0 -193
  146. data/ext/iodine/xor-crypt.h +0 -107
@@ -2,16 +2,15 @@
2
2
  #define H_FIOBJ_JSON_H
3
3
 
4
4
  /*
5
- Copyright: Boaz Segev, 2017
5
+ Copyright: Boaz Segev, 2017-2018
6
6
  License: MIT
7
7
  */
8
8
 
9
9
  #include "fiobj_ary.h"
10
10
  #include "fiobj_hash.h"
11
11
  #include "fiobj_numbers.h"
12
- #include "fiobj_primitives.h"
13
12
  #include "fiobj_str.h"
14
- #include "fiobj_sym.h"
13
+ #include "fiobject.h"
15
14
 
16
15
  #ifdef __cplusplus
17
16
  extern "C" {
@@ -21,9 +20,10 @@ extern "C" {
21
20
  JSON API
22
21
  ***************************************************************************** */
23
22
 
24
- /** Limit JSON nesting, we can handle more, but this is mostly for security. */
25
- #ifndef JSON_MAX_DEPTH
26
- #define JSON_MAX_DEPTH 24
23
+ /** Limit JSON nesting, 32 is the limit to accomodate a 32 bit type. */
24
+ #if !defined(JSON_MAX_DEPTH) || JSON_MAX_DEPTH > 32
25
+ #undef JSON_MAX_DEPTH
26
+ #define JSON_MAX_DEPTH 32
27
27
  #endif
28
28
 
29
29
  /**
@@ -32,9 +32,34 @@ JSON API
32
32
  * Returns the number of bytes consumed. On Error, 0 is returned and no data is
33
33
  * consumed.
34
34
  */
35
- size_t fiobj_json2obj(fiobj_s **pobj, const void *data, size_t len);
36
- /* Formats an object into a JSON string. Remember to `fiobj_free`. */
37
- fiobj_s *fiobj_obj2json(fiobj_s *, uint8_t pretty);
35
+ size_t fiobj_json2obj(FIOBJ *pobj, const void *data, size_t len);
36
+ /**
37
+ * Stringify an object into a JSON string. Remember to `fiobj_free`.
38
+ *
39
+ * Note that only the foloowing basic fiobj types are supported: Primitives
40
+ * (True / False / NULL), Numbers (Number / Float), Strings, Hashes and Arrays.
41
+ *
42
+ * Some objects (such as the POSIX specific IO type) are unsupported and may be
43
+ * formatted incorrectly.
44
+ */
45
+ FIOBJ fiobj_obj2json(FIOBJ, uint8_t pretty);
46
+
47
+ /**
48
+ * Formats an object into a JSON string, appending the JSON string to an
49
+ * existing String. Remember to `fiobj_free` as usual.
50
+ *
51
+ * Note that only the following basic fiobj types are supported: Primitives
52
+ * (True / False / NULL), Numbers (Number / Float), Strings, Hashes and
53
+ * Arrays.
54
+ *
55
+ * Some objects (such as the POSIX specific IO type) are unsupported and may be
56
+ * formatted incorrectly.
57
+ */
58
+ FIOBJ fiobj_obj2json2(FIOBJ dest, FIOBJ object, uint8_t pretty);
59
+
60
+ #if DEBUG
61
+ void fiobj_test_json(void);
62
+ #endif
38
63
 
39
64
  #ifdef __cplusplus
40
65
  } /* extern "C" */
@@ -1,26 +1,34 @@
1
1
  /*
2
- Copyright: Boaz Segev, 2017
2
+ Copyright: Boaz Segev, 2017-2018
3
3
  License: MIT
4
4
  */
5
5
 
6
- #include "fiobj_internal.h"
6
+ #include "fiobj_numbers.h"
7
+ #include "fiobject.h"
8
+
9
+ #define FIO_OVERRIDE_MALLOC 1
10
+ #include "fio_mem.h"
11
+
12
+ #include <assert.h>
13
+ #include <errno.h>
14
+ #include <math.h>
7
15
 
8
16
  /* *****************************************************************************
9
17
  Numbers Type
10
18
  ***************************************************************************** */
11
19
 
12
20
  typedef struct {
13
- struct fiobj_vtable_s *vtable;
14
- uint64_t i;
21
+ fiobj_object_header_s head;
22
+ intptr_t i;
15
23
  } fiobj_num_s;
16
24
 
17
25
  typedef struct {
18
- struct fiobj_vtable_s *vtable;
26
+ fiobj_object_header_s head;
19
27
  double f;
20
28
  } fiobj_float_s;
21
29
 
22
- #define obj2num(o) ((fiobj_num_s *)(o))
23
- #define obj2float(o) ((fiobj_float_s *)(o))
30
+ #define obj2num(o) ((fiobj_num_s *)FIOBJ2PTR(o))
31
+ #define obj2float(o) ((fiobj_float_s *)FIOBJ2PTR(o))
24
32
 
25
33
  /* *****************************************************************************
26
34
  Numbers VTable
@@ -28,22 +36,22 @@ Numbers VTable
28
36
 
29
37
  static __thread char num_buffer[512];
30
38
 
31
- static int64_t fio_i2i(const fiobj_s *o) { return obj2num(o)->i; }
32
- static int64_t fio_f2i(const fiobj_s *o) {
33
- return (int64_t)floorl(obj2float(o)->f);
39
+ static intptr_t fio_i2i(const FIOBJ o) { return obj2num(o)->i; }
40
+ static intptr_t fio_f2i(const FIOBJ o) {
41
+ return (intptr_t)floorl(obj2float(o)->f);
34
42
  }
35
- static double fio_i2f(const fiobj_s *o) { return (double)obj2num(o)->i; }
36
- static double fio_f2f(const fiobj_s *o) { return obj2float(o)->f; }
43
+ static double fio_i2f(const FIOBJ o) { return (double)obj2num(o)->i; }
44
+ static double fio_f2f(const FIOBJ o) { return obj2float(o)->f; }
37
45
 
38
- static int fio_itrue(const fiobj_s *o) { return (obj2num(o)->i != 0); }
39
- static int fio_ftrue(const fiobj_s *o) { return (obj2float(o)->f != 0); }
46
+ static size_t fio_itrue(const FIOBJ o) { return (obj2num(o)->i != 0); }
47
+ static size_t fio_ftrue(const FIOBJ o) { return (obj2float(o)->f != 0); }
40
48
 
41
- static fio_cstr_s fio_i2str(const fiobj_s *o) {
49
+ static fio_cstr_s fio_i2str(const FIOBJ o) {
42
50
  return (fio_cstr_s){
43
51
  .buffer = num_buffer, .len = fio_ltoa(num_buffer, obj2num(o)->i, 10),
44
52
  };
45
53
  }
46
- static fio_cstr_s fio_f2str(const fiobj_s *o) {
54
+ static fio_cstr_s fio_f2str(const FIOBJ o) {
47
55
  if (isnan(obj2float(o)->f))
48
56
  return (fio_cstr_s){.buffer = "NaN", .len = 3};
49
57
  else if (isinf(obj2float(o)->f)) {
@@ -57,75 +65,399 @@ static fio_cstr_s fio_f2str(const fiobj_s *o) {
57
65
  };
58
66
  }
59
67
 
60
- static int fiobj_i_is_eq(const fiobj_s *self, const fiobj_s *other) {
61
- return self->type == other->type && obj2num(self)->i == obj2num(other)->i;
68
+ static size_t fiobj_i_is_eq(const FIOBJ self, const FIOBJ other) {
69
+ return obj2num(self)->i == obj2num(other)->i;
62
70
  }
63
- static int fiobj_f_is_eq(const fiobj_s *self, const fiobj_s *other) {
64
- return self->type == other->type && obj2float(self)->f == obj2float(other)->f;
71
+ static size_t fiobj_f_is_eq(const FIOBJ self, const FIOBJ other) {
72
+ return obj2float(self)->f == obj2float(other)->f;
65
73
  }
66
74
 
67
- static struct fiobj_vtable_s FIOBJ_VTABLE_INT = {
68
- .name = "Number",
69
- .free = fiobj_simple_dealloc,
75
+ void fiobject___simple_dealloc(FIOBJ o, void (*task)(FIOBJ, void *), void *arg);
76
+ uintptr_t fiobject___noop_count(FIOBJ o);
77
+
78
+ const fiobj_object_vtable_s FIOBJECT_VTABLE_NUMBER = {
79
+ .class_name = "Number",
70
80
  .to_i = fio_i2i,
71
81
  .to_f = fio_i2f,
72
82
  .to_str = fio_i2str,
73
83
  .is_true = fio_itrue,
74
84
  .is_eq = fiobj_i_is_eq,
75
- .count = fiobj_noop_count,
76
- .unwrap = fiobj_noop_unwrap,
77
- .each1 = fiobj_noop_each1,
85
+ .count = fiobject___noop_count,
86
+ .dealloc = fiobject___simple_dealloc,
78
87
  };
79
88
 
80
- const uintptr_t FIOBJ_T_NUMBER = (uintptr_t)&FIOBJ_VTABLE_INT;
81
-
82
- static struct fiobj_vtable_s FIOBJ_VTABLE_FLOAT = {
83
- .name = "Float",
84
- .free = fiobj_simple_dealloc,
89
+ const fiobj_object_vtable_s FIOBJECT_VTABLE_FLOAT = {
90
+ .class_name = "Float",
85
91
  .to_i = fio_f2i,
86
92
  .to_f = fio_f2f,
87
93
  .is_true = fio_ftrue,
88
94
  .to_str = fio_f2str,
89
95
  .is_eq = fiobj_f_is_eq,
90
- .count = fiobj_noop_count,
91
- .unwrap = fiobj_noop_unwrap,
92
- .each1 = fiobj_noop_each1,
96
+ .count = fiobject___noop_count,
97
+ .dealloc = fiobject___simple_dealloc,
93
98
  };
94
99
 
95
- const uintptr_t FIOBJ_T_FLOAT = (uintptr_t)&FIOBJ_VTABLE_FLOAT;
96
-
97
100
  /* *****************************************************************************
98
101
  Number API
99
102
  ***************************************************************************** */
100
103
 
101
104
  /** Creates a Number object. Remember to use `fiobj_free`. */
102
- fiobj_s *fiobj_num_new(int64_t num) {
103
- fiobj_num_s *o = (fiobj_num_s *)fiobj_alloc(sizeof(*o));
104
- if (!o)
105
- perror("ERROR: fiobj number couldn't allocate memory"), exit(errno);
105
+ FIOBJ fiobj_num_new_bignum(intptr_t num) {
106
+ fiobj_num_s *o = malloc(sizeof(*o));
107
+ if (!o) {
108
+ perror("ERROR: fiobj number couldn't allocate memory");
109
+ exit(errno);
110
+ }
106
111
  *o = (fiobj_num_s){
107
- .vtable = &FIOBJ_VTABLE_INT, .i = num,
112
+ .head =
113
+ {
114
+ .type = FIOBJ_T_NUMBER, .ref = 1,
115
+ },
116
+ .i = num,
108
117
  };
109
- return (fiobj_s *)o;
118
+ return (FIOBJ)o;
110
119
  }
111
120
 
112
- /** Mutates a Number object's value. Effects every object's reference! */
113
- void fiobj_num_set(fiobj_s *target, int64_t num) { obj2num(target)->i = num; }
121
+ /** Mutates a Big Number object's value. Effects every object's reference! */
122
+ // void fiobj_num_set(FIOBJ target, intptr_t num) {
123
+ // assert(FIOBJ_TYPE_IS(target, FIOBJ_T_NUMBER) &&
124
+ // FIOBJ_IS_ALLOCATED(target)); obj2num(target)->i = num;
125
+ // }
126
+
127
+ /** Creates a temporary Number object. This ignores `fiobj_free`. */
128
+ FIOBJ fiobj_num_tmp(intptr_t num) {
129
+ static __thread fiobj_num_s ret;
130
+ ret = (fiobj_num_s){
131
+ .head = {.type = FIOBJ_T_NUMBER, .ref = ((~(uint32_t)0) >> 4)}, .i = num,
132
+ };
133
+ return (FIOBJ)&ret;
134
+ }
114
135
 
115
136
  /* *****************************************************************************
116
137
  Float API
117
138
  ***************************************************************************** */
118
139
 
119
140
  /** Creates a Float object. Remember to use `fiobj_free`. */
120
- fiobj_s *fiobj_float_new(double num) {
121
- fiobj_float_s *o = (fiobj_float_s *)fiobj_alloc(sizeof(*o));
122
- if (!o)
123
- perror("ERROR: fiobj float couldn't allocate memory"), exit(errno);
141
+ FIOBJ fiobj_float_new(double num) {
142
+ fiobj_float_s *o = malloc(sizeof(*o));
143
+ if (!o) {
144
+ perror("ERROR: fiobj float couldn't allocate memory");
145
+ exit(errno);
146
+ }
124
147
  *o = (fiobj_float_s){
125
- .vtable = &FIOBJ_VTABLE_FLOAT, .f = num,
148
+ .head =
149
+ {
150
+ .type = FIOBJ_T_FLOAT, .ref = 1,
151
+ },
152
+ .f = num,
126
153
  };
127
- return (fiobj_s *)o;
154
+ return (FIOBJ)o;
128
155
  }
129
156
 
130
157
  /** Mutates a Float object's value. Effects every object's reference! */
131
- void fiobj_float_set(fiobj_s *obj, double num) { obj2float(obj)->f = num; }
158
+ void fiobj_float_set(FIOBJ obj, double num) {
159
+ assert(FIOBJ_TYPE_IS(obj, FIOBJ_T_FLOAT));
160
+ obj2float(obj)->f = num;
161
+ }
162
+
163
+ /** Creates a temporary Number object. This ignores `fiobj_free`. */
164
+ FIOBJ fiobj_float_tmp(double num) {
165
+ static __thread fiobj_float_s ret;
166
+ ret = (fiobj_float_s){
167
+ .head =
168
+ {
169
+ .type = FIOBJ_T_FLOAT, .ref = ((~(uint32_t)0) >> 4),
170
+ },
171
+ .f = num,
172
+ };
173
+ return (FIOBJ)&ret;
174
+ }
175
+
176
+ /* *****************************************************************************
177
+ Number and Float Helpers
178
+ ***************************************************************************** */
179
+ static const char hex_notation[] = {'0', '1', '2', '3', '4', '5', '6', '7',
180
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
181
+
182
+ /**
183
+ * A helper function that converts between String data to a signed int64_t.
184
+ *
185
+ * Numbers are assumed to be in base 10. `0x##` (or `x##`) and `0b##` (or
186
+ * `b##`) are recognized as base 16 and base 2 (binary MSB first)
187
+ * respectively.
188
+ */
189
+ intptr_t fio_atol(char **pstr) {
190
+ /* use strtol ?*/
191
+ char *str = *pstr;
192
+ uintptr_t result = 0;
193
+ uint8_t invert = 0;
194
+ while (str[0] == '-') {
195
+ invert ^= 1;
196
+ str++;
197
+ }
198
+ if (str[0] == 'B' || str[0] == 'b' ||
199
+ (str[0] == '0' && (str[1] == 'b' || str[1] == 'B'))) {
200
+ /* base 2 */
201
+ if (str[0] == '0')
202
+ str++;
203
+ str++;
204
+ while (str[0] == '0' || str[0] == '1') {
205
+ result = (result << 1) | (str[0] == '1');
206
+ str++;
207
+ }
208
+ } else if (str[0] == 'x' || str[0] == 'X' ||
209
+ (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))) {
210
+ /* base 16 */
211
+ uint8_t tmp;
212
+ if (str[0] == '0')
213
+ str++;
214
+ str++;
215
+ while (1) {
216
+ if (str[0] >= '0' && str[0] <= '9')
217
+ tmp = str[0] - '0';
218
+ else if (str[0] >= 'A' && str[0] <= 'F')
219
+ tmp = str[0] - ('A' - 10);
220
+ else if (str[0] >= 'a' && str[0] <= 'f')
221
+ tmp = str[0] - ('a' - 10);
222
+ else
223
+ goto finish;
224
+ result = (result << 4) | tmp;
225
+ str++;
226
+ }
227
+ } else {
228
+ /* base 10 */
229
+ const char *end = str;
230
+ while (end[0] >= '0' && end[0] <= '9' && (uintptr_t)(end - str) < 22)
231
+ end++;
232
+ if ((uintptr_t)(end - str) > 21) /* too large for a number */
233
+ return 0;
234
+
235
+ while (str < end) {
236
+ result = (result * 10) + (str[0] - '0');
237
+ str++;
238
+ }
239
+ }
240
+ finish:
241
+ if (invert)
242
+ result = 0 - result;
243
+ *pstr = str;
244
+ return (intptr_t)result;
245
+ }
246
+
247
+ /** A helper function that convers between String data to a signed double. */
248
+ double fio_atof(char **pstr) { return strtold(*pstr, pstr); }
249
+
250
+ /* *****************************************************************************
251
+ Numbers to Strings
252
+ ***************************************************************************** */
253
+
254
+ /**
255
+ * A helper function that convers between a signed int64_t to a string.
256
+ *
257
+ * No overflow guard is provided, make sure there's at least 66 bytes
258
+ * available (for base 2).
259
+ *
260
+ * Supports base 2, base 10 and base 16. An unsupported base will silently
261
+ * default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
262
+ * beginning of the string).
263
+ *
264
+ * Returns the number of bytes actually written (excluding the NUL
265
+ * terminator).
266
+ */
267
+ size_t fio_ltoa(char *dest, int64_t num, uint8_t base) {
268
+ if (!num) {
269
+ *(dest++) = '0';
270
+ *(dest++) = 0;
271
+ return 1;
272
+ }
273
+
274
+ size_t len = 0;
275
+
276
+ if (base == 2) {
277
+ uint64_t n = num; /* avoid bit shifting inconsistencies with signed bit */
278
+ uint8_t i = 0; /* counting bits */
279
+
280
+ while ((i < 64) && (n & 0x8000000000000000) == 0) {
281
+ n = n << 1;
282
+ i++;
283
+ }
284
+ /* make sure the Binary representation doesn't appear signed. */
285
+ if (i) {
286
+ dest[len++] = '0';
287
+ }
288
+ /* write to dest. */
289
+ while (i < 64) {
290
+ dest[len++] = ((n & 0x8000000000000000) ? '1' : '0');
291
+ n = n << 1;
292
+ i++;
293
+ }
294
+ dest[len] = 0;
295
+ return len;
296
+
297
+ } else if (base == 16) {
298
+ uint64_t n = num; /* avoid bit shifting inconsistencies with signed bit */
299
+ uint8_t i = 0; /* counting bytes */
300
+ while (i < 8 && (n & 0xFF00000000000000) == 0) {
301
+ n = n << 8;
302
+ i++;
303
+ }
304
+ /* make sure the Hex representation doesn't appear signed. */
305
+ if (i && (n & 0x8000000000000000)) {
306
+ dest[len++] = '0';
307
+ dest[len++] = '0';
308
+ }
309
+ /* write the damn thing */
310
+ while (i < 8) {
311
+ uint8_t tmp = (n & 0xF000000000000000) >> 60;
312
+ dest[len++] = hex_notation[tmp];
313
+ tmp = (n & 0x0F00000000000000) >> 56;
314
+ dest[len++] = hex_notation[tmp];
315
+ i++;
316
+ n = n << 8;
317
+ }
318
+ dest[len] = 0;
319
+ return len;
320
+ }
321
+
322
+ /* fallback to base 10 */
323
+ uint64_t rem = 0;
324
+ uint64_t factor = 1;
325
+ if (num < 0) {
326
+ dest[len++] = '-';
327
+ num = 0 - num;
328
+ }
329
+
330
+ while (num / factor)
331
+ factor *= 10;
332
+
333
+ while (factor > 1) {
334
+ factor = factor / 10;
335
+ rem = (rem * 10);
336
+ dest[len++] = '0' + ((num / factor) - rem);
337
+ rem += ((num / factor) - rem);
338
+ }
339
+ dest[len] = 0;
340
+ return len;
341
+ }
342
+
343
+ /**
344
+ * A helper function that convers between a double to a string.
345
+ *
346
+ * No overflow guard is provided, make sure there's at least 130 bytes
347
+ * available (for base 2).
348
+ *
349
+ * Supports base 2, base 10 and base 16. An unsupported base will silently
350
+ * default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
351
+ * beginning of the string).
352
+ *
353
+ * Returns the number of bytes actually written (excluding the NUL
354
+ * terminator).
355
+ */
356
+ size_t fio_ftoa(char *dest, double num, uint8_t base) {
357
+ if (base == 2 || base == 16) {
358
+ /* handle the binary / Hex representation the same as if it were an
359
+ * int64_t
360
+ */
361
+ int64_t *i = (void *)&num;
362
+ return fio_ltoa(dest, *i, base);
363
+ }
364
+
365
+ size_t written = sprintf(dest, "%g", num);
366
+ uint8_t need_zero = 1;
367
+ char *start = dest;
368
+ while (*start) {
369
+ if (*start == ',') // locale issues?
370
+ *start = '.';
371
+ if (*start == '.' || *start == 'e') {
372
+ need_zero = 0;
373
+ break;
374
+ }
375
+ start++;
376
+ }
377
+ if (need_zero) {
378
+ dest[written++] = '.';
379
+ dest[written++] = '0';
380
+ }
381
+ return written;
382
+ }
383
+
384
+ static __thread char num_buffer[512];
385
+
386
+ fio_cstr_s fio_ltocstr(long i) {
387
+ return (fio_cstr_s){.buffer = num_buffer, .len = fio_ltoa(num_buffer, i, 10)};
388
+ }
389
+ fio_cstr_s fio_ftocstr(double f) {
390
+ return (fio_cstr_s){.buffer = num_buffer, .len = fio_ftoa(num_buffer, f, 10)};
391
+ }
392
+
393
+ /* *****************************************************************************
394
+ Tests
395
+ ***************************************************************************** */
396
+
397
+ #if DEBUG
398
+ void fiobj_test_numbers(void) {
399
+ #define NUMTEST_ASSERT(cond, ...) \
400
+ if (!(cond)) { \
401
+ fprintf(stderr, __VA_ARGS__); \
402
+ fprintf(stderr, "Testing failed.\n"); \
403
+ exit(-1); \
404
+ }
405
+ FIOBJ i = fiobj_num_new(8);
406
+ fprintf(stderr, "=== Testing Numbers\n");
407
+ fprintf(stderr, "* FIOBJ_NUMBER_SIGN_MASK == %p\n",
408
+ (void *)FIOBJ_NUMBER_SIGN_MASK);
409
+ fprintf(stderr, "* FIOBJ_NUMBER_SIGN_BIT == %p\n",
410
+ (void *)FIOBJ_NUMBER_SIGN_BIT);
411
+ fprintf(stderr, "* FIOBJ_NUMBER_SIGN_EXCLUDE_BIT == %p\n",
412
+ (void *)FIOBJ_NUMBER_SIGN_EXCLUDE_BIT);
413
+ NUMTEST_ASSERT(FIOBJ_TYPE_IS(i, FIOBJ_T_NUMBER),
414
+ "* FIOBJ_TYPE_IS failed to return true.");
415
+ NUMTEST_ASSERT((FIOBJ_TYPE(i) == FIOBJ_T_NUMBER),
416
+ "* FIOBJ_TYPE failed to return type.");
417
+ NUMTEST_ASSERT(!FIOBJ_TYPE_IS(i, FIOBJ_T_NULL),
418
+ "* FIOBJ_TYPE_IS failed to return false.");
419
+ NUMTEST_ASSERT((i & FIOBJECT_NUMBER_FLAG),
420
+ "* Number 8 was dynamically allocated?! %p\n", (void *)i);
421
+ NUMTEST_ASSERT((fiobj_obj2num(i) == 8), "* Number 8 was not returned! %p\n",
422
+ (void *)i);
423
+ fiobj_free(i);
424
+ i = fiobj_num_new(-1);
425
+ NUMTEST_ASSERT((i & FIOBJECT_NUMBER_FLAG),
426
+ "* Number -1 was dynamically allocated?! %p\n", (void *)i);
427
+ NUMTEST_ASSERT((fiobj_obj2num(i) == -1), "* Number -1 was not returned! %p\n",
428
+ (void *)i);
429
+ fiobj_free(i);
430
+ i = fiobj_num_new(INTPTR_MAX);
431
+ NUMTEST_ASSERT((i & FIOBJECT_NUMBER_FLAG) == 0,
432
+ "* INTPTR_MAX was statically allocated?! %p\n", (void *)i);
433
+ NUMTEST_ASSERT((fiobj_obj2num(i) == INTPTR_MAX),
434
+ "* INTPTR_MAX was not returned! %p\n", (void *)i);
435
+ NUMTEST_ASSERT(
436
+ FIOBJ_TYPE_IS(i, FIOBJ_T_NUMBER),
437
+ "* FIOBJ_TYPE_IS failed to return true for dynamic allocation.");
438
+ NUMTEST_ASSERT((FIOBJ_TYPE(i) == FIOBJ_T_NUMBER),
439
+ "* FIOBJ_TYPE failed to return type for dynamic allocation.");
440
+ fiobj_free(i);
441
+ i = fiobj_num_new(INTPTR_MIN);
442
+ NUMTEST_ASSERT((i & FIOBJECT_NUMBER_FLAG) == 0,
443
+ "* INTPTR_MIN was statically allocated?! %p\n", (void *)i);
444
+ NUMTEST_ASSERT((fiobj_obj2num(i) == INTPTR_MIN),
445
+ "* INTPTR_MIN was not returned! %p\n", (void *)i);
446
+ fiobj_free(i);
447
+ fprintf(stderr, "* passed.\n");
448
+ fprintf(stderr, "=== Testing Floats\n");
449
+ i = fiobj_float_new(1.0);
450
+ NUMTEST_ASSERT(((i & FIOBJECT_NUMBER_FLAG) == 0),
451
+ "* float 1 was statically allocated?! %p\n", (void *)i);
452
+ NUMTEST_ASSERT((fiobj_obj2float(i) == 1.0),
453
+ "* Float 1.0 was not returned! %p\n", (void *)i);
454
+ fiobj_free(i);
455
+ i = fiobj_float_new(-1.0);
456
+ NUMTEST_ASSERT((i & FIOBJECT_NUMBER_FLAG) == 0,
457
+ "* Float -1 was statically allocated?! %p\n", (void *)i);
458
+ NUMTEST_ASSERT((fiobj_obj2float(i) == -1.0),
459
+ "* Float -1 was not returned! %p\n", (void *)i);
460
+ fiobj_free(i);
461
+ fprintf(stderr, "* passed.\n");
462
+ }
463
+ #endif