iodine 0.6.5 → 0.7.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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +4 -4
  4. data/SPEC-Websocket-Draft.md +3 -6
  5. data/bin/mustache.rb +128 -0
  6. data/examples/test_template.mustache +16 -0
  7. data/ext/iodine/fio.c +9397 -0
  8. data/ext/iodine/fio.h +4723 -0
  9. data/ext/iodine/fio_ary.h +353 -54
  10. data/ext/iodine/fio_cli.c +351 -361
  11. data/ext/iodine/fio_cli.h +84 -105
  12. data/ext/iodine/fio_hashmap.h +70 -16
  13. data/ext/iodine/fio_json_parser.h +35 -24
  14. data/ext/iodine/fio_siphash.c +104 -4
  15. data/ext/iodine/fio_siphash.h +18 -2
  16. data/ext/iodine/fio_str.h +1218 -0
  17. data/ext/iodine/fio_tmpfile.h +1 -1
  18. data/ext/iodine/fiobj.h +13 -8
  19. data/ext/iodine/fiobj4sock.h +6 -8
  20. data/ext/iodine/fiobj_ary.c +107 -17
  21. data/ext/iodine/fiobj_ary.h +36 -4
  22. data/ext/iodine/fiobj_data.c +146 -127
  23. data/ext/iodine/fiobj_data.h +25 -23
  24. data/ext/iodine/fiobj_hash.c +7 -7
  25. data/ext/iodine/fiobj_hash.h +6 -5
  26. data/ext/iodine/fiobj_json.c +20 -17
  27. data/ext/iodine/fiobj_json.h +5 -5
  28. data/ext/iodine/fiobj_mem.h +71 -0
  29. data/ext/iodine/fiobj_mustache.c +310 -0
  30. data/ext/iodine/fiobj_mustache.h +40 -0
  31. data/ext/iodine/fiobj_numbers.c +199 -94
  32. data/ext/iodine/fiobj_numbers.h +7 -7
  33. data/ext/iodine/fiobj_str.c +142 -333
  34. data/ext/iodine/fiobj_str.h +65 -55
  35. data/ext/iodine/fiobject.c +49 -11
  36. data/ext/iodine/fiobject.h +40 -39
  37. data/ext/iodine/http.c +382 -190
  38. data/ext/iodine/http.h +124 -80
  39. data/ext/iodine/http1.c +99 -127
  40. data/ext/iodine/http1.h +5 -5
  41. data/ext/iodine/http1_parser.c +3 -2
  42. data/ext/iodine/http1_parser.h +2 -2
  43. data/ext/iodine/http_internal.c +14 -12
  44. data/ext/iodine/http_internal.h +25 -19
  45. data/ext/iodine/iodine.c +37 -18
  46. data/ext/iodine/iodine.h +4 -0
  47. data/ext/iodine/iodine_caller.c +9 -2
  48. data/ext/iodine/iodine_caller.h +2 -0
  49. data/ext/iodine/iodine_connection.c +82 -117
  50. data/ext/iodine/iodine_defer.c +57 -50
  51. data/ext/iodine/iodine_defer.h +0 -1
  52. data/ext/iodine/iodine_fiobj2rb.h +4 -2
  53. data/ext/iodine/iodine_helpers.c +4 -4
  54. data/ext/iodine/iodine_http.c +25 -32
  55. data/ext/iodine/iodine_json.c +2 -1
  56. data/ext/iodine/iodine_mustache.c +423 -0
  57. data/ext/iodine/iodine_mustache.h +6 -0
  58. data/ext/iodine/iodine_pubsub.c +48 -153
  59. data/ext/iodine/iodine_pubsub.h +5 -4
  60. data/ext/iodine/iodine_rack_io.c +7 -5
  61. data/ext/iodine/iodine_store.c +16 -13
  62. data/ext/iodine/iodine_tcp.c +26 -34
  63. data/ext/iodine/mustache_parser.h +1085 -0
  64. data/ext/iodine/redis_engine.c +740 -646
  65. data/ext/iodine/redis_engine.h +13 -15
  66. data/ext/iodine/resp_parser.h +11 -5
  67. data/ext/iodine/websocket_parser.h +13 -13
  68. data/ext/iodine/websockets.c +240 -393
  69. data/ext/iodine/websockets.h +52 -113
  70. data/lib/iodine.rb +1 -1
  71. data/lib/iodine/mustache.rb +140 -0
  72. data/lib/iodine/version.rb +1 -1
  73. metadata +15 -28
  74. data/ext/iodine/defer.c +0 -566
  75. data/ext/iodine/defer.h +0 -148
  76. data/ext/iodine/evio.c +0 -26
  77. data/ext/iodine/evio.h +0 -161
  78. data/ext/iodine/evio_callbacks.c +0 -26
  79. data/ext/iodine/evio_epoll.c +0 -251
  80. data/ext/iodine/evio_kqueue.c +0 -194
  81. data/ext/iodine/facil.c +0 -2325
  82. data/ext/iodine/facil.h +0 -616
  83. data/ext/iodine/fio_base64.c +0 -277
  84. data/ext/iodine/fio_base64.h +0 -71
  85. data/ext/iodine/fio_llist.h +0 -257
  86. data/ext/iodine/fio_mem.c +0 -675
  87. data/ext/iodine/fio_mem.h +0 -143
  88. data/ext/iodine/fio_random.c +0 -248
  89. data/ext/iodine/fio_random.h +0 -45
  90. data/ext/iodine/fio_sha1.c +0 -362
  91. data/ext/iodine/fio_sha1.h +0 -107
  92. data/ext/iodine/fio_sha2.c +0 -842
  93. data/ext/iodine/fio_sha2.h +0 -169
  94. data/ext/iodine/pubsub.c +0 -867
  95. data/ext/iodine/pubsub.h +0 -221
  96. data/ext/iodine/sock.c +0 -1366
  97. data/ext/iodine/sock.h +0 -566
  98. data/ext/iodine/spnlock.inc +0 -111
@@ -5,19 +5,25 @@ License: MIT
5
5
  */
6
6
 
7
7
  /**
8
- * A Dynamic Array for general use (void * pointers).
8
+ * A Dynamic Array for general use (for void * pointers or a predefined type).
9
+ *
10
+ * It's possible to switch from the default `void *` type to any type by
11
+ * defining the `FIO_ARY_TYPE`, `FIO_ARY_TYPE_COMPARE(a,b)` and
12
+ * `FIO_ARY_TYPE_INVALID` macros.
13
+ *
14
+ * However, this will effect ALL the arrays that share the same translation unit
15
+ * (the same *.c file).
9
16
  *
10
17
  * The file was written to be compatible with C++ as well as C, hence some
11
18
  * pointer casting.
12
19
  *
13
20
  * Use:
14
-
15
- fio_ary_s ary; // a container can be placed on the stack.
16
- fio_ary_new(&ary); // initialize the container
17
- fio_ary_push(&ary, (void*)1 ); // add / remove / read data...
18
- fio_ary_free(&ary) // free any resources, not the container.
19
-
20
-
21
+ *
22
+ * fio_ary_s ary; // a container can be placed on the stack.
23
+ * fio_ary_new(&ary); // initialize the container
24
+ * fio_ary_push(&ary, (void*)1 ); // add / remove / read data...
25
+ * fio_ary_free(&ary) // free any resources, not the container.
26
+ *
21
27
  */
22
28
  #define H_FIO_ARRAY_H
23
29
 
@@ -30,6 +36,20 @@ fio_ary_free(&ary) // free any resources, not the container.
30
36
  #define _GNU_SOURCE
31
37
  #endif
32
38
 
39
+ /**
40
+ * Both FIO_ARY_TYPE, FIO_ARY_TYPE_INVALID and FIO_ARY_TYPE_COMPARE must be
41
+ * defined to change default behavior.
42
+ */
43
+ #if !defined(FIO_ARY_TYPE) || !defined(FIO_ARY_TYPE_INVALID) || \
44
+ !defined(FIO_ARY_TYPE_COMPARE)
45
+ #undef FIO_ARY_TYPE
46
+ #undef FIO_ARY_TYPE_INVALID
47
+ #undef FIO_ARY_TYPE_COMPARE
48
+ #define FIO_ARY_TYPE void *
49
+ #define FIO_ARY_TYPE_INVALID NULL
50
+ #define FIO_ARY_TYPE_COMPARE(a, b) ((a) == (b))
51
+ #endif
52
+
33
53
  #ifndef FIO_FUNC
34
54
  #define FIO_FUNC static __attribute__((unused))
35
55
  #endif
@@ -42,7 +62,7 @@ typedef struct fio_ary_s {
42
62
  size_t start;
43
63
  size_t end;
44
64
  size_t capa;
45
- void **arry;
65
+ FIO_ARY_TYPE *arry;
46
66
  } fio_ary_s;
47
67
 
48
68
  /** this value can be used for lazy initialization. */
@@ -58,6 +78,13 @@ FIO_FUNC inline void fio_ary_new(fio_ary_s *ary, size_t capa);
58
78
  /** Frees the array's internal data. */
59
79
  FIO_FUNC inline void fio_ary_free(fio_ary_s *ary);
60
80
 
81
+ /**
82
+ * Adds all the items in the `src` Array to the end of the `dest` Array.
83
+ *
84
+ * The `src` Array remain untouched.
85
+ */
86
+ FIO_FUNC inline void fio_ary_concat(fio_ary_s *dest, fio_ary_s *src);
87
+
61
88
  /** Returns the number of elements in the Array. */
62
89
  FIO_FUNC inline size_t fio_ary_count(fio_ary_s *ary);
63
90
 
@@ -65,13 +92,18 @@ FIO_FUNC inline size_t fio_ary_count(fio_ary_s *ary);
65
92
  FIO_FUNC inline size_t fio_ary_capa(fio_ary_s *ary);
66
93
 
67
94
  /**
68
- * Returns the object placed in the Array, if any. Returns NULL if no data or if
69
- * the index is out of bounds.
95
+ * Returns the object placed in the Array, if any. Returns FIO_ARY_TYPE_INVALID
96
+ * if no data or if the index is out of bounds.
70
97
  *
71
- * Negative values are retrived from the end of the array. i.e., `-1`
98
+ * Negative values are retrieved from the end of the array. i.e., `-1`
72
99
  * is the last item.
73
100
  */
74
- FIO_FUNC inline void *fio_ary_index(fio_ary_s *ary, intptr_t pos);
101
+ FIO_FUNC inline FIO_ARY_TYPE fio_ary_index(fio_ary_s *ary, intptr_t pos);
102
+
103
+ /**
104
+ * Returns the index of the object or -1 if the object wasn't found.
105
+ */
106
+ FIO_FUNC inline intptr_t fio_ary_find(fio_ary_s *ary, FIO_ARY_TYPE data);
75
107
 
76
108
  /** alias for `fiobj_ary_index` */
77
109
  #define fio_ary_entry(a, p) fiobj_ary_index((a), (p))
@@ -83,25 +115,43 @@ FIO_FUNC inline void *fio_ary_index(fio_ary_s *ary, intptr_t pos);
83
115
  *
84
116
  * If an error occurs, the same data passed to the function is returned.
85
117
  */
86
- FIO_FUNC inline void *fio_ary_set(fio_ary_s *ary, void *data, intptr_t pos);
118
+ FIO_FUNC inline FIO_ARY_TYPE fio_ary_set(fio_ary_s *ary, FIO_ARY_TYPE data,
119
+ intptr_t pos);
87
120
 
88
121
  /**
89
122
  * Pushes an object to the end of the Array. Returns -1 on error.
90
123
  */
91
- FIO_FUNC inline int fio_ary_push(fio_ary_s *ary, void *data);
124
+ FIO_FUNC inline int fio_ary_push(fio_ary_s *ary, FIO_ARY_TYPE data);
92
125
 
93
126
  /** Pops an object from the end of the Array. */
94
- FIO_FUNC inline void *fio_ary_pop(fio_ary_s *ary);
127
+ FIO_FUNC inline FIO_ARY_TYPE fio_ary_pop(fio_ary_s *ary);
95
128
 
96
129
  /**
97
130
  * Unshifts an object to the beginning of the Array. Returns -1 on error.
98
131
  *
99
132
  * This could be expensive, causing `memmove`.
100
133
  */
101
- FIO_FUNC inline int fio_ary_unshift(fio_ary_s *ary, void *data);
134
+ FIO_FUNC inline int fio_ary_unshift(fio_ary_s *ary, FIO_ARY_TYPE data);
102
135
 
103
136
  /** Shifts an object from the beginning of the Array. */
104
- FIO_FUNC inline void *fio_ary_shift(fio_ary_s *ary);
137
+ FIO_FUNC inline FIO_ARY_TYPE fio_ary_shift(fio_ary_s *ary);
138
+
139
+ /**
140
+ * Removes an object from the array, MOVING all the other objects to prevent
141
+ * "holes" in the data.
142
+ *
143
+ * Returns the removed object or FIO_ARY_TYPE_INVALID (on error).
144
+ */
145
+ FIO_FUNC inline FIO_ARY_TYPE fio_ary_remove(fio_ary_s *ary, intptr_t index);
146
+
147
+ /**
148
+ * Removes an object from the array, if it exists, MOVING all the other objects
149
+ * to prevent "holes" in the data.
150
+ *
151
+ * Returns -1 if the object wasn't found or 0 if the object was successfully
152
+ * removed.
153
+ */
154
+ FIO_FUNC inline int fio_ary_remove2(fio_ary_s *ary, FIO_ARY_TYPE data);
105
155
 
106
156
  /**
107
157
  * Iteration using a callback for each entry in the array.
@@ -115,11 +165,11 @@ FIO_FUNC inline void *fio_ary_shift(fio_ary_s *ary);
115
165
  * the starting point.
116
166
  */
117
167
  FIO_FUNC inline size_t fio_ary_each(fio_ary_s *ary, size_t start_at,
118
- int (*task)(void *pt, void *arg),
168
+ int (*task)(FIO_ARY_TYPE pt, void *arg),
119
169
  void *arg);
120
170
  /**
121
- * Removes any NULL *pointers* from an Array, keeping all other data in the
122
- * array.
171
+ * Removes any FIO_ARY_TYPE_INVALID object from an Array (NULL pointers by
172
+ * default), keeping all other data in the array.
123
173
  *
124
174
  * This action is O(n) where n in the length of the array.
125
175
  * It could get expensive.
@@ -140,7 +190,7 @@ FIO_FUNC inline void fio_ary_compact(fio_ary_s *ary);
140
190
  (++pos.i))
141
191
  struct fio_ary_pos_for_loop_s {
142
192
  unsigned long i;
143
- void *obj;
193
+ FIO_ARY_TYPE obj;
144
194
  };
145
195
 
146
196
  /* *****************************************************************************
@@ -150,7 +200,7 @@ Array creation API
150
200
  FIO_FUNC inline void fio_ary_new(fio_ary_s *ary, size_t capa) {
151
201
  if (!capa)
152
202
  capa = 32;
153
- *ary = (fio_ary_s){.arry = (void **)malloc(capa * sizeof(*ary->arry)),
203
+ *ary = (fio_ary_s){.arry = (FIO_ARY_TYPE *)malloc(capa * sizeof(*ary->arry)),
154
204
  .capa = capa};
155
205
  if (!ary->arry) {
156
206
  perror("ERROR: facil.io dynamic array couldn't be allocated");
@@ -158,8 +208,10 @@ FIO_FUNC inline void fio_ary_new(fio_ary_s *ary, size_t capa) {
158
208
  }
159
209
  }
160
210
  FIO_FUNC inline void fio_ary_free(fio_ary_s *ary) {
161
- if (ary)
211
+ if (ary) {
162
212
  free(ary->arry);
213
+ *ary = FIO_ARY_INIT;
214
+ }
163
215
  }
164
216
 
165
217
  /* *****************************************************************************
@@ -170,13 +222,18 @@ Array memory management
170
222
  FIO_FUNC void fio_ary_getmem(fio_ary_s *ary, intptr_t needed) {
171
223
  /* we have enough memory, but we need to re-organize it. */
172
224
  if (needed == -1) {
225
+ if (ary->start > 0) {
226
+ return;
227
+ }
173
228
  if (ary->end < ary->capa) {
174
229
  /* since allocation can be cheaper than memmove (depending on size),
175
230
  * we'll just shove everything to the end...
176
231
  */
177
232
  size_t len = ary->end - ary->start;
178
- memmove(ary->arry + ary->capa - len, ary->arry + ary->start,
179
- len * sizeof(*ary->arry));
233
+ if (len) {
234
+ memmove(ary->arry + ary->capa - len, ary->arry + ary->start,
235
+ len * sizeof(*ary->arry));
236
+ }
180
237
  ary->start = ary->capa - len;
181
238
  ary->end = ary->capa;
182
239
  return;
@@ -184,10 +241,12 @@ FIO_FUNC void fio_ary_getmem(fio_ary_s *ary, intptr_t needed) {
184
241
  /* add some breathing room for future `unshift`s */
185
242
  needed = 0 - ((ary->capa < 1024) ? (ary->capa >> 1) : 1024);
186
243
 
187
- } else if (needed == 1 && ary->start >= (ary->capa >> 1)) {
244
+ } else if (needed == 1 && ary->start && ary->start >= (ary->capa >> 1)) {
188
245
  /* FIFO support optimizes smaller FIFO ranges over bloating allocations. */
189
246
  size_t len = ary->end - ary->start;
190
- memmove(ary->arry + 2, ary->arry + ary->start, len * sizeof(*ary->arry));
247
+ if (len) {
248
+ memmove(ary->arry + 2, ary->arry + ary->start, len * sizeof(*ary->arry));
249
+ }
191
250
  ary->start = 2;
192
251
  ary->end = len + 2;
193
252
  return;
@@ -196,13 +255,17 @@ FIO_FUNC void fio_ary_getmem(fio_ary_s *ary, intptr_t needed) {
196
255
  /* alocate using exponential growth, up to single page size. */
197
256
  size_t updated_capa = ary->capa;
198
257
  size_t minimum = ary->capa + ((needed < 0) ? (0 - needed) : needed);
258
+ if (!updated_capa) {
259
+ updated_capa = 1;
260
+ }
199
261
  while (updated_capa <= minimum)
200
262
  updated_capa =
201
263
  (updated_capa <= 4096) ? (updated_capa << 1) : (updated_capa + 4096);
202
264
 
203
265
  /* we assume memory allocation works. it's better to crash than to continue
204
266
  * living without memory... besides, malloc is optimistic these days. */
205
- ary->arry = (void **)realloc(ary->arry, updated_capa * sizeof(*ary->arry));
267
+ ary->arry =
268
+ (FIO_ARY_TYPE *)realloc(ary->arry, updated_capa * sizeof(*ary->arry));
206
269
  ary->capa = updated_capa;
207
270
  if (!ary->arry) {
208
271
  perror("ERROR: facil.io dynamic array couldn't be reallocated");
@@ -212,7 +275,7 @@ FIO_FUNC void fio_ary_getmem(fio_ary_s *ary, intptr_t needed) {
212
275
  if (needed >= 0) /* we're done, realloc grows the top of the address space*/
213
276
  return;
214
277
 
215
- /* move everything to the max, since memmove could get expensive */
278
+ /* move everything to the max, since memmove could get expensive */
216
279
  size_t len = ary->end - ary->start;
217
280
  memmove((ary->arry + ary->capa) - len, ary->arry + ary->start,
218
281
  len * sizeof(*ary->arry));
@@ -224,6 +287,28 @@ FIO_FUNC inline void fiobj_ary_init(fio_ary_s *ary) {
224
287
  *ary = (fio_ary_s){.arry = NULL};
225
288
  }
226
289
 
290
+ /* *****************************************************************************
291
+ Array concat API
292
+ ***************************************************************************** */
293
+
294
+ /**
295
+ * Adds all the items in the `src` Array to the end of the `dest` Array.
296
+ *
297
+ * The `src` Array remain untouched.
298
+ */
299
+ FIO_FUNC inline void fio_ary_concat(fio_ary_s *dest, fio_ary_s *src) {
300
+ if (!dest || !src || src->start == src->end)
301
+ return;
302
+ const intptr_t len = src->end - src->start;
303
+ fio_ary_getmem(dest, len);
304
+ memcpy(dest->arry + dest->end, src->arry + src->start,
305
+ (len * sizeof(*dest->arry)));
306
+ // memcpy((void *)((uintptr_t)dest->arry + (sizeof(*dest->arry) * dest->end)),
307
+ // (void *)((uintptr_t)src->arry + (sizeof(*dest->arry) * src->start)),
308
+ // (len * sizeof(*dest->arry)));
309
+ dest->end += len;
310
+ }
311
+
227
312
  /* *****************************************************************************
228
313
  Array direct entry access API
229
314
  ***************************************************************************** */
@@ -243,29 +328,46 @@ FIO_FUNC inline size_t fio_ary_capa(fio_ary_s *ary) { return ary->capa; }
243
328
  *
244
329
  * fiobj_dup(fiobj_ary_index(array, 0));
245
330
  *
246
- * Negative values are retrived from the end of the array. i.e., `-1`
331
+ * Negative values are retrieved from the end of the array. i.e., `-1`
247
332
  * is the last item.
248
333
  */
249
- FIO_FUNC inline void *fio_ary_index(fio_ary_s *ary, intptr_t pos) {
334
+ FIO_FUNC inline FIO_ARY_TYPE fio_ary_index(fio_ary_s *ary, intptr_t pos) {
250
335
  if (!ary || !ary->arry)
251
- return NULL;
336
+ return FIO_ARY_TYPE_INVALID;
252
337
  /* position is relative to `start`*/
253
338
  if (pos >= 0) {
254
339
  pos = pos + ary->start;
255
340
  if ((size_t)pos >= ary->end)
256
- return NULL;
341
+ return FIO_ARY_TYPE_INVALID;
257
342
  return ary->arry[pos];
258
343
  }
259
344
  /* position is relative to `end`*/
260
345
  pos = (intptr_t)ary->end + pos;
261
346
  if (pos < 0 || (size_t)pos < ary->start)
262
- return NULL;
347
+ return FIO_ARY_TYPE_INVALID;
263
348
  return ary->arry[pos];
264
349
  }
265
350
 
266
351
  /** alias for `fiobj_ary_index` */
267
352
  #define fio_ary_entry(a, p) fiobj_ary_index((a), (p))
268
353
 
354
+ /**
355
+ * Returns the index of the object or -1 if the object wasn't found.
356
+ */
357
+ FIO_FUNC inline intptr_t fio_ary_find(fio_ary_s *ary, FIO_ARY_TYPE data) {
358
+ if (!ary || ary->start == ary->end || !ary->arry) {
359
+ return -1;
360
+ }
361
+ size_t pos = ary->start;
362
+ register const size_t end = ary->end;
363
+ while (pos < end && !FIO_ARY_TYPE_COMPARE(data, ary->arry[pos])) {
364
+ ++pos;
365
+ }
366
+ if (pos == end)
367
+ return -1;
368
+ return (pos - ary->start);
369
+ }
370
+
269
371
  /**
270
372
  * Sets an object at the requested position.
271
373
  *
@@ -273,11 +375,12 @@ FIO_FUNC inline void *fio_ary_index(fio_ary_s *ary, intptr_t pos) {
273
375
  *
274
376
  * If an error occurs, the same data passed to the function is returned.
275
377
  */
276
- FIO_FUNC inline void *fio_ary_set(fio_ary_s *ary, void *data, intptr_t pos) {
378
+ FIO_FUNC inline FIO_ARY_TYPE fio_ary_set(fio_ary_s *ary, FIO_ARY_TYPE data,
379
+ intptr_t pos) {
277
380
  if (!ary->arry) {
278
381
  fio_ary_new(ary, 0);
279
382
  }
280
- void *old = NULL;
383
+ FIO_ARY_TYPE old = FIO_ARY_TYPE_INVALID;
281
384
  /* test for memory and request memory if missing, promises valid bounds. */
282
385
  if (pos >= 0) {
283
386
  if ((size_t)pos + ary->start >= ary->capa)
@@ -290,13 +393,13 @@ FIO_FUNC inline void *fio_ary_set(fio_ary_s *ary, void *data, intptr_t pos) {
290
393
  pos = pos + ary->start;
291
394
  /* initialize empty spaces, if any, setting new boundries */
292
395
  while ((size_t)pos >= ary->end)
293
- ary->arry[(ary->end)++] = NULL;
396
+ ary->arry[(ary->end)++] = FIO_ARY_TYPE_INVALID;
294
397
  } else {
295
398
  /* position relative to end */
296
399
  pos = pos + (intptr_t)ary->end;
297
400
  /* initialize empty spaces, if any, setting new boundries */
298
401
  while (ary->start > (size_t)pos)
299
- ary->arry[--(ary->start)] = NULL;
402
+ ary->arry[--(ary->start)] = FIO_ARY_TYPE_INVALID;
300
403
  }
301
404
 
302
405
  /* check for an existing object and set new objects */
@@ -313,7 +416,7 @@ Array push / shift API
313
416
  /**
314
417
  * Pushes an object to the end of the Array. Returns -1 on error.
315
418
  */
316
- FIO_FUNC inline int fio_ary_push(fio_ary_s *ary, void *data) {
419
+ FIO_FUNC inline int fio_ary_push(fio_ary_s *ary, FIO_ARY_TYPE data) {
317
420
  if (!ary->arry)
318
421
  fio_ary_new(ary, 0);
319
422
  else if (ary->capa <= ary->end)
@@ -324,32 +427,31 @@ FIO_FUNC inline int fio_ary_push(fio_ary_s *ary, void *data) {
324
427
  }
325
428
 
326
429
  /** Pops an object from the end of the Array. */
327
- FIO_FUNC inline void *fio_ary_pop(fio_ary_s *ary) {
430
+ FIO_FUNC inline FIO_ARY_TYPE fio_ary_pop(fio_ary_s *ary) {
328
431
  if (!ary || ary->start == ary->end)
329
- return NULL;
432
+ return FIO_ARY_TYPE_INVALID;
330
433
  ary->end -= 1;
331
434
  return ary->arry[ary->end];
332
435
  }
333
436
 
334
437
  /**
335
- * Unshifts an object to the begining of the Array. Returns -1 on error.
438
+ * Unshifts an object to the beginning of the Array. Returns -1 on error.
336
439
  *
337
440
  * This could be expensive, causing `memmove`.
338
441
  */
339
- FIO_FUNC inline int fio_ary_unshift(fio_ary_s *ary, void *data) {
442
+ FIO_FUNC inline int fio_ary_unshift(fio_ary_s *ary, FIO_ARY_TYPE data) {
340
443
  if (!ary->arry)
341
444
  fio_ary_new(ary, 0);
342
- else if (!ary->start)
343
- fio_ary_getmem(ary, -1);
445
+ fio_ary_getmem(ary, -1);
344
446
  ary->start -= 1;
345
447
  ary->arry[ary->start] = data;
346
448
  return 0;
347
449
  }
348
450
 
349
451
  /** Shifts an object from the beginning of the Array. */
350
- FIO_FUNC inline void *fio_ary_shift(fio_ary_s *ary) {
452
+ FIO_FUNC inline FIO_ARY_TYPE fio_ary_shift(fio_ary_s *ary) {
351
453
  if (!ary || ary->start == ary->end)
352
- return NULL;
454
+ return FIO_ARY_TYPE_INVALID;
353
455
  #ifdef __cplusplus
354
456
  const size_t pos = ary->start;
355
457
  #else
@@ -359,6 +461,55 @@ FIO_FUNC inline void *fio_ary_shift(fio_ary_s *ary) {
359
461
  return ary->arry[pos];
360
462
  }
361
463
 
464
+ /**
465
+ * Removes an object from the array, MOVING all the other objects to prevent
466
+ * "holes" in the data.
467
+ *
468
+ * Returns the removed object or FIO_ARY_TYPE_INVALID (on error).
469
+ */
470
+ FIO_FUNC inline FIO_ARY_TYPE fio_ary_remove(fio_ary_s *ary, intptr_t index) {
471
+ if (!ary || ary->start == ary->end || !ary->arry) {
472
+ return FIO_ARY_TYPE_INVALID;
473
+ }
474
+ if (index < 0) {
475
+ index = (ary->end - ary->start) + index;
476
+ if (index < 0) {
477
+ return FIO_ARY_TYPE_INVALID;
478
+ }
479
+ }
480
+ --ary->end;
481
+ index += ary->start;
482
+ FIO_ARY_TYPE old = ary->arry[(size_t)index];
483
+ register const size_t len = ary->end;
484
+ while ((size_t)index < len) {
485
+ ary->arry[(size_t)index] = ary->arry[(size_t)index + 1];
486
+ ++index;
487
+ }
488
+ return old;
489
+ }
490
+
491
+ /**
492
+ * Removes an object from the array, if it exists, MOVING all the other objects
493
+ * to prevent "holes" in the data.
494
+ *
495
+ * Returns -1 if the object wasn't found or 0 if the object was successfully
496
+ * removed.
497
+ */
498
+ FIO_FUNC inline int fio_ary_remove2(fio_ary_s *ary, FIO_ARY_TYPE data) {
499
+ intptr_t index = fio_ary_find(ary, data);
500
+ if (index == -1) {
501
+ return -1;
502
+ }
503
+ index += ary->start;
504
+ --ary->end;
505
+ register const size_t len = ary->end;
506
+ while ((size_t)index < len) {
507
+ ary->arry[(size_t)index] = ary->arry[(size_t)index + 1];
508
+ ++index;
509
+ }
510
+ return 0;
511
+ }
512
+
362
513
  /**
363
514
  * Single layer iteration using a callback for each entry in the array.
364
515
  *
@@ -371,7 +522,7 @@ FIO_FUNC inline void *fio_ary_shift(fio_ary_s *ary) {
371
522
  * the starting point.
372
523
  */
373
524
  FIO_FUNC inline size_t fio_ary_each(fio_ary_s *ary, size_t start_at,
374
- int (*task)(void *data, void *arg),
525
+ int (*task)(FIO_ARY_TYPE data, void *arg),
375
526
  void *arg) {
376
527
  if (!ary || ary->start == ary->end)
377
528
  return 0;
@@ -387,8 +538,8 @@ Array compacting (untested)
387
538
  ***************************************************************************** */
388
539
 
389
540
  /**
390
- * Removes any NULL *pointers* from an Array, keeping all other data in the
391
- * array.
541
+ * Removes any FIO_ARY_TYPE_INVALID *pointers* from an Array, keeping all other
542
+ * data in the array.
392
543
  *
393
544
  * This action is O(n) where n in the length of the array.
394
545
  * It could get expensive.
@@ -396,9 +547,9 @@ Array compacting (untested)
396
547
  FIO_FUNC inline void fio_ary_compact(fio_ary_s *ary) {
397
548
  if (!ary || ary->start == ary->end)
398
549
  return;
399
- register void **pos = ary->arry + ary->start;
400
- register void **reader = ary->arry + ary->start;
401
- register void **stop = ary->arry + ary->end;
550
+ register FIO_ARY_TYPE *pos = ary->arry + ary->start;
551
+ register FIO_ARY_TYPE *reader = ary->arry + ary->start;
552
+ register FIO_ARY_TYPE *stop = ary->arry + ary->end;
402
553
  while (reader < stop) {
403
554
  if (*reader) {
404
555
  *pos = *reader;
@@ -409,6 +560,154 @@ FIO_FUNC inline void fio_ary_compact(fio_ary_s *ary) {
409
560
  ary->end = (size_t)(pos - ary->arry);
410
561
  }
411
562
 
563
+ /* *****************************************************************************
564
+ Testing
565
+ ***************************************************************************** */
566
+
567
+ #if DEBUG
568
+ #include <stdio.h>
569
+ #define TEST_LIMIT 1016
570
+ #define TEST_ASSERT(cond, ...) \
571
+ if (!(cond)) { \
572
+ fprintf(stderr, "* " __VA_ARGS__); \
573
+ fprintf(stderr, "\n !!! Testing failed !!!\n"); \
574
+ exit(-1); \
575
+ }
576
+ /**
577
+ * Removes any FIO_ARY_TYPE_INVALID *pointers* from an Array, keeping all other
578
+ * data in the array.
579
+ *
580
+ * This action is O(n) where n in the length of the array.
581
+ * It could get expensive.
582
+ */
583
+ FIO_FUNC inline void fio_ary_test(void) {
584
+ union {
585
+ FIO_ARY_TYPE obj;
586
+ uintptr_t i;
587
+ } mem[TEST_LIMIT];
588
+ fio_ary_s ary = FIO_ARY_INIT;
589
+ fprintf(stderr, "=== Testing Core Array features (fio_ary.h)\n");
590
+
591
+ for (uintptr_t i = 0; i < TEST_LIMIT; ++i) {
592
+ mem[i].i = i + 1;
593
+ fio_ary_push(&ary, mem[i].obj);
594
+ }
595
+ fprintf(stderr,
596
+ "* Array populated using `push` with %zu items,\n"
597
+ " with capacity limit of %zu and start index %zu\n",
598
+ (size_t)fio_ary_count(&ary), (size_t)fio_ary_capa(&ary), ary.start);
599
+ TEST_ASSERT(fio_ary_count(&ary) == TEST_LIMIT,
600
+ "Wrong object count for array %zu", (size_t)fio_ary_count(&ary));
601
+ for (uintptr_t i = 0; i < TEST_LIMIT; ++i) {
602
+ mem[0].obj = fio_ary_shift(&ary);
603
+ TEST_ASSERT(mem[0].i == i + 1, "Array shift value error");
604
+ }
605
+
606
+ fio_ary_free(&ary);
607
+ TEST_ASSERT(!ary.arry, "Array not reset after fio_ary_free");
608
+
609
+ for (uintptr_t i = 0; i < TEST_LIMIT; ++i) {
610
+ mem[i].i = TEST_LIMIT - i;
611
+ fio_ary_unshift(&ary, mem[i].obj);
612
+ }
613
+ fprintf(stderr,
614
+ "* Array populated using `unshift` with %zu items,\n"
615
+ " with capacity limit of %zu and start index %zu\n",
616
+ (size_t)fio_ary_count(&ary), (size_t)fio_ary_capa(&ary), ary.start);
617
+ TEST_ASSERT(fio_ary_count(&ary) == TEST_LIMIT,
618
+ "Wrong object count for array %zu", (size_t)fio_ary_count(&ary));
619
+ for (uintptr_t i = 0; i < TEST_LIMIT; ++i) {
620
+ mem[0].obj = fio_ary_pop(&ary);
621
+ TEST_ASSERT(mem[0].i == TEST_LIMIT - i, "Array pop value error");
622
+ }
623
+ fio_ary_free(&ary);
624
+ TEST_ASSERT(!ary.arry, "Array not reset after fio_ary_free");
625
+
626
+ for (uintptr_t i = 0; i < TEST_LIMIT; ++i) {
627
+ mem[i].i = TEST_LIMIT - i;
628
+ fio_ary_unshift(&ary, mem[i].obj);
629
+ }
630
+
631
+ for (int i = 0; i < TEST_LIMIT; ++i) {
632
+ mem[0].i = i + 1;
633
+ TEST_ASSERT(fio_ary_find(&ary, mem[0].obj) == i,
634
+ "Wrong object index - ary[%zd] != %zu",
635
+ (ssize_t)fio_ary_find(&ary, mem[0].obj), (size_t)mem[0].i);
636
+ mem[0].obj = fio_ary_index(&ary, i);
637
+ TEST_ASSERT(mem[0].i == (uintptr_t)(i + 1),
638
+ "Wrong object returned from fio_ary_index - ary[%d] != %d", i,
639
+ i + 1);
640
+ }
641
+
642
+ TEST_ASSERT((mem[0].obj = fio_ary_pop(&ary)), "Couldn't pop element.");
643
+ TEST_ASSERT(mem[0].i == TEST_LIMIT, "Element value error (%zu).",
644
+ (size_t)mem[0].i);
645
+ TEST_ASSERT(fio_ary_count(&ary) == TEST_LIMIT - 1,
646
+ "Wrong object count after pop %zu", (size_t)fio_ary_count(&ary));
647
+
648
+ mem[0].i = (TEST_LIMIT >> 1);
649
+ TEST_ASSERT(!fio_ary_remove2(&ary, mem[0].obj),
650
+ "Couldn't fio_ary_remove2 object from Array (%zu)",
651
+ (size_t)mem[0].i);
652
+ TEST_ASSERT(fio_ary_count(&ary) == TEST_LIMIT - 2,
653
+ "Wrong object count after remove2 %zu",
654
+ (size_t)fio_ary_count(&ary));
655
+ mem[0].i = (TEST_LIMIT >> 1) + 1;
656
+ TEST_ASSERT(fio_ary_find(&ary, mem[0].obj) != (TEST_LIMIT >> 1) + 1,
657
+ "fio_ary_remove2 didn't clear holes from Array (%zu)",
658
+ (size_t)fio_ary_find(&ary, mem[0].obj));
659
+
660
+ mem[0].obj = fio_ary_remove(&ary, 0);
661
+ TEST_ASSERT(mem[0].i == 1, "Couldn't fio_ary_remove object from Array (%zd)",
662
+ (ssize_t)mem[0].i);
663
+ TEST_ASSERT(fio_ary_count(&ary) == TEST_LIMIT - 3,
664
+ "Wrong object count after remove %zu",
665
+ (size_t)fio_ary_count(&ary));
666
+ TEST_ASSERT(fio_ary_find(&ary, mem[0].obj) == -1,
667
+ "fio_ary_find should have failed after fio_ary_remove (%zd)",
668
+ (ssize_t)fio_ary_find(&ary, mem[0].obj));
669
+ mem[0].i = 2;
670
+ TEST_ASSERT(fio_ary_find(&ary, mem[0].obj) == 0,
671
+ "fio_ary_remove didn't clear holes from Array (%zu)",
672
+ (size_t)fio_ary_find(&ary, mem[0].obj));
673
+
674
+ fio_ary_free(&ary);
675
+
676
+ fio_ary_s ary2 = FIO_ARY_INIT;
677
+ for (uintptr_t i = 0; i < (TEST_LIMIT >> 1); ++i) {
678
+ mem[i].i = ((TEST_LIMIT >> 1) << 1) - i;
679
+ fio_ary_unshift(&ary2, mem[i].obj);
680
+ mem[i].i = (TEST_LIMIT >> 1) - i;
681
+ fio_ary_unshift(&ary, mem[i].obj);
682
+ }
683
+ fio_ary_concat(&ary, &ary2);
684
+ fio_ary_free(&ary2);
685
+ TEST_ASSERT(fio_ary_count(&ary) == ((TEST_LIMIT >> 1) << 1),
686
+ "Wrong object count after fio_ary_concat %zu",
687
+ (size_t)fio_ary_count(&ary));
688
+ for (int i = 0; i < ((TEST_LIMIT >> 1) << 1); ++i) {
689
+ mem[0].obj = fio_ary_index(&ary, i);
690
+ TEST_ASSERT(
691
+ mem[0].i == (uintptr_t)(i + 1),
692
+ "Wrong object returned from fio_ary_index after concat - ary[%d] != %d",
693
+ i, i + 1);
694
+ }
695
+ mem[1].i = 0;
696
+ while ((mem[0].obj = fio_ary_pop(&ary))) {
697
+ ++mem[1].i;
698
+ }
699
+ TEST_ASSERT(mem[1].i == ((TEST_LIMIT >> 1) << 1),
700
+ "fio_ary_pop overflow (%zu)?", (size_t)mem[1].i);
701
+ fio_ary_free(&ary);
702
+ }
703
+ #undef TEST_LIMIT
704
+ #undef TEST_ASSERT
705
+ #endif
706
+
707
+ /* *****************************************************************************
708
+ Done
709
+ ***************************************************************************** */
710
+
412
711
  #ifdef __cplusplus
413
712
  #undef register
414
713
  #endif