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 +4 -4
- data/ext/fast_mmaped_file/mmap.c +58 -0
- data/ext/fast_mmaped_file/mmap.h +6 -0
- data/ext/fast_mmaped_file/value_access.c +9 -6
- data/lib/prometheus/client/version.rb +1 -1
- metadata +8 -11
- data/ext/fast_mmaped_file/hashmap.c +0 -635
- data/ext/fast_mmaped_file/jsmn.c +0 -329
- data/lib/fast_mmaped_file.bundle +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 98e93b1f6cdd34ff4621cc7401fa1b02b6e81b069313899aabd28d1a1650cf7d
|
4
|
+
data.tar.gz: d0ceb3c1535fa17e609baef147217593366bd3e0351254d231848ec18653c06d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7cc06bff586223b764f098d18c213e59f072a3ab906c7f4c2e844bcc419c9b1037d39b564d2a441a8fac50fc913107f2c5864f06fe37b15e54eb1c828cf8f118
|
7
|
+
data.tar.gz: 72c42e4bb89b7b0bfff4f5855ca3b42eff07c5dc1b8317940739fa0304e79eafca131c9909a80588d6ce1362313175c8a21bacb46b2ed2066114a49f8f3fdb31
|
data/ext/fast_mmaped_file/mmap.c
CHANGED
@@ -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);
|
data/ext/fast_mmaped_file/mmap.h
CHANGED
@@ -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
|
}
|
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.
|
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-
|
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:
|
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.
|
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
|
data/ext/fast_mmaped_file/jsmn.c
DELETED
@@ -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
|
-
}
|
data/lib/fast_mmaped_file.bundle
DELETED
Binary file
|