prometheus-client-mmap 0.9.4 → 0.9.5

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
  SHA1:
3
- metadata.gz: 282d373787d189c268737c396aa12cec78914357
4
- data.tar.gz: 2952e20d2b4297fe16826a42b53e092f48013892
3
+ metadata.gz: ffa6a21d29d372519ce95f9cfd004aac10fb9b4c
4
+ data.tar.gz: 2d8e3faa150d597c1f78d88341cd7eec6b58a6c1
5
5
  SHA512:
6
- metadata.gz: 4fe32ca10b96c5ae37e4fc2d17b810c84749875e498739aefb3ada8908aec672a58448d15cb0ca00b2d190055e50b95f7382df1e09e8eb4ffb4d3c3add1e1e74
7
- data.tar.gz: be318ffec99871cb2bb608e164bebb6cf4b0da5616824c606e9b63bddcdcfc5456bd804775f03a05c485d494d855097407347dcbfd1a54c91ecde5976132253e
6
+ metadata.gz: b971c4fa6d54421a8178c1c0d16fbe019cf878ba4574affd199d74c3aa72723f554f25eafd66f77f432d451bdd68a77118bf22938e6ea857997978f892f8d124
7
+ data.tar.gz: 71d75733d5107297c62f9fde343257dd9d73d079c8a7bb586d90fc5fac1f3f2c8121ac03d765a66bcccad0490c4d3b4214eee244c8345f25fa48fae87c8b0d93
data/README.md CHANGED
@@ -164,6 +164,16 @@ summary.get({ service: 'database' })
164
164
  # => { 0.5 => 0.1233122, 0.9 => 3.4323, 0.99 => 5.3428231 }
165
165
  ```
166
166
 
167
+ ## Configuration
168
+
169
+ ### Memory mapped files storage location
170
+
171
+ Set `prometheus_multiproc_dir` environment variable to the path where you want metric files to be stored. Example:
172
+
173
+ ```
174
+ prometheus_multiproc_dir=/tmp
175
+ ```
176
+
167
177
  ## Pitfalls
168
178
 
169
179
  ### PID cardinality
@@ -1,6 +1,7 @@
1
1
  require 'prometheus/client/registry'
2
2
  require 'prometheus/client/mmaped_value'
3
3
  require 'logger'
4
+ require 'tmpdir'
4
5
 
5
6
  module Prometheus
6
7
  module Client
@@ -9,10 +10,14 @@ module Prometheus
9
10
 
10
11
  def initialize
11
12
  @value_class = ::Prometheus::Client::MmapedValue
12
- @multiprocess_files_dir = ENV['prometheus_multiproc_dir']
13
13
  @initial_mmap_file_size = 4 * 1024
14
14
  @logger = Logger.new($stdout)
15
15
  @pid_provider = Process.method(:pid)
16
+ @multiprocess_files_dir = ENV.fetch('prometheus_multiproc_dir') do
17
+ Dir.mktmpdir("prometheus-mmap").tap do |tmp|
18
+ @logger.warn("Please set prometheus_multiproc_dir to persistent directory path. Temporarily writing mmap files to #{tmp}")
19
+ end
20
+ end
16
21
  end
17
22
  end
18
23
  end
@@ -1,5 +1,5 @@
1
1
  module Prometheus
2
2
  module Client
3
- VERSION = '0.9.4'.freeze
3
+ VERSION = '0.9.5'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prometheus-client-mmap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.4
4
+ version: 0.9.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tobias Schmidt
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2018-07-28 00:00:00.000000000 Z
12
+ date: 2018-11-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fuzzbert
@@ -92,8 +92,6 @@ files:
92
92
  - ext/fast_mmaped_file/file_reading.c
93
93
  - ext/fast_mmaped_file/file_reading.h
94
94
  - ext/fast_mmaped_file/globals.h
95
- - ext/fast_mmaped_file/hashmap.c
96
- - ext/fast_mmaped_file/jsmn.c
97
95
  - ext/fast_mmaped_file/mmap.c
98
96
  - ext/fast_mmaped_file/mmap.h
99
97
  - ext/fast_mmaped_file/rendering.c
@@ -102,7 +100,6 @@ files:
102
100
  - ext/fast_mmaped_file/utils.h
103
101
  - ext/fast_mmaped_file/value_access.c
104
102
  - ext/fast_mmaped_file/value_access.h
105
- - lib/fast_mmaped_file.bundle
106
103
  - lib/prometheus.rb
107
104
  - lib/prometheus/client.rb
108
105
  - lib/prometheus/client/configuration.rb
@@ -1,692 +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 <stdlib.h>
9
- #include <stdint.h>
10
- #include <stdbool.h>
11
- #include <string.h>
12
- #include <errno.h>
13
-
14
- #include "hashmap.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) \
35
- ((a) == (b) || (((b) - (a)) & ((map)->table_size >> 1)) != 0)
36
-
37
-
38
- struct hashmap_entry {
39
- void *key;
40
- void *data;
41
- #ifdef HASHMAP_METRICS
42
- size_t num_collisions;
43
- #endif
44
- };
45
-
46
-
47
- /*
48
- * Enforce a maximum 0.75 load factor.
49
- */
50
- static inline size_t hashmap_table_min_size_calc(size_t num_entries)
51
- {
52
- return num_entries + (num_entries / 3);
53
- }
54
-
55
- /*
56
- * Calculate the optimal table size, given the specified max number
57
- * of elements.
58
- */
59
- static size_t hashmap_table_size_calc(size_t num_entries)
60
- {
61
- size_t table_size;
62
- size_t min_size;
63
-
64
- table_size = hashmap_table_min_size_calc(num_entries);
65
-
66
- /* Table size is always a power of 2 */
67
- min_size = HASHMAP_SIZE_MIN;
68
- while (min_size < table_size) {
69
- min_size <<= 1;
70
- }
71
- return min_size;
72
- }
73
-
74
- /*
75
- * Get a valid hash table index from a key.
76
- */
77
- static inline size_t hashmap_calc_index(const struct hashmap *map,
78
- const void *key)
79
- {
80
- return HASHMAP_SIZE_MOD(map, map->hash(key));
81
- }
82
-
83
- /*
84
- * Return the next populated entry, starting with the specified one.
85
- * Returns NULL if there are no more valid entries.
86
- */
87
- static struct hashmap_entry *hashmap_entry_get_populated(
88
- const struct hashmap *map, struct hashmap_entry *entry)
89
- {
90
- for (; entry < &map->table[map->table_size]; ++entry) {
91
- if (entry->key) {
92
- return entry;
93
- }
94
- }
95
- return NULL;
96
- }
97
-
98
- /*
99
- * Find the hashmap entry with the specified key, or an empty slot.
100
- * Returns NULL if the entire table has been searched without finding a match.
101
- */
102
- static struct hashmap_entry *hashmap_entry_find(const struct hashmap *map,
103
- const void *key, bool find_empty)
104
- {
105
- size_t i;
106
- size_t index;
107
- size_t probe_len = HASHMAP_PROBE_LEN(map);
108
- struct hashmap_entry *entry;
109
-
110
- index = hashmap_calc_index(map, key);
111
-
112
- /* Linear probing */
113
- for (i = 0; i < probe_len; ++i) {
114
- entry = &map->table[index];
115
- if (!entry->key) {
116
- if (find_empty) {
117
- #ifdef HASHMAP_METRICS
118
- entry->num_collisions = i;
119
- #endif
120
- return entry;
121
- }
122
- return NULL;
123
- }
124
- if (map->key_compare(key, entry->key) == 0) {
125
- return entry;
126
- }
127
- index = HASHMAP_PROBE_NEXT(map, index);
128
- }
129
- return NULL;
130
- }
131
-
132
- /*
133
- * Removes the specified entry and processes the proceeding entries to reduce
134
- * the load factor and keep the chain continuous. This is a required
135
- * step for hash maps using linear probing.
136
- */
137
- static void hashmap_entry_remove(struct hashmap *map,
138
- struct hashmap_entry *removed_entry)
139
- {
140
- size_t i;
141
- #ifdef HASHMAP_METRICS
142
- size_t removed_i = 0;
143
- #endif
144
- size_t index;
145
- size_t entry_index;
146
- size_t removed_index = (removed_entry - map->table);
147
- struct hashmap_entry *entry;
148
-
149
- /* Free the key */
150
- if (map->key_free) {
151
- map->key_free(removed_entry->key);
152
- }
153
- --map->num_entries;
154
-
155
- /* Fill the free slot in the chain */
156
- index = HASHMAP_PROBE_NEXT(map, removed_index);
157
- for (i = 1; i < map->table_size; ++i) {
158
- entry = &map->table[index];
159
- if (!entry->key) {
160
- /* Reached end of chain */
161
- break;
162
- }
163
- entry_index = hashmap_calc_index(map, entry->key);
164
- /* Shift in entries with an index <= to the removed slot */
165
- if (HASHMAP_INDEX_LE(map, removed_index, entry_index)) {
166
- #ifdef HASHMAP_METRICS
167
- entry->num_collisions -= (i - removed_i);
168
- removed_i = i;
169
- #endif
170
- memcpy(removed_entry, entry, sizeof(*removed_entry));
171
- removed_index = index;
172
- removed_entry = entry;
173
- }
174
- index = HASHMAP_PROBE_NEXT(map, index);
175
- }
176
- /* Clear the last removed entry */
177
- memset(removed_entry, 0, sizeof(*removed_entry));
178
- }
179
-
180
- /*
181
- * Reallocates the hash table to the new size and rehashes all entries.
182
- * new_size MUST be a power of 2.
183
- * Returns 0 on success and -errno on allocation or hash function failure.
184
- */
185
- static int hashmap_rehash(struct hashmap *map, size_t new_size)
186
- {
187
- size_t old_size;
188
- struct hashmap_entry *old_table;
189
- struct hashmap_entry *new_table;
190
- struct hashmap_entry *entry;
191
- struct hashmap_entry *new_entry;
192
-
193
- HASHMAP_ASSERT(new_size >= HASHMAP_SIZE_MIN);
194
- HASHMAP_ASSERT((new_size & (new_size - 1)) == 0);
195
-
196
- new_table = (struct hashmap_entry *)calloc(new_size,
197
- sizeof(struct hashmap_entry));
198
- if (!new_table) {
199
- return -ENOMEM;
200
- }
201
- /* Backup old elements in case of rehash failure */
202
- old_size = map->table_size;
203
- old_table = map->table;
204
- map->table_size = new_size;
205
- map->table = new_table;
206
- /* Rehash */
207
- for (entry = old_table; entry < &old_table[old_size]; ++entry) {
208
- if (!entry->data) {
209
- /* Only copy entries with data */
210
- continue;
211
- }
212
- new_entry = hashmap_entry_find(map, entry->key, true);
213
- if (!new_entry) {
214
- /*
215
- * The load factor is too high with the new table
216
- * size, or a poor hash function was used.
217
- */
218
- goto revert;
219
- }
220
- /* Shallow copy (intentionally omits num_collisions) */
221
- new_entry->key = entry->key;
222
- new_entry->data = entry->data;
223
- }
224
- free(old_table);
225
- return 0;
226
- revert:
227
- map->table_size = old_size;
228
- map->table = old_table;
229
- free(new_table);
230
- return -EINVAL;
231
- }
232
-
233
- /*
234
- * Iterate through all entries and free all keys.
235
- */
236
- static void hashmap_free_keys(struct hashmap *map)
237
- {
238
- struct hashmap_iter *iter;
239
-
240
- if (!map->key_free) {
241
- return;
242
- }
243
- for (iter = hashmap_iter(map); iter;
244
- iter = hashmap_iter_next(map, iter)) {
245
- map->key_free((void *)hashmap_iter_get_key(iter));
246
- }
247
- }
248
-
249
- /*
250
- * Initialize an empty hashmap. A hash function and a key comparator are
251
- * required.
252
- *
253
- * hash_func should return an even distribution of numbers between 0
254
- * and SIZE_MAX varying on the key provided.
255
- *
256
- * key_compare_func should return 0 if the keys match, and non-zero otherwise.
257
- *
258
- * initial_size is optional, and may be set to the max number of entries
259
- * expected to be put in the hash table. This is used as a hint to
260
- * pre-allocate the hash table to the minimum size needed to avoid
261
- * gratuitous rehashes. If initial_size 0, a default size will be used.
262
- *
263
- * Returns 0 on success and -errno on failure.
264
- */
265
- int hashmap_init(struct hashmap *map, size_t (*hash_func)(const void *),
266
- int (*key_compare_func)(const void *, const void *),
267
- size_t initial_size)
268
- {
269
- HASHMAP_ASSERT(map != NULL);
270
- HASHMAP_ASSERT(hash_func != NULL);
271
- HASHMAP_ASSERT(key_compare_func != NULL);
272
-
273
- if (!initial_size) {
274
- initial_size = HASHMAP_SIZE_DEFAULT;
275
- } else {
276
- /* Convert init size to valid table size */
277
- initial_size = hashmap_table_size_calc(initial_size);
278
- }
279
- map->table_size_init = initial_size;
280
- map->table_size = initial_size;
281
- map->num_entries = 0;
282
- map->table = (struct hashmap_entry *)calloc(initial_size,
283
- sizeof(struct hashmap_entry));
284
- if (!map->table) {
285
- return -ENOMEM;
286
- }
287
- map->hash = hash_func;
288
- map->key_compare = key_compare_func;
289
- map->key_alloc = NULL;
290
- map->key_free = NULL;
291
- return 0;
292
- }
293
-
294
- /*
295
- * Free the hashmap and all associated memory.
296
- */
297
- void hashmap_destroy(struct hashmap *map)
298
- {
299
- if (!map) {
300
- return;
301
- }
302
- hashmap_free_keys(map);
303
- free(map->table);
304
- memset(map, 0, sizeof(*map));
305
- }
306
-
307
- /*
308
- * Enable internal memory management of hash keys.
309
- */
310
- void hashmap_set_key_alloc_funcs(struct hashmap *map,
311
- void *(*key_alloc_func)(const void *),
312
- void (*key_free_func)(void *))
313
- {
314
- HASHMAP_ASSERT(map != NULL);
315
-
316
- map->key_alloc = key_alloc_func;
317
- map->key_free = key_free_func;
318
- }
319
-
320
- /*
321
- * Add an entry to the hashmap. If an entry with a matching key already
322
- * exists and has a data pointer associated with it, the existing data
323
- * pointer is returned, instead of assigning the new value. Compare
324
- * the return value with the data passed in to determine if a new entry was
325
- * created. Returns NULL if memory allocation failed.
326
- */
327
- void *hashmap_put(struct hashmap *map, const void *key, void *data)
328
- {
329
- struct hashmap_entry *entry;
330
-
331
- HASHMAP_ASSERT(map != NULL);
332
- HASHMAP_ASSERT(key != NULL);
333
-
334
- /* Rehash with 2x capacity if load factor is approaching 0.75 */
335
- if (map->table_size <= hashmap_table_min_size_calc(map->num_entries)) {
336
- hashmap_rehash(map, map->table_size << 1);
337
- }
338
- entry = hashmap_entry_find(map, key, true);
339
- if (!entry) {
340
- /*
341
- * Cannot find an empty slot. Either out of memory, or using
342
- * a poor hash function. Attempt to rehash once to reduce
343
- * chain length.
344
- */
345
- if (hashmap_rehash(map, map->table_size << 1) < 0) {
346
- return NULL;
347
- }
348
- entry = hashmap_entry_find(map, key, true);
349
- if (!entry) {
350
- return NULL;
351
- }
352
- }
353
- if (!entry->key) {
354
- /* Allocate copy of key to simplify memory management */
355
- if (map->key_alloc) {
356
- entry->key = map->key_alloc(key);
357
- if (!entry->key) {
358
- return NULL;
359
- }
360
- } else {
361
- entry->key = (void *)key;
362
- }
363
- ++map->num_entries;
364
- } else if (entry->data) {
365
- /* Do not overwrite existing data */
366
- return entry->data;
367
- }
368
- entry->data = data;
369
- return data;
370
- }
371
-
372
- /*
373
- * Return the data pointer, or NULL if no entry exists.
374
- */
375
- void *hashmap_get(const struct hashmap *map, const void *key)
376
- {
377
- struct hashmap_entry *entry;
378
-
379
- HASHMAP_ASSERT(map != NULL);
380
- HASHMAP_ASSERT(key != NULL);
381
-
382
- entry = hashmap_entry_find(map, key, false);
383
- if (!entry) {
384
- return NULL;
385
- }
386
- return entry->data;
387
- }
388
-
389
- /*
390
- * Remove an entry with the specified key from the map.
391
- * Returns the data pointer, or NULL, if no entry was found.
392
- */
393
- void *hashmap_remove(struct hashmap *map, const void *key)
394
- {
395
- struct hashmap_entry *entry;
396
- void *data;
397
-
398
- HASHMAP_ASSERT(map != NULL);
399
- HASHMAP_ASSERT(key != NULL);
400
-
401
- entry = hashmap_entry_find(map, key, false);
402
- if (!entry) {
403
- return NULL;
404
- }
405
- data = entry->data;
406
- /* Clear the entry and make the chain contiguous */
407
- hashmap_entry_remove(map, entry);
408
- return data;
409
- }
410
-
411
- /*
412
- * Remove all entries.
413
- */
414
- void hashmap_clear(struct hashmap *map)
415
- {
416
- HASHMAP_ASSERT(map != NULL);
417
-
418
- hashmap_free_keys(map);
419
- map->num_entries = 0;
420
- memset(map->table, 0, sizeof(struct hashmap_entry) * map->table_size);
421
- }
422
-
423
- /*
424
- * Remove all entries and reset the hash table to its initial size.
425
- */
426
- void hashmap_reset(struct hashmap *map)
427
- {
428
- struct hashmap_entry *new_table;
429
-
430
- HASHMAP_ASSERT(map != NULL);
431
-
432
- hashmap_clear(map);
433
- if (map->table_size == map->table_size_init) {
434
- return;
435
- }
436
- new_table = (struct hashmap_entry *)realloc(map->table,
437
- sizeof(struct hashmap_entry) * map->table_size_init);
438
- if (!new_table) {
439
- return;
440
- }
441
- map->table = new_table;
442
- map->table_size = map->table_size_init;
443
- }
444
-
445
- /*
446
- * Return the number of entries in the hash map.
447
- */
448
- size_t hashmap_size(const struct hashmap *map)
449
- {
450
- HASHMAP_ASSERT(map != NULL);
451
-
452
- return map->num_entries;
453
- }
454
-
455
- /*
456
- * Get a new hashmap iterator. The iterator is an opaque
457
- * pointer that may be used with hashmap_iter_*() functions.
458
- * Hashmap iterators are INVALID after a put or remove operation is performed.
459
- * hashmap_iter_remove() allows safe removal during iteration.
460
- */
461
- struct hashmap_iter *hashmap_iter(const struct hashmap *map)
462
- {
463
- HASHMAP_ASSERT(map != NULL);
464
-
465
- if (!map->num_entries) {
466
- return NULL;
467
- }
468
- return (struct hashmap_iter *)hashmap_entry_get_populated(map,
469
- map->table);
470
- }
471
-
472
- /*
473
- * Return an iterator to the next hashmap entry. Returns NULL if there are
474
- * no more entries.
475
- */
476
- struct hashmap_iter *hashmap_iter_next(const struct hashmap *map,
477
- const struct hashmap_iter *iter)
478
- {
479
- struct hashmap_entry *entry = (struct hashmap_entry *)iter;
480
-
481
- HASHMAP_ASSERT(map != NULL);
482
-
483
- if (!iter) {
484
- return NULL;
485
- }
486
- return (struct hashmap_iter *)hashmap_entry_get_populated(map,
487
- entry + 1);
488
- }
489
-
490
- /*
491
- * Remove the hashmap entry pointed to by this iterator and return an
492
- * iterator to the next entry. Returns NULL if there are no more entries.
493
- */
494
- struct hashmap_iter *hashmap_iter_remove(struct hashmap *map,
495
- const struct hashmap_iter *iter)
496
- {
497
- struct hashmap_entry *entry = (struct hashmap_entry *)iter;
498
-
499
- HASHMAP_ASSERT(map != NULL);
500
-
501
- if (!iter) {
502
- return NULL;
503
- }
504
- if (!entry->key) {
505
- /* Iterator is invalid, so just return the next valid entry */
506
- return hashmap_iter_next(map, iter);
507
- }
508
- hashmap_entry_remove(map, entry);
509
- return (struct hashmap_iter *)hashmap_entry_get_populated(map, entry);
510
- }
511
-
512
- /*
513
- * Return the key of the entry pointed to by the iterator.
514
- */
515
- const void *hashmap_iter_get_key(const struct hashmap_iter *iter)
516
- {
517
- if (!iter) {
518
- return NULL;
519
- }
520
- return (const void *)((struct hashmap_entry *)iter)->key;
521
- }
522
-
523
- /*
524
- * Return the data of the entry pointed to by the iterator.
525
- */
526
- void *hashmap_iter_get_data(const struct hashmap_iter *iter)
527
- {
528
- if (!iter) {
529
- return NULL;
530
- }
531
- return ((struct hashmap_entry *)iter)->data;
532
- }
533
-
534
- /*
535
- * Set the data pointer of the entry pointed to by the iterator.
536
- */
537
- void hashmap_iter_set_data(const struct hashmap_iter *iter, void *data)
538
- {
539
- if (!iter) {
540
- return;
541
- }
542
- ((struct hashmap_entry *)iter)->data = data;
543
- }
544
-
545
- /*
546
- * Invoke func for each entry in the hashmap. Unlike the hashmap_iter_*()
547
- * interface, this function supports calls to hashmap_remove() during iteration.
548
- * However, it is an error to put or remove an entry other than the current one,
549
- * and doing so will immediately halt iteration and return an error.
550
- * Iteration is stopped if func returns non-zero. Returns func's return
551
- * value if it is < 0, otherwise, 0.
552
- */
553
- int hashmap_foreach(const struct hashmap *map,
554
- int (*func)(const void *, void *, void *), void *arg)
555
- {
556
- struct hashmap_entry *entry;
557
- size_t num_entries;
558
- const void *key;
559
- int rc;
560
-
561
- HASHMAP_ASSERT(map != NULL);
562
- HASHMAP_ASSERT(func != NULL);
563
-
564
- entry = map->table;
565
- for (entry = map->table; entry < &map->table[map->table_size];
566
- ++entry) {
567
- if (!entry->key) {
568
- continue;
569
- }
570
- num_entries = map->num_entries;
571
- key = entry->key;
572
- rc = func(entry->key, entry->data, arg);
573
- if (rc < 0) {
574
- return rc;
575
- }
576
- if (rc > 0) {
577
- return 0;
578
- }
579
- /* Run this entry again if func() deleted it */
580
- if (entry->key != key) {
581
- --entry;
582
- } else if (num_entries != map->num_entries) {
583
- /* Stop immediately if func put/removed another entry */
584
- return -1;
585
- }
586
- }
587
- return 0;
588
- }
589
-
590
- /*
591
- * Default hash function for string keys.
592
- * This is an implementation of the well-documented Jenkins one-at-a-time
593
- * hash function.
594
- */
595
- size_t hashmap_hash_string(const void *key)
596
- {
597
- const char *key_str = (const char *)key;
598
- size_t hash = 0;
599
-
600
- for (; *key_str; ++key_str) {
601
- hash += *key_str;
602
- hash += (hash << 10);
603
- hash ^= (hash >> 6);
604
- }
605
- hash += (hash << 3);
606
- hash ^= (hash >> 11);
607
- hash += (hash << 15);
608
- return hash;
609
- }
610
-
611
- /*
612
- * Default key comparator function for string keys.
613
- */
614
- int hashmap_compare_string(const void *a, const void *b)
615
- {
616
- return strcmp((const char *)a, (const char *)b);
617
- }
618
-
619
- /*
620
- * Default key allocation function for string keys. Use free() for the
621
- * key_free_func.
622
- */
623
- void *hashmap_alloc_key_string(const void *key)
624
- {
625
- return (void *)strdup((const char *)key);
626
- }
627
-
628
- #ifdef HASHMAP_METRICS
629
- /*
630
- * Return the load factor.
631
- */
632
- double hashmap_load_factor(const struct hashmap *map)
633
- {
634
- HASHMAP_ASSERT(map != NULL);
635
-
636
- if (!map->table_size) {
637
- return 0;
638
- }
639
- return (double)map->num_entries / map->table_size;
640
- }
641
-
642
- /*
643
- * Return the average number of collisions per entry.
644
- */
645
- double hashmap_collisions_mean(const struct hashmap *map)
646
- {
647
- struct hashmap_entry *entry;
648
- size_t total_collisions = 0;
649
-
650
- HASHMAP_ASSERT(map != NULL);
651
-
652
- if (!map->num_entries) {
653
- return 0;
654
- }
655
- for (entry = map->table; entry < &map->table[map->table_size];
656
- ++entry) {
657
- if (!entry->key) {
658
- continue;
659
- }
660
- total_collisions += entry->num_collisions;
661
- }
662
- return (double)total_collisions / map->num_entries;
663
- }
664
-
665
- /*
666
- * Return the variance between entry collisions. The higher the variance,
667
- * the more likely the hash function is poor and is resulting in clustering.
668
- */
669
- double hashmap_collisions_variance(const struct hashmap *map)
670
- {
671
- struct hashmap_entry *entry;
672
- double mean_collisions;
673
- double variance;
674
- double total_variance = 0;
675
-
676
- HASHMAP_ASSERT(map != NULL);
677
-
678
- if (!map->num_entries) {
679
- return 0;
680
- }
681
- mean_collisions = hashmap_collisions_mean(map);
682
- for (entry = map->table; entry < &map->table[map->table_size];
683
- ++entry) {
684
- if (!entry->key) {
685
- continue;
686
- }
687
- variance = (double)entry->num_collisions - mean_collisions;
688
- total_variance += variance * variance;
689
- }
690
- return total_variance / map->num_entries;
691
- }
692
- #endif
@@ -1,314 +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,
7
- jsmntok_t *tokens, size_t num_tokens) {
8
- jsmntok_t *tok;
9
- if (parser->toknext >= num_tokens) {
10
- return NULL;
11
- }
12
- tok = &tokens[parser->toknext++];
13
- tok->start = tok->end = -1;
14
- tok->size = 0;
15
- #ifdef JSMN_PARENT_LINKS
16
- tok->parent = -1;
17
- #endif
18
- return tok;
19
- }
20
-
21
- /**
22
- * Fills token type and boundaries.
23
- */
24
- static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type,
25
- int start, int end) {
26
- token->type = type;
27
- token->start = start;
28
- token->end = end;
29
- token->size = 0;
30
- }
31
-
32
- /**
33
- * Fills next available token with JSON primitive.
34
- */
35
- static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
36
- size_t len, jsmntok_t *tokens, size_t num_tokens) {
37
- jsmntok_t *token;
38
- int start;
39
-
40
- start = parser->pos;
41
-
42
- for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
43
- switch (js[parser->pos]) {
44
- #ifndef JSMN_STRICT
45
- /* In strict mode primitive must be followed by "," or "}" or "]" */
46
- case ':':
47
- #endif
48
- case '\t' : case '\r' : case '\n' : case ' ' :
49
- case ',' : case ']' : case '}' :
50
- goto found;
51
- }
52
- if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
53
- parser->pos = start;
54
- return JSMN_ERROR_INVAL;
55
- }
56
- }
57
- #ifdef JSMN_STRICT
58
- /* In strict mode primitive must be followed by a comma/object/array */
59
- parser->pos = start;
60
- return JSMN_ERROR_PART;
61
- #endif
62
-
63
- found:
64
- if (tokens == NULL) {
65
- parser->pos--;
66
- return 0;
67
- }
68
- token = jsmn_alloc_token(parser, tokens, num_tokens);
69
- if (token == NULL) {
70
- parser->pos = start;
71
- return JSMN_ERROR_NOMEM;
72
- }
73
- jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
74
- #ifdef JSMN_PARENT_LINKS
75
- token->parent = parser->toksuper;
76
- #endif
77
- parser->pos--;
78
- return 0;
79
- }
80
-
81
- /**
82
- * Fills next token with JSON string.
83
- */
84
- static int jsmn_parse_string(jsmn_parser *parser, const char *js,
85
- size_t len, jsmntok_t *tokens, size_t num_tokens) {
86
- jsmntok_t *token;
87
-
88
- int start = parser->pos;
89
-
90
- parser->pos++;
91
-
92
- /* Skip starting quote */
93
- for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
94
- char c = js[parser->pos];
95
-
96
- /* Quote: end of string */
97
- if (c == '\"') {
98
- if (tokens == NULL) {
99
- return 0;
100
- }
101
- token = jsmn_alloc_token(parser, tokens, num_tokens);
102
- if (token == NULL) {
103
- parser->pos = start;
104
- return JSMN_ERROR_NOMEM;
105
- }
106
- jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos);
107
- #ifdef JSMN_PARENT_LINKS
108
- token->parent = parser->toksuper;
109
- #endif
110
- return 0;
111
- }
112
-
113
- /* Backslash: Quoted symbol expected */
114
- if (c == '\\' && parser->pos + 1 < len) {
115
- int i;
116
- parser->pos++;
117
- switch (js[parser->pos]) {
118
- /* Allowed escaped symbols */
119
- case '\"': case '/' : case '\\' : case 'b' :
120
- case 'f' : case 'r' : case 'n' : case 't' :
121
- break;
122
- /* Allows escaped symbol \uXXXX */
123
- case 'u':
124
- parser->pos++;
125
- for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) {
126
- /* If it isn't a hex character we have an error */
127
- if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
128
- (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
129
- (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
130
- parser->pos = start;
131
- return JSMN_ERROR_INVAL;
132
- }
133
- parser->pos++;
134
- }
135
- parser->pos--;
136
- break;
137
- /* Unexpected symbol */
138
- default:
139
- parser->pos = start;
140
- return JSMN_ERROR_INVAL;
141
- }
142
- }
143
- }
144
- parser->pos = start;
145
- return JSMN_ERROR_PART;
146
- }
147
-
148
- /**
149
- * Parse JSON string and fill tokens.
150
- */
151
- int jsmn_parse(jsmn_parser *parser, const char *js, size_t len,
152
- jsmntok_t *tokens, unsigned int num_tokens) {
153
- int r;
154
- int i;
155
- jsmntok_t *token;
156
- int count = parser->toknext;
157
-
158
- for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
159
- char c;
160
- jsmntype_t type;
161
-
162
- c = js[parser->pos];
163
- switch (c) {
164
- case '{': case '[':
165
- count++;
166
- if (tokens == NULL) {
167
- break;
168
- }
169
- token = jsmn_alloc_token(parser, tokens, num_tokens);
170
- if (token == NULL)
171
- return JSMN_ERROR_NOMEM;
172
- if (parser->toksuper != -1) {
173
- tokens[parser->toksuper].size++;
174
- #ifdef JSMN_PARENT_LINKS
175
- token->parent = parser->toksuper;
176
- #endif
177
- }
178
- token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
179
- token->start = parser->pos;
180
- parser->toksuper = parser->toknext - 1;
181
- break;
182
- case '}': case ']':
183
- if (tokens == NULL)
184
- break;
185
- type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
186
- #ifdef JSMN_PARENT_LINKS
187
- if (parser->toknext < 1) {
188
- return JSMN_ERROR_INVAL;
189
- }
190
- token = &tokens[parser->toknext - 1];
191
- for (;;) {
192
- if (token->start != -1 && token->end == -1) {
193
- if (token->type != type) {
194
- return JSMN_ERROR_INVAL;
195
- }
196
- token->end = parser->pos + 1;
197
- parser->toksuper = token->parent;
198
- break;
199
- }
200
- if (token->parent == -1) {
201
- if(token->type != type || parser->toksuper == -1) {
202
- return JSMN_ERROR_INVAL;
203
- }
204
- break;
205
- }
206
- token = &tokens[token->parent];
207
- }
208
- #else
209
- for (i = parser->toknext - 1; i >= 0; i--) {
210
- token = &tokens[i];
211
- if (token->start != -1 && token->end == -1) {
212
- if (token->type != type) {
213
- return JSMN_ERROR_INVAL;
214
- }
215
- parser->toksuper = -1;
216
- token->end = parser->pos + 1;
217
- break;
218
- }
219
- }
220
- /* Error if unmatched closing bracket */
221
- if (i == -1) return JSMN_ERROR_INVAL;
222
- for (; i >= 0; i--) {
223
- token = &tokens[i];
224
- if (token->start != -1 && token->end == -1) {
225
- parser->toksuper = i;
226
- break;
227
- }
228
- }
229
- #endif
230
- break;
231
- case '\"':
232
- r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
233
- if (r < 0) return r;
234
- count++;
235
- if (parser->toksuper != -1 && tokens != NULL)
236
- tokens[parser->toksuper].size++;
237
- break;
238
- case '\t' : case '\r' : case '\n' : case ' ':
239
- break;
240
- case ':':
241
- parser->toksuper = parser->toknext - 1;
242
- break;
243
- case ',':
244
- if (tokens != NULL && parser->toksuper != -1 &&
245
- tokens[parser->toksuper].type != JSMN_ARRAY &&
246
- tokens[parser->toksuper].type != JSMN_OBJECT) {
247
- #ifdef JSMN_PARENT_LINKS
248
- parser->toksuper = tokens[parser->toksuper].parent;
249
- #else
250
- for (i = parser->toknext - 1; i >= 0; i--) {
251
- if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
252
- if (tokens[i].start != -1 && tokens[i].end == -1) {
253
- parser->toksuper = i;
254
- break;
255
- }
256
- }
257
- }
258
- #endif
259
- }
260
- break;
261
- #ifdef JSMN_STRICT
262
- /* In strict mode primitives are: numbers and booleans */
263
- case '-': case '0': case '1' : case '2': case '3' : case '4':
264
- case '5': case '6': case '7' : case '8': case '9':
265
- case 't': case 'f': case 'n' :
266
- /* And they must not be keys of the object */
267
- if (tokens != NULL && parser->toksuper != -1) {
268
- jsmntok_t *t = &tokens[parser->toksuper];
269
- if (t->type == JSMN_OBJECT ||
270
- (t->type == JSMN_STRING && t->size != 0)) {
271
- return JSMN_ERROR_INVAL;
272
- }
273
- }
274
- #else
275
- /* In non-strict mode every unquoted value is a primitive */
276
- default:
277
- #endif
278
- r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
279
- if (r < 0) return r;
280
- count++;
281
- if (parser->toksuper != -1 && tokens != NULL)
282
- tokens[parser->toksuper].size++;
283
- break;
284
-
285
- #ifdef JSMN_STRICT
286
- /* Unexpected char in strict mode */
287
- default:
288
- return JSMN_ERROR_INVAL;
289
- #endif
290
- }
291
- }
292
-
293
- if (tokens != NULL) {
294
- for (i = parser->toknext - 1; i >= 0; i--) {
295
- /* Unmatched opened object or array */
296
- if (tokens[i].start != -1 && tokens[i].end == -1) {
297
- return JSMN_ERROR_PART;
298
- }
299
- }
300
- }
301
-
302
- return count;
303
- }
304
-
305
- /**
306
- * Creates a new parser based over a given buffer with an array of tokens
307
- * available.
308
- */
309
- void jsmn_init(jsmn_parser *parser) {
310
- parser->pos = 0;
311
- parser->toknext = 0;
312
- parser->toksuper = -1;
313
- }
314
-
Binary file