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
@@ -1,6 +1,6 @@
1
1
  #ifndef H_FIOBJ_STR_H
2
2
  /*
3
- Copyright: Boaz Segev, 2017
3
+ Copyright: Boaz Segev, 2017-2018
4
4
  License: MIT
5
5
  */
6
6
  #define H_FIOBJ_STR_H
@@ -11,27 +11,20 @@ License: MIT
11
11
  extern "C" {
12
12
  #endif
13
13
 
14
- /** String type identifier */
15
- extern const uintptr_t FIOBJ_T_STRING;
16
-
17
- /** Static String type identifier */
18
- extern const uintptr_t FIOBJ_T_STRING_STATIC;
19
-
20
- #define FIOBJ_IS_STRING(obj) \
21
- (obj->type == FIOBJ_T_STRING || obj->type == FIOBJ_T_STRING_STATIC)
14
+ #define FIOBJ_IS_STRING(obj) FIOBJ_TYPE_IS((obj), FIOBJ_T_STRING)
22
15
 
23
16
  /* *****************************************************************************
24
- String API
17
+ API: Creating a String Object
25
18
  ***************************************************************************** */
26
19
 
27
20
  /** Creates a String object. Remember to use `fiobj_free`. */
28
- fiobj_s *fiobj_str_new(const char *str, size_t len);
21
+ FIOBJ fiobj_str_new(const char *str, size_t len);
29
22
 
30
23
  /** Creates a buffer String object. capa includes NUL.
31
24
  *
32
25
  * Remember to use `fiobj_free`.
33
26
  */
34
- fiobj_s *fiobj_str_buf(size_t capa);
27
+ FIOBJ fiobj_str_buf(size_t capa);
35
28
 
36
29
  /**
37
30
  * Creates a static String object from a static C string. Remember `fiobj_free`.
@@ -43,68 +36,124 @@ fiobj_s *fiobj_str_buf(size_t capa);
43
36
  *
44
37
  * NOTICE: static strings can't be written to.
45
38
  */
46
- fiobj_s *fiobj_str_static(const char *str, size_t len);
39
+ FIOBJ fiobj_str_static(const char *str, size_t len);
47
40
 
48
41
  /** Creates a copy from an existing String. Remember to use `fiobj_free`. */
49
- fiobj_s *fiobj_str_copy(fiobj_s *src);
42
+ static inline __attribute__((unused)) FIOBJ fiobj_str_copy(FIOBJ src) {
43
+ fio_cstr_s s = fiobj_obj2cstr(src);
44
+ return fiobj_str_new(s.data, s.len);
45
+ }
46
+
47
+ /**
48
+ * Creates a String object. Remember to use `fiobj_free`.
49
+ *
50
+ * The ownership of the memory indicated by `str` will now "move" to the object.
51
+ *
52
+ * The original memory MUST be allocated using `fio_malloc` (NOT the system's
53
+ * `malloc`) and it will be freed by the `fiobj` library using `fio_free`.
54
+ */
55
+ FIOBJ fiobj_str_move(char *str, size_t len, size_t capacity);
56
+
57
+ /**
58
+ * Returns a thread-static temporary string. Avoid calling `fiobj_dup` or
59
+ * `fiobj_free`.
60
+ */
61
+ FIOBJ fiobj_str_tmp(void);
50
62
 
51
63
  /** Creates a String object using a printf like interface. */
52
- __attribute__((format(printf, 1, 0))) fiobj_s *
53
- fiobj_strvprintf(const char *format, va_list argv);
64
+ __attribute__((format(printf, 1, 0))) FIOBJ fiobj_strvprintf(const char *format,
65
+ va_list argv);
54
66
 
55
67
  /** Creates a String object using a printf like interface. */
56
- __attribute__((format(printf, 1, 2))) fiobj_s *
57
- fiobj_strprintf(const char *format, ...);
68
+ __attribute__((format(printf, 1, 2))) FIOBJ fiobj_strprintf(const char *format,
69
+ ...);
58
70
 
59
71
  /** Dumps the `filename` file's contents into a new String. If `limit == 0`,
60
72
  * than the data will be read until EOF.
61
73
  *
62
- * If the file can't be located, opened or read, or if `start_at` is beyond the
63
- * EOF position, NULL is returned.
74
+ * If the file can't be located, opened or read, or if `start_at` is out of
75
+ * bounds (i.e., beyond the EOF position), FIOBJ_INVALID is returned.
76
+ *
77
+ * If `start_at` is negative, it will be computed from the end of the file.
64
78
  *
65
79
  * Remember to use `fiobj_free`.
66
80
  *
67
- * NOTE: Requires a UNIX system, otherwise always returns NULL.
81
+ * NOTE: Requires a UNIX system, otherwise always returns FIOBJ_INVALID.
82
+ */
83
+ FIOBJ fiobj_str_readfile(const char *filename, intptr_t start_at,
84
+ intptr_t limit);
85
+
86
+ /* *****************************************************************************
87
+ API: Editing a String
88
+ ***************************************************************************** */
89
+
90
+ /**
91
+ * Prevents the String object from being changed.
92
+ *
93
+ * When a String is used as a key for a Hash, it is automatically frozenn to
94
+ * prevent the Hash from becoming broken.
95
+ *
96
+ * A call to `fiobj_str_hash` or `fiobj_obj2hash` will automactically freeze the
97
+ * String.
68
98
  */
69
- fiobj_s *fiobj_str_readfile(const char *filename, size_t start_at,
70
- size_t limit);
99
+ void fiobj_str_freeze(FIOBJ str);
100
+
71
101
  /**
72
102
  * Confirms the requested capacity is available and allocates as required.
73
103
  *
74
104
  * Returns updated capacity.
75
105
  */
76
- size_t fiobj_str_capa_assert(fiobj_s *str, size_t size);
106
+ size_t fiobj_str_capa_assert(FIOBJ str, size_t size);
77
107
 
78
108
  /** Return's a String's capacity, if any. This should include the NUL byte. */
79
- size_t fiobj_str_capa(fiobj_s *str);
109
+ size_t fiobj_str_capa(FIOBJ str);
80
110
 
81
111
  /** Resizes a String object, allocating more memory if required. */
82
- void fiobj_str_resize(fiobj_s *str, size_t size);
112
+ void fiobj_str_resize(FIOBJ str, size_t size);
83
113
 
84
114
  /** Deallocates any unnecessary memory (if supported by OS). */
85
- void fiobj_str_minimize(fiobj_s *str);
115
+ void fiobj_str_minimize(FIOBJ str);
86
116
 
87
117
  /** Empties a String's data. */
88
- void fiobj_str_clear(fiobj_s *str);
118
+ void fiobj_str_clear(FIOBJ str);
89
119
 
90
120
  /**
91
121
  * Writes data at the end of the string, resizing the string as required.
92
122
  * Returns the new length of the String
93
123
  */
94
- size_t fiobj_str_write(fiobj_s *dest, const char *data, size_t len);
124
+ size_t fiobj_str_write(FIOBJ dest, const char *data, size_t len);
95
125
 
96
126
  /**
97
127
  * Writes data at the end of the string, resizing the string as required.
98
128
  * Returns the new length of the String
99
129
  */
100
130
  __attribute__((format(printf, 2, 3))) size_t
101
- fiobj_str_write2(fiobj_s *dest, const char *format, ...);
131
+ fiobj_str_write2(FIOBJ dest, const char *format, ...);
102
132
 
103
133
  /**
104
134
  * Writes data at the end of the string, resizing the string as required.
105
- * Returns the new length of the String
135
+ *
136
+ * Remember to call `fiobj_free` to free the source (when done with it).
137
+ *
138
+ * Returns the new length of the String.
139
+ */
140
+ size_t fiobj_str_join(FIOBJ dest, FIOBJ source);
141
+
142
+ /* *****************************************************************************
143
+ API: String Values
144
+ ***************************************************************************** */
145
+
146
+ /**
147
+ * Calculates a String's SipHash value for possible use as a HashMap key.
148
+ *
149
+ * Hashing the String's value automatically freezes the string, preventing
150
+ * future changes.
106
151
  */
107
- size_t fiobj_str_join(fiobj_s *dest, fiobj_s *obj);
152
+ uint64_t fiobj_str_hash(FIOBJ o);
153
+
154
+ #if DEBUG
155
+ void fiobj_test_string(void);
156
+ #endif
108
157
 
109
158
  #ifdef __cplusplus
110
159
  } /* extern "C" */
@@ -1,19 +1,18 @@
1
1
  /*
2
- Copyright: Boaz Segev, 2017
2
+ Copyright: Boaz Segev, 2017-2018
3
3
  License: MIT
4
4
  */
5
5
 
6
6
  /**
7
7
  This facil.io core library provides wrappers around complex and (or) dynamic
8
8
  types, abstracting some complexity and making dynamic type related tasks easier.
9
+ */
10
+ #include "fiobject.h"
9
11
 
12
+ #include "fio_ary.h"
10
13
 
11
- The library offers a rudementry protection against cyclic references using the
12
- `FIOBJ_NESTING_PROTECTION` flag (i.e., nesting an Array within itself)...
13
- however, this isn't fully tested and the performance price is high.
14
- */
15
- #include "fiobj_internal.h"
16
- #include "fiobj_primitives.h"
14
+ #define FIO_OVERRIDE_MALLOC 1
15
+ #include "fio_mem.h"
17
16
 
18
17
  #include <stdarg.h>
19
18
  #include <stdint.h>
@@ -21,569 +20,321 @@ however, this isn't fully tested and the performance price is high.
21
20
  #include <stdlib.h>
22
21
 
23
22
  /* *****************************************************************************
24
- Cyclic Protection helpers & API
23
+ the `fiobj_each2` function
25
24
  ***************************************************************************** */
26
-
27
- static __thread fiobj_s *fiobj_cyclic_protection = NULL;
28
- fiobj_s *fiobj_each_get_cyclic(void) { return fiobj_cyclic_protection; }
29
-
30
- static inline fiobj_s *protected_pop_obj(fio_ls_s *queue, fio_ls_s *history) {
31
- #if FIOBJ_NESTING_PROTECTION
32
- fiobj_cyclic_protection = NULL;
33
-
34
- fiobj_s *obj = fio_ls_pop(queue);
35
- if (!obj)
36
- return NULL;
37
- fiobj_s *child = OBJVTBL(obj)->unwrap(obj);
38
- if (!child)
39
- return obj;
40
- if (OBJVTBL(child)->count(child) == 0)
41
- return obj;
42
- fio_ls_s *pos = history->next;
43
- while (pos != history) {
44
- if (child == pos->obj) {
45
- fiobj_cyclic_protection = obj;
46
- return NULL;
47
- }
48
- pos = pos->next;
25
+ struct task_packet_s {
26
+ int (*task)(FIOBJ obj, void *arg);
27
+ void *arg;
28
+ fio_ary_s *stack;
29
+ FIOBJ next;
30
+ uintptr_t counter;
31
+ uint8_t stop;
32
+ uint8_t incomplete;
33
+ };
34
+
35
+ static int fiobj_task_wrapper(FIOBJ o, void *p_) {
36
+ struct task_packet_s *p = p_;
37
+ ++p->counter;
38
+ int ret = p->task(o, p->arg);
39
+ if (ret == -1) {
40
+ p->stop = 1;
41
+ return -1;
49
42
  }
50
- return obj;
51
- #else
52
- return fio_ls_pop(queue);
53
- (void)history;
54
- #endif
55
- }
56
-
57
- static inline void protected_push_obj(const fiobj_s *obj, fio_ls_s *history) {
58
- #if FIOBJ_NESTING_PROTECTION
59
- fio_ls_push(history, OBJVTBL(obj)->unwrap(obj));
60
- #else
61
- (void)obj;
62
- (void)history;
63
- #endif
64
- }
65
-
66
- /* *****************************************************************************
67
- Generic Object API
68
- ***************************************************************************** */
69
-
70
- /** Returns a C string naming the objects dynamic type. */
71
- const char *fiobj_type_name(const fiobj_s *obj) { return OBJVTBL(obj)->name; }
72
-
73
- /**
74
- * Copy by reference(!) - increases an object's (and any nested object's)
75
- * reference count.
76
- *
77
- * Always returns the value passed along.
78
- *
79
- * Future implementations might provide `fiobj_dup2` providing a deep copy.
80
- *
81
- * We don't need this feature just yet, so I'm not working on it.
82
- */
83
- fiobj_s *fiobj_dup(fiobj_s *obj) {
84
- OBJREF_ADD(obj);
85
- return obj;
86
- }
87
-
88
- static int fiobj_free_or_mark(fiobj_s *o, void *arg) {
89
- if (!o)
90
- return 0;
91
- #if FIOBJ_NESTING_PROTECTION
92
- if (OBJ2HEAD(o)->ref == 0) /* maybe a nested returning... */
93
- return 0;
94
- #elif DEBUG
95
- if (OBJ2HEAD(o)->ref == 0) {
96
- fprintf(stderr,
97
- "ERROR: attempting to free an object that isn't a fiobj or already "
98
- "freed (%p)\n",
99
- (void *)o);
100
- kill(0, SIGABRT);
101
- }
102
- #endif
103
-
104
- if (OBJREF_REM(o))
105
- return 0;
106
-
107
- /* reference count is zero: free memory or add to queue */
108
-
109
- /* test for wrapped object (i.e., Hash Couplet) */
110
- fiobj_s *child = OBJVTBL(o)->unwrap(o);
111
-
112
- if (child != o) {
113
- if (!child || OBJREF_REM(child)) {
114
- OBJVTBL(o)->free(o);
115
- return 0;
116
- }
117
- OBJVTBL(o)->free(o);
118
- o = child;
43
+ if (FIOBJ_IS_ALLOCATED(o) && FIOBJECT2VTBL(o)->each) {
44
+ p->incomplete = 1;
45
+ p->next = o;
46
+ return -1;
119
47
  }
120
-
121
- if (OBJVTBL(o)->count(o)) {
122
- fio_ls_push(arg, o);
123
- } else
124
- OBJVTBL(o)->free(o);
125
-
126
- /* handle nesting / wrapping (i.e., Array, Hash, Couplets ) */
127
48
  return 0;
128
49
  }
129
-
130
- /**
131
- * Decreases an object's reference count, releasing memory and
132
- * resources.
133
- *
134
- * This function affects nested objects, meaning that when an Array or
135
- * a Hash object is passed along, it's children (nested objects) are
136
- * also freed.
137
- */
138
- void fiobj_free(fiobj_s *o) {
139
- #if DEBUG
140
- if (!o)
141
- return;
142
- if (OBJ2HEAD(o)->ref == 0) {
143
- fprintf(stderr,
144
- "ERROR: attempting to free an object that isn't a fiobj or already "
145
- "freed (%p)\n",
146
- (void *)o);
147
- kill(0, SIGABRT);
148
- }
149
- #endif
150
- if (!o || OBJREF_REM(o))
151
- return;
152
-
153
- /* handle wrapping */
154
- {
155
- fiobj_s *child = OBJVTBL(o)->unwrap(o);
156
- if (child != o) {
157
- OBJVTBL(o)->free(o);
158
- if (OBJREF_REM(child))
159
- return;
160
- o = child;
161
- }
162
- }
163
- if (OBJVTBL(o)->count(o) == 0) {
164
- OBJVTBL(o)->free(o);
165
- return;
166
- }
167
- /* nested free */
168
- fio_ls_s queue = FIO_LS_INIT(queue);
169
- fio_ls_s history = FIO_LS_INIT(history);
170
- while (o) {
171
- /* the queue always contains valid enumerable objects that are unwrapped. */
172
- OBJVTBL(o)->each1(o, 0, fiobj_free_or_mark, &queue);
173
- fio_ls_push(&history, o);
174
- o = protected_pop_obj(&queue, &history);
175
- }
176
- /* clean up and free enumerables */
177
- while ((o = fio_ls_pop(&history)))
178
- OBJVTBL(o)->free(o);
179
- return;
180
- }
181
-
182
- /**
183
- * Attempts to return the object's current reference count.
184
- *
185
- * This is mostly for testing rather than normal library operations.
186
- */
187
- uintptr_t fiobj_reference_count(const fiobj_s *o) { return OBJ2HEAD(o)->ref; }
188
-
189
- /**
190
- * Tests if an object evaluates as TRUE.
191
- *
192
- * This is object type specific. For example, empty strings might evaluate as
193
- * FALSE, even though they aren't a boolean type.
194
- */
195
- int fiobj_is_true(const fiobj_s *o) { return (o && OBJVTBL(o)->is_true(o)); }
196
-
197
- /**
198
- * Returns an Object's numerical value.
199
- *
200
- * If a String or Symbol are passed to the function, they will be
201
- * parsed assuming base 10 numerical data.
202
- *
203
- * Hashes and Arrays return their object count.
204
- *
205
- * IO and File objects return their underlying file descriptor.
206
- *
207
- * A type error results in 0.
208
- */
209
- int64_t fiobj_obj2num(const fiobj_s *o) { return o ? OBJVTBL(o)->to_i(o) : 0; }
210
-
211
- /**
212
- * Returns a Float's value.
213
- *
214
- * If a String or Symbol are passed to the function, they will be
215
- * parsed assuming base 10 numerical data.
216
- *
217
- * Hashes and Arrays return their object count.
218
- *
219
- * IO and File objects return their underlying file descriptor.
220
- *
221
- * A type error results in 0.
222
- */
223
- double fiobj_obj2float(const fiobj_s *o) { return o ? OBJVTBL(o)->to_f(o) : 0; }
224
-
225
- /**
226
- * Returns a C String (NUL terminated) using the `fio_cstr_s` data type.
227
- *
228
- * The Sting in binary safe and might contain NUL bytes in the middle as well as
229
- * a terminating NUL.
230
- *
231
- * If a Symbol, a Number or a Float are passed to the function, they
232
- * will be parsed as a *temporary*, thread-safe, String.
233
- *
234
- * Numbers will be represented in base 10 numerical data.
235
- *
236
- * A type error results in NULL (i.e. object isn't a String).
237
- */
238
- fio_cstr_s fiobj_obj2cstr(const fiobj_s *o) {
239
- return o ? OBJVTBL(o)->to_str(o) : fiobj_noop_str(NULL);
240
- }
241
-
242
50
  /**
243
51
  * Single layer iteration using a callback for each nested fio object.
244
52
  *
245
- * Accepts any `fiobj_s *` type but only collections (Arrays and Hashes) are
53
+ * Accepts any `FIOBJ ` type but only collections (Arrays and Hashes) are
246
54
  * processed. The container itself (the Array or the Hash) is **not** processed
247
55
  * (unlike `fiobj_each2`).
248
56
  *
249
57
  * The callback task function must accept an object and an opaque user pointer.
250
58
  *
251
59
  * Hash objects pass along a `FIOBJ_T_COUPLET` object, containing
252
- * references for both the key (Symbol) and the object (any object).
60
+ * references for both the key and the object. Keys shouldn't be altered once
61
+ * placed as a key (or the Hash will break). Collections (Arrays / Hashes) can't
62
+ * be used as keeys.
253
63
  *
254
64
  * If the callback returns -1, the loop is broken. Any other value is ignored.
255
65
  *
256
66
  * Returns the "stop" position, i.e., the number of items processed + the
257
67
  * starting point.
258
68
  */
259
- size_t fiobj_each1(fiobj_s *o, size_t start_at,
260
- int (*task)(fiobj_s *obj, void *arg), void *arg) {
261
- return o ? OBJVTBL(o)->each1(o, start_at, task, arg) : 0;
262
- }
263
-
264
- /* *****************************************************************************
265
- Nested concern (each2, is_eq)
266
- ***************************************************************************** */
267
-
268
- static int each2_add_to_queue(fiobj_s *obj, void *arg) {
269
- fio_ls_s *const queue = arg;
270
- fio_ls_unshift(queue, obj);
271
- return 0;
272
- }
273
-
274
- /**
275
- * Deep iteration using a callback for each fio object, including the parent.
276
- *
277
- *
278
- * Notice that when passing collections to the function, the collection itself
279
- * is sent to the callback followed by it's children (if any). This is true also
280
- * for nested collections (a nested Hash will be sent first, followed by the
281
- * nested Hash's children and then followed by the rest of it's siblings.
282
- *
283
- * If the callback returns -1, the loop is broken. Any other value is ignored.
284
- */
285
- void fiobj_each2(fiobj_s *obj, int (*task)(fiobj_s *obj, void *arg),
286
- void *arg) {
287
- if (!obj)
288
- goto single;
289
- size_t count = OBJVTBL(obj)->count(obj);
290
- if (!count)
291
- goto single;
292
-
293
- fio_ls_s queue = FIO_LS_INIT(queue), history = FIO_LS_INIT(history);
294
- while (obj || queue.next != &queue) {
295
- int i = task(obj, arg);
296
- if (i == -1)
297
- goto finish;
298
- if (obj && OBJVTBL(obj)->count(obj)) {
299
- protected_push_obj(obj, &history);
300
- OBJVTBL(obj)->each1(obj, 0, each2_add_to_queue, queue.next);
301
- }
302
- obj = protected_pop_obj(&queue, &history);
303
- }
304
- finish:
305
- while (fio_ls_pop(&history))
306
- ;
307
- while (fio_ls_pop(&queue))
308
- ;
309
- return;
310
- single:
311
- task(obj, arg);
312
- return;
313
- }
314
-
315
- /**
316
- * Deeply compare two objects. No hashing is involved.
317
- *
318
- * KNOWN ISSUES:
319
- *
320
- * * Cyclic nesting might cause this function to hang (much like `fiobj_each2`).
321
- *
322
- * * `FIOBJ_NESTING_PROTECTION` might be ignored when testing nested objects.
323
- *
324
- * * Hash order might be ignored when comapring Hashes, which means that equal
325
- * Hases might behave differently during iteration.
326
- *
327
- */
328
- int fiobj_iseq(const fiobj_s *self, const fiobj_s *other) {
329
- if (self == other)
69
+ size_t fiobj_each2(FIOBJ o, int (*task)(FIOBJ obj, void *arg), void *arg) {
70
+ if (!o || !FIOBJ_IS_ALLOCATED(o) || (FIOBJECT2VTBL(o)->each == NULL)) {
71
+ task(o, arg);
330
72
  return 1;
331
- if (!self)
332
- return other->type == FIOBJ_T_NULL;
333
- if (!other)
334
- return self->type == FIOBJ_T_NULL;
335
-
336
- if (!OBJVTBL(self)->is_eq(self, other))
337
- return 0;
338
- if (!OBJVTBL(self)->count(self))
73
+ }
74
+ /* run task for root object */
75
+ if (task(o, arg) == -1)
339
76
  return 1;
340
-
341
- uint8_t eq = 0;
342
- fio_ls_s self_queue = FIO_LS_INIT(self_queue);
343
- fio_ls_s self_history = FIO_LS_INIT(self_history);
344
- fio_ls_s other_queue = FIO_LS_INIT(other_queue);
345
- fio_ls_s other_history = FIO_LS_INIT(other_history);
346
-
347
- while (self) {
348
- protected_push_obj(self, &self_history);
349
- protected_push_obj(other, &other_history);
350
- OBJVTBL(self)->each1((fiobj_s *)self, 0, each2_add_to_queue,
351
- self_queue.next);
352
- OBJVTBL(other)->each1((fiobj_s *)other, 0, each2_add_to_queue,
353
- other_queue.next);
354
- while (self_queue.next != &self_queue || self) {
355
- self = protected_pop_obj(&self_queue, &self_history);
356
- other = protected_pop_obj(&other_queue, &other_history);
357
- if (self == other)
358
- continue;
359
- if (!self && other->type != FIOBJ_T_NULL)
360
- goto finish;
361
- if (!other && self->type != FIOBJ_T_NULL)
362
- goto finish;
363
- if (OBJVTBL(self)->count(self))
364
- break;
365
- if (!OBJVTBL(self)->is_eq(self, other))
366
- goto finish;
367
- }
368
- if (self && !OBJVTBL(self)->is_eq(self, other))
77
+ uintptr_t pos = 0;
78
+ fio_ary_s stack = FIO_ARY_INIT;
79
+ struct task_packet_s packet = {
80
+ .task = task, .arg = arg, .stack = &stack, .counter = 1,
81
+ };
82
+ do {
83
+ if (!pos)
84
+ packet.next = 0;
85
+ packet.incomplete = 0;
86
+ pos = FIOBJECT2VTBL(o)->each(o, pos, fiobj_task_wrapper, &packet);
87
+ if (packet.stop)
369
88
  goto finish;
370
- }
371
- eq = 1;
89
+ if (packet.incomplete) {
90
+ fio_ary_push(&stack, (void *)pos);
91
+ fio_ary_push(&stack, (void *)o);
92
+ }
372
93
 
94
+ if (packet.next) {
95
+ fio_ary_push(&stack, (void *)0);
96
+ fio_ary_push(&stack, (void *)packet.next);
97
+ }
98
+ o = (FIOBJ)fio_ary_pop(&stack);
99
+ pos = (uintptr_t)fio_ary_pop(&stack);
100
+ } while (o);
373
101
  finish:
374
- while (fio_ls_pop(&self_history))
375
- ;
376
- while (fio_ls_pop(&self_queue))
377
- ;
378
- while (fio_ls_pop(&other_history))
379
- ;
380
- while (fio_ls_pop(&other_queue))
381
- ;
382
- return eq;
102
+ fio_ary_free(&stack);
103
+ return packet.counter;
383
104
  }
384
105
 
385
106
  /* *****************************************************************************
386
- Number and Float Helpers
107
+ Free complex objects (objects with nesting)
387
108
  ***************************************************************************** */
388
- static const char hex_notation[] = {'0', '1', '2', '3', '4', '5', '6', '7',
389
- '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
390
109
 
110
+ static void fiobj_dealloc_task(FIOBJ o, void *stack_) {
111
+ // if (!o)
112
+ // fprintf(stderr, "* WARN: freeing a NULL no-object\n");
113
+ // else
114
+ // fprintf(stderr, "* freeing object %s\n", fiobj_obj2cstr(o).data);
115
+ if (!o || !FIOBJ_IS_ALLOCATED(o))
116
+ return;
117
+ if (OBJREF_REM(o))
118
+ return;
119
+ if (!FIOBJECT2VTBL(o)->each || !FIOBJECT2VTBL(o)->count(o)) {
120
+ FIOBJECT2VTBL(o)->dealloc(o, NULL, NULL);
121
+ return;
122
+ }
123
+ fio_ary_s *s = stack_;
124
+ fio_ary_push(s, (void *)o);
125
+ }
391
126
  /**
392
- * A helper function that converts between String data to a signed int64_t.
127
+ * Decreases an object's reference count, releasing memory and
128
+ * resources.
393
129
  *
394
- * Numbers are assumed to be in base 10. `0x##` (or `x##`) and `0b##` (or
395
- * `b##`) are recognized as base 16 and base 2 (binary MSB first)
396
- * respectively.
130
+ * This function affects nested objects, meaning that when an Array or
131
+ * a Hash object is passed along, it's children (nested objects) are
132
+ * also freed.
397
133
  */
398
- int64_t fio_atol(char **pstr) {
399
- char *str = *pstr;
400
- uint64_t result = 0;
401
- uint8_t invert = 0;
402
- while (str[0] == '0')
403
- str++;
404
- while (str[0] == '-') {
405
- invert ^= 1;
406
- str++;
407
- }
408
- while (str[0] == '0')
409
- str++;
410
- if (str[0] == 'b' || str[0] == 'B') {
411
- /* base 2 */
412
- str++;
413
- while (str[0] == '0' || str[0] == '1') {
414
- result = (result << 1) | (str[0] == '1');
415
- str++;
416
- }
417
- } else if (str[0] == 'x' || str[0] == 'X') {
418
- /* base 16 */
419
- uint8_t tmp;
420
- str++;
421
- while (1) {
422
- if (str[0] >= '0' && str[0] <= '9')
423
- tmp = str[0] - '0';
424
- else if (str[0] >= 'A' && str[0] <= 'F')
425
- tmp = str[0] - ('A' - 10);
426
- else if (str[0] >= 'a' && str[0] <= 'f')
427
- tmp = str[0] - ('a' - 10);
428
- else
429
- goto finish;
430
- result = (result << 4) | tmp;
431
- str++;
432
- }
433
- } else {
434
- /* base 10 */
435
- const char *end = str;
436
- while (end[0] >= '0' && end[0] <= '9' && (uintptr_t)(end - str) < 22)
437
- end++;
438
- if ((uintptr_t)(end - str) > 21) /* too large for a number */
439
- return 0;
440
-
441
- while (str < end) {
442
- result = (result * 10) + (str[0] - '0');
443
- str++;
444
- }
445
- }
446
- finish:
447
- if (invert)
448
- result = 0 - result;
449
- *pstr = str;
450
- return (int64_t)result;
134
+ void fiobj_free_complex_object(FIOBJ o) {
135
+ fio_ary_s stack = FIO_ARY_INIT;
136
+ do {
137
+ FIOBJECT2VTBL(o)->dealloc(o, fiobj_dealloc_task, &stack);
138
+ } while ((o = (FIOBJ)fio_ary_pop(&stack)));
139
+ fio_ary_free(&stack);
451
140
  }
452
141
 
453
- /** A helper function that convers between String data to a signed double. */
454
- double fio_atof(char **pstr) { return strtold(*pstr, pstr); }
455
-
456
142
  /* *****************************************************************************
457
- String Helpers
143
+ Is Equal?
458
144
  ***************************************************************************** */
145
+ #include "fiobj_hash.h"
459
146
 
460
- /**
461
- * A helper function that convers between a signed int64_t to a string.
462
- *
463
- * No overflow guard is provided, make sure there's at least 66 bytes
464
- * available (for base 2).
465
- *
466
- * Supports base 2, base 10 and base 16. An unsupported base will silently
467
- * default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
468
- * beginning of the string).
469
- *
470
- * Returns the number of bytes actually written (excluding the NUL
471
- * terminator).
472
- */
473
- size_t fio_ltoa(char *dest, int64_t num, uint8_t base) {
474
- if (!num) {
475
- *(dest++) = '0';
476
- *(dest++) = 0;
147
+ static inline int fiobj_iseq_simple(const FIOBJ o, const FIOBJ o2) {
148
+ if (o == o2)
477
149
  return 1;
478
- }
479
-
480
- size_t len = 0;
150
+ if (!o || !o2)
151
+ return 0; /* they should have compared equal before. */
152
+ if (!FIOBJ_IS_ALLOCATED(o) || !FIOBJ_IS_ALLOCATED(o2))
153
+ return 0; /* they should have compared equal before. */
154
+ if (FIOBJECT2HEAD(o)->type != FIOBJECT2HEAD(o2)->type)
155
+ return 0; /* non-type equality is a barriar to equality. */
156
+ if (!FIOBJECT2VTBL(o)->is_eq(o, o2))
157
+ return 0;
158
+ return 1;
159
+ }
481
160
 
482
- if (base == 2) {
483
- uint64_t n = num; /* avoid bit shifting inconsistencies with signed bit */
484
- uint8_t i = 0; /* counting bits */
161
+ int fiobj_iseq____internal_complex__task(FIOBJ o, void *ary_) {
162
+ fio_ary_s *ary = ary_;
163
+ fio_ary_push(ary, (void *)o);
164
+ if (fiobj_hash_key_in_loop())
165
+ fio_ary_push(ary, (void *)fiobj_hash_key_in_loop());
166
+ return 0;
167
+ }
485
168
 
486
- while ((i < 64) && (n & 0x8000000000000000) == 0) {
487
- n = n << 1;
488
- i++;
489
- }
490
- /* make sure the Binary representation doesn't appear signed. */
491
- if (i) {
492
- dest[len++] = '0';
493
- }
494
- /* write to dest. */
495
- while (i < 64) {
496
- dest[len++] = ((n & 0x8000000000000000) ? '1' : '0');
497
- n = n << 1;
498
- i++;
169
+ /** used internally for complext nested tests (Array / Hash types) */
170
+ int fiobj_iseq____internal_complex__(FIOBJ o, FIOBJ o2) {
171
+ // if (FIOBJECT2VTBL(o)->each && FIOBJECT2VTBL(o)->count(o))
172
+ // return int fiobj_iseq____internal_complex__(const FIOBJ o, const FIOBJ
173
+ // o2);
174
+ fio_ary_s left = FIO_ARY_INIT, right = FIO_ARY_INIT, queue = FIO_ARY_INIT;
175
+ do {
176
+ fiobj_each1(o, 0, fiobj_iseq____internal_complex__task, &left);
177
+ fiobj_each1(o2, 0, fiobj_iseq____internal_complex__task, &right);
178
+ while (fio_ary_count(&left)) {
179
+ o = (FIOBJ)fio_ary_pop(&left);
180
+ o2 = (FIOBJ)fio_ary_pop(&right);
181
+ if (!fiobj_iseq_simple(o, o2))
182
+ goto unequal;
183
+ if (FIOBJ_IS_ALLOCATED(o) && FIOBJECT2VTBL(o)->each &&
184
+ FIOBJECT2VTBL(o)->count(o)) {
185
+ fio_ary_push(&queue, (void *)o);
186
+ fio_ary_push(&queue, (void *)o2);
187
+ }
499
188
  }
500
- dest[len] = 0;
501
- return len;
189
+ o2 = (FIOBJ)fio_ary_pop(&queue);
190
+ o = (FIOBJ)fio_ary_pop(&queue);
191
+ if (!fiobj_iseq_simple(o, o2))
192
+ goto unequal;
193
+ } while (o);
194
+ fio_ary_free(&left);
195
+ fio_ary_free(&right);
196
+ fio_ary_free(&queue);
197
+ return 1;
198
+ unequal:
199
+ fio_ary_free(&left);
200
+ fio_ary_free(&right);
201
+ fio_ary_free(&queue);
202
+ return 0;
203
+ }
502
204
 
503
- } else if (base == 16) {
504
- uint64_t n = num; /* avoid bit shifting inconsistencies with signed bit */
505
- uint8_t i = 0; /* counting bytes */
506
- uint8_t tmp = 0;
507
- while (i < 8 && (n & 0xFF00000000000000) == 0) {
508
- n = n << 8;
509
- i++;
510
- }
511
- /* make sure the Hex representation doesn't appear signed. */
512
- if (i && (n & 0x8000000000000000)) {
513
- dest[len++] = '0';
514
- dest[len++] = '0';
515
- }
516
- /* write the damn thing */
517
- while (i < 8) {
518
- tmp = (n & 0xF000000000000000) >> 60;
519
- dest[len++] = hex_notation[tmp];
520
- tmp = (n & 0x0F00000000000000) >> 56;
521
- dest[len++] = hex_notation[tmp];
522
- i++;
523
- n = n << 8;
524
- }
525
- dest[len] = 0;
526
- return len;
527
- }
205
+ /* *****************************************************************************
206
+ Defaults / NOOPs
207
+ ***************************************************************************** */
528
208
 
529
- /* fallback to base 10 */
530
- uint64_t rem = 0;
531
- uint64_t factor = 1;
532
- if (num < 0) {
533
- dest[len++] = '-';
534
- num = 0 - num;
535
- }
209
+ void fiobject___noop_dealloc(FIOBJ o, void (*task)(FIOBJ, void *), void *arg) {
210
+ (void)o;
211
+ (void)task;
212
+ (void)arg;
213
+ }
214
+ void fiobject___simple_dealloc(FIOBJ o, void (*task)(FIOBJ, void *),
215
+ void *arg) {
216
+ free(FIOBJ2PTR(o));
217
+ (void)task;
218
+ (void)arg;
219
+ }
536
220
 
537
- while (num / factor)
538
- factor *= 10;
221
+ uintptr_t fiobject___noop_count(const FIOBJ o) {
222
+ (void)o;
223
+ return 0;
224
+ }
225
+ size_t fiobject___noop_is_eq(const FIOBJ o1, const FIOBJ o2) {
226
+ (void)o1;
227
+ (void)o2;
228
+ return 0;
229
+ }
539
230
 
540
- while (factor > 1) {
541
- factor = factor / 10;
542
- rem = (rem * 10);
543
- dest[len++] = '0' + ((num / factor) - rem);
544
- rem += ((num / factor) - rem);
545
- }
546
- dest[len] = 0;
547
- return len;
231
+ fio_cstr_s fiobject___noop_to_str(const FIOBJ o) {
232
+ (void)o;
233
+ return (fio_cstr_s){.len = 0, .data = NULL};
234
+ }
235
+ intptr_t fiobject___noop_to_i(const FIOBJ o) {
236
+ (void)o;
237
+ return 0;
238
+ }
239
+ double fiobject___noop_to_f(const FIOBJ o) {
240
+ (void)o;
241
+ return 0;
548
242
  }
549
243
 
550
- /**
551
- * A helper function that convers between a double to a string.
552
- *
553
- * No overflow guard is provided, make sure there's at least 130 bytes
554
- * available (for base 2).
555
- *
556
- * Supports base 2, base 10 and base 16. An unsupported base will silently
557
- * default to base 10. Prefixes aren't added (i.e., no "0x" or "0b" at the
558
- * beginning of the string).
559
- *
560
- * Returns the number of bytes actually written (excluding the NUL
561
- * terminator).
562
- */
563
- size_t fio_ftoa(char *dest, double num, uint8_t base) {
564
- if (base == 2 || base == 16) {
565
- /* handle the binary / Hex representation the same as if it were an
566
- * int64_t
567
- */
568
- int64_t *i = (void *)&num;
569
- return fio_ltoa(dest, *i, base);
570
- }
244
+ #if DEBUG
571
245
 
572
- size_t written = sprintf(dest, "%g", num);
573
- uint8_t need_zero = 1;
574
- char *start = dest;
575
- while (*start) {
576
- if (*start == ',') // locale issues?
577
- *start = '.';
578
- if (*start == '.' || *start == 'e') {
579
- need_zero = 0;
580
- break;
581
- }
582
- start++;
583
- }
584
- if (need_zero) {
585
- dest[written++] = '.';
586
- dest[written++] = '0';
246
+ #include "fiobj_ary.h"
247
+ #include "fiobj_numbers.h"
248
+
249
+ static int fiobject_test_task(FIOBJ o, void *arg) {
250
+ ++((uintptr_t *)arg)[0];
251
+ if (!o)
252
+ fprintf(stderr, "* WARN: counting a NULL no-object\n");
253
+ // else
254
+ // fprintf(stderr, "* counting object %s\n", fiobj_obj2cstr(o).data);
255
+ return 0;
256
+ (void)o;
257
+ }
258
+
259
+ void fiobj_test_core(void) {
260
+ #define TEST_ASSERT(cond, ...) \
261
+ if (!(cond)) { \
262
+ fprintf(stderr, __VA_ARGS__); \
263
+ fprintf(stderr, "Testing failed.\n"); \
264
+ exit(-1); \
587
265
  }
588
- return written;
266
+ fprintf(stderr, "=== Testing Primitives\n");
267
+ FIOBJ o = fiobj_null();
268
+ TEST_ASSERT(o == (FIOBJ)FIOBJ_T_NULL, "fiobj_null isn't NULL!\n");
269
+ TEST_ASSERT(FIOBJ_TYPE(0) == FIOBJ_T_NULL, "NULL isn't NULL!\n");
270
+ TEST_ASSERT(FIOBJ_TYPE_IS(0, FIOBJ_T_NULL), "NULL isn't NULL! (2)\n");
271
+ TEST_ASSERT(!FIOBJ_IS_ALLOCATED(fiobj_null()),
272
+ "fiobj_null claims to be allocated!\n");
273
+ TEST_ASSERT(!FIOBJ_IS_ALLOCATED(fiobj_true()),
274
+ "fiobj_true claims to be allocated!\n");
275
+ TEST_ASSERT(!FIOBJ_IS_ALLOCATED(fiobj_false()),
276
+ "fiobj_false claims to be allocated!\n");
277
+ TEST_ASSERT(FIOBJ_TYPE(fiobj_true()) == FIOBJ_T_TRUE,
278
+ "fiobj_true isn't FIOBJ_T_TRUE!\n");
279
+ TEST_ASSERT(FIOBJ_TYPE_IS(fiobj_true(), FIOBJ_T_TRUE),
280
+ "fiobj_true isn't FIOBJ_T_TRUE! (2)\n");
281
+ TEST_ASSERT(FIOBJ_TYPE(fiobj_false()) == FIOBJ_T_FALSE,
282
+ "fiobj_false isn't FIOBJ_T_TRUE!\n");
283
+ TEST_ASSERT(FIOBJ_TYPE_IS(fiobj_false(), FIOBJ_T_FALSE),
284
+ "fiobj_false isn't FIOBJ_T_TRUE! (2)\n");
285
+ fiobj_free(o); /* testing for crash*/
286
+ fprintf(stderr, "* passed.\n");
287
+ fprintf(stderr, "=== Testing fioj_each2\n");
288
+ o = fiobj_ary_new2(4);
289
+ FIOBJ tmp = fiobj_ary_new();
290
+ fiobj_ary_push(o, tmp);
291
+ fiobj_ary_push(o, fiobj_true());
292
+ fiobj_ary_push(o, fiobj_null());
293
+ fiobj_ary_push(o, fiobj_num_new(10));
294
+ fiobj_ary_push(tmp, fiobj_num_new(13));
295
+ fiobj_ary_push(tmp, fiobj_hash_new());
296
+ FIOBJ key = fiobj_str_new("my key", 6);
297
+ fiobj_hash_set(fiobj_ary_entry(tmp, -1), key, fiobj_true());
298
+ fiobj_free(key);
299
+ /* we have root array + 4 children (w/ array) + 2 children (w/ hash) + 1 */
300
+ uintptr_t count = 0;
301
+ size_t each_ret = 0;
302
+ TEST_ASSERT(fiobj_each2(o, fiobject_test_task, (void *)&count) == 8,
303
+ "fiobj_each1 didn't count everything... (%d != %d)", (int)count,
304
+ (int)each_ret);
305
+ TEST_ASSERT(count == 8, "Something went wrong with the counter task... (%d)",
306
+ (int)count)
307
+ fprintf(stderr, "* passed.\n");
308
+ fprintf(stderr, "=== Testing fioj_iseq with nested items\n");
309
+ FIOBJ o2 = fiobj_ary_new2(4);
310
+ tmp = fiobj_ary_new();
311
+ fiobj_ary_push(o2, tmp);
312
+ fiobj_ary_push(o2, fiobj_true());
313
+ fiobj_ary_push(o2, fiobj_null());
314
+ fiobj_ary_push(o2, fiobj_num_new(10));
315
+ fiobj_ary_push(tmp, fiobj_num_new(13));
316
+ fiobj_ary_push(tmp, fiobj_hash_new());
317
+ key = fiobj_str_new("my key", 6);
318
+ fiobj_hash_set(fiobj_ary_entry(tmp, -1), key, fiobj_true());
319
+ fiobj_free(key);
320
+ TEST_ASSERT(!fiobj_iseq(o, FIOBJ_INVALID),
321
+ "Array and FIOBJ_INVALID can't be equal!");
322
+ TEST_ASSERT(!fiobj_iseq(o, fiobj_null()),
323
+ "Array and fiobj_null can't be equal!");
324
+ TEST_ASSERT(fiobj_iseq(o, o2), "Arrays aren't euqal!");
325
+ fiobj_free(o);
326
+ fiobj_free(o2);
327
+ TEST_ASSERT(fiobj_iseq(fiobj_null(), fiobj_null()),
328
+ "fiobj_null() not equal to self!");
329
+ TEST_ASSERT(fiobj_iseq(fiobj_false(), fiobj_false()),
330
+ "fiobj_false() not equal to self!");
331
+ TEST_ASSERT(fiobj_iseq(fiobj_true(), fiobj_true()),
332
+ "fiobj_true() not equal to self!");
333
+ TEST_ASSERT(!fiobj_iseq(fiobj_null(), fiobj_false()),
334
+ "fiobj_null eqal to fiobj_false!");
335
+ TEST_ASSERT(!fiobj_iseq(fiobj_null(), fiobj_true()),
336
+ "fiobj_null eqal to fiobj_true!");
337
+ fprintf(stderr, "* passed.\n");
589
338
  }
339
+
340
+ #endif