iodine 0.4.8 → 0.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +26 -0
  4. data/README.md +18 -12
  5. data/SPEC-Websocket-Draft.md +9 -5
  6. data/bin/ws-echo +3 -0
  7. data/examples/config.ru +0 -1
  8. data/examples/echo.ru +3 -1
  9. data/examples/redis.ru +0 -1
  10. data/ext/iodine/base64.c +97 -105
  11. data/ext/iodine/defer.c +16 -1
  12. data/ext/iodine/defer.h +10 -0
  13. data/ext/iodine/evio.c +35 -13
  14. data/ext/iodine/extconf.rb +1 -1
  15. data/ext/iodine/facil.c +12 -1
  16. data/ext/iodine/facil.h +3 -1
  17. data/ext/iodine/fio2resp.c +71 -0
  18. data/ext/iodine/fio2resp.h +50 -0
  19. data/ext/iodine/fio_cli_helper.c +404 -0
  20. data/ext/iodine/fio_cli_helper.h +152 -0
  21. data/ext/iodine/fiobj.h +631 -0
  22. data/ext/iodine/fiobj_alloc.c +81 -0
  23. data/ext/iodine/fiobj_ary.c +290 -0
  24. data/ext/iodine/fiobj_generic.c +260 -0
  25. data/ext/iodine/fiobj_hash.c +447 -0
  26. data/ext/iodine/fiobj_io.c +58 -0
  27. data/ext/iodine/fiobj_json.c +779 -0
  28. data/ext/iodine/fiobj_misc.c +213 -0
  29. data/ext/iodine/fiobj_numbers.c +113 -0
  30. data/ext/iodine/fiobj_primitives.c +98 -0
  31. data/ext/iodine/fiobj_str.c +261 -0
  32. data/ext/iodine/fiobj_sym.c +213 -0
  33. data/ext/iodine/fiobj_tests.c +474 -0
  34. data/ext/iodine/fiobj_types.h +290 -0
  35. data/ext/iodine/http1.c +54 -36
  36. data/ext/iodine/http1_parser.c +143 -35
  37. data/ext/iodine/http1_parser.h +6 -3
  38. data/ext/iodine/http1_response.c +0 -1
  39. data/ext/iodine/http_response.c +1 -1
  40. data/ext/iodine/iodine.c +20 -4
  41. data/ext/iodine/iodine_protocol.c +5 -4
  42. data/ext/iodine/iodine_pubsub.c +1 -1
  43. data/ext/iodine/random.c +5 -5
  44. data/ext/iodine/sha1.c +5 -8
  45. data/ext/iodine/sha2.c +8 -11
  46. data/ext/iodine/sha2.h +3 -3
  47. data/ext/iodine/sock.c +29 -31
  48. data/ext/iodine/websocket_parser.h +428 -0
  49. data/ext/iodine/websockets.c +112 -377
  50. data/ext/iodine/xor-crypt.c +16 -12
  51. data/lib/iodine/version.rb +1 -1
  52. metadata +21 -3
  53. data/ext/iodine/empty.h +0 -26
@@ -0,0 +1,213 @@
1
+ /*
2
+ Copyright: Boaz segev, 2017
3
+ License: MIT
4
+
5
+ Feel free to copy, use and enjoy according to the license provided.
6
+ */
7
+
8
+ #include "fiobj_types.h"
9
+
10
+ /* *****************************************************************************
11
+ Hashing (SipHash copy)
12
+ ***************************************************************************** */
13
+
14
+ #if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) && \
15
+ __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
16
+ /* the algorithm was designed as little endian... so, byte swap 64 bit. */
17
+ #define sip_local64(i) \
18
+ (((i)&0xFFULL) << 56) | (((i)&0xFF00ULL) << 40) | \
19
+ (((i)&0xFF0000ULL) << 24) | (((i)&0xFF000000ULL) << 8) | \
20
+ (((i)&0xFF00000000ULL) >> 8) | (((i)&0xFF0000000000ULL) >> 24) | \
21
+ (((i)&0xFF000000000000ULL) >> 40) | (((i)&0xFF00000000000000ULL) >> 56)
22
+ #else
23
+ /* no need */
24
+ #define sip_local64(i) (i)
25
+ #endif
26
+
27
+ /* 64Bit left rotation, inlined. */
28
+ #define lrot64(i, bits) \
29
+ (((uint64_t)(i) << (bits)) | ((uint64_t)(i) >> (64 - (bits))))
30
+
31
+ uint64_t fiobj_sym_hash(const void *data, size_t len) {
32
+ /* initialize the 4 words */
33
+ uint64_t v0 = (0x0706050403020100ULL ^ 0x736f6d6570736575ULL);
34
+ uint64_t v1 = (0x0f0e0d0c0b0a0908ULL ^ 0x646f72616e646f6dULL);
35
+ uint64_t v2 = (0x0706050403020100ULL ^ 0x6c7967656e657261ULL);
36
+ uint64_t v3 = (0x0f0e0d0c0b0a0908ULL ^ 0x7465646279746573ULL);
37
+ const uint64_t *w64 = data;
38
+ uint8_t len_mod = len & 255;
39
+ union {
40
+ uint64_t i;
41
+ uint8_t str[8];
42
+ } word;
43
+
44
+ #define hash_map_SipRound \
45
+ do { \
46
+ v2 += v3; \
47
+ v3 = lrot64(v3, 16) ^ v2; \
48
+ v0 += v1; \
49
+ v1 = lrot64(v1, 13) ^ v0; \
50
+ v0 = lrot64(v0, 32); \
51
+ v2 += v1; \
52
+ v0 += v3; \
53
+ v1 = lrot64(v1, 17) ^ v2; \
54
+ v3 = lrot64(v3, 21) ^ v0; \
55
+ v2 = lrot64(v2, 32); \
56
+ } while (0);
57
+
58
+ while (len >= 8) {
59
+ word.i = sip_local64(*w64);
60
+ v3 ^= word.i;
61
+ /* Sip Rounds */
62
+ hash_map_SipRound;
63
+ hash_map_SipRound;
64
+ v0 ^= word.i;
65
+ w64 += 1;
66
+ len -= 8;
67
+ }
68
+ word.i = 0;
69
+ uint8_t *pos = word.str;
70
+ uint8_t *w8 = (void *)w64;
71
+ switch (len) { /* fallthrough is intentional */
72
+ case 7:
73
+ pos[6] = w8[6];
74
+ case 6:
75
+ pos[5] = w8[5];
76
+ case 5:
77
+ pos[4] = w8[4];
78
+ case 4:
79
+ pos[3] = w8[3];
80
+ case 3:
81
+ pos[2] = w8[2];
82
+ case 2:
83
+ pos[1] = w8[1];
84
+ case 1:
85
+ pos[0] = w8[0];
86
+ }
87
+ word.str[7] = len_mod;
88
+
89
+ /* last round */
90
+ v3 ^= word.i;
91
+ hash_map_SipRound;
92
+ hash_map_SipRound;
93
+ v0 ^= word.i;
94
+ /* Finalization */
95
+ v2 ^= 0xff;
96
+ /* d iterations of SipRound */
97
+ hash_map_SipRound;
98
+ hash_map_SipRound;
99
+ hash_map_SipRound;
100
+ hash_map_SipRound;
101
+ /* XOR it all together */
102
+ v0 ^= v1 ^ v2 ^ v3;
103
+ #undef hash_map_SipRound
104
+ return v0;
105
+ }
106
+
107
+ /* *****************************************************************************
108
+ Symbol VTable
109
+ ***************************************************************************** */
110
+
111
+ static int fiobj_sym_is_eq(fiobj_s *self, fiobj_s *other) {
112
+ if (!other || other->type != self->type)
113
+ return 0;
114
+ return obj2sym(self)->hash == obj2sym(other)->hash;
115
+ }
116
+
117
+ static fio_cstr_s fio_sym2str(fiobj_s *o) {
118
+ return (fio_cstr_s){.buffer = obj2sym(o)->str, .len = obj2sym(o)->len};
119
+ }
120
+ static int64_t fio_sym2i(fiobj_s *o) {
121
+ char *s = obj2str(o)->str;
122
+ return fio_atol(&s);
123
+ }
124
+ static double fio_sym2f(fiobj_s *o) {
125
+ char *s = obj2sym(o)->str;
126
+ return fio_atof(&s);
127
+ }
128
+
129
+ static struct fiobj_vtable_s FIOBJ_VTABLE_SYMBOL = {
130
+ .free = fiobj_simple_dealloc,
131
+ .to_i = fio_sym2i,
132
+ .to_f = fio_sym2f,
133
+ .to_str = fio_sym2str,
134
+ .is_eq = fiobj_sym_is_eq,
135
+ .count = fiobj_noop_count,
136
+ .each1 = fiobj_noop_each1,
137
+ };
138
+
139
+ /* *****************************************************************************
140
+ Symbol API
141
+ ***************************************************************************** */
142
+
143
+ static inline fiobj_s *fiobj_sym_alloc(size_t len) {
144
+ fiobj_head_s *head;
145
+ head = malloc(sizeof(*head) + sizeof(fio_sym_s) + len + 1);
146
+ if (!head)
147
+ perror("ERROR: fiobj symbol couldn't allocate memory"), exit(errno);
148
+ *head = (fiobj_head_s){
149
+ .ref = 1, .vtable = &FIOBJ_VTABLE_SYMBOL,
150
+ };
151
+ *obj2sym(HEAD2OBJ(head)) = (fio_sym_s){
152
+ .type = FIOBJ_T_SYMBOL, .len = len,
153
+ };
154
+ obj2sym(HEAD2OBJ(head))->str[len] = 0;
155
+ return HEAD2OBJ(head);
156
+ }
157
+
158
+ /** Creates a Symbol object. Use `fiobj_free`. */
159
+ fiobj_s *fiobj_sym_new(const char *str, size_t len) {
160
+ fiobj_s *s = fiobj_sym_alloc(len);
161
+ if (str)
162
+ memcpy(obj2sym(s)->str, str, len);
163
+ obj2sym(s)->hash = (uintptr_t)fiobj_sym_hash(str, len);
164
+ return s;
165
+ }
166
+
167
+ /** Creates a Symbol object using a printf like interface. */
168
+ __attribute__((format(printf, 1, 0))) fiobj_s *
169
+ fiobj_symvprintf(const char *format, va_list argv) {
170
+ fiobj_s *sym = NULL;
171
+ va_list argv_cpy;
172
+ va_copy(argv_cpy, argv);
173
+ int len = vsnprintf(NULL, 0, format, argv_cpy);
174
+ va_end(argv_cpy);
175
+ if (len == 0) {
176
+ sym = fiobj_sym_alloc(0);
177
+ ((fio_sym_s *)(sym))->hash = fiobj_sym_hash(NULL, 0);
178
+ }
179
+ if (len <= 0)
180
+ return sym;
181
+ sym = fiobj_sym_alloc(len); /* adds 1 to len, for NUL */
182
+ vsnprintf(((fio_sym_s *)(sym))->str, len + 1, format, argv);
183
+ ((fio_sym_s *)(sym))->hash =
184
+ (uintptr_t)fiobj_sym_hash(((fio_sym_s *)(sym))->str, len);
185
+ return sym;
186
+ }
187
+ __attribute__((format(printf, 1, 2))) fiobj_s *
188
+ fiobj_symprintf(const char *format, ...) {
189
+ va_list argv;
190
+ va_start(argv, format);
191
+ fiobj_s *sym = fiobj_symvprintf(format, argv);
192
+ va_end(argv);
193
+ return sym;
194
+ }
195
+
196
+ /** Returns 1 if both Symbols are equal and 0 if not. */
197
+ int fiobj_sym_iseql(fiobj_s *sym1, fiobj_s *sym2) {
198
+ if (sym1->type != FIOBJ_T_SYMBOL || sym2->type != FIOBJ_T_SYMBOL)
199
+ return 0;
200
+ return (((fio_sym_s *)sym1)->hash == ((fio_sym_s *)sym2)->hash);
201
+ }
202
+
203
+ /**
204
+ * Returns a symbol's identifier.
205
+ *
206
+ * The unique identifier is calculated using SipHash and is equal for all Symbol
207
+ * objects that were created using the same data.
208
+ */
209
+ uintptr_t fiobj_sym_id(fiobj_s *sym) {
210
+ if (sym->type != FIOBJ_T_SYMBOL)
211
+ return 0;
212
+ return obj2sym(sym)->hash;
213
+ }
@@ -0,0 +1,474 @@
1
+ #ifdef DEBUG
2
+ /*
3
+ Copyright: Boaz segev, 2017
4
+ License: MIT
5
+
6
+ Feel free to copy, use and enjoy according to the license provided.
7
+ */
8
+
9
+ #include "bscrypt.h"
10
+ #include "fio2resp.h"
11
+ #include "fiobj_types.h"
12
+
13
+ /* *****************************************************************************
14
+ A JSON testing
15
+ ***************************************************************************** */
16
+
17
+ int fiobj_test_json_str(char const *json, size_t len, uint8_t print_result) {
18
+ clock_t start, end;
19
+
20
+ fiobj_s *result = NULL;
21
+ start = clock();
22
+ size_t i = fiobj_json2obj(&result, json, len);
23
+ end = clock();
24
+ if (!i || !result) {
25
+ fprintf(stderr, "FAILED to parse JSON?! consumed %lu and result == %p\n", i,
26
+ (void *)result);
27
+ return -1;
28
+ }
29
+ fprintf(stderr, "* Parsed JSON in %lu\n", end - start);
30
+ start = clock();
31
+ fiobj_s *jstr = fiobj_obj2json(result, 1);
32
+ end = clock();
33
+ fprintf(stderr, "* Formatted JSON in %lu\n", end - start);
34
+ fprintf(stderr,
35
+ "Consumed %lu bytes out of %lu with result length %llu:\n%s\n", i,
36
+ len, fiobj_obj2cstr(jstr).length,
37
+ (print_result) ? fiobj_obj2cstr(jstr).buffer
38
+ : "\t\tfiles aren't printed.");
39
+
40
+ start = clock();
41
+ fiobj_free(result);
42
+ end = clock();
43
+ fprintf(stderr, "* Freed JSON result in %lu\n", end - start);
44
+
45
+ start = clock();
46
+ fiobj_free(jstr);
47
+ end = clock();
48
+ fprintf(stderr, "* Freed JSON String in %lu\n", end - start);
49
+ return 0;
50
+ }
51
+ void fiobj_test_hash_json(void) {
52
+ fiobj_pt hash = fiobj_hash_new();
53
+ fiobj_pt hash2 = fiobj_hash_new();
54
+ fiobj_pt hash3 = fiobj_hash_new();
55
+ fiobj_pt syms = fiobj_ary_new(); /* freed within Hashes */
56
+ char num_buffer[68] = "0x";
57
+ fiobj_pt tmp, sym;
58
+
59
+ sym = fiobj_sym_new("id", 2);
60
+ fiobj_ary_push(syms, sym);
61
+ fiobj_hash_set(hash, sym, fiobj_num_new(bscrypt_rand64()));
62
+
63
+ sym = fiobj_sym_new("number", 6);
64
+ fiobj_ary_push(syms, sym);
65
+ fiobj_hash_set(hash, sym, fiobj_num_new(42));
66
+
67
+ sym = fiobj_sym_new("float", 5);
68
+ fiobj_ary_push(syms, sym);
69
+ fiobj_hash_set(hash, sym, fiobj_float_new(42.42));
70
+
71
+ sym = fiobj_sym_new("string", 6);
72
+ fiobj_ary_push(syms, sym);
73
+ fiobj_hash_set(
74
+ hash, sym,
75
+ fiobj_strprintf(u8"𝄞\n\ttake \\ my \\ %s"
76
+ "\n\ttake \\ my \\ whole ❤️ %s ❤️ too...\n",
77
+ "hand", "heart"));
78
+ sym = fiobj_sym_new("hash", 4);
79
+ fiobj_ary_push(syms, sym);
80
+ tmp = fiobj_hash_new();
81
+ fiobj_hash_set(hash, sym, tmp);
82
+
83
+ for (int i = 1; i < 6; i++) {
84
+ /* creates a temporary symbol, since we're not retriving the data */
85
+ sym = fiobj_symprintf("%d", i);
86
+ /* make sure the Hash isn't leaking */
87
+ for (size_t j = 0; j < 7; j++) {
88
+ /* set alternating key-value pairs */
89
+ if (i & 1)
90
+ fiobj_hash_set(
91
+ tmp, sym,
92
+ fiobj_str_new(num_buffer, fio_ltoa(num_buffer + 2, i, 16) + 2));
93
+ else
94
+ fiobj_hash_set(tmp, sym, fiobj_num_new(i));
95
+ }
96
+ fiobj_free(sym);
97
+ }
98
+
99
+ sym = fiobj_sym_new("symbols", 7);
100
+ fiobj_ary_push(syms, sym);
101
+ fiobj_hash_set(hash, sym, syms);
102
+
103
+ /* test`fiobj_iseq */
104
+ {
105
+ /* shallow copy in order */
106
+ size_t len = fiobj_ary_count(syms) - 1;
107
+ for (size_t i = 0; i <= len; i++) {
108
+ sym = fiobj_ary_entry(syms, i);
109
+ fiobj_hash_set(hash2, sym, fiobj_dup(fiobj_hash_get(hash, sym)));
110
+ }
111
+ /* shallow copy in reverse order */
112
+ for (size_t i = 0; i <= len; i++) {
113
+ sym = fiobj_ary_entry(syms, len - i);
114
+ fiobj_hash_set(hash3, sym, fiobj_dup(fiobj_hash_get(hash, sym)));
115
+ }
116
+ fprintf(stderr, "* Testing shallow copy reference count: %s\n",
117
+ (OBJ2HEAD(syms).ref == 3) ? "passed." : "FAILED!");
118
+ fprintf(stderr,
119
+ "* Testing deep object equality review:\n"
120
+ " * Eq. Hash (ordered): %s\n"
121
+ " * Eq. Hash (unordered): %s\n"
122
+ " * Hash vs. Array: %s\n",
123
+ (fiobj_iseq(hash, hash2)) ? "passed." : "FAILED!",
124
+ (fiobj_iseq(hash, hash3)) ? "passed." : "FAILED!",
125
+ (fiobj_iseq(hash, syms)) ? "FAILED!" : "passed.");
126
+ }
127
+ /* print JSON string and test parser */
128
+ {
129
+ tmp = fiobj_obj2json(hash, 0);
130
+ fprintf(stderr,
131
+ "* Printing JSON (len: %llu real len: %lu capa: %lu ref: %llu):\n "
132
+ " %s\n",
133
+ fiobj_obj2cstr(tmp).len, strlen(fiobj_obj2cstr(tmp).data),
134
+ fiobj_str_capa(tmp), OBJ2HEAD(tmp).ref, fiobj_obj2cstr(tmp).data);
135
+ fiobj_s *parsed = NULL;
136
+ if (fiobj_json2obj(&parsed, fiobj_obj2cstr(tmp).buffer,
137
+ fiobj_obj2cstr(tmp).len) == 0) {
138
+ fprintf(stderr, "* FAILD to parse the JSON printed.\n");
139
+ } else {
140
+ // if (!fiobj_iseq(parsed, hash)) {
141
+ // fiobj_free(tmp);
142
+ // tmp = fiobj_obj2json(parsed);
143
+ // fprintf(stderr, "* Parsed JSON is NOT EQUAL to original:\n%s\n\n",
144
+ // fiobj_obj2cstr(tmp).data);
145
+ // fiobj_free(tmp);
146
+ // tmp = fiobj_obj2json(fiobj_hash_get2(parsed, "symbols", 7));
147
+ // fprintf(stderr, "* Just the Symbols array (str eql == %u):\n%s\n\n",
148
+ // fiobj_iseq(fiobj_hash_get2(parsed, "string", 6),
149
+ // fiobj_hash_get2(hash, "string", 6)),
150
+ // fiobj_obj2cstr(tmp).data);
151
+ //
152
+ // } else {
153
+ // fprintf(stderr, "* Parsed JSON is equal to original.\n");
154
+ // }
155
+ fiobj_free(parsed);
156
+ }
157
+ fiobj_free(tmp);
158
+ }
159
+ #ifdef H_FIO2RESP_FORMAT_H
160
+ /* print RESP string */
161
+ tmp = resp_fioformat(hash);
162
+ fprintf(stderr, "* Printing RESP (len: %llu capa: %lu):\n %s\n",
163
+ fiobj_obj2cstr(tmp).len, fiobj_str_capa(tmp),
164
+ fiobj_obj2cstr(tmp).data);
165
+ fiobj_free(tmp);
166
+ #endif
167
+
168
+ sym = fiobj_sym_new("hash", 4);
169
+ tmp = fiobj_sym_new("1", 1);
170
+
171
+ fprintf(stderr,
172
+ "* Reference count for "
173
+ "couplet in nested Hash: %llu\n",
174
+ OBJ2HEAD(obj2hash(hash3)->items.next->obj).ref);
175
+
176
+ fiobj_free(hash);
177
+ fprintf(stderr, "* Testing nested Array delete reference count: %s\n",
178
+ (OBJ2HEAD(syms).ref == 2) ? "passed." : "FAILED!");
179
+ fprintf(stderr, "* Testing nested Hash delete reference count: %s\n",
180
+ (OBJ2HEAD(obj2hash(hash3)->items.next->obj).ref == 2) ? "passed."
181
+ : "FAILED!");
182
+ fprintf(stderr,
183
+ "* Testing reference count for "
184
+ "nested nested object in nessted Hash: %s\n",
185
+ (OBJ2HEAD(fiobj_hash_get(fiobj_hash_get(hash3, sym), tmp)).ref == 2)
186
+ ? "passed."
187
+ : "FAILED!");
188
+ fprintf(stderr,
189
+ "* Reference count for "
190
+ "nested nested object in nested Hash: %llu\n",
191
+ OBJ2HEAD(fiobj_hash_get(fiobj_hash_get(hash3, sym), tmp)).ref);
192
+
193
+ fiobj_free(hash2);
194
+ fprintf(stderr, "* Testing nested Array delete reference count: %s\n",
195
+ (OBJ2HEAD(syms).ref == 1) ? "passed." : "FAILED!");
196
+ fprintf(stderr, "* Testing nested Hash delete reference count: %s\n",
197
+ (OBJ2HEAD(obj2hash(hash3)->items.next->obj).ref == 1) ? "passed."
198
+ : "FAILED!");
199
+ fprintf(stderr,
200
+ "* Testing reference count for "
201
+ "nested nested object in nessted Hash: %s\n",
202
+ (OBJ2HEAD(obj2hash(hash3)->items.next->obj).ref == 1) ? "passed."
203
+ : "FAILED!");
204
+ fprintf(stderr,
205
+ "* Reference count for "
206
+ "nested nested object in nested Hash: %llu\n",
207
+ OBJ2HEAD(fiobj_hash_get(fiobj_hash_get(hash3, sym), tmp)).ref);
208
+
209
+ fiobj_free(hash3);
210
+ fiobj_free(sym);
211
+ fiobj_free(tmp);
212
+ }
213
+
214
+ /* *****************************************************************************
215
+ Test Hash performance
216
+ ***************************************************************************** */
217
+
218
+ #include <time.h>
219
+ #define HASH_TEST_SIZE ((4194304 >> 3) + (4194304 >> 4))
220
+ #define HASH_TEST_REPEAT 1
221
+
222
+ void fiobj_hash_test(void) {
223
+ clock_t start, end;
224
+ fiobj_s *syms;
225
+ fiobj_s *strings;
226
+ fiobj_s *hash;
227
+ fprintf(stderr, "\nTesting Hash and Array allocations\n");
228
+
229
+ start = clock();
230
+ syms = fiobj_ary_new();
231
+ strings = fiobj_ary_new();
232
+ for (size_t i = 0; i < HASH_TEST_SIZE; i++) {
233
+ fiobj_ary_push(syms, fiobj_symprintf("sym %lu", i));
234
+ fiobj_ary_push(strings, fiobj_strprintf("str %lu", i));
235
+ }
236
+ end = clock();
237
+ fprintf(stderr,
238
+ "* Created 2 arrays with %d symbols and strings "
239
+ "using printf in %lu.%lus\n",
240
+ HASH_TEST_SIZE << 1, (end - start) / CLOCKS_PER_SEC,
241
+ (end - start) - ((end - start) / CLOCKS_PER_SEC));
242
+
243
+ /******* Repeat Testing starts here *******/
244
+ for (size_t i = 0; i < HASH_TEST_REPEAT; i++) {
245
+
246
+ start = clock();
247
+ hash = fiobj_hash_new();
248
+ for (size_t i = 0; i < HASH_TEST_SIZE; i++) {
249
+ fiobj_hash_set(hash, fiobj_ary_entry(syms, i),
250
+ fiobj_dup(fiobj_ary_entry(strings, i)));
251
+ }
252
+ end = clock();
253
+ fprintf(stderr, "* Set %d items in %lu.%lus\n", HASH_TEST_SIZE,
254
+ (end - start) / CLOCKS_PER_SEC,
255
+ (end - start) - ((end - start) / CLOCKS_PER_SEC));
256
+ fprintf(stderr,
257
+ " - Final hash-map "
258
+ "length/capacity == %lu/%lu\n",
259
+ obj2hash(hash)->count, obj2hash(hash)->map.capa);
260
+ start = clock();
261
+ for (size_t i = 0; i < HASH_TEST_SIZE; i++) {
262
+ fiobj_hash_set(hash, fiobj_ary_entry(syms, i),
263
+ fiobj_dup(fiobj_ary_entry(strings, i)));
264
+ }
265
+ end = clock();
266
+ fprintf(stderr,
267
+ "* Resetting %d (test count == %lu) "
268
+ "items in %lu.%lus\n",
269
+ HASH_TEST_SIZE, fiobj_hash_count(hash),
270
+ (end - start) / CLOCKS_PER_SEC,
271
+ (end - start) - ((end - start) / CLOCKS_PER_SEC));
272
+
273
+ start = clock();
274
+ for (size_t i = 0; i < HASH_TEST_SIZE; i++) {
275
+ if (fiobj_hash_get(hash, fiobj_ary_entry(syms, i)) !=
276
+ fiobj_ary_entry(strings, i))
277
+ fprintf(stderr, "ERROR: fiobj_hash_get FAILED for %s != %s\n",
278
+ fiobj_obj2cstr(fiobj_ary_entry(strings, i)).data,
279
+ fiobj_obj2cstr(fiobj_hash_get(hash, fiobj_ary_entry(syms, i)))
280
+ .data),
281
+ exit(-1);
282
+ }
283
+ end = clock();
284
+ fprintf(stderr, "* Seek and test %d items in %lu.%lus\n", HASH_TEST_SIZE,
285
+ (end - start) / CLOCKS_PER_SEC,
286
+ (end - start) - ((end - start) / CLOCKS_PER_SEC));
287
+
288
+ start = clock();
289
+ fiobj_free(hash);
290
+ end = clock();
291
+ fprintf(stderr, "* Destroy hash with %d items in %lu.%lus\n",
292
+ HASH_TEST_SIZE, (end - start) / CLOCKS_PER_SEC,
293
+ (end - start) - ((end - start) / CLOCKS_PER_SEC));
294
+
295
+ /******* Repeat Testing ends here *******/
296
+ }
297
+
298
+ /** cleanup **/
299
+
300
+ start = clock();
301
+ fiobj_free(syms);
302
+ fiobj_free(strings);
303
+ end = clock();
304
+ fprintf(stderr,
305
+ "Deallocated 2 arrays with %d symbols and strings "
306
+ "in %lu.%lus\n\n",
307
+ HASH_TEST_SIZE << 1, (end - start) / CLOCKS_PER_SEC,
308
+ (end - start) - ((end - start) / CLOCKS_PER_SEC));
309
+ }
310
+
311
+ /* *****************************************************************************
312
+ Basic tests fiobj types
313
+ ***************************************************************************** */
314
+ static char num_buffer[148];
315
+
316
+ /* test were written for OSX (fprintf types) with clang (%s for NULL is okay)
317
+ */
318
+ void fiobj_test(void) {
319
+ /* test hash+array for memory leaks and performance*/
320
+ fiobj_hash_test();
321
+ /* test JSON (I know... it assumes everything else works...) */
322
+ for (int i = 0; i < 1; ++i) {
323
+ fiobj_test_hash_json();
324
+ }
325
+ /* start simple tests */
326
+
327
+ fiobj_s *obj;
328
+ size_t i;
329
+ fprintf(stderr, "\n===\nStarting fiobj basic testing:\n");
330
+
331
+ obj = fiobj_null();
332
+ if (obj->type != FIOBJ_T_NULL)
333
+ fprintf(stderr, "* FAILED null object test.\n");
334
+ fiobj_free(obj);
335
+
336
+ obj = fiobj_false();
337
+ if (obj->type != FIOBJ_T_FALSE)
338
+ fprintf(stderr, "* FAILED false object test.\n");
339
+ fiobj_free(obj);
340
+
341
+ obj = fiobj_true();
342
+ if (obj->type != FIOBJ_T_TRUE)
343
+ fprintf(stderr, "* FAILED true object test.\n");
344
+ fiobj_free(obj);
345
+
346
+ obj = fiobj_num_new(255);
347
+ if (obj->type != FIOBJ_T_NUMBER || fiobj_obj2num(obj) != 255)
348
+ fprintf(stderr, "* FAILED 255 object test i == %llu with type %d.\n",
349
+ fiobj_obj2num(obj), obj->type);
350
+ if (strcmp(fiobj_obj2cstr(obj).data, "255"))
351
+ fprintf(stderr, "* FAILED base 10 fiobj_obj2cstr test with %s.\n",
352
+ fiobj_obj2cstr(obj).data);
353
+ i = fio_ltoa(num_buffer, fiobj_obj2num(obj), 16);
354
+ if (strcmp(num_buffer, "00FF"))
355
+ fprintf(stderr, "* FAILED base 16 fiobj_obj2cstr test with (%lu): %s.\n", i,
356
+ num_buffer);
357
+ i = fio_ltoa(num_buffer, fiobj_obj2num(obj), 2);
358
+ if (strcmp(num_buffer, "011111111"))
359
+ fprintf(stderr, "* FAILED base 2 fiobj_obj2cstr test with (%lu): %s.\n", i,
360
+ num_buffer);
361
+ fiobj_free(obj);
362
+
363
+ obj = fiobj_float_new(77.777);
364
+ if (obj->type != FIOBJ_T_FLOAT || fiobj_obj2num(obj) != 77 ||
365
+ fiobj_obj2float(obj) != 77.777)
366
+ fprintf(stderr, "* FAILED 77.777 object test.\n");
367
+ if (strcmp(fiobj_obj2cstr(obj).data, "77.777"))
368
+ fprintf(stderr, "* FAILED float2str test with %s.\n",
369
+ fiobj_obj2cstr(obj).data);
370
+ fiobj_free(obj);
371
+
372
+ obj = fiobj_str_new("0x7F", 4);
373
+ if (obj->type != FIOBJ_T_STRING || fiobj_obj2num(obj) != 127)
374
+ fprintf(stderr, "* FAILED 0x7F object test.\n");
375
+ fiobj_free(obj);
376
+
377
+ obj = fiobj_str_new("0b01111111", 10);
378
+ if (obj->type != FIOBJ_T_STRING || fiobj_obj2num(obj) != 127)
379
+ fprintf(stderr, "* FAILED 0b01111111 object test.\n");
380
+ fiobj_free(obj);
381
+
382
+ obj = fiobj_str_new("232.79", 6);
383
+ if (obj->type != FIOBJ_T_STRING || fiobj_obj2num(obj) != 232)
384
+ fprintf(stderr, "* FAILED 232 object test. %llu\n", fiobj_obj2num(obj));
385
+ if (fiobj_obj2float(obj) != 232.79)
386
+ fprintf(stderr, "* FAILED fiobj_obj2float test with %f.\n",
387
+ fiobj_obj2float(obj));
388
+ fiobj_free(obj);
389
+
390
+ /* test array */
391
+ obj = fiobj_ary_new();
392
+ if (obj->type == FIOBJ_T_ARRAY) {
393
+ fprintf(stderr, "* testing Array. \n");
394
+ for (size_t i = 0; i < 128; i++) {
395
+ fiobj_ary_unshift(obj, fiobj_num_new(i));
396
+ if (fiobj_ary_count(obj) != i + 1)
397
+ fprintf(stderr, "* FAILED Array count. %lu/%llu != %lu\n",
398
+ fiobj_ary_count(obj), obj2ary(obj)->capa, i + 1);
399
+ }
400
+ fiobj_s *tmp = fiobj_obj2json(obj, 0);
401
+ fprintf(stderr, "Array test printout:\n%s\n",
402
+ tmp ? obj2str(tmp)->str : "ERROR");
403
+ fiobj_free(tmp);
404
+ fiobj_free(obj);
405
+ } else {
406
+ fprintf(stderr, "* FAILED to initialize Array test!\n");
407
+ fiobj_free(obj);
408
+ }
409
+
410
+ /* test cyclic protection */
411
+ #if FIOBJ_NESTING_PROTECTION == 1
412
+ {
413
+ fprintf(stderr, "* testing cyclic protection. \n");
414
+ fiobj_s *a1 = fiobj_ary_new();
415
+ fiobj_s *a2 = fiobj_ary_new();
416
+ for (size_t i = 0; i < 129; i++) {
417
+ obj = fiobj_num_new(1024 + i);
418
+ fiobj_ary_push(a1, fiobj_num_new(i));
419
+ fiobj_ary_unshift(a2, fiobj_num_new(i));
420
+ fiobj_ary_push(a1, fiobj_dup(obj));
421
+ fiobj_ary_unshift(a2, obj);
422
+ }
423
+ fiobj_ary_push(a1, a2); /* the intentionally offending code */
424
+ fiobj_ary_push(a2, a1);
425
+ fprintf(stderr,
426
+ "* Printing cyclic array references with "
427
+ "a1, pos %llu == a2 and a2, pos %llu == a1\n",
428
+ obj2ary(a1)->end, obj2ary(a2)->end);
429
+ {
430
+ fiobj_s *tmp = fiobj_obj2json(a1, 0);
431
+ fprintf(stderr, "%s\n", tmp ? obj2str(tmp)->str : "ERROR");
432
+ fiobj_free(tmp);
433
+ }
434
+
435
+ obj = fiobj_dup(fiobj_ary_entry(a2, -3));
436
+ if (!obj || obj->type != FIOBJ_T_NUMBER)
437
+ fprintf(stderr, "* FAILED unexpected object %p with type %d\n",
438
+ (void *)obj, obj ? obj->type : 0);
439
+ if (OBJ2HEAD(obj).ref < 2)
440
+ fprintf(stderr, "* FAILED object reference counting test (%llu)\n",
441
+ OBJ2HEAD(obj).ref);
442
+ fiobj_free(a1); /* frees both... */
443
+ // fiobj_free(a2);
444
+ if (OBJ2HEAD(obj).ref != 1)
445
+ fprintf(stderr,
446
+ "* FAILED to free cyclic nested "
447
+ "array members (%llu)\n ",
448
+ OBJ2HEAD(obj).ref);
449
+ fiobj_free(obj);
450
+ }
451
+ #endif
452
+
453
+ /* test deep nesting */
454
+ {
455
+ fprintf(stderr, "* testing deep array nesting. \n");
456
+ fiobj_s *top = fiobj_ary_new();
457
+ fiobj_s *pos = top;
458
+ for (size_t i = 0; i < 128; i++) {
459
+ for (size_t j = 0; j < 128; j++) {
460
+ fiobj_ary_push(pos, fiobj_num_new(j));
461
+ }
462
+ fiobj_ary_push(pos, fiobj_ary_new());
463
+ pos = fiobj_ary_entry(pos, -1);
464
+ if (!pos || pos->type != FIOBJ_T_ARRAY) {
465
+ fprintf(stderr, "* FAILED Couldn't retrive position -1 (%d)\n",
466
+ pos ? pos->type : 0);
467
+ break;
468
+ }
469
+ }
470
+ fiobj_free(top); /* frees both... */
471
+ }
472
+ fprintf(stderr, "* finished fiobj testing.\n");
473
+ }
474
+ #endif