iodine 0.3.6 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -0
- data/LIMITS.md +25 -0
- data/README.md +39 -80
- data/SPEC-Websocket-Draft.md +129 -4
- data/bin/echo +2 -2
- data/bin/http-hello +1 -0
- data/bin/updated api +113 -0
- data/bin/ws-echo +0 -1
- data/examples/broadcast.ru +56 -0
- data/examples/echo.ru +57 -0
- data/examples/hello.ru +30 -0
- data/examples/redis.ru +69 -0
- data/examples/shootout.ru +53 -0
- data/exe/iodine +2 -80
- data/ext/iodine/defer.c +11 -5
- data/ext/iodine/empty.h +26 -0
- data/ext/iodine/evio.h +1 -1
- data/ext/iodine/facil.c +103 -61
- data/ext/iodine/facil.h +20 -12
- data/ext/iodine/fio_dict.c +446 -0
- data/ext/iodine/fio_dict.h +90 -0
- data/ext/iodine/fio_hash_table.h +370 -0
- data/ext/iodine/fio_list.h +30 -3
- data/ext/iodine/http.c +169 -37
- data/ext/iodine/http.h +33 -10
- data/ext/iodine/http1.c +78 -42
- data/ext/iodine/http_request.c +6 -0
- data/ext/iodine/http_request.h +3 -0
- data/ext/iodine/http_response.c +43 -11
- data/ext/iodine/iodine.c +380 -0
- data/ext/iodine/iodine.h +62 -0
- data/ext/iodine/iodine_helpers.c +235 -0
- data/ext/iodine/iodine_helpers.h +13 -0
- data/ext/iodine/iodine_http.c +409 -241
- data/ext/iodine/iodine_http.h +7 -14
- data/ext/iodine/iodine_protocol.c +626 -0
- data/ext/iodine/iodine_protocol.h +13 -0
- data/ext/iodine/iodine_pubsub.c +646 -0
- data/ext/iodine/iodine_pubsub.h +27 -0
- data/ext/iodine/iodine_websockets.c +796 -0
- data/ext/iodine/iodine_websockets.h +19 -0
- data/ext/iodine/pubsub.c +544 -0
- data/ext/iodine/pubsub.h +215 -0
- data/ext/iodine/random.c +4 -4
- data/ext/iodine/rb-call.c +1 -5
- data/ext/iodine/rb-defer.c +3 -20
- data/ext/iodine/rb-rack-io.c +22 -22
- data/ext/iodine/rb-rack-io.h +3 -4
- data/ext/iodine/rb-registry.c +111 -118
- data/ext/iodine/redis_connection.c +277 -0
- data/ext/iodine/redis_connection.h +77 -0
- data/ext/iodine/redis_engine.c +398 -0
- data/ext/iodine/redis_engine.h +68 -0
- data/ext/iodine/resp.c +842 -0
- data/ext/iodine/resp.h +253 -0
- data/ext/iodine/sock.c +26 -12
- data/ext/iodine/sock.h +14 -3
- data/ext/iodine/spnlock.inc +19 -2
- data/ext/iodine/websockets.c +299 -11
- data/ext/iodine/websockets.h +159 -6
- data/lib/iodine.rb +104 -1
- data/lib/iodine/cli.rb +106 -0
- data/lib/iodine/monkeypatch.rb +40 -0
- data/lib/iodine/pubsub.rb +70 -0
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine/websocket.rb +12 -0
- data/lib/rack/handler/iodine.rb +33 -7
- metadata +35 -7
- data/ext/iodine/iodine_core.c +0 -760
- data/ext/iodine/iodine_core.h +0 -79
- data/ext/iodine/iodine_websocket.c +0 -551
- data/ext/iodine/iodine_websocket.h +0 -22
- data/lib/iodine/http.rb +0 -4
data/ext/iodine/facil.h
CHANGED
@@ -14,8 +14,8 @@ extern "C" {
|
|
14
14
|
|
15
15
|
#define H_FACIL_H
|
16
16
|
#define FACIL_VERSION_MAJOR 0
|
17
|
-
#define FACIL_VERSION_MINOR
|
18
|
-
#define FACIL_VERSION_PATCH
|
17
|
+
#define FACIL_VERSION_MINOR 5
|
18
|
+
#define FACIL_VERSION_PATCH 0
|
19
19
|
|
20
20
|
#ifndef FACIL_PRINT_STATE
|
21
21
|
/**
|
@@ -67,7 +67,7 @@ struct FacilIOProtocol {
|
|
67
67
|
* but before closing the connection. */
|
68
68
|
void (*on_shutdown)(intptr_t uuid, protocol_s *protocol);
|
69
69
|
/** called when the connection was closed, but will not run concurrently */
|
70
|
-
void (*on_close)(protocol_s *protocol);
|
70
|
+
void (*on_close)(intptr_t uuid, protocol_s *protocol);
|
71
71
|
/** called when a connection's timeout was reached */
|
72
72
|
void (*ping)(intptr_t uuid, protocol_s *protocol);
|
73
73
|
/** private metadata used by facil. */
|
@@ -178,12 +178,16 @@ struct facil_listen_args {
|
|
178
178
|
* timed event scheduling.
|
179
179
|
*
|
180
180
|
* This will be called seperately for every process. */
|
181
|
-
void (*on_start)(void *udata);
|
181
|
+
void (*on_start)(intptr_t uuid, void *udata);
|
182
182
|
/**
|
183
183
|
* Called when the server is done, usable for cleanup.
|
184
184
|
*
|
185
185
|
* This will be called seperately for every process. */
|
186
|
-
void (*on_finish)(void *udata);
|
186
|
+
void (*on_finish)(intptr_t uuid, void *udata);
|
187
|
+
/**
|
188
|
+
* A cleanup callback for the `rw_udata`.
|
189
|
+
*/
|
190
|
+
void (*on_finish_rw)(intptr_t uuid, void *rw_udata);
|
187
191
|
};
|
188
192
|
|
189
193
|
/** Schedule a network service on a listening socket. */
|
@@ -215,9 +219,10 @@ struct facil_connect_args {
|
|
215
219
|
*/
|
216
220
|
protocol_s *(*on_connect)(intptr_t uuid, void *udata);
|
217
221
|
/**
|
218
|
-
* The `on_fail` is called when a socket fails to connect.
|
222
|
+
* The `on_fail` is called when a socket fails to connect. The old sock UUID
|
223
|
+
* is passed along.
|
219
224
|
*/
|
220
|
-
void (*on_fail)(void *udata);
|
225
|
+
void (*on_fail)(intptr_t uuid, void *udata);
|
221
226
|
/** Opaque user data. */
|
222
227
|
void *udata;
|
223
228
|
/** Opaque user data for `set_rw_hooks`. */
|
@@ -402,7 +407,10 @@ int facil_each(struct facil_each_args_s args);
|
|
402
407
|
#define facil_each(...) facil_each((struct facil_each_args_s){__VA_ARGS__})
|
403
408
|
|
404
409
|
/* *****************************************************************************
|
405
|
-
Cluster specific API - cluster messaging.
|
410
|
+
Cluster specific API - local cluster messaging.
|
411
|
+
|
412
|
+
Facil supports message process clustering, so that a multi-process application
|
413
|
+
can easily send and receive messages across process boundries.
|
406
414
|
***************************************************************************** */
|
407
415
|
|
408
416
|
/**
|
@@ -411,10 +419,10 @@ Sets a callback / handler for a message of type `msg_type`.
|
|
411
419
|
Callbacks are invoked using an O(n) matching, where `n` is the number of
|
412
420
|
registered callbacks.
|
413
421
|
|
414
|
-
The `msg_type` value can be any number
|
415
|
-
|
422
|
+
The `msg_type` value can be any positive number up to 2^31-1 (2,147,483,647).
|
423
|
+
All values less than 0 are reserved for internal use.
|
416
424
|
*/
|
417
|
-
void facil_cluster_set_handler(
|
425
|
+
void facil_cluster_set_handler(int32_t msg_type,
|
418
426
|
void (*on_message)(void *data, uint32_t len));
|
419
427
|
|
420
428
|
/** Sends a message of type `msg_type` to the **other** cluster processes.
|
@@ -430,7 +438,7 @@ starting at 1,073,741,824 are reserved for internal use.
|
|
430
438
|
Callbacks are invoked using an O(n) matching, where `n` is the number of
|
431
439
|
registered callbacks.
|
432
440
|
*/
|
433
|
-
int facil_cluster_send(
|
441
|
+
int facil_cluster_send(int32_t msg_type, void *data, uint32_t len);
|
434
442
|
|
435
443
|
/* *****************************************************************************
|
436
444
|
Lower Level API - for special circumstances, use with care under .
|
@@ -0,0 +1,446 @@
|
|
1
|
+
/*
|
2
|
+
Copyright: Boaz segev, 2016-2017
|
3
|
+
License: MIT
|
4
|
+
|
5
|
+
Feel free to copy, use and enjoy according to the license provided.
|
6
|
+
*/
|
7
|
+
#include "fio_dict.h"
|
8
|
+
/**
|
9
|
+
`fio_dict_s` Is based on a 4-bit trie structure, allowing for fast no-collisions
|
10
|
+
key-value matching while avoiding any hashing.
|
11
|
+
|
12
|
+
It's memory intensive... very memory intensive... but it has 0 collision risk
|
13
|
+
and offers fairly high performance.
|
14
|
+
|
15
|
+
Just to offer some insight, a single key-value pair for the key "hello" will
|
16
|
+
require ~1,360 bytes. Add the key "bye!" ad you'll add ~1,088 bytes more... but
|
17
|
+
the key "hello1" will cost only 272 bytes... brrr.
|
18
|
+
*/
|
19
|
+
|
20
|
+
/* *****************************************************************************
|
21
|
+
Inline variation
|
22
|
+
***************************************************************************** */
|
23
|
+
|
24
|
+
static inline fio_dict_s *fio_dict_step_inline(fio_dict_s *dict,
|
25
|
+
uint8_t prefix) {
|
26
|
+
if (!dict)
|
27
|
+
return NULL;
|
28
|
+
dict = dict->trie[prefix & 0xf];
|
29
|
+
if (!dict)
|
30
|
+
return NULL;
|
31
|
+
return dict->trie[(prefix >> 4) & 0xf];
|
32
|
+
}
|
33
|
+
|
34
|
+
static inline fio_dict_s *fio_dict_prefix_inline(fio_dict_s *dict,
|
35
|
+
void *prefix_, size_t len) {
|
36
|
+
uint8_t *prefix = prefix_;
|
37
|
+
while (dict && len) {
|
38
|
+
dict = fio_dict_step_inline(dict, *prefix);
|
39
|
+
prefix++;
|
40
|
+
len--;
|
41
|
+
}
|
42
|
+
if (len)
|
43
|
+
return NULL;
|
44
|
+
return dict;
|
45
|
+
}
|
46
|
+
|
47
|
+
/* *****************************************************************************
|
48
|
+
Implementation
|
49
|
+
***************************************************************************** */
|
50
|
+
|
51
|
+
/** Returns the `fio_dict_s *` object associated with the key, NULL if none.
|
52
|
+
*/
|
53
|
+
fio_dict_s *fio_dict_get(fio_dict_s *dict, void *key, size_t key_len) {
|
54
|
+
dict = fio_dict_prefix_inline(dict, key, key_len);
|
55
|
+
if (dict && dict->used)
|
56
|
+
return dict;
|
57
|
+
return NULL;
|
58
|
+
}
|
59
|
+
|
60
|
+
/** Returns the old `fio_dict_s *` object associated with the key, if any.*/
|
61
|
+
fio_dict_s *fio_dict_set(fio_dict_s *dict, void *key, size_t key_len,
|
62
|
+
fio_dict_s *node) {
|
63
|
+
uint8_t *pos = key;
|
64
|
+
uint8_t tr;
|
65
|
+
fio_dict_s *old;
|
66
|
+
if (!key_len || !dict)
|
67
|
+
return NULL;
|
68
|
+
if (!node) {
|
69
|
+
dict = fio_dict_get(dict, key, key_len);
|
70
|
+
fio_dict_remove(dict);
|
71
|
+
return dict;
|
72
|
+
}
|
73
|
+
if (key_len > 1) {
|
74
|
+
pos += key_len - 1;
|
75
|
+
dict = fio_dict_ensure_prefix(dict, key, key_len - 1);
|
76
|
+
}
|
77
|
+
tr = *pos & 0xf;
|
78
|
+
if (!dict->trie[tr]) {
|
79
|
+
if (node == NULL)
|
80
|
+
return NULL;
|
81
|
+
dict->trie[tr] = malloc(sizeof(fio_dict_s));
|
82
|
+
*dict->trie[tr] = (fio_dict_s){.trie_val = tr, .parent = dict};
|
83
|
+
}
|
84
|
+
dict = dict->trie[tr];
|
85
|
+
|
86
|
+
tr = ((*pos) >> 4) & 0xf;
|
87
|
+
if ((old = dict->trie[tr]))
|
88
|
+
goto replace_node;
|
89
|
+
|
90
|
+
/* no old, but we have a new node we need to initialize and add. */
|
91
|
+
*node = (fio_dict_s){.parent = dict, .trie_val = tr, .used = 1};
|
92
|
+
dict->trie[tr] = node;
|
93
|
+
return NULL;
|
94
|
+
|
95
|
+
replace_node:
|
96
|
+
/* We have an old node to be replaced with a new one. */
|
97
|
+
*node = *old;
|
98
|
+
node->used = 1;
|
99
|
+
dict->trie[tr] = node;
|
100
|
+
for (size_t i = 0; i < 16; i++) {
|
101
|
+
if (node->trie[i])
|
102
|
+
node->trie[i]->parent = node;
|
103
|
+
}
|
104
|
+
if (old->used)
|
105
|
+
return old;
|
106
|
+
return NULL;
|
107
|
+
}
|
108
|
+
|
109
|
+
/** Returns the old `fio_dict_s *` object associated with the key, if any.*/
|
110
|
+
fio_dict_s *fio_dict_remove(fio_dict_s *node) {
|
111
|
+
fio_dict_s *tmp;
|
112
|
+
fio_dict_s *dict = node->parent;
|
113
|
+
/* We need to remove the existing node and clear the branch if empty. */
|
114
|
+
if (fio_dict_isempty(node)) {
|
115
|
+
dict->trie[node->trie_val] = NULL;
|
116
|
+
while (fio_dict_isempty(dict)) {
|
117
|
+
if (dict->used || !dict->parent)
|
118
|
+
break;
|
119
|
+
tmp = dict;
|
120
|
+
dict = dict->parent;
|
121
|
+
dict->trie[tmp->trie_val] = NULL;
|
122
|
+
free(tmp);
|
123
|
+
}
|
124
|
+
if (node->used)
|
125
|
+
return node;
|
126
|
+
return NULL;
|
127
|
+
}
|
128
|
+
/* We need to place an alternative node. */
|
129
|
+
if (!node->used)
|
130
|
+
return NULL;
|
131
|
+
tmp = malloc(sizeof(fio_dict_s));
|
132
|
+
*tmp = *node;
|
133
|
+
tmp->used = 0;
|
134
|
+
for (size_t i = 0; i < 16; i++) {
|
135
|
+
if (tmp->trie[i])
|
136
|
+
tmp->trie[i]->parent = tmp;
|
137
|
+
}
|
138
|
+
return node;
|
139
|
+
}
|
140
|
+
|
141
|
+
/** Returns a `fio_dict_s *` dictionary (or NULL) of all `prefix` children. */
|
142
|
+
fio_dict_s *fio_dict_step(fio_dict_s *dict, uint8_t prefix) {
|
143
|
+
return fio_dict_step_inline(dict, prefix);
|
144
|
+
}
|
145
|
+
|
146
|
+
/** Returns a `fio_dict_s *` dictionary (or NULL) of all `prefix` children. */
|
147
|
+
fio_dict_s *fio_dict_prefix(fio_dict_s *dict, void *prefix_, size_t len) {
|
148
|
+
return fio_dict_prefix_inline(dict, prefix_, len);
|
149
|
+
}
|
150
|
+
|
151
|
+
/**
|
152
|
+
* Creates a `fio_dict_s *` dictionary (if missing) for the `prefix`...
|
153
|
+
*
|
154
|
+
* After calling this function a node MUST be added to this dictionary, or
|
155
|
+
* memory leaks will occure.
|
156
|
+
*/
|
157
|
+
fio_dict_s *fio_dict_ensure_prefix(fio_dict_s *dict, void *prefix, size_t len) {
|
158
|
+
uint8_t *pos = prefix;
|
159
|
+
uint8_t tr;
|
160
|
+
|
161
|
+
while (dict && len) {
|
162
|
+
tr = *pos & 0xf;
|
163
|
+
if (!dict->trie[tr]) {
|
164
|
+
dict->trie[tr] = malloc(sizeof(fio_dict_s));
|
165
|
+
*dict->trie[tr] = (fio_dict_s){.trie_val = tr, .parent = dict};
|
166
|
+
}
|
167
|
+
dict = dict->trie[tr];
|
168
|
+
tr = (*pos >> 4) & 0xf;
|
169
|
+
if (!dict->trie[tr]) {
|
170
|
+
dict->trie[tr] = malloc(sizeof(fio_dict_s));
|
171
|
+
*dict->trie[tr] = (fio_dict_s){.trie_val = tr, .parent = dict};
|
172
|
+
}
|
173
|
+
dict = dict->trie[tr];
|
174
|
+
pos++;
|
175
|
+
len--;
|
176
|
+
}
|
177
|
+
return dict;
|
178
|
+
}
|
179
|
+
|
180
|
+
/** Traverses a dictionary, performing an action for each item. */
|
181
|
+
void fio_dict_each(fio_dict_s *dict,
|
182
|
+
void (*action)(fio_dict_s *node, void *arg), void *arg) {
|
183
|
+
if (!dict)
|
184
|
+
return;
|
185
|
+
fio_dict_s *child, *head = dict->parent, *to_call = NULL;
|
186
|
+
uint8_t tr;
|
187
|
+
tr = 0;
|
188
|
+
|
189
|
+
/* We use this to make sure that the function doesn't break if `to_call` is
|
190
|
+
* removed from the trie while we are iterating. */
|
191
|
+
#define test_callback() \
|
192
|
+
do { \
|
193
|
+
if (to_call) \
|
194
|
+
action(to_call, arg); \
|
195
|
+
to_call = dict; \
|
196
|
+
} while (0);
|
197
|
+
|
198
|
+
while (dict != head) {
|
199
|
+
top:
|
200
|
+
while (tr < 16) {
|
201
|
+
/* walk child */
|
202
|
+
if ((child = dict->trie[tr])) {
|
203
|
+
dict = child;
|
204
|
+
tr = 0;
|
205
|
+
goto top;
|
206
|
+
}
|
207
|
+
tr++;
|
208
|
+
}
|
209
|
+
if (dict->used)
|
210
|
+
test_callback();
|
211
|
+
tr = dict->trie_val + 1;
|
212
|
+
dict = dict->parent;
|
213
|
+
}
|
214
|
+
test_callback();
|
215
|
+
#undef test_callback
|
216
|
+
}
|
217
|
+
|
218
|
+
/**
|
219
|
+
|
220
|
+
Traverses a dictionary, performing an action for each item.
|
221
|
+
|
222
|
+
based on the same matching behavior used by Redis... hopefully...
|
223
|
+
https://github.com/antirez/redis/blob/d680eb6dbdf2d2030cb96edfb089be1e2a775ac1/src/util.c#L47
|
224
|
+
*/
|
225
|
+
void fio_dict_each_match_glob(fio_dict_s *dict, void *pattern, size_t len,
|
226
|
+
void (*action)(fio_dict_s *node, void *arg),
|
227
|
+
void *arg) {
|
228
|
+
|
229
|
+
if (!dict || !pattern || !action)
|
230
|
+
return;
|
231
|
+
fio_dict_s *child, *head = dict->parent;
|
232
|
+
uint8_t *pos = pattern;
|
233
|
+
|
234
|
+
while (len && dict) {
|
235
|
+
|
236
|
+
if (*pos == '*') {
|
237
|
+
/* eat up all '*' and match whatever */
|
238
|
+
while (*pos == '*' && len) {
|
239
|
+
len--;
|
240
|
+
pos++;
|
241
|
+
}
|
242
|
+
if (!len) {
|
243
|
+
fio_dict_each(dict, action, arg);
|
244
|
+
return;
|
245
|
+
}
|
246
|
+
/* test each "tail"... (brrr). */
|
247
|
+
head = dict->parent;
|
248
|
+
uint8_t tr = 0;
|
249
|
+
while (dict != head) {
|
250
|
+
glob_top:
|
251
|
+
while (tr < 16) {
|
252
|
+
/* walk child */
|
253
|
+
if ((child = dict->trie[tr])) {
|
254
|
+
dict = child;
|
255
|
+
tr = 0;
|
256
|
+
goto glob_top;
|
257
|
+
}
|
258
|
+
tr++;
|
259
|
+
}
|
260
|
+
fio_dict_each_match_glob(dict, pos, len, action, arg);
|
261
|
+
tr = dict->trie_val + 1;
|
262
|
+
dict = dict->parent;
|
263
|
+
}
|
264
|
+
fio_dict_each_match_glob(dict, pos, len, action, arg);
|
265
|
+
return;
|
266
|
+
}
|
267
|
+
|
268
|
+
if (*pos == '?') {
|
269
|
+
pos++;
|
270
|
+
len--;
|
271
|
+
for (size_t i = 0; i < 256; i++) {
|
272
|
+
if (dict->trie[i & 0xf])
|
273
|
+
fio_dict_each_match_glob(dict->trie[i & 0xf]->trie[(i >> 4) & 0xf],
|
274
|
+
pos, len, action, arg);
|
275
|
+
}
|
276
|
+
return;
|
277
|
+
}
|
278
|
+
|
279
|
+
if (*pos == '[') {
|
280
|
+
int state = 0;
|
281
|
+
uint8_t map[256] = {0};
|
282
|
+
pos++;
|
283
|
+
len--;
|
284
|
+
if (*pos == '^') {
|
285
|
+
state = 1;
|
286
|
+
pos++;
|
287
|
+
len--;
|
288
|
+
}
|
289
|
+
if (!len)
|
290
|
+
return; /* pattern error */
|
291
|
+
while (*pos != ']' && len) {
|
292
|
+
if (*pos == '\\') {
|
293
|
+
pos++;
|
294
|
+
len--;
|
295
|
+
}
|
296
|
+
if (pos[1] == '-') {
|
297
|
+
if (len < 4) {
|
298
|
+
return; /* pattern error */
|
299
|
+
}
|
300
|
+
uint8_t end, start;
|
301
|
+
start = *pos;
|
302
|
+
pos += 2;
|
303
|
+
len -= 2;
|
304
|
+
if (*pos == '\\') {
|
305
|
+
pos++;
|
306
|
+
len--;
|
307
|
+
}
|
308
|
+
end = *pos;
|
309
|
+
if (start > end) {
|
310
|
+
uint8_t tmp;
|
311
|
+
tmp = start;
|
312
|
+
start = end;
|
313
|
+
end = tmp;
|
314
|
+
}
|
315
|
+
while (start < end)
|
316
|
+
map[start++] = 1;
|
317
|
+
map[end] = 1; /* prevent endless loop where `end == 255`*/
|
318
|
+
} else
|
319
|
+
map[*pos] = 1;
|
320
|
+
pos++;
|
321
|
+
len--;
|
322
|
+
}
|
323
|
+
if (!len) {
|
324
|
+
return;
|
325
|
+
}
|
326
|
+
pos++;
|
327
|
+
len--;
|
328
|
+
for (size_t i = 0; i < 256; i++) {
|
329
|
+
/* code */
|
330
|
+
if (map[i] == state)
|
331
|
+
continue;
|
332
|
+
if (dict->trie[i & 0xf])
|
333
|
+
fio_dict_each_match_glob(dict->trie[i & 0xf]->trie[(i >> 4) & 0xf],
|
334
|
+
pos, len, action, arg);
|
335
|
+
}
|
336
|
+
return;
|
337
|
+
}
|
338
|
+
|
339
|
+
if (*pos == '\\' && len > 1) {
|
340
|
+
pos++;
|
341
|
+
len--;
|
342
|
+
}
|
343
|
+
dict = fio_dict_step_inline(dict, *pos);
|
344
|
+
pos++;
|
345
|
+
len--;
|
346
|
+
}
|
347
|
+
|
348
|
+
if (dict && dict->used)
|
349
|
+
action(dict, arg);
|
350
|
+
}
|
351
|
+
|
352
|
+
/** A binary glob matching helper. Returns 1 on match, otherwise returns 0. */
|
353
|
+
int fio_glob_match(uint8_t *data, size_t data_len, uint8_t *pattern,
|
354
|
+
size_t pat_len) {
|
355
|
+
/* adapted and rewritten, with thankfulness, from the code at:
|
356
|
+
* https://github.com/opnfv/kvmfornfv/blob/master/kernel/lib/glob.c
|
357
|
+
*
|
358
|
+
* Original version's copyright:
|
359
|
+
* Copyright 2015 Open Platform for NFV Project, Inc. and its contributors
|
360
|
+
* Under the MIT license.
|
361
|
+
*/
|
362
|
+
|
363
|
+
/*
|
364
|
+
* Backtrack to previous * on mismatch and retry starting one
|
365
|
+
* character later in the string. Because * matches all characters
|
366
|
+
* (no exception for /), it can be easily proved that there's
|
367
|
+
* never a need to backtrack multiple levels.
|
368
|
+
*/
|
369
|
+
uint8_t *back_pat = NULL, *back_str = data;
|
370
|
+
size_t back_pat_len = 0, back_str_len = data_len;
|
371
|
+
|
372
|
+
/*
|
373
|
+
* Loop over each token (character or class) in pat, matching
|
374
|
+
* it against the remaining unmatched tail of str. Return false
|
375
|
+
* on mismatch, or true after matching the trailing nul bytes.
|
376
|
+
*/
|
377
|
+
while (data_len) {
|
378
|
+
uint8_t c = *data++;
|
379
|
+
uint8_t d = *pattern++;
|
380
|
+
data_len--;
|
381
|
+
pat_len--;
|
382
|
+
|
383
|
+
switch (d) {
|
384
|
+
case '?': /* Wildcard: anything goes */
|
385
|
+
break;
|
386
|
+
|
387
|
+
case '*': /* Any-length wildcard */
|
388
|
+
if (!pat_len) /* Optimize trailing * case */
|
389
|
+
return 1;
|
390
|
+
back_pat = pattern;
|
391
|
+
back_pat_len = pat_len;
|
392
|
+
back_str = --data; /* Allow zero-length match */
|
393
|
+
back_str_len = ++data_len;
|
394
|
+
break;
|
395
|
+
|
396
|
+
case '[': { /* Character class */
|
397
|
+
uint8_t match = 0, inverted = (*pattern == '^');
|
398
|
+
uint8_t *cls = pattern + inverted;
|
399
|
+
uint8_t a = *cls++;
|
400
|
+
|
401
|
+
/*
|
402
|
+
* Iterate over each span in the character class.
|
403
|
+
* A span is either a single character a, or a
|
404
|
+
* range a-b. The first span may begin with ']'.
|
405
|
+
*/
|
406
|
+
do {
|
407
|
+
uint8_t b = a;
|
408
|
+
|
409
|
+
if (cls[0] == '-' && cls[1] != ']') {
|
410
|
+
b = cls[1];
|
411
|
+
|
412
|
+
cls += 2;
|
413
|
+
if (a > b) {
|
414
|
+
uint8_t tmp = a;
|
415
|
+
a = b;
|
416
|
+
b = tmp;
|
417
|
+
}
|
418
|
+
}
|
419
|
+
match |= (a <= c && c <= b);
|
420
|
+
} while ((a = *cls++) != ']');
|
421
|
+
|
422
|
+
if (match == inverted)
|
423
|
+
goto backtrack;
|
424
|
+
pat_len -= cls - pattern;
|
425
|
+
pattern = cls;
|
426
|
+
|
427
|
+
} break;
|
428
|
+
case '\\':
|
429
|
+
d = *pattern++;
|
430
|
+
pat_len--;
|
431
|
+
/*FALLTHROUGH*/
|
432
|
+
default: /* Literal character */
|
433
|
+
if (c == d)
|
434
|
+
break;
|
435
|
+
backtrack:
|
436
|
+
if (!back_pat)
|
437
|
+
return 0; /* No point continuing */
|
438
|
+
/* Try again from last *, one character later in str. */
|
439
|
+
pattern = back_pat;
|
440
|
+
data = ++back_str;
|
441
|
+
data_len = --back_str_len;
|
442
|
+
pat_len = back_pat_len;
|
443
|
+
}
|
444
|
+
}
|
445
|
+
return !data_len && !pat_len;
|
446
|
+
}
|