iodine 0.7.3 → 0.7.4

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.

@@ -5,12 +5,13 @@
5
5
  #include <inttypes.h>
6
6
  #include <stdint.h>
7
7
 
8
- #define FIO_SET_NAME fio_hash
8
+ #define FIO_SET_NAME fio_store
9
9
  #define FIO_SET_OBJ_TYPE uintptr_t
10
10
  #include <fio.h>
11
11
 
12
- fio_lock_i lock = FIO_LOCK_INIT;
13
- fio_hash_s storage = FIO_SET_INIT;
12
+ static fio_lock_i iodine_storage_lock = FIO_LOCK_INIT;
13
+ static fio_store_s iodine_storage = FIO_SET_INIT;
14
+ static size_t iodine_storage_count_max = 0;
14
15
 
15
16
  #ifndef IODINE_DEBUG
16
17
  #define IODINE_DEBUG 0
@@ -24,36 +25,38 @@ API
24
25
  static VALUE storage_add(VALUE obj) {
25
26
  if (!obj || obj == Qnil || obj == Qtrue || obj == Qfalse)
26
27
  return obj;
27
- fio_lock(&lock);
28
28
  uintptr_t old = 0;
29
- fio_hash_overwrite(&storage, obj, 1, &old);
29
+ fio_lock(&iodine_storage_lock);
30
+ fio_store_overwrite(&iodine_storage, obj, 1, &old);
30
31
  if (old)
31
- fio_hash_overwrite(&storage, obj, old + 1, NULL);
32
- fio_unlock(&lock);
32
+ fio_store_overwrite(&iodine_storage, obj, old + 1, NULL);
33
+ if (iodine_storage_count_max < fio_store_count(&iodine_storage))
34
+ iodine_storage_count_max = fio_store_count(&iodine_storage);
35
+ fio_unlock(&iodine_storage_lock);
33
36
  return obj;
34
37
  }
35
38
  /** Removes an object from the storage (or decreases it's reference count). */
36
39
  static VALUE storage_remove(VALUE obj) {
37
40
  if (!obj || obj == Qnil || obj == Qtrue || obj == Qfalse ||
38
- storage.count == 0)
41
+ iodine_storage.count == 0)
39
42
  return obj;
40
- fio_lock(&lock);
43
+ fio_lock(&iodine_storage_lock);
41
44
  uintptr_t old = 0;
42
- fio_hash_remove(&storage, obj, 0, &old);
45
+ fio_store_remove(&iodine_storage, obj, 0, &old);
43
46
  if (old > 1)
44
- fio_hash_overwrite(&storage, obj, old - 1, NULL);
45
- fio_unlock(&lock);
47
+ fio_store_overwrite(&iodine_storage, obj, old - 1, NULL);
48
+ fio_unlock(&iodine_storage_lock);
46
49
  return obj;
47
50
  }
48
51
  /** Should be called after forking to reset locks */
49
- static void storage_after_fork(void) { lock = FIO_LOCK_INIT; }
52
+ static void storage_after_fork(void) { iodine_storage_lock = FIO_LOCK_INIT; }
50
53
 
51
54
  /** Prints debugging information to the console. */
52
55
  static void storage_print(void) {
53
56
  FIO_LOG_DEBUG("Ruby <=> C Memory storage stats (pid: %d):\n", getpid());
54
- fio_lock(&lock);
57
+ fio_lock(&iodine_storage_lock);
55
58
  uintptr_t index = 0;
56
- FIO_SET_FOR_LOOP(&storage, pos) {
59
+ FIO_SET_FOR_LOOP(&iodine_storage, pos) {
57
60
  if (pos->obj) {
58
61
  fprintf(stderr, "[%" PRIuPTR "] => %" PRIuPTR " X obj %p type %d\n",
59
62
  index++, pos->obj, (void *)pos->hash, TYPE(pos->hash));
@@ -61,9 +64,10 @@ static void storage_print(void) {
61
64
  }
62
65
  fprintf(stderr, "Total of %" PRIuPTR " objects protected form GC\n", index);
63
66
  fprintf(stderr,
64
- "Storage uses %" PRIuPTR " Hash bins for %" PRIuPTR " objects",
65
- storage.capa, storage.count);
66
- fio_unlock(&lock);
67
+ "Storage uses %" PRIuPTR " Hash bins for %" PRIuPTR " objects\n"
68
+ "The largest collection was %zu objects.",
69
+ iodine_storage.capa, iodine_storage.count, iodine_storage_count_max);
70
+ fio_unlock(&iodine_storage_lock);
67
71
  }
68
72
 
69
73
  /**
@@ -83,24 +87,24 @@ static void storage_mark(void *ignore) {
83
87
  (void)ignore;
84
88
  if (FIO_LOG_LEVEL >= FIO_LOG_LEVEL_DEBUG)
85
89
  storage_print();
86
- fio_lock(&lock);
87
- // fio_hash_compact(&storage);
88
- FIO_SET_FOR_LOOP(&storage, pos) {
90
+ fio_lock(&iodine_storage_lock);
91
+ // fio_store_compact(&iodine_storage);
92
+ FIO_SET_FOR_LOOP(&iodine_storage, pos) {
89
93
  if (pos->obj) {
90
94
  rb_gc_mark((VALUE)pos->hash);
91
95
  }
92
96
  }
93
- fio_unlock(&lock);
97
+ fio_unlock(&iodine_storage_lock);
94
98
  }
95
99
 
96
100
  /* clear the registry (end of lifetime) */
97
101
  static void storage_clear(void *ignore) {
98
102
  (void)ignore;
99
103
  FIO_LOG_DEBUG("Ruby<=>C Storage cleared.\n");
100
- fio_lock(&lock);
101
- fio_hash_free(&storage);
102
- storage = (fio_hash_s)FIO_SET_INIT;
103
- fio_unlock(&lock);
104
+ fio_lock(&iodine_storage_lock);
105
+ fio_store_free(&iodine_storage);
106
+ iodine_storage = (fio_store_s)FIO_SET_INIT;
107
+ fio_unlock(&iodine_storage_lock);
104
108
  }
105
109
 
106
110
  /*
@@ -126,12 +130,12 @@ struct IodineStorage_s IodineStore = {
126
130
 
127
131
  /** Initializes the storage unit for first use. */
128
132
  void iodine_storage_init(void) {
129
- fio_hash_capa_require(&storage, 512);
133
+ fio_store_capa_require(&iodine_storage, 512);
130
134
  VALUE tmp =
131
135
  rb_define_class_under(rb_cObject, "IodineObjectStorage", rb_cData);
132
136
  VALUE storage_obj =
133
- TypedData_Wrap_Struct(tmp, &storage_type_struct, &storage);
134
- // rb_global_variable(&storage_obj);
137
+ TypedData_Wrap_Struct(tmp, &storage_type_struct, &iodine_storage);
138
+ // rb_global_variable(&iodine_storage_obj);
135
139
  rb_ivar_set(IodineModule, rb_intern2("storage", 7), storage_obj);
136
140
  rb_define_module_function(IodineBaseModule, "db_print_protected_objects",
137
141
  storage_print_rb, 0);
@@ -43,7 +43,5 @@ Gem::Specification.new do |spec|
43
43
  spec.add_development_dependency 'minitest', '>=5', '< 6.0'
44
44
  spec.add_development_dependency 'rake-compiler', '>= 1', '< 2.0'
45
45
 
46
- # spec.post_install_message = "** WARNING!\n" \
47
- # "Iodine 0.2.0 is NOT an upgrade - it's a total rewrite, it's written in C specifically for Ruby MRI.\n\n" \
48
- # 'If your application was using Iodine 0.1.x, it might not work after this "upgrade".'
46
+ spec.post_install_message = "Thank you for installing Iodine #{Iodine::VERSION}.\n"
49
47
  end
@@ -138,9 +138,9 @@ if(!defined?(after_fork_in_master))
138
138
  end
139
139
  end
140
140
  if(!defined?(on_worker_boot))
141
- # Performs a block of code whenever a new worker process spins up (performed once per worker).
141
+ # Performs a block of code before a new worker process spins up (performed once per worker).
142
142
  def on_worker_boot(*args, &block)
143
- Iodine.after_fork(*args, &block)
143
+ Iodine.before_fork(*args, &block)
144
144
  end
145
145
  end
146
146
  if(!defined?(before_fork))
@@ -1,7 +1,9 @@
1
1
  module Iodine
2
2
  # Iodine includes a lenient JSON parser that attempts to ignore JSON errors when possible and adds some extensions such as Hex numerical representations and comments.
3
3
  #
4
- # On my system, the Iodine JSON parser is more than 40% faster than the native Ruby parser. When using symbols the speed increase is even higher.
4
+ # Depending on content (specifically, the effects of float number parsing), the Iodine JSON parser speed varies.
5
+ #
6
+ # On my system, Iodine is about 20%-30% faster than Ruby MRI's parser (Ruby version 2.5.1 vs. Iodine version 0.7.4). When using symbols the speed increase is even higher (50%-90% faster).
5
7
  #
6
8
  # It's easy to monkey-patch the system's `JSON.parse` method (not the `JSON.parse!` method) by using `Iodine.patch_json`.
7
9
  #
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.7.3'.freeze
2
+ VERSION = '0.7.4'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.3
4
+ version: 0.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-10-04 00:00:00.000000000 Z
11
+ date: 2018-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -163,7 +163,6 @@ files:
163
163
  - ext/iodine/extconf.rb
164
164
  - ext/iodine/fio.c
165
165
  - ext/iodine/fio.h
166
- - ext/iodine/fio_ary.h
167
166
  - ext/iodine/fio_cli.c
168
167
  - ext/iodine/fio_cli.h
169
168
  - ext/iodine/fio_json_parser.h
@@ -245,7 +244,9 @@ licenses:
245
244
  - MIT
246
245
  metadata:
247
246
  allowed_push_host: https://rubygems.org
248
- post_install_message:
247
+ post_install_message: 'Thank you for installing Iodine 0.7.4.
248
+
249
+ '
249
250
  rdoc_options: []
250
251
  require_paths:
251
252
  - lib
@@ -1,717 +0,0 @@
1
- #ifndef H_FIO_ARRAY_H
2
- /*
3
- Copyright: Boaz Segev, 2017-2018
4
- License: MIT
5
- */
6
-
7
- /**
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).
16
- *
17
- * The file was written to be compatible with C++ as well as C, hence some
18
- * pointer casting.
19
- *
20
- * Use:
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
- *
27
- */
28
- #define H_FIO_ARRAY_H
29
-
30
- #include <errno.h>
31
- #include <stdint.h>
32
- #include <stdio.h>
33
- #include <stdlib.h>
34
- #include <string.h>
35
- #ifndef _GNU_SOURCE
36
- #define _GNU_SOURCE
37
- #endif
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
-
53
- #ifndef FIO_FUNC
54
- #define FIO_FUNC static __attribute__((unused))
55
- #endif
56
-
57
- #ifdef __cplusplus
58
- #define register
59
- #endif
60
-
61
- typedef struct fio_ary_s {
62
- size_t start;
63
- size_t end;
64
- size_t capa;
65
- FIO_ARY_TYPE *arry;
66
- } fio_ary_s;
67
-
68
- /** this value can be used for lazy initialization. */
69
- #define FIO_ARY_INIT ((fio_ary_s){.arry = NULL})
70
-
71
- /* *****************************************************************************
72
- Array API
73
- ***************************************************************************** */
74
-
75
- /** Initializes the array and allocates memory for it's internal data. */
76
- FIO_FUNC inline void fio_ary_new(fio_ary_s *ary, size_t capa);
77
-
78
- /** Frees the array's internal data. */
79
- FIO_FUNC inline void fio_ary_free(fio_ary_s *ary);
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
-
88
- /** Returns the number of elements in the Array. */
89
- FIO_FUNC inline size_t fio_ary_count(fio_ary_s *ary);
90
-
91
- /** Returns the current, temporary, array capacity (it's dynamic). */
92
- FIO_FUNC inline size_t fio_ary_capa(fio_ary_s *ary);
93
-
94
- /**
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.
97
- *
98
- * Negative values are retrieved from the end of the array. i.e., `-1`
99
- * is the last item.
100
- */
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);
107
-
108
- /** alias for `fiobj_ary_index` */
109
- #define fio_ary_entry(a, p) fiobj_ary_index((a), (p))
110
-
111
- /**
112
- * Sets an object at the requested position.
113
- *
114
- * Returns the old value, if any.
115
- *
116
- * If an error occurs, the same data passed to the function is returned.
117
- */
118
- FIO_FUNC inline FIO_ARY_TYPE fio_ary_set(fio_ary_s *ary, FIO_ARY_TYPE data,
119
- intptr_t pos);
120
-
121
- /**
122
- * Pushes an object to the end of the Array. Returns -1 on error.
123
- */
124
- FIO_FUNC inline int fio_ary_push(fio_ary_s *ary, FIO_ARY_TYPE data);
125
-
126
- /** Pops an object from the end of the Array. */
127
- FIO_FUNC inline FIO_ARY_TYPE fio_ary_pop(fio_ary_s *ary);
128
-
129
- /**
130
- * Unshifts an object to the beginning of the Array. Returns -1 on error.
131
- *
132
- * This could be expensive, causing `memmove`.
133
- */
134
- FIO_FUNC inline int fio_ary_unshift(fio_ary_s *ary, FIO_ARY_TYPE data);
135
-
136
- /** Shifts an object from the beginning of the Array. */
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);
155
-
156
- /**
157
- * Iteration using a callback for each entry in the array.
158
- *
159
- * The callback task function must accept an the entry data as well as an opaque
160
- * user pointer.
161
- *
162
- * If the callback returns -1, the loop is broken. Any other value is ignored.
163
- *
164
- * Returns the relative "stop" position, i.e., the number of items processed +
165
- * the starting point.
166
- */
167
- FIO_FUNC inline size_t fio_ary_each(fio_ary_s *ary, size_t start_at,
168
- int (*task)(FIO_ARY_TYPE pt, void *arg),
169
- void *arg);
170
- /**
171
- * Removes any FIO_ARY_TYPE_INVALID object from an Array (NULL pointers by
172
- * default), keeping all other data in the array.
173
- *
174
- * This action is O(n) where n in the length of the array.
175
- * It could get expensive.
176
- */
177
- FIO_FUNC inline void fio_ary_compact(fio_ary_s *ary);
178
-
179
- /**
180
- * Iterates through the list using a `for` loop.
181
- *
182
- * Access the data with `pos.obj` and it's index with `pos.i`. the `pos`
183
- * variable can be named however you please.
184
- */
185
- #define FIO_ARY_FOR(ary, pos) \
186
- if ((ary)->arry) \
187
- for (struct fio_ary_pos_for_loop_s pos = {0, (ary)->arry[(ary)->start]}; \
188
- (pos.i + (ary)->start) < (ary)->end && \
189
- ((pos.obj = (ary)->arry[pos.i + (ary)->start]), 1); \
190
- (++pos.i))
191
- struct fio_ary_pos_for_loop_s {
192
- unsigned long i;
193
- FIO_ARY_TYPE obj;
194
- };
195
-
196
- /* *****************************************************************************
197
- Array creation API
198
- ***************************************************************************** */
199
-
200
- FIO_FUNC inline void fio_ary_new(fio_ary_s *ary, size_t capa) {
201
- if (!capa)
202
- capa = 32;
203
- *ary = (fio_ary_s){.arry = (FIO_ARY_TYPE *)malloc(capa * sizeof(*ary->arry)),
204
- .capa = capa};
205
- if (!ary->arry) {
206
- perror("ERROR: facil.io dynamic array couldn't be allocated");
207
- exit(errno);
208
- }
209
- }
210
- FIO_FUNC inline void fio_ary_free(fio_ary_s *ary) {
211
- if (ary) {
212
- free(ary->arry);
213
- *ary = FIO_ARY_INIT;
214
- }
215
- }
216
-
217
- /* *****************************************************************************
218
- Array memory management
219
- ***************************************************************************** */
220
-
221
- /* This funcation manages the Array's memory. */
222
- FIO_FUNC void fio_ary_getmem(fio_ary_s *ary, intptr_t needed) {
223
- /* we have enough memory, but we need to re-organize it. */
224
- if (needed == -1) {
225
- if (ary->start > 0) {
226
- return;
227
- }
228
- if (ary->end < ary->capa) {
229
- /* since allocation can be cheaper than memmove (depending on size),
230
- * we'll just shove everything to the end...
231
- */
232
- size_t len = ary->end - ary->start;
233
- if (len) {
234
- memmove(ary->arry + ary->capa - len, ary->arry + ary->start,
235
- len * sizeof(*ary->arry));
236
- }
237
- ary->start = ary->capa - len;
238
- ary->end = ary->capa;
239
- return;
240
- }
241
- /* add some breathing room for future `unshift`s */
242
- needed = 0 - ((ary->capa < 1024) ? (ary->capa >> 1) : 1024);
243
-
244
- } else if (needed == 1 && ary->start && ary->start >= (ary->capa >> 1)) {
245
- /* FIFO support optimizes smaller FIFO ranges over bloating allocations. */
246
- size_t len = ary->end - ary->start;
247
- if (len) {
248
- memmove(ary->arry + 2, ary->arry + ary->start, len * sizeof(*ary->arry));
249
- }
250
- ary->start = 2;
251
- ary->end = len + 2;
252
- return;
253
- }
254
-
255
- /* alocate using exponential growth, up to single page size. */
256
- size_t updated_capa = ary->capa;
257
- size_t minimum = ary->capa + ((needed < 0) ? (0 - needed) : needed);
258
- if (!updated_capa) {
259
- updated_capa = 1;
260
- }
261
- while (updated_capa <= minimum)
262
- updated_capa =
263
- (updated_capa <= 4096) ? (updated_capa << 1) : (updated_capa + 4096);
264
-
265
- /* we assume memory allocation works. it's better to crash than to continue
266
- * living without memory... besides, malloc is optimistic these days. */
267
- ary->arry =
268
- (FIO_ARY_TYPE *)realloc(ary->arry, updated_capa * sizeof(*ary->arry));
269
- ary->capa = updated_capa;
270
- if (!ary->arry) {
271
- perror("ERROR: facil.io dynamic array couldn't be reallocated");
272
- exit(errno);
273
- }
274
-
275
- if (needed >= 0) /* we're done, realloc grows the top of the address space*/
276
- return;
277
-
278
- /* move everything to the max, since memmove could get expensive */
279
- size_t len = ary->end - ary->start;
280
- memmove((ary->arry + ary->capa) - len, ary->arry + ary->start,
281
- len * sizeof(*ary->arry));
282
- ary->end = ary->capa;
283
- ary->start = ary->capa - len;
284
- }
285
- /** Creates a mutable empty Array object with the requested capacity. */
286
- FIO_FUNC inline void fiobj_ary_init(fio_ary_s *ary) {
287
- *ary = (fio_ary_s){.arry = NULL};
288
- }
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
-
312
- /* *****************************************************************************
313
- Array direct entry access API
314
- ***************************************************************************** */
315
-
316
- /** Returns the number of elements in the Array. */
317
- FIO_FUNC inline size_t fio_ary_count(fio_ary_s *ary) {
318
- return ary->end - ary->start;
319
- }
320
-
321
- /** Returns the current, temporary, array capacity (it's dynamic). */
322
- FIO_FUNC inline size_t fio_ary_capa(fio_ary_s *ary) { return ary->capa; }
323
-
324
- /**
325
- * Returns a temporary object owned by the Array.
326
- *
327
- * Wrap this function call within `fiobj_dup` to get a persistent handle. i.e.:
328
- *
329
- * fiobj_dup(fiobj_ary_index(array, 0));
330
- *
331
- * Negative values are retrieved from the end of the array. i.e., `-1`
332
- * is the last item.
333
- */
334
- FIO_FUNC inline FIO_ARY_TYPE fio_ary_index(fio_ary_s *ary, intptr_t pos) {
335
- if (!ary || !ary->arry)
336
- return FIO_ARY_TYPE_INVALID;
337
- /* position is relative to `start`*/
338
- if (pos >= 0) {
339
- pos = pos + ary->start;
340
- if ((size_t)pos >= ary->end)
341
- return FIO_ARY_TYPE_INVALID;
342
- return ary->arry[pos];
343
- }
344
- /* position is relative to `end`*/
345
- pos = (intptr_t)ary->end + pos;
346
- if (pos < 0 || (size_t)pos < ary->start)
347
- return FIO_ARY_TYPE_INVALID;
348
- return ary->arry[pos];
349
- }
350
-
351
- /** alias for `fiobj_ary_index` */
352
- #define fio_ary_entry(a, p) fiobj_ary_index((a), (p))
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
-
371
- /**
372
- * Sets an object at the requested position.
373
- *
374
- * Returns the old value, if any.
375
- *
376
- * If an error occurs, the same data passed to the function is returned.
377
- */
378
- FIO_FUNC inline FIO_ARY_TYPE fio_ary_set(fio_ary_s *ary, FIO_ARY_TYPE data,
379
- intptr_t pos) {
380
- if (!ary->arry) {
381
- fio_ary_new(ary, 0);
382
- }
383
- FIO_ARY_TYPE old = FIO_ARY_TYPE_INVALID;
384
- /* test for memory and request memory if missing, promises valid bounds. */
385
- if (pos >= 0) {
386
- if ((size_t)pos + ary->start >= ary->capa)
387
- fio_ary_getmem(ary, (((size_t)pos + ary->start) - (ary->capa - 1)));
388
- } else if (pos + (intptr_t)ary->end < 0)
389
- fio_ary_getmem(ary, pos + ary->end);
390
-
391
- if (pos >= 0) {
392
- /* position relative to start */
393
- pos = pos + ary->start;
394
- /* initialize empty spaces, if any, setting new boundries */
395
- while ((size_t)pos >= ary->end)
396
- ary->arry[(ary->end)++] = FIO_ARY_TYPE_INVALID;
397
- } else {
398
- /* position relative to end */
399
- pos = pos + (intptr_t)ary->end;
400
- /* initialize empty spaces, if any, setting new boundries */
401
- while (ary->start > (size_t)pos)
402
- ary->arry[--(ary->start)] = FIO_ARY_TYPE_INVALID;
403
- }
404
-
405
- /* check for an existing object and set new objects */
406
- if (ary->arry[pos])
407
- old = ary->arry[pos];
408
- ary->arry[pos] = data;
409
- return old;
410
- }
411
-
412
- /* *****************************************************************************
413
- Array push / shift API
414
- ***************************************************************************** */
415
-
416
- /**
417
- * Pushes an object to the end of the Array. Returns -1 on error.
418
- */
419
- FIO_FUNC inline int fio_ary_push(fio_ary_s *ary, FIO_ARY_TYPE data) {
420
- if (!ary->arry)
421
- fio_ary_new(ary, 0);
422
- else if (ary->capa <= ary->end)
423
- fio_ary_getmem(ary, 1);
424
- ary->arry[ary->end] = data;
425
- ary->end += 1;
426
- return 0;
427
- }
428
-
429
- /** Pops an object from the end of the Array. */
430
- FIO_FUNC inline FIO_ARY_TYPE fio_ary_pop(fio_ary_s *ary) {
431
- if (!ary || ary->start == ary->end)
432
- return FIO_ARY_TYPE_INVALID;
433
- ary->end -= 1;
434
- return ary->arry[ary->end];
435
- }
436
-
437
- /**
438
- * Unshifts an object to the beginning of the Array. Returns -1 on error.
439
- *
440
- * This could be expensive, causing `memmove`.
441
- */
442
- FIO_FUNC inline int fio_ary_unshift(fio_ary_s *ary, FIO_ARY_TYPE data) {
443
- if (!ary->arry)
444
- fio_ary_new(ary, 0);
445
- fio_ary_getmem(ary, -1);
446
- ary->start -= 1;
447
- ary->arry[ary->start] = data;
448
- return 0;
449
- }
450
-
451
- /** Shifts an object from the beginning of the Array. */
452
- FIO_FUNC inline FIO_ARY_TYPE fio_ary_shift(fio_ary_s *ary) {
453
- if (!ary || ary->start == ary->end)
454
- return FIO_ARY_TYPE_INVALID;
455
- #ifdef __cplusplus
456
- const size_t pos = ary->start;
457
- #else
458
- register const size_t pos = ary->start;
459
- #endif
460
- ary->start += 1;
461
- return ary->arry[pos];
462
- }
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
-
513
- /**
514
- * Single layer iteration using a callback for each entry in the array.
515
- *
516
- * The callback task function must accept an the entry data as well as an opaque
517
- * user pointer.
518
- *
519
- * If the callback returns -1, the loop is broken. Any other value is ignored.
520
- *
521
- * Returns the relative "stop" position, i.e., the number of items processed +
522
- * the starting point.
523
- */
524
- FIO_FUNC inline size_t fio_ary_each(fio_ary_s *ary, size_t start_at,
525
- int (*task)(FIO_ARY_TYPE data, void *arg),
526
- void *arg) {
527
- if (!ary || ary->start == ary->end)
528
- return 0;
529
- const size_t start_pos = ary->start;
530
- start_at += start_pos;
531
- while (start_at < ary->end && task(ary->arry[start_at++], arg) != -1)
532
- ;
533
- return start_at - start_pos;
534
- }
535
-
536
- /* *****************************************************************************
537
- Array compacting (untested)
538
- ***************************************************************************** */
539
-
540
- /**
541
- * Removes any FIO_ARY_TYPE_INVALID *pointers* from an Array, keeping all other
542
- * data in the array.
543
- *
544
- * This action is O(n) where n in the length of the array.
545
- * It could get expensive.
546
- */
547
- FIO_FUNC inline void fio_ary_compact(fio_ary_s *ary) {
548
- if (!ary || ary->start == ary->end)
549
- return;
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;
553
- while (reader < stop) {
554
- if (*reader) {
555
- *pos = *reader;
556
- pos += 1;
557
- }
558
- reader += 1;
559
- }
560
- ary->end = (size_t)(pos - ary->arry);
561
- }
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
-
711
- #ifdef __cplusplus
712
- #undef register
713
- #endif
714
-
715
- #undef FIO_FUNC
716
-
717
- #endif