prometheus-client-mmap 0.16.2 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35a2193b5080273f3311b1a3dbaf2fcd854af38f4cc30017da7b91c0879dbaa0
4
- data.tar.gz: 503c7bcfbc1acf0ff4a1dfb88e151bd71720c03569f91e4b9b61e16591ad063f
3
+ metadata.gz: 98e93b1f6cdd34ff4621cc7401fa1b02b6e81b069313899aabd28d1a1650cf7d
4
+ data.tar.gz: d0ceb3c1535fa17e609baef147217593366bd3e0351254d231848ec18653c06d
5
5
  SHA512:
6
- metadata.gz: a774996689975c51d3d763a6e7d9187ccba7238212db72762df8981b1476559cd6c92b24d20b869f7402fdcf4ee4b9256efdc46f46dc7280d80a4024baf508f5
7
- data.tar.gz: a190ed7b811320c9c3f48cf084330e7347cfe173fd09deae795d991c6b3d0a7d8737940de0b50970eb34fcda61092864b4266d74d6220dad90d9c097071459f0
6
+ metadata.gz: 7cc06bff586223b764f098d18c213e59f072a3ab906c7f4c2e844bcc419c9b1037d39b564d2a441a8fac50fc913107f2c5864f06fe37b15e54eb1c828cf8f118
7
+ data.tar.gz: 72c42e4bb89b7b0bfff4f5855ca3b42eff07c5dc1b8317940739fa0304e79eafca131c9909a80588d6ce1362313175c8a21bacb46b2ed2066114a49f8f3fdb31
@@ -15,6 +15,58 @@
15
15
  #define DEBUGF(format, ...)
16
16
  #endif
17
17
 
18
+ /* This is the ID of the WeakMap used to track strings allocated that
19
+ * are backed by a memory-mapped file.
20
+ */
21
+ #define WEAK_OBJ_TRACKER "@weak_obj_tracker"
22
+
23
+ /**
24
+ * Maps a given VALUE to some key for the WeakMap. For now, we just use
25
+ * the integer value as the key since that suffices, though this does
26
+ * require Ruby 2.7 due to https://bugs.ruby-lang.org/issues/16035.
27
+ */
28
+ static VALUE weak_obj_tracker_get_key(VALUE val) { return val; }
29
+
30
+ /**
31
+ * Adds a T_STRING type to the WeakMap. The WeakMap should be stored
32
+ * as an instance variable.
33
+ */
34
+ static void weak_obj_tracker_add(VALUE obj, VALUE val) {
35
+ Check_Type(val, T_STRING);
36
+
37
+ VALUE tracker = rb_iv_get(obj, WEAK_OBJ_TRACKER);
38
+ VALUE key = weak_obj_tracker_get_key(val);
39
+
40
+ rb_funcall(tracker, rb_intern("[]="), 2, key, val);
41
+ }
42
+
43
+ /**
44
+ * Iterator function for updating a single element from the WeakMap.
45
+ */
46
+ VALUE mm_update_obj_i(RB_BLOCK_CALL_FUNC_ARGLIST(i, self)) {
47
+ Check_Type(self, T_DATA);
48
+ Check_Type(i, T_STRING);
49
+ rb_check_arity(argc, 1, 1);
50
+
51
+ mm_ipc *i_mm;
52
+ GET_MMAP(self, i_mm, MM_MODIFY);
53
+
54
+ RSTRING(i)->as.heap.ptr = i_mm->t->addr;
55
+ RSTRING(i)->as.heap.len = i_mm->t->real;
56
+
57
+ return Qtrue;
58
+ }
59
+
60
+ /**
61
+ * This iterates through the WeakMap defined on the class and updates
62
+ * the RStrings to use the newly-allocated memory region.
63
+ */
64
+ void mm_update(VALUE obj) {
65
+ VALUE tracker = rb_iv_get(obj, WEAK_OBJ_TRACKER);
66
+
67
+ rb_block_call(tracker, rb_intern("each_value"), 0, NULL, mm_update_obj_i, obj);
68
+ }
69
+
18
70
  typedef struct {
19
71
  VALUE obj, *argv;
20
72
  ID id;
@@ -48,6 +100,8 @@ static VALUE mm_str(VALUE obj, int modify) {
48
100
  RSTRING(ret)->as.heap.aux.capa = i_mm->t->len;
49
101
  RSTRING(ret)->as.heap.len = i_mm->t->real;
50
102
 
103
+ weak_obj_tracker_add(obj, ret);
104
+
51
105
  DEBUGF("RString capa: %d, len: %d", RSTRING(ret)->as.heap.aux.capa, RSTRING(ret)->as.heap.len);
52
106
 
53
107
  if (modify & MM_ORIGIN) {
@@ -208,6 +262,10 @@ VALUE mm_init(VALUE obj, VALUE fname) {
208
262
  path = 0;
209
263
  fd = -1;
210
264
 
265
+ VALUE klass = rb_eval_string("ObjectSpace::WeakMap");
266
+ VALUE weak_obj_tracker = rb_class_new_instance(0, NULL, klass);
267
+ rb_iv_set(obj, WEAK_OBJ_TRACKER, weak_obj_tracker);
268
+
211
269
  fname = rb_str_to_str(fname);
212
270
  SafeStringValue(fname);
213
271
  path = StringValuePtr(fname);
@@ -52,4 +52,10 @@ VALUE mm_aref_m(int argc, VALUE *argv, VALUE obj);
52
52
  VALUE mm_msync(int argc, VALUE *argv, VALUE obj);
53
53
  VALUE mm_unmap(VALUE obj);
54
54
 
55
+ /* If memory is ever reallocated, any allocated Ruby strings that have not been
56
+ * garbage collected need to be updated with the new region. If this isn't done,
57
+ * iterating over the Ruby object space and accessing the string data will seg fault.
58
+ */
59
+ void mm_update(VALUE obj);
60
+
55
61
  #endif
@@ -66,7 +66,7 @@ static int perform_mmap(mm_ipc *i_mm, size_t len) {
66
66
  return SUCCESS;
67
67
  }
68
68
 
69
- static int expand(mm_ipc *i_mm, size_t len) {
69
+ static int expand(VALUE self, mm_ipc *i_mm, size_t len) {
70
70
  if (len < i_mm->t->len) {
71
71
  return with_exception(rb_eArgError, "Can't reduce the size of mmap");
72
72
  }
@@ -88,6 +88,8 @@ static int expand(mm_ipc *i_mm, size_t len) {
88
88
  return with_exception_errno(rb_eArgError, "mlock(%d)", errno);
89
89
  }
90
90
 
91
+ mm_update(self);
92
+
91
93
  return SUCCESS;
92
94
  }
93
95
 
@@ -151,7 +153,7 @@ uint32_t load_used(mm_ipc *i_mm) {
151
153
 
152
154
  void save_used(mm_ipc *i_mm, uint32_t used) { *((uint32_t *)i_mm->t->addr) = used; }
153
155
 
154
- static VALUE initialize_entry(mm_ipc *i_mm, VALUE positions, VALUE key, VALUE value) {
156
+ static VALUE initialize_entry(VALUE self, mm_ipc *i_mm, VALUE positions, VALUE key, VALUE value) {
155
157
  if (i_mm->t->flag & MM_FROZEN) {
156
158
  rb_error_frozen("mmap");
157
159
  }
@@ -166,10 +168,11 @@ static VALUE initialize_entry(mm_ipc *i_mm, VALUE positions, VALUE key, VALUE va
166
168
 
167
169
  uint32_t used = load_used(i_mm);
168
170
  while (i_mm->t->len < (used + entry_length)) {
169
- if (!expand(i_mm, i_mm->t->len * 2)) {
171
+ if (!expand(self, i_mm, i_mm->t->len * 2)) {
170
172
  raise_last_exception();
171
173
  }
172
174
  }
175
+
173
176
  save_entry(i_mm, used, key, value);
174
177
  save_used(i_mm, used + entry_length);
175
178
 
@@ -189,7 +192,7 @@ VALUE method_fetch_entry(VALUE self, VALUE positions, VALUE key, VALUE default_v
189
192
  return load_value(i_mm, position);
190
193
  }
191
194
 
192
- position = initialize_entry(i_mm, positions, key, default_value);
195
+ position = initialize_entry(self, i_mm, positions, key, default_value);
193
196
  return load_value(i_mm, position);
194
197
  }
195
198
 
@@ -207,7 +210,7 @@ VALUE method_upsert_entry(VALUE self, VALUE positions, VALUE key, VALUE value) {
207
210
  return load_value(i_mm, position);
208
211
  }
209
212
 
210
- position = initialize_entry(i_mm, positions, key, value);
213
+ position = initialize_entry(self, i_mm, positions, key, value);
211
214
  return load_value(i_mm, position);
212
215
  }
213
216
 
@@ -229,7 +232,7 @@ VALUE method_save_used(VALUE self, VALUE value) {
229
232
  }
230
233
 
231
234
  if (i_mm->t->len < INITIAL_SIZE) {
232
- if (!expand(i_mm, INITIAL_SIZE)) {
235
+ if (!expand(self, i_mm, INITIAL_SIZE)) {
233
236
  raise_last_exception();
234
237
  }
235
238
  }
@@ -1,5 +1,5 @@
1
1
  module Prometheus
2
2
  module Client
3
- VERSION = '0.16.2'.freeze
3
+ VERSION = '0.17.0'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prometheus-client-mmap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.16.2
4
+ version: 0.17.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Schmidt
8
8
  - Paweł Chojnacki
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-07-08 00:00:00.000000000 Z
12
+ date: 2022-12-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fuzzbert
@@ -87,7 +87,7 @@ dependencies:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
89
  version: 0.16.2
90
- description:
90
+ description:
91
91
  email:
92
92
  - ts@soundcloud.com
93
93
  - pawel@gitlab.com
@@ -106,8 +106,6 @@ files:
106
106
  - ext/fast_mmaped_file/file_reading.c
107
107
  - ext/fast_mmaped_file/file_reading.h
108
108
  - ext/fast_mmaped_file/globals.h
109
- - ext/fast_mmaped_file/hashmap.c
110
- - ext/fast_mmaped_file/jsmn.c
111
109
  - ext/fast_mmaped_file/mmap.c
112
110
  - ext/fast_mmaped_file/mmap.h
113
111
  - ext/fast_mmaped_file/rendering.c
@@ -116,7 +114,6 @@ files:
116
114
  - ext/fast_mmaped_file/utils.h
117
115
  - ext/fast_mmaped_file/value_access.c
118
116
  - ext/fast_mmaped_file/value_access.h
119
- - lib/fast_mmaped_file.bundle
120
117
  - lib/prometheus.rb
121
118
  - lib/prometheus/client.rb
122
119
  - lib/prometheus/client/configuration.rb
@@ -166,7 +163,7 @@ homepage: https://gitlab.com/gitlab-org/prometheus-client-mmap
166
163
  licenses:
167
164
  - Apache-2.0
168
165
  metadata: {}
169
- post_install_message:
166
+ post_install_message:
170
167
  rdoc_options: []
171
168
  require_paths:
172
169
  - lib
@@ -174,15 +171,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
174
171
  requirements:
175
172
  - - ">="
176
173
  - !ruby/object:Gem::Version
177
- version: '0'
174
+ version: 2.7.0
178
175
  required_rubygems_version: !ruby/object:Gem::Requirement
179
176
  requirements:
180
177
  - - ">="
181
178
  - !ruby/object:Gem::Version
182
179
  version: '0'
183
180
  requirements: []
184
- rubygems_version: 3.3.16
185
- signing_key:
181
+ rubygems_version: 3.2.33
182
+ signing_key:
186
183
  specification_version: 4
187
184
  summary: A suite of instrumentation metric primitivesthat can be exposed through a
188
185
  web services interface.
@@ -1,635 +0,0 @@
1
- /*
2
- * Copyright (c) 2016-2017 David Leeds <davidesleeds@gmail.com>
3
- *
4
- * Hashmap is free software; you can redistribute it and/or modify
5
- * it under the terms of the MIT license. See LICENSE for details.
6
- */
7
-
8
- #include "hashmap.h"
9
-
10
- #include <errno.h>
11
- #include <stdbool.h>
12
- #include <stdint.h>
13
- #include <stdlib.h>
14
- #include <string.h>
15
-
16
- #ifndef HASHMAP_NOASSERT
17
- #include <assert.h>
18
- #define HASHMAP_ASSERT(expr) assert(expr)
19
- #else
20
- #define HASHMAP_ASSERT(expr)
21
- #endif
22
-
23
- /* Table sizes must be powers of 2 */
24
- #define HASHMAP_SIZE_MIN (1 << 5) /* 32 */
25
- #define HASHMAP_SIZE_DEFAULT (1 << 8) /* 256 */
26
- #define HASHMAP_SIZE_MOD(map, val) ((val) & ((map)->table_size - 1))
27
-
28
- /* Limit for probing is 1/2 of table_size */
29
- #define HASHMAP_PROBE_LEN(map) ((map)->table_size >> 1)
30
- /* Return the next linear probe index */
31
- #define HASHMAP_PROBE_NEXT(map, index) HASHMAP_SIZE_MOD(map, (index) + 1)
32
-
33
- /* Check if index b is less than or equal to index a */
34
- #define HASHMAP_INDEX_LE(map, a, b) ((a) == (b) || (((b) - (a)) & ((map)->table_size >> 1)) != 0)
35
-
36
- struct hashmap_entry {
37
- void *key;
38
- void *data;
39
- #ifdef HASHMAP_METRICS
40
- size_t num_collisions;
41
- #endif
42
- };
43
-
44
- /*
45
- * Enforce a maximum 0.75 load factor.
46
- */
47
- static inline size_t hashmap_table_min_size_calc(size_t num_entries) { return num_entries + (num_entries / 3); }
48
-
49
- /*
50
- * Calculate the optimal table size, given the specified max number
51
- * of elements.
52
- */
53
- static size_t hashmap_table_size_calc(size_t num_entries) {
54
- size_t table_size;
55
- size_t min_size;
56
-
57
- table_size = hashmap_table_min_size_calc(num_entries);
58
-
59
- /* Table size is always a power of 2 */
60
- min_size = HASHMAP_SIZE_MIN;
61
- while (min_size < table_size) {
62
- min_size <<= 1;
63
- }
64
- return min_size;
65
- }
66
-
67
- /*
68
- * Get a valid hash table index from a key.
69
- */
70
- static inline size_t hashmap_calc_index(const struct hashmap *map, const void *key) {
71
- return HASHMAP_SIZE_MOD(map, map->hash(key));
72
- }
73
-
74
- /*
75
- * Return the next populated entry, starting with the specified one.
76
- * Returns NULL if there are no more valid entries.
77
- */
78
- static struct hashmap_entry *hashmap_entry_get_populated(const struct hashmap *map, struct hashmap_entry *entry) {
79
- for (; entry < &map->table[map->table_size]; ++entry) {
80
- if (entry->key) {
81
- return entry;
82
- }
83
- }
84
- return NULL;
85
- }
86
-
87
- /*
88
- * Find the hashmap entry with the specified key, or an empty slot.
89
- * Returns NULL if the entire table has been searched without finding a match.
90
- */
91
- static struct hashmap_entry *hashmap_entry_find(const struct hashmap *map, const void *key, bool find_empty) {
92
- size_t i;
93
- size_t index;
94
- size_t probe_len = HASHMAP_PROBE_LEN(map);
95
- struct hashmap_entry *entry;
96
-
97
- index = hashmap_calc_index(map, key);
98
-
99
- /* Linear probing */
100
- for (i = 0; i < probe_len; ++i) {
101
- entry = &map->table[index];
102
- if (!entry->key) {
103
- if (find_empty) {
104
- #ifdef HASHMAP_METRICS
105
- entry->num_collisions = i;
106
- #endif
107
- return entry;
108
- }
109
- return NULL;
110
- }
111
- if (map->key_compare(key, entry->key) == 0) {
112
- return entry;
113
- }
114
- index = HASHMAP_PROBE_NEXT(map, index);
115
- }
116
- return NULL;
117
- }
118
-
119
- /*
120
- * Removes the specified entry and processes the proceeding entries to reduce
121
- * the load factor and keep the chain continuous. This is a required
122
- * step for hash maps using linear probing.
123
- */
124
- static void hashmap_entry_remove(struct hashmap *map, struct hashmap_entry *removed_entry) {
125
- size_t i;
126
- #ifdef HASHMAP_METRICS
127
- size_t removed_i = 0;
128
- #endif
129
- size_t index;
130
- size_t entry_index;
131
- size_t removed_index = (removed_entry - map->table);
132
- struct hashmap_entry *entry;
133
-
134
- /* Free the key */
135
- if (map->key_free) {
136
- map->key_free(removed_entry->key);
137
- }
138
- --map->num_entries;
139
-
140
- /* Fill the free slot in the chain */
141
- index = HASHMAP_PROBE_NEXT(map, removed_index);
142
- for (i = 1; i < map->table_size; ++i) {
143
- entry = &map->table[index];
144
- if (!entry->key) {
145
- /* Reached end of chain */
146
- break;
147
- }
148
- entry_index = hashmap_calc_index(map, entry->key);
149
- /* Shift in entries with an index <= to the removed slot */
150
- if (HASHMAP_INDEX_LE(map, removed_index, entry_index)) {
151
- #ifdef HASHMAP_METRICS
152
- entry->num_collisions -= (i - removed_i);
153
- removed_i = i;
154
- #endif
155
- memcpy(removed_entry, entry, sizeof(*removed_entry));
156
- removed_index = index;
157
- removed_entry = entry;
158
- }
159
- index = HASHMAP_PROBE_NEXT(map, index);
160
- }
161
- /* Clear the last removed entry */
162
- memset(removed_entry, 0, sizeof(*removed_entry));
163
- }
164
-
165
- /*
166
- * Reallocates the hash table to the new size and rehashes all entries.
167
- * new_size MUST be a power of 2.
168
- * Returns 0 on success and -errno on allocation or hash function failure.
169
- */
170
- static int hashmap_rehash(struct hashmap *map, size_t new_size) {
171
- size_t old_size;
172
- struct hashmap_entry *old_table;
173
- struct hashmap_entry *new_table;
174
- struct hashmap_entry *entry;
175
- struct hashmap_entry *new_entry;
176
-
177
- HASHMAP_ASSERT(new_size >= HASHMAP_SIZE_MIN);
178
- HASHMAP_ASSERT((new_size & (new_size - 1)) == 0);
179
-
180
- new_table = (struct hashmap_entry *)calloc(new_size, sizeof(struct hashmap_entry));
181
- if (!new_table) {
182
- return -ENOMEM;
183
- }
184
- /* Backup old elements in case of rehash failure */
185
- old_size = map->table_size;
186
- old_table = map->table;
187
- map->table_size = new_size;
188
- map->table = new_table;
189
- /* Rehash */
190
- for (entry = old_table; entry < &old_table[old_size]; ++entry) {
191
- if (!entry->data) {
192
- /* Only copy entries with data */
193
- continue;
194
- }
195
- new_entry = hashmap_entry_find(map, entry->key, true);
196
- if (!new_entry) {
197
- /*
198
- * The load factor is too high with the new table
199
- * size, or a poor hash function was used.
200
- */
201
- goto revert;
202
- }
203
- /* Shallow copy (intentionally omits num_collisions) */
204
- new_entry->key = entry->key;
205
- new_entry->data = entry->data;
206
- }
207
- free(old_table);
208
- return 0;
209
- revert:
210
- map->table_size = old_size;
211
- map->table = old_table;
212
- free(new_table);
213
- return -EINVAL;
214
- }
215
-
216
- /*
217
- * Iterate through all entries and free all keys.
218
- */
219
- static void hashmap_free_keys(struct hashmap *map) {
220
- struct hashmap_iter *iter;
221
-
222
- if (!map->key_free) {
223
- return;
224
- }
225
- for (iter = hashmap_iter(map); iter; iter = hashmap_iter_next(map, iter)) {
226
- map->key_free((void *)hashmap_iter_get_key(iter));
227
- }
228
- }
229
-
230
- /*
231
- * Initialize an empty hashmap. A hash function and a key comparator are
232
- * required.
233
- *
234
- * hash_func should return an even distribution of numbers between 0
235
- * and SIZE_MAX varying on the key provided.
236
- *
237
- * key_compare_func should return 0 if the keys match, and non-zero otherwise.
238
- *
239
- * initial_size is optional, and may be set to the max number of entries
240
- * expected to be put in the hash table. This is used as a hint to
241
- * pre-allocate the hash table to the minimum size needed to avoid
242
- * gratuitous rehashes. If initial_size 0, a default size will be used.
243
- *
244
- * Returns 0 on success and -errno on failure.
245
- */
246
- int hashmap_init(struct hashmap *map, size_t (*hash_func)(const void *),
247
- int (*key_compare_func)(const void *, const void *), size_t initial_size) {
248
- HASHMAP_ASSERT(map != NULL);
249
- HASHMAP_ASSERT(hash_func != NULL);
250
- HASHMAP_ASSERT(key_compare_func != NULL);
251
-
252
- if (!initial_size) {
253
- initial_size = HASHMAP_SIZE_DEFAULT;
254
- } else {
255
- /* Convert init size to valid table size */
256
- initial_size = hashmap_table_size_calc(initial_size);
257
- }
258
- map->table_size_init = initial_size;
259
- map->table_size = initial_size;
260
- map->num_entries = 0;
261
- map->table = (struct hashmap_entry *)calloc(initial_size, sizeof(struct hashmap_entry));
262
- if (!map->table) {
263
- return -ENOMEM;
264
- }
265
- map->hash = hash_func;
266
- map->key_compare = key_compare_func;
267
- map->key_alloc = NULL;
268
- map->key_free = NULL;
269
- return 0;
270
- }
271
-
272
- /*
273
- * Free the hashmap and all associated memory.
274
- */
275
- void hashmap_destroy(struct hashmap *map) {
276
- if (!map) {
277
- return;
278
- }
279
- hashmap_free_keys(map);
280
- free(map->table);
281
- memset(map, 0, sizeof(*map));
282
- }
283
-
284
- /*
285
- * Enable internal memory management of hash keys.
286
- */
287
- void hashmap_set_key_alloc_funcs(struct hashmap *map, void *(*key_alloc_func)(const void *),
288
- void (*key_free_func)(void *)) {
289
- HASHMAP_ASSERT(map != NULL);
290
-
291
- map->key_alloc = key_alloc_func;
292
- map->key_free = key_free_func;
293
- }
294
-
295
- /*
296
- * Add an entry to the hashmap. If an entry with a matching key already
297
- * exists and has a data pointer associated with it, the existing data
298
- * pointer is returned, instead of assigning the new value. Compare
299
- * the return value with the data passed in to determine if a new entry was
300
- * created. Returns NULL if memory allocation failed.
301
- */
302
- void *hashmap_put(struct hashmap *map, const void *key, void *data) {
303
- struct hashmap_entry *entry;
304
-
305
- HASHMAP_ASSERT(map != NULL);
306
- HASHMAP_ASSERT(key != NULL);
307
-
308
- /* Rehash with 2x capacity if load factor is approaching 0.75 */
309
- if (map->table_size <= hashmap_table_min_size_calc(map->num_entries)) {
310
- hashmap_rehash(map, map->table_size << 1);
311
- }
312
- entry = hashmap_entry_find(map, key, true);
313
- if (!entry) {
314
- /*
315
- * Cannot find an empty slot. Either out of memory, or using
316
- * a poor hash function. Attempt to rehash once to reduce
317
- * chain length.
318
- */
319
- if (hashmap_rehash(map, map->table_size << 1) < 0) {
320
- return NULL;
321
- }
322
- entry = hashmap_entry_find(map, key, true);
323
- if (!entry) {
324
- return NULL;
325
- }
326
- }
327
- if (!entry->key) {
328
- /* Allocate copy of key to simplify memory management */
329
- if (map->key_alloc) {
330
- entry->key = map->key_alloc(key);
331
- if (!entry->key) {
332
- return NULL;
333
- }
334
- } else {
335
- entry->key = (void *)key;
336
- }
337
- ++map->num_entries;
338
- } else if (entry->data) {
339
- /* Do not overwrite existing data */
340
- return entry->data;
341
- }
342
- entry->data = data;
343
- return data;
344
- }
345
-
346
- /*
347
- * Return the data pointer, or NULL if no entry exists.
348
- */
349
- void *hashmap_get(const struct hashmap *map, const void *key) {
350
- struct hashmap_entry *entry;
351
-
352
- HASHMAP_ASSERT(map != NULL);
353
- HASHMAP_ASSERT(key != NULL);
354
-
355
- entry = hashmap_entry_find(map, key, false);
356
- if (!entry) {
357
- return NULL;
358
- }
359
- return entry->data;
360
- }
361
-
362
- /*
363
- * Remove an entry with the specified key from the map.
364
- * Returns the data pointer, or NULL, if no entry was found.
365
- */
366
- void *hashmap_remove(struct hashmap *map, const void *key) {
367
- struct hashmap_entry *entry;
368
- void *data;
369
-
370
- HASHMAP_ASSERT(map != NULL);
371
- HASHMAP_ASSERT(key != NULL);
372
-
373
- entry = hashmap_entry_find(map, key, false);
374
- if (!entry) {
375
- return NULL;
376
- }
377
- data = entry->data;
378
- /* Clear the entry and make the chain contiguous */
379
- hashmap_entry_remove(map, entry);
380
- return data;
381
- }
382
-
383
- /*
384
- * Remove all entries.
385
- */
386
- void hashmap_clear(struct hashmap *map) {
387
- HASHMAP_ASSERT(map != NULL);
388
-
389
- hashmap_free_keys(map);
390
- map->num_entries = 0;
391
- memset(map->table, 0, sizeof(struct hashmap_entry) * map->table_size);
392
- }
393
-
394
- /*
395
- * Remove all entries and reset the hash table to its initial size.
396
- */
397
- void hashmap_reset(struct hashmap *map) {
398
- struct hashmap_entry *new_table;
399
-
400
- HASHMAP_ASSERT(map != NULL);
401
-
402
- hashmap_clear(map);
403
- if (map->table_size == map->table_size_init) {
404
- return;
405
- }
406
- new_table = (struct hashmap_entry *)realloc(map->table, sizeof(struct hashmap_entry) * map->table_size_init);
407
- if (!new_table) {
408
- return;
409
- }
410
- map->table = new_table;
411
- map->table_size = map->table_size_init;
412
- }
413
-
414
- /*
415
- * Return the number of entries in the hash map.
416
- */
417
- size_t hashmap_size(const struct hashmap *map) {
418
- HASHMAP_ASSERT(map != NULL);
419
-
420
- return map->num_entries;
421
- }
422
-
423
- /*
424
- * Get a new hashmap iterator. The iterator is an opaque
425
- * pointer that may be used with hashmap_iter_*() functions.
426
- * Hashmap iterators are INVALID after a put or remove operation is performed.
427
- * hashmap_iter_remove() allows safe removal during iteration.
428
- */
429
- struct hashmap_iter *hashmap_iter(const struct hashmap *map) {
430
- HASHMAP_ASSERT(map != NULL);
431
-
432
- if (!map->num_entries) {
433
- return NULL;
434
- }
435
- return (struct hashmap_iter *)hashmap_entry_get_populated(map, map->table);
436
- }
437
-
438
- /*
439
- * Return an iterator to the next hashmap entry. Returns NULL if there are
440
- * no more entries.
441
- */
442
- struct hashmap_iter *hashmap_iter_next(const struct hashmap *map, const struct hashmap_iter *iter) {
443
- struct hashmap_entry *entry = (struct hashmap_entry *)iter;
444
-
445
- HASHMAP_ASSERT(map != NULL);
446
-
447
- if (!iter) {
448
- return NULL;
449
- }
450
- return (struct hashmap_iter *)hashmap_entry_get_populated(map, entry + 1);
451
- }
452
-
453
- /*
454
- * Remove the hashmap entry pointed to by this iterator and return an
455
- * iterator to the next entry. Returns NULL if there are no more entries.
456
- */
457
- struct hashmap_iter *hashmap_iter_remove(struct hashmap *map, const struct hashmap_iter *iter) {
458
- struct hashmap_entry *entry = (struct hashmap_entry *)iter;
459
-
460
- HASHMAP_ASSERT(map != NULL);
461
-
462
- if (!iter) {
463
- return NULL;
464
- }
465
- if (!entry->key) {
466
- /* Iterator is invalid, so just return the next valid entry */
467
- return hashmap_iter_next(map, iter);
468
- }
469
- hashmap_entry_remove(map, entry);
470
- return (struct hashmap_iter *)hashmap_entry_get_populated(map, entry);
471
- }
472
-
473
- /*
474
- * Return the key of the entry pointed to by the iterator.
475
- */
476
- const void *hashmap_iter_get_key(const struct hashmap_iter *iter) {
477
- if (!iter) {
478
- return NULL;
479
- }
480
- return (const void *)((struct hashmap_entry *)iter)->key;
481
- }
482
-
483
- /*
484
- * Return the data of the entry pointed to by the iterator.
485
- */
486
- void *hashmap_iter_get_data(const struct hashmap_iter *iter) {
487
- if (!iter) {
488
- return NULL;
489
- }
490
- return ((struct hashmap_entry *)iter)->data;
491
- }
492
-
493
- /*
494
- * Set the data pointer of the entry pointed to by the iterator.
495
- */
496
- void hashmap_iter_set_data(const struct hashmap_iter *iter, void *data) {
497
- if (!iter) {
498
- return;
499
- }
500
- ((struct hashmap_entry *)iter)->data = data;
501
- }
502
-
503
- /*
504
- * Invoke func for each entry in the hashmap. Unlike the hashmap_iter_*()
505
- * interface, this function supports calls to hashmap_remove() during iteration.
506
- * However, it is an error to put or remove an entry other than the current one,
507
- * and doing so will immediately halt iteration and return an error.
508
- * Iteration is stopped if func returns non-zero. Returns func's return
509
- * value if it is < 0, otherwise, 0.
510
- */
511
- int hashmap_foreach(const struct hashmap *map, int (*func)(const void *, void *, void *), void *arg) {
512
- struct hashmap_entry *entry;
513
- size_t num_entries;
514
- const void *key;
515
- int rc;
516
-
517
- HASHMAP_ASSERT(map != NULL);
518
- HASHMAP_ASSERT(func != NULL);
519
-
520
- entry = map->table;
521
- for (entry = map->table; entry < &map->table[map->table_size]; ++entry) {
522
- if (!entry->key) {
523
- continue;
524
- }
525
- num_entries = map->num_entries;
526
- key = entry->key;
527
- rc = func(entry->key, entry->data, arg);
528
- if (rc < 0) {
529
- return rc;
530
- }
531
- if (rc > 0) {
532
- return 0;
533
- }
534
- /* Run this entry again if func() deleted it */
535
- if (entry->key != key) {
536
- --entry;
537
- } else if (num_entries != map->num_entries) {
538
- /* Stop immediately if func put/removed another entry */
539
- return -1;
540
- }
541
- }
542
- return 0;
543
- }
544
-
545
- /*
546
- * Default hash function for string keys.
547
- * This is an implementation of the well-documented Jenkins one-at-a-time
548
- * hash function.
549
- */
550
- size_t hashmap_hash_string(const void *key) {
551
- const char *key_str = (const char *)key;
552
- size_t hash = 0;
553
-
554
- for (; *key_str; ++key_str) {
555
- hash += *key_str;
556
- hash += (hash << 10);
557
- hash ^= (hash >> 6);
558
- }
559
- hash += (hash << 3);
560
- hash ^= (hash >> 11);
561
- hash += (hash << 15);
562
- return hash;
563
- }
564
-
565
- /*
566
- * Default key comparator function for string keys.
567
- */
568
- int hashmap_compare_string(const void *a, const void *b) { return strcmp((const char *)a, (const char *)b); }
569
-
570
- /*
571
- * Default key allocation function for string keys. Use free() for the
572
- * key_free_func.
573
- */
574
- void *hashmap_alloc_key_string(const void *key) { return (void *)strdup((const char *)key); }
575
-
576
- #ifdef HASHMAP_METRICS
577
- /*
578
- * Return the load factor.
579
- */
580
- double hashmap_load_factor(const struct hashmap *map) {
581
- HASHMAP_ASSERT(map != NULL);
582
-
583
- if (!map->table_size) {
584
- return 0;
585
- }
586
- return (double)map->num_entries / map->table_size;
587
- }
588
-
589
- /*
590
- * Return the average number of collisions per entry.
591
- */
592
- double hashmap_collisions_mean(const struct hashmap *map) {
593
- struct hashmap_entry *entry;
594
- size_t total_collisions = 0;
595
-
596
- HASHMAP_ASSERT(map != NULL);
597
-
598
- if (!map->num_entries) {
599
- return 0;
600
- }
601
- for (entry = map->table; entry < &map->table[map->table_size]; ++entry) {
602
- if (!entry->key) {
603
- continue;
604
- }
605
- total_collisions += entry->num_collisions;
606
- }
607
- return (double)total_collisions / map->num_entries;
608
- }
609
-
610
- /*
611
- * Return the variance between entry collisions. The higher the variance,
612
- * the more likely the hash function is poor and is resulting in clustering.
613
- */
614
- double hashmap_collisions_variance(const struct hashmap *map) {
615
- struct hashmap_entry *entry;
616
- double mean_collisions;
617
- double variance;
618
- double total_variance = 0;
619
-
620
- HASHMAP_ASSERT(map != NULL);
621
-
622
- if (!map->num_entries) {
623
- return 0;
624
- }
625
- mean_collisions = hashmap_collisions_mean(map);
626
- for (entry = map->table; entry < &map->table[map->table_size]; ++entry) {
627
- if (!entry->key) {
628
- continue;
629
- }
630
- variance = (double)entry->num_collisions - mean_collisions;
631
- total_variance += variance * variance;
632
- }
633
- return total_variance / map->num_entries;
634
- }
635
- #endif
@@ -1,329 +0,0 @@
1
- #include "jsmn.h"
2
-
3
- /**
4
- * Allocates a fresh unused token from the token pull.
5
- */
6
- static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens, size_t num_tokens) {
7
- jsmntok_t *tok;
8
- if (parser->toknext >= num_tokens) {
9
- return NULL;
10
- }
11
- tok = &tokens[parser->toknext++];
12
- tok->start = tok->end = -1;
13
- tok->size = 0;
14
- #ifdef JSMN_PARENT_LINKS
15
- tok->parent = -1;
16
- #endif
17
- return tok;
18
- }
19
-
20
- /**
21
- * Fills token type and boundaries.
22
- */
23
- static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, int start, int end) {
24
- token->type = type;
25
- token->start = start;
26
- token->end = end;
27
- token->size = 0;
28
- }
29
-
30
- /**
31
- * Fills next available token with JSON primitive.
32
- */
33
- static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens) {
34
- jsmntok_t *token;
35
- int start;
36
-
37
- start = parser->pos;
38
-
39
- for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
40
- switch (js[parser->pos]) {
41
- #ifndef JSMN_STRICT
42
- /* In strict mode primitive must be followed by "," or "}" or "]" */
43
- case ':':
44
- #endif
45
- case '\t':
46
- case '\r':
47
- case '\n':
48
- case ' ':
49
- case ',':
50
- case ']':
51
- case '}':
52
- goto found;
53
- }
54
- if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
55
- parser->pos = start;
56
- return JSMN_ERROR_INVAL;
57
- }
58
- }
59
- #ifdef JSMN_STRICT
60
- /* In strict mode primitive must be followed by a comma/object/array */
61
- parser->pos = start;
62
- return JSMN_ERROR_PART;
63
- #endif
64
-
65
- found:
66
- if (tokens == NULL) {
67
- parser->pos--;
68
- return 0;
69
- }
70
- token = jsmn_alloc_token(parser, tokens, num_tokens);
71
- if (token == NULL) {
72
- parser->pos = start;
73
- return JSMN_ERROR_NOMEM;
74
- }
75
- jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
76
- #ifdef JSMN_PARENT_LINKS
77
- token->parent = parser->toksuper;
78
- #endif
79
- parser->pos--;
80
- return 0;
81
- }
82
-
83
- /**
84
- * Fills next token with JSON string.
85
- */
86
- static int jsmn_parse_string(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens) {
87
- jsmntok_t *token;
88
-
89
- int start = parser->pos;
90
-
91
- parser->pos++;
92
-
93
- /* Skip starting quote */
94
- for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
95
- char c = js[parser->pos];
96
-
97
- /* Quote: end of string */
98
- if (c == '\"') {
99
- if (tokens == NULL) {
100
- return 0;
101
- }
102
- token = jsmn_alloc_token(parser, tokens, num_tokens);
103
- if (token == NULL) {
104
- parser->pos = start;
105
- return JSMN_ERROR_NOMEM;
106
- }
107
- jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
108
- #ifdef JSMN_PARENT_LINKS
109
- token->parent = parser->toksuper;
110
- #endif
111
- return 0;
112
- }
113
-
114
- /* Backslash: Quoted symbol expected */
115
- if (c == '\\' && parser->pos + 1 < len) {
116
- int i;
117
- parser->pos++;
118
- switch (js[parser->pos]) {
119
- /* Allowed escaped symbols */
120
- case '\"':
121
- case '/':
122
- case '\\':
123
- case 'b':
124
- case 'f':
125
- case 'r':
126
- case 'n':
127
- case 't':
128
- break;
129
- /* Allows escaped symbol \uXXXX */
130
- case 'u':
131
- parser->pos++;
132
- for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
133
- /* If it isn't a hex character we have an error */
134
- if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
135
- (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
136
- (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
137
- parser->pos = start;
138
- return JSMN_ERROR_INVAL;
139
- }
140
- parser->pos++;
141
- }
142
- parser->pos--;
143
- break;
144
- /* Unexpected symbol */
145
- default:
146
- parser->pos = start;
147
- return JSMN_ERROR_INVAL;
148
- }
149
- }
150
- }
151
- parser->pos = start;
152
- return JSMN_ERROR_PART;
153
- }
154
-
155
- /**
156
- * Parse JSON string and fill tokens.
157
- */
158
- int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens) {
159
- int r;
160
- int i;
161
- jsmntok_t *token;
162
- int count = parser->toknext;
163
-
164
- for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
165
- char c;
166
- jsmntype_t type;
167
-
168
- c = js[parser->pos];
169
- switch (c) {
170
- case '{':
171
- case '[':
172
- count++;
173
- if (tokens == NULL) {
174
- break;
175
- }
176
- token = jsmn_alloc_token(parser, tokens, num_tokens);
177
- if (token == NULL) return JSMN_ERROR_NOMEM;
178
- if (parser->toksuper != -1) {
179
- tokens[parser->toksuper].size++;
180
- #ifdef JSMN_PARENT_LINKS
181
- token->parent = parser->toksuper;
182
- #endif
183
- }
184
- token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
185
- token->start = parser->pos;
186
- parser->toksuper = parser->toknext - 1;
187
- break;
188
- case '}':
189
- case ']':
190
- if (tokens == NULL) break;
191
- type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
192
- #ifdef JSMN_PARENT_LINKS
193
- if (parser->toknext < 1) {
194
- return JSMN_ERROR_INVAL;
195
- }
196
- token = &tokens[parser->toknext - 1];
197
- for (;;) {
198
- if (token->start != -1 && token->end == -1) {
199
- if (token->type != type) {
200
- return JSMN_ERROR_INVAL;
201
- }
202
- token->end = parser->pos + 1;
203
- parser->toksuper = token->parent;
204
- break;
205
- }
206
- if (token->parent == -1) {
207
- if (token->type != type || parser->toksuper == -1) {
208
- return JSMN_ERROR_INVAL;
209
- }
210
- break;
211
- }
212
- token = &tokens[token->parent];
213
- }
214
- #else
215
- for (i = parser->toknext - 1; i >= 0; i--) {
216
- token = &tokens[i];
217
- if (token->start != -1 && token->end == -1) {
218
- if (token->type != type) {
219
- return JSMN_ERROR_INVAL;
220
- }
221
- parser->toksuper = -1;
222
- token->end = parser->pos + 1;
223
- break;
224
- }
225
- }
226
- /* Error if unmatched closing bracket */
227
- if (i == -1) return JSMN_ERROR_INVAL;
228
- for (; i >= 0; i--) {
229
- token = &tokens[i];
230
- if (token->start != -1 && token->end == -1) {
231
- parser->toksuper = i;
232
- break;
233
- }
234
- }
235
- #endif
236
- break;
237
- case '\"':
238
- r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
239
- if (r < 0) return r;
240
- count++;
241
- if (parser->toksuper != -1 && tokens != NULL) tokens[parser->toksuper].size++;
242
- break;
243
- case '\t':
244
- case '\r':
245
- case '\n':
246
- case ' ':
247
- break;
248
- case ':':
249
- parser->toksuper = parser->toknext - 1;
250
- break;
251
- case ',':
252
- if (tokens != NULL && parser->toksuper != -1 && tokens[parser->toksuper].type != JSMN_ARRAY &&
253
- tokens[parser->toksuper].type != JSMN_OBJECT) {
254
- #ifdef JSMN_PARENT_LINKS
255
- parser->toksuper = tokens[parser->toksuper].parent;
256
- #else
257
- for (i = parser->toknext - 1; i >= 0; i--) {
258
- if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
259
- if (tokens[i].start != -1 && tokens[i].end == -1) {
260
- parser->toksuper = i;
261
- break;
262
- }
263
- }
264
- }
265
- #endif
266
- }
267
- break;
268
- #ifdef JSMN_STRICT
269
- /* In strict mode primitives are: numbers and booleans */
270
- case '-':
271
- case '0':
272
- case '1':
273
- case '2':
274
- case '3':
275
- case '4':
276
- case '5':
277
- case '6':
278
- case '7':
279
- case '8':
280
- case '9':
281
- case 't':
282
- case 'f':
283
- case 'n':
284
- /* And they must not be keys of the object */
285
- if (tokens != NULL && parser->toksuper != -1) {
286
- jsmntok_t *t = &tokens[parser->toksuper];
287
- if (t->type == JSMN_OBJECT || (t->type == JSMN_STRING && t->size != 0)) {
288
- return JSMN_ERROR_INVAL;
289
- }
290
- }
291
- #else
292
- /* In non-strict mode every unquoted value is a primitive */
293
- default:
294
- #endif
295
- r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
296
- if (r < 0) return r;
297
- count++;
298
- if (parser->toksuper != -1 && tokens != NULL) tokens[parser->toksuper].size++;
299
- break;
300
-
301
- #ifdef JSMN_STRICT
302
- /* Unexpected char in strict mode */
303
- default:
304
- return JSMN_ERROR_INVAL;
305
- #endif
306
- }
307
- }
308
-
309
- if (tokens != NULL) {
310
- for (i = parser->toknext - 1; i >= 0; i--) {
311
- /* Unmatched opened object or array */
312
- if (tokens[i].start != -1 && tokens[i].end == -1) {
313
- return JSMN_ERROR_PART;
314
- }
315
- }
316
- }
317
-
318
- return count;
319
- }
320
-
321
- /**
322
- * Creates a new parser based over a given buffer with an array of tokens
323
- * available.
324
- */
325
- void jsmn_init(jsmn_parser *parser) {
326
- parser->pos = 0;
327
- parser->toknext = 0;
328
- parser->toksuper = -1;
329
- }
Binary file