durable_rules 0.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/src/rules/json.c ADDED
@@ -0,0 +1,423 @@
1
+
2
+ #include <stdio.h>
3
+ #include <string.h>
4
+ #include "rules.h"
5
+ #include "json.h"
6
+
7
+ #define ST_OBJECT_SEEK 0
8
+ #define ST_OBJECT_PROP_SEEK 1
9
+ #define ST_OBJECT_PROP_NAME 2
10
+ #define ST_OBJECT_PROP_PARSE 3
11
+ #define ST_OBJECT_PROP_VAL 4
12
+
13
+ #define ST_ARRAY_SEEK 0
14
+ #define ST_ARRAY_VAL_SEEK 1
15
+ #define ST_ARRAY_VAL_PARSE 2
16
+
17
+ #define ST_STRING_SEEK 0
18
+ #define ST_STRING_PARSE 1
19
+ #define ST_STRING_ESC 2
20
+
21
+ #define ST_NUMBER_SEEK 0
22
+ #define ST_NUMBER_INT 1
23
+ #define ST_NUMBER_FRA 2
24
+ #define ST_NUMBER_EXP 3
25
+
26
+ #define IS_WHITESPACE(value) (value == ' ' || value == '\t' || value == '\n' || value == '\t')
27
+
28
+ static unsigned int getValue(char *start, char **first, char **last, unsigned char *type);
29
+ static unsigned int getObject(char *start, char **first, char **last);
30
+ static unsigned int getArray(char *start, char **first, char **last);
31
+ static unsigned int getNumber(char *start, char **first, char **last, unsigned char *type);
32
+ static unsigned int getString(char *start, char **first, char **last);
33
+ static unsigned int getStringAndHash(char *start, char **first, char **last, unsigned int *hash);
34
+
35
+ unsigned int readNextName(char *start, char **first, char **last, unsigned int *hash) {
36
+ unsigned char state = ST_OBJECT_SEEK;
37
+ while(start[0] != '\0') {
38
+ switch(state) {
39
+ case ST_OBJECT_SEEK:
40
+ if (start[0] == '{') {
41
+ state = ST_OBJECT_PROP_NAME;
42
+ } else if (!IS_WHITESPACE(start[0])) {
43
+ state = ST_OBJECT_PROP_SEEK;
44
+ }
45
+ break;
46
+ case ST_OBJECT_PROP_SEEK:
47
+ if (start[0] == ',') {
48
+ state = ST_OBJECT_PROP_NAME;
49
+ } else if (start[0] == '}') {
50
+ *first = start;
51
+ *last = start;
52
+ return PARSE_END;
53
+ } else if (!IS_WHITESPACE(start[0])) {
54
+ return ERR_PARSE_OBJECT;
55
+ }
56
+ break;
57
+ case ST_OBJECT_PROP_NAME:
58
+ if (!IS_WHITESPACE(start[0])) {
59
+ return getStringAndHash(start, first, last, hash);
60
+ }
61
+ break;
62
+ }
63
+
64
+ ++start;
65
+ }
66
+
67
+ return ERR_PARSE_OBJECT;
68
+ }
69
+
70
+ unsigned int readNextValue(char *start, char **first, char **last, unsigned char *type) {
71
+ unsigned char state = ST_OBJECT_PROP_PARSE;
72
+ ++start;
73
+
74
+ while(start[0] != '\0') {
75
+ switch(state) {
76
+ case ST_OBJECT_PROP_PARSE:
77
+ if (start[0] == ':') {
78
+ state = ST_OBJECT_PROP_VAL;
79
+ } else if (!IS_WHITESPACE(start[0])) {
80
+ return ERR_PARSE_OBJECT;
81
+ }
82
+ break;
83
+ case ST_OBJECT_PROP_VAL:
84
+ return getValue(start, first, last, type);
85
+ }
86
+
87
+ ++start;
88
+ }
89
+
90
+ return ERR_PARSE_OBJECT;
91
+ }
92
+
93
+ unsigned int readNextString(char *start, char **first, char **last, unsigned int *hash) {
94
+ unsigned char state = ST_OBJECT_PROP_PARSE;
95
+ ++start;
96
+ while(start[0] != '\0') {
97
+ switch(state) {
98
+ case ST_OBJECT_PROP_PARSE:
99
+ if (start[0] == ':') {
100
+ state = ST_OBJECT_PROP_VAL;
101
+ } else if (!IS_WHITESPACE(start[0])) {
102
+ return ERR_PARSE_OBJECT;
103
+ }
104
+ break;
105
+ case ST_OBJECT_PROP_VAL:
106
+ return getStringAndHash(start, first, last, hash);
107
+ }
108
+
109
+ ++start;
110
+ }
111
+
112
+ return ERR_PARSE_OBJECT;
113
+ }
114
+
115
+ unsigned int readNextArrayValue(char *start, char **first, char **last, unsigned char *type) {
116
+ unsigned char state = ST_OBJECT_PROP_PARSE;
117
+ if (start[0] == '[') {
118
+ state = ST_OBJECT_PROP_VAL;
119
+ }
120
+ ++start;
121
+ while(start[0] != '\0') {
122
+ switch(state) {
123
+ case ST_OBJECT_PROP_PARSE:
124
+ if (start[0] == ',') {
125
+ state = ST_OBJECT_PROP_VAL;
126
+ } else if (start[0] == ']') {
127
+ *first = start;
128
+ *last = start;
129
+ return PARSE_END;
130
+ } else if (!IS_WHITESPACE(start[0])) {
131
+ return ERR_PARSE_OBJECT;
132
+ }
133
+ break;
134
+ case ST_OBJECT_PROP_VAL:
135
+ return getValue(start, first, last, type);
136
+ break;
137
+ }
138
+
139
+ ++start;
140
+ }
141
+
142
+ return ERR_PARSE_OBJECT;
143
+ }
144
+
145
+ static unsigned int getValue(char *start, char **first, char **last, unsigned char *type) {
146
+ *type = JSON_OBJECT;
147
+ while(start[0] != '\0') {
148
+ if (start[0] == '"' || start[0] == '\'') {
149
+ *type = JSON_STRING;
150
+ return getString(start, first, last);
151
+ } else if ((start[0] >= '0' && start[0] <= '9') || start[0] == '-') {
152
+ return getNumber(start, first, last, type);
153
+ } else if (start[0] == 'f') {
154
+ if (strncmp(start, "false", 5) == 0) {
155
+ *first = start;
156
+ *last = start + 4;
157
+ *type = JSON_BOOL;
158
+ return PARSE_OK;
159
+ } else {
160
+ return ERR_PARSE_STRING;
161
+ }
162
+ } else if (start[0] == 't') {
163
+ if (strncmp(start, "true", 4) == 0) {
164
+ *first = start;
165
+ *last = start + 3;
166
+ *type = JSON_BOOL;
167
+ return PARSE_OK;
168
+ } else {
169
+ return ERR_PARSE_STRING;
170
+ }
171
+ } else if (start[0] == 'N') {
172
+ if (strncmp(start, "NaN", 3) == 0) {
173
+ *first = start;
174
+ *last = start + 2;
175
+ *type = JSON_NIL;
176
+ return PARSE_OK;
177
+ } else {
178
+ return ERR_PARSE_STRING;
179
+ }
180
+ } else if (start[0] == '{') {
181
+ *type = JSON_OBJECT;
182
+ unsigned int result = getObject(start, first, last);
183
+ return result;
184
+ } else if (start[0] == '[') {
185
+ *type = JSON_ARRAY;
186
+ return getArray(start, first, last);
187
+ } else if (!IS_WHITESPACE(start[0])) {
188
+ return ERR_PARSE_STRING;
189
+ }
190
+
191
+ ++start;
192
+ }
193
+
194
+ return ERR_PARSE_VALUE;
195
+ }
196
+
197
+ static unsigned int getObject(char *start, char **first, char **last) {
198
+ unsigned char state = ST_OBJECT_SEEK;
199
+ char *dummy;
200
+ unsigned char dummyType;
201
+ unsigned int result;
202
+ while(start[0] != '\0') {
203
+ switch(state) {
204
+ case ST_OBJECT_SEEK:
205
+ if (start[0] == '{') {
206
+ state = ST_OBJECT_PROP_NAME;
207
+ *first = start;
208
+ } else if (!IS_WHITESPACE(start[0])) {
209
+ return ERR_PARSE_OBJECT;
210
+ }
211
+ break;
212
+ case ST_OBJECT_PROP_SEEK:
213
+ if (start[0] == '}') {
214
+ *last = start;
215
+ return PARSE_OK;
216
+ } else if (start[0] == ',') {
217
+ state = ST_OBJECT_PROP_NAME;
218
+ } else if (!IS_WHITESPACE(start[0])) {
219
+ return ERR_PARSE_OBJECT;
220
+ }
221
+ break;
222
+ case ST_OBJECT_PROP_NAME:
223
+ if (start[0] == '}') {
224
+ *last = start;
225
+ return PARSE_OK;
226
+ } else if (!IS_WHITESPACE(start[0])) {
227
+ result = getString(start, &dummy , &start);
228
+ if (result != PARSE_OK) {
229
+ return result;
230
+ } else {
231
+ state = ST_OBJECT_PROP_PARSE;
232
+ }
233
+ }
234
+ break;
235
+ case ST_OBJECT_PROP_PARSE:
236
+ if (start[0] == ':') {
237
+ state = ST_OBJECT_PROP_VAL;
238
+ } else if (!IS_WHITESPACE(start[0])) {
239
+ return ERR_PARSE_OBJECT;
240
+ }
241
+ break;
242
+ case ST_OBJECT_PROP_VAL:
243
+ result = getValue(start, &dummy , &start, &dummyType);
244
+ if (result != PARSE_OK) {
245
+ return result;
246
+ } else {
247
+ state = ST_OBJECT_PROP_SEEK;
248
+ }
249
+ break;
250
+ }
251
+
252
+ ++start;
253
+ }
254
+
255
+ return ERR_PARSE_OBJECT;
256
+ }
257
+
258
+ static unsigned int getArray(char *start, char** first, char **last) {
259
+ unsigned char state = ST_ARRAY_SEEK;
260
+ char *dummy;
261
+ unsigned char dummyType;
262
+ unsigned int result;
263
+ while(start[0] != '\0') {
264
+ switch(state) {
265
+ case ST_ARRAY_SEEK:
266
+ if (start[0] == '[') {
267
+ state = ST_ARRAY_VAL_PARSE;
268
+ *first = start;
269
+ } else if (!IS_WHITESPACE(start[0])) {
270
+ return ERR_PARSE_ARRAY;
271
+ }
272
+ break;
273
+ case ST_ARRAY_VAL_SEEK:
274
+ if (start[0] == ']') {
275
+ *last = start;
276
+ return PARSE_OK;
277
+ }
278
+ else if (start[0] == ',') {
279
+ state = ST_ARRAY_VAL_PARSE;
280
+ } else if (!IS_WHITESPACE(start[0])) {
281
+ return ERR_PARSE_ARRAY;
282
+ }
283
+ break;
284
+ case ST_ARRAY_VAL_PARSE:
285
+ if (start[0] == ']') {
286
+ *last = start;
287
+ return PARSE_OK;
288
+ } else if (!IS_WHITESPACE(start[0])) {
289
+ result = getValue(start, &dummy , &start, &dummyType);
290
+ if (result != PARSE_OK) {
291
+ return result;
292
+ } else {
293
+ state = ST_ARRAY_VAL_SEEK;
294
+ }
295
+ }
296
+ break;
297
+ }
298
+
299
+ ++start;
300
+ }
301
+
302
+ return ERR_PARSE_ARRAY;
303
+ }
304
+
305
+ static unsigned int getNumber(char *start, char **first, char **last, unsigned char *type) {
306
+ unsigned char state = ST_NUMBER_SEEK;
307
+ *type = JSON_INT;
308
+ while(start[0] != '\0') {
309
+ switch(state) {
310
+ case ST_NUMBER_SEEK:
311
+ if ((start[0] >= '0' && start[0] <= '9') || start[0] == '-') {
312
+ state = ST_NUMBER_INT;
313
+ *first = start;
314
+ } else if (!IS_WHITESPACE(start[0])) {
315
+ return ERR_PARSE_NUMBER;
316
+ }
317
+ break;
318
+ case ST_NUMBER_INT:
319
+ if (start[0] == '.') {
320
+ state = ST_NUMBER_FRA;
321
+ *type = JSON_DOUBLE;
322
+ } else if (start[0] < '0' || start[0] > '9') {
323
+ *last = start - 1;
324
+ return PARSE_OK;
325
+ }
326
+ break;
327
+ case ST_NUMBER_FRA:
328
+ if (start[0] == 'e' || start[0] == 'E') {
329
+ state = ST_NUMBER_EXP;
330
+ if (start[1] == '+' || start[1] == '-') {
331
+ ++ start;
332
+ }
333
+ } else if (start[0] < '0' || start[0] > '9') {
334
+ *last = start - 1;
335
+ return PARSE_OK;
336
+ }
337
+ break;
338
+ case ST_NUMBER_EXP:
339
+ if (start[0] < '0' || start[0] > '9') {
340
+ *last = start - 1;
341
+ return PARSE_OK;
342
+ }
343
+ break;
344
+ }
345
+
346
+ ++start;
347
+ }
348
+
349
+ return ERR_PARSE_NUMBER;
350
+ }
351
+
352
+ static unsigned int getString(char *start, char **first, char **last) {
353
+ unsigned char state = ST_STRING_SEEK;
354
+ char delimiter = '\0';
355
+ while(start[0] != '\0') {
356
+ switch (state) {
357
+ case ST_STRING_SEEK:
358
+ if (start[0] == '"' || start[0] == '\'') {
359
+ state = ST_STRING_PARSE;
360
+ delimiter = start[0];
361
+ *first = start + 1;
362
+ }
363
+ else if (!IS_WHITESPACE(start[0])) {
364
+ return ERR_PARSE_STRING;
365
+ }
366
+ break;
367
+ case ST_STRING_PARSE:
368
+ if (start[0] == delimiter) {
369
+ *last = start;
370
+ return PARSE_OK;
371
+ } else if (start[0] == '\\') {
372
+ state = ST_STRING_ESC;
373
+ }
374
+ break;
375
+ case ST_STRING_ESC:
376
+ state = ST_STRING_PARSE;
377
+ break;
378
+ }
379
+
380
+ ++ start;
381
+ }
382
+
383
+ return ERR_PARSE_STRING;
384
+ }
385
+
386
+ static unsigned int getStringAndHash(char *start, char **first, char **last, unsigned int *hash) {
387
+ unsigned char state = ST_STRING_SEEK;
388
+ char delimiter = '\0';
389
+ unsigned int currentHash = 5381;
390
+ while(start[0] != '\0') {
391
+ switch (state) {
392
+ case ST_STRING_SEEK:
393
+ if (start[0] == '"' || start[0] == '\'') {
394
+ state = ST_STRING_PARSE;
395
+ delimiter = start[0];
396
+ *first = start + 1;
397
+ }
398
+ else if (!IS_WHITESPACE(start[0])) {
399
+ return ERR_PARSE_STRING;
400
+ }
401
+ break;
402
+ case ST_STRING_PARSE:
403
+ if (start[0] == delimiter) {
404
+ *last = start;
405
+ *hash = currentHash;
406
+ return PARSE_OK;
407
+ } else if (start[0] == '\\') {
408
+ state = ST_STRING_ESC;
409
+ }
410
+
411
+ currentHash = ((currentHash << 5) + currentHash) + (*start);
412
+ break;
413
+ case ST_STRING_ESC:
414
+ state = ST_STRING_PARSE;
415
+ currentHash = ((currentHash << 5) + currentHash) + (*start);
416
+ break;
417
+ }
418
+
419
+ ++ start;
420
+ }
421
+
422
+ return ERR_PARSE_STRING;
423
+ }
data/src/rules/json.h ADDED
@@ -0,0 +1,24 @@
1
+
2
+
3
+ #define PARSE_OK 0
4
+ #define PARSE_END 100
5
+
6
+ #define JSON_STRING 0x01
7
+ #define JSON_INT 0x02
8
+ #define JSON_DOUBLE 0x03
9
+ #define JSON_BOOL 0x04
10
+ #define JSON_ARRAY 0x05
11
+ #define JSON_OBJECT 0x06
12
+ #define JSON_NIL 0x07
13
+ #define JSON_STATE_PROPERTY 0x08
14
+ #define JSON_EVENT_PROPERTY 0x09
15
+ #define JSON_STATE_IDIOM 0x0A
16
+ #define JSON_EVENT_IDIOM 0x0B
17
+
18
+ unsigned int readNextName(char *start, char **first, char **last, unsigned int *hash);
19
+ unsigned int readNextValue(char *start, char **first, char **last, unsigned char *type);
20
+ unsigned int readNextArrayValue(char *start, char **first, char **last, unsigned char *type);
21
+ unsigned int readNextString(char *start, char **first, char **last, unsigned int *hash);
22
+
23
+
24
+
data/src/rules/net.c ADDED
@@ -0,0 +1,2559 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ #include <time.h>
5
+ #include "net.h"
6
+ #include "rules.h"
7
+ #include "json.h"
8
+
9
+ #ifdef _WIN32
10
+ int asprintf(char** ret, char* format, ...){
11
+ va_list args;
12
+ *ret = NULL;
13
+ if (!format) return 0;
14
+ va_start(args, format);
15
+ int size = _vscprintf(format, args);
16
+ if (size == 0) {
17
+ *ret = (char*)malloc(1);
18
+ **ret = 0;
19
+ }
20
+ else {
21
+ size++; //for null
22
+ *ret = (char*)malloc(size + 2);
23
+ if (*ret) {
24
+ _vsnprintf(*ret, size, format, args);
25
+ }
26
+ else {
27
+ return -1;
28
+ }
29
+ }
30
+
31
+ va_end(args);
32
+ return size;
33
+ }
34
+ #endif
35
+
36
+ static unsigned int createIdiom(ruleset *tree, jsonValue *newValue, char **idiomString) {
37
+ char *rightProperty;
38
+ char *rightAlias;
39
+ char *valueString;
40
+ idiom *newIdiom;
41
+ switch (newValue->type) {
42
+ case JSON_EVENT_PROPERTY:
43
+ rightProperty = &tree->stringPool[newValue->value.property.nameOffset];
44
+ rightAlias = &tree->stringPool[newValue->value.property.idOffset];
45
+ if (asprintf(idiomString, "frame[\"%s\"][\"%s\"]", rightAlias, rightProperty) == -1) {
46
+ return ERR_OUT_OF_MEMORY;
47
+ }
48
+ break;
49
+ case JSON_EVENT_IDIOM:
50
+ newIdiom = &tree->idiomPool[newValue->value.idiomOffset];
51
+ char *op = "";
52
+ switch (newIdiom->operator) {
53
+ case OP_ADD:
54
+ op = "+";
55
+ break;
56
+ case OP_SUB:
57
+ op = "-";
58
+ break;
59
+ case OP_MUL:
60
+ op = "*";
61
+ break;
62
+ case OP_DIV:
63
+ op = "/";
64
+ break;
65
+ }
66
+
67
+ char *rightIdiomString = NULL;
68
+ unsigned int result = createIdiom(tree, &newIdiom->right, &rightIdiomString);
69
+ if (result != RULES_OK) {
70
+ return result;
71
+ }
72
+
73
+ char *leftIdiomString = NULL;
74
+ result = createIdiom(tree, &newIdiom->left, &leftIdiomString);
75
+ if (result != RULES_OK) {
76
+ return result;
77
+ }
78
+
79
+ if (asprintf(idiomString, "(%s %s %s)", leftIdiomString, op, rightIdiomString) == -1) {
80
+ return ERR_OUT_OF_MEMORY;
81
+ }
82
+
83
+ free(rightIdiomString);
84
+ free(leftIdiomString);
85
+ break;
86
+ case JSON_STRING:
87
+ valueString = &tree->stringPool[newValue->value.stringOffset];
88
+ if (asprintf(idiomString, "%s", valueString) == -1) {
89
+ return ERR_OUT_OF_MEMORY;
90
+ }
91
+ break;
92
+ case JSON_INT:
93
+ if (asprintf(idiomString, "%ld", newValue->value.i) == -1) {
94
+ return ERR_OUT_OF_MEMORY;
95
+ }
96
+ break;
97
+ case JSON_DOUBLE:
98
+ if (asprintf(idiomString, "%g", newValue->value.d) == -1) {
99
+ return ERR_OUT_OF_MEMORY;
100
+ }
101
+ case JSON_BOOL:
102
+ if (newValue->value.b == 0) {
103
+ if (asprintf(idiomString, "false") == -1) {
104
+ return ERR_OUT_OF_MEMORY;
105
+ }
106
+ }
107
+ else {
108
+ if (asprintf(idiomString, "true") == -1) {
109
+ return ERR_OUT_OF_MEMORY;
110
+ }
111
+ }
112
+ break;
113
+ }
114
+
115
+ return RULES_OK;
116
+ }
117
+
118
+ static unsigned int createTest(ruleset *tree, expression *expr, char **test, char **primaryKey, char **primaryFrameKey) {
119
+ char *comp = NULL;
120
+ char *compStack[32];
121
+ unsigned char compTop = 0;
122
+ unsigned char first = 1;
123
+ unsigned char setPrimaryKey = 0;
124
+ *primaryKey = NULL;
125
+ *primaryFrameKey = NULL;
126
+ if (asprintf(test, "") == -1) {
127
+ return ERR_OUT_OF_MEMORY;
128
+ }
129
+
130
+ for (unsigned short i = 0; i < expr->termsLength; ++i) {
131
+ unsigned int currentNodeOffset = tree->nextPool[expr->t.termsOffset + i];
132
+ node *currentNode = &tree->nodePool[currentNodeOffset];
133
+ if (currentNode->value.a.operator == OP_AND) {
134
+ char *oldTest = *test;
135
+ if (first) {
136
+ setPrimaryKey = 1;
137
+ if (asprintf(test, "%s(", *test) == -1) {
138
+ return ERR_OUT_OF_MEMORY;
139
+ }
140
+ } else {
141
+ if (asprintf(test, "%s %s (", *test, comp) == -1) {
142
+ return ERR_OUT_OF_MEMORY;
143
+ }
144
+ }
145
+ free(oldTest);
146
+
147
+ compStack[compTop] = comp;
148
+ ++compTop;
149
+ comp = "and";
150
+ first = 1;
151
+ } else if (currentNode->value.a.operator == OP_OR) {
152
+ char *oldTest = *test;
153
+ if (first) {
154
+ if (asprintf(test, "%s(", *test) == -1) {
155
+ return ERR_OUT_OF_MEMORY;
156
+ }
157
+ } else {
158
+ setPrimaryKey = 0;
159
+ if (asprintf(test, "%s %s (", *test, comp) == -1) {
160
+ return ERR_OUT_OF_MEMORY;
161
+ }
162
+ }
163
+ free(oldTest);
164
+
165
+ compStack[compTop] = comp;
166
+ ++compTop;
167
+ comp = "or";
168
+ first = 1;
169
+ } else if (currentNode->value.a.operator == OP_END) {
170
+ --compTop;
171
+ comp = compStack[compTop];
172
+
173
+ char *oldTest = *test;
174
+ if (asprintf(test, "%s)", *test) == -1) {
175
+ return ERR_OUT_OF_MEMORY;
176
+ }
177
+ free(oldTest);
178
+ } else {
179
+ char *leftProperty = &tree->stringPool[currentNode->nameOffset];
180
+ char *op = "";
181
+ switch (currentNode->value.a.operator) {
182
+ case OP_LT:
183
+ op = "<";
184
+ break;
185
+ case OP_LTE:
186
+ op = "<=";
187
+ break;
188
+ case OP_GT:
189
+ op = ">";
190
+ break;
191
+ case OP_GTE:
192
+ op = ">=";
193
+ break;
194
+ case OP_EQ:
195
+ op = "==";
196
+ break;
197
+ case OP_NEQ:
198
+ op = "~=";
199
+ break;
200
+ }
201
+
202
+ char *idiomString = NULL;
203
+ unsigned int result = createIdiom(tree, &currentNode->value.a.right, &idiomString);
204
+ if (result != RULES_OK) {
205
+ return result;
206
+ }
207
+
208
+ char *oldTest = *test;
209
+ if (first) {
210
+ if (asprintf(test, "%smessage[\"%s\"] %s %s", *test, leftProperty, op, idiomString) == -1) {
211
+ return ERR_OUT_OF_MEMORY;
212
+ }
213
+ first = 0;
214
+ } else {
215
+ if (asprintf(test, "%s %s message[\"%s\"] %s %s", *test, comp, leftProperty, op, idiomString) == -1) {
216
+ return ERR_OUT_OF_MEMORY;
217
+ }
218
+ }
219
+
220
+ if (setPrimaryKey && currentNode->value.a.operator == OP_EQ) {
221
+ if (*primaryKey == NULL) {
222
+ if (asprintf(primaryKey,
223
+ " if not message[\"%s\"] then\n"
224
+ " return \"\"\n"
225
+ " else\n"
226
+ " result = result .. message[\"%s\"]\n"
227
+ " end\n",
228
+ leftProperty,
229
+ leftProperty) == -1) {
230
+ return ERR_OUT_OF_MEMORY;
231
+ }
232
+
233
+ if (asprintf(primaryFrameKey,
234
+ " if not %s then\n"
235
+ " return \"\"\n"
236
+ " else\n"
237
+ " result = result .. %s\n"
238
+ " end\n",
239
+ idiomString,
240
+ idiomString) == -1) {
241
+ return ERR_OUT_OF_MEMORY;
242
+ }
243
+ }
244
+ else {
245
+ char *oldKey = *primaryKey;
246
+ if (asprintf(primaryKey,
247
+ "%s if not message[\"%s\"] then\n"
248
+ " return \"\"\n"
249
+ " else\n"
250
+ " result = result .. message[\"%s\"]\n"
251
+ " end\n",
252
+ *primaryKey,
253
+ leftProperty,
254
+ leftProperty) == -1) {
255
+ return ERR_OUT_OF_MEMORY;
256
+ }
257
+ free(oldKey);
258
+ oldKey = *primaryFrameKey;
259
+ if (asprintf(primaryFrameKey,
260
+ "%s if not %s then\n"
261
+ " return \"\"\n"
262
+ " else\n"
263
+ " result = result .. %s\n"
264
+ " end\n",
265
+ *primaryFrameKey,
266
+ idiomString,
267
+ idiomString) == -1) {
268
+ return ERR_OUT_OF_MEMORY;
269
+ }
270
+ free(oldKey);
271
+ }
272
+ }
273
+
274
+ free(idiomString);
275
+ free(oldTest);
276
+ }
277
+ }
278
+
279
+ if (first) {
280
+ free(*test);
281
+ if (asprintf(test, "1") == -1) {
282
+ return ERR_OUT_OF_MEMORY;
283
+ }
284
+ }
285
+
286
+ if (*primaryKey == NULL) {
287
+ if (asprintf(primaryKey, "") == -1) {
288
+ return ERR_OUT_OF_MEMORY;
289
+ }
290
+ if (asprintf(primaryFrameKey, "") == -1) {
291
+ return ERR_OUT_OF_MEMORY;
292
+ }
293
+ }
294
+ return RULES_OK;
295
+ }
296
+
297
+ static unsigned int loadCommands(ruleset *tree, binding *rulesBinding) {
298
+ redisContext *reContext = rulesBinding->reContext;
299
+ redisReply *reply;
300
+ char *name = &tree->stringPool[tree->nameOffset];
301
+ int nameLength = strlen(name);
302
+ #ifdef _WIN32
303
+ char *actionKey = (char *)_alloca(sizeof(char)*(nameLength + 3));
304
+ sprintf_s(actionKey, nameLength + 3, "%s!a", name);
305
+ #else
306
+ char actionKey[nameLength + 3];
307
+ snprintf(actionKey, nameLength + 3, "%s!a", name);
308
+ #endif
309
+ char *lua = NULL;
310
+ char *peekActionLua = NULL;
311
+ char *addMessageLua = NULL;
312
+ char *oldLua;
313
+ if (asprintf(&peekActionLua, "") == -1) {
314
+ return ERR_OUT_OF_MEMORY;
315
+ }
316
+
317
+ if (asprintf(&lua, "") == -1) {
318
+ return ERR_OUT_OF_MEMORY;
319
+ }
320
+
321
+ if (asprintf(&addMessageLua, "") == -1) {
322
+ return ERR_OUT_OF_MEMORY;
323
+ }
324
+
325
+ for (unsigned int i = 0; i < tree->nodeOffset; ++i) {
326
+ char *oldPeekActionLua;
327
+ node *currentNode = &tree->nodePool[i];
328
+
329
+ if (currentNode->type == NODE_ACTION) {
330
+ char *packFrameLua = NULL;
331
+ char *unpackFrameLua = NULL;
332
+ char *oldPackFrameLua = NULL;
333
+ char *oldUnpackFrameLua = NULL;
334
+ char *oldAddMessageLua = NULL;
335
+ char *actionName = &tree->stringPool[currentNode->nameOffset];
336
+ char *actionLastName = strchr(actionName, '!');
337
+ #ifdef _WIN32
338
+ char *actionAlias = (char *)_alloca(sizeof(char)*(actionLastName - actionName + 1));
339
+ #else
340
+ char actionAlias[actionLastName - actionName + 1];
341
+ #endif
342
+
343
+ strncpy(actionAlias, actionName, actionLastName - actionName);
344
+ actionAlias[actionLastName - actionName] = '\0';
345
+
346
+ for (unsigned int ii = 0; ii < currentNode->value.c.joinsLength; ++ii) {
347
+ unsigned int currentJoinOffset = tree->nextPool[currentNode->value.c.joinsOffset + ii];
348
+ join *currentJoin = &tree->joinPool[currentJoinOffset];
349
+
350
+ oldPeekActionLua = peekActionLua;
351
+ if (asprintf(&peekActionLua,
352
+ "%sif input_keys[\"%s!%d!r!\"] then\n"
353
+ "local context = {}\n"
354
+ "context_directory[\"%s!%d!r!\"] = context\n"
355
+ "reviewers = {}\n"
356
+ "context[\"reviewers\"] = reviewers\n"
357
+ "keys = {}\n"
358
+ "context[\"keys\"] = keys\n"
359
+ "primary_frame_keys = {}\n"
360
+ "context[\"primary_frame_keys\"] = primary_frame_keys\n",
361
+ peekActionLua,
362
+ actionName,
363
+ ii,
364
+ actionName,
365
+ ii) == -1) {
366
+ return ERR_OUT_OF_MEMORY;
367
+ }
368
+ free(oldPeekActionLua);
369
+
370
+ oldLua = lua;
371
+ if (asprintf(&lua,
372
+ "%stoggle = false\n"
373
+ "context = {}\n"
374
+ "reviewers = {}\n"
375
+ "context[\"reviewers\"] = reviewers\n"
376
+ "frame_packers = {}\n"
377
+ "context[\"frame_packers\"] = frame_packers\n"
378
+ "frame_unpackers = {}\n"
379
+ "context[\"frame_unpackers\"] = frame_unpackers\n"
380
+ "primary_message_keys = {}\n"
381
+ "context[\"primary_message_keys\"] = primary_message_keys\n"
382
+ "primary_frame_keys = {}\n"
383
+ "context[\"primary_frame_keys\"] = primary_frame_keys\n"
384
+ "keys = {}\n"
385
+ "context[\"keys\"] = keys\n"
386
+ "inverse_directory = {}\n"
387
+ "context[\"inverse_directory\"] = inverse_directory\n"
388
+ "directory = {[\"0\"] = 1}\n"
389
+ "context[\"directory\"] = directory\n"
390
+ "context[\"results_key\"] = \"%s!%d!r!\" .. sid\n"
391
+ "context[\"expressions_count\"] = %d\n",
392
+ lua,
393
+ actionName,
394
+ ii,
395
+ currentJoin->expressionsLength) == -1) {
396
+ return ERR_OUT_OF_MEMORY;
397
+ }
398
+ free(oldLua);
399
+
400
+ for (unsigned int iii = 0; iii < currentJoin->expressionsLength; ++iii) {
401
+ unsigned int expressionOffset = tree->nextPool[currentJoin->expressionsOffset + iii];
402
+ expression *expr = &tree->expressionPool[expressionOffset];
403
+ char *currentAlias = &tree->stringPool[expr->aliasOffset];
404
+ char *currentKey = &tree->stringPool[expr->nameOffset];
405
+ char *nextKeyTest;
406
+ if (iii == (currentJoin->expressionsLength - 1)) {
407
+ if (asprintf(&nextKeyTest, "") == -1) {
408
+ return ERR_OUT_OF_MEMORY;
409
+ }
410
+ } else {
411
+ unsigned int nextExpressionOffset = tree->nextPool[currentJoin->expressionsOffset + iii + 1];
412
+ expression *nextExpr = &tree->expressionPool[nextExpressionOffset];
413
+ if (asprintf(&nextKeyTest,
414
+ "or input_keys[\"%s\"]",
415
+ &tree->stringPool[nextExpr->nameOffset]) == -1) {
416
+ return ERR_OUT_OF_MEMORY;
417
+ }
418
+ }
419
+
420
+ if (iii == 0) {
421
+ oldAddMessageLua = addMessageLua;
422
+ if (asprintf(&addMessageLua,
423
+ "%sif input_keys[\"%s\"] then\n"
424
+ " primary_message_keys[\"%s\"] = function(message)\n"
425
+ " return \"\"\n"
426
+ " end\n"
427
+ "end\n",
428
+ addMessageLua,
429
+ currentKey,
430
+ currentKey) == -1) {
431
+ return ERR_OUT_OF_MEMORY;
432
+ }
433
+ free(oldAddMessageLua);
434
+
435
+ if (expr->not) {
436
+ if (asprintf(&packFrameLua,
437
+ " result[1] = \"$n\"\n") == -1) {
438
+ return ERR_OUT_OF_MEMORY;
439
+ }
440
+
441
+ if (asprintf(&unpackFrameLua,
442
+ " result[\"%s\"] = \"$n\"\n",
443
+ currentAlias) == -1) {
444
+ return ERR_OUT_OF_MEMORY;
445
+ }
446
+
447
+ oldLua = lua;
448
+ if (asprintf(&lua,
449
+ "%sif input_keys[\"%s\"] %s then\n"
450
+ " toggle = true\n"
451
+ " context_directory[\"%s\"] = context\n"
452
+ " keys[1] = \"%s\"\n"
453
+ " inverse_directory[1] = true\n"
454
+ " directory[\"%s\"] = 1\n"
455
+ " reviewers[1] = function(message, frame, index)\n"
456
+ " if not message then\n"
457
+ " frame[\"%s\"] = \"$n\"\n"
458
+ " return true\n"
459
+ " end\n"
460
+ " return false\n"
461
+ " end\n"
462
+ " frame_packers[1] = function(frame, full_encode)\n"
463
+ " local result = {}\n%s"
464
+ " return cmsgpack.pack(result)\n"
465
+ " end\n"
466
+ " frame_unpackers[1] = function(packed_frame)\n"
467
+ " local frame = cmsgpack.unpack(packed_frame)\n"
468
+ " local result = {}\n%s"
469
+ " return result\n"
470
+ " end\n"
471
+ " primary_message_keys[1] = function(message)\n"
472
+ " return \"\"\n"
473
+ " end\n"
474
+ " primary_frame_keys[1] = function(frame)\n"
475
+ " return \"\"\n"
476
+ " end\n"
477
+ "end\n",
478
+ lua,
479
+ currentKey,
480
+ nextKeyTest,
481
+ currentKey,
482
+ currentKey,
483
+ currentKey,
484
+ currentAlias,
485
+ packFrameLua,
486
+ unpackFrameLua) == -1) {
487
+ return ERR_OUT_OF_MEMORY;
488
+ }
489
+ free(oldLua);
490
+
491
+ oldPeekActionLua = peekActionLua;
492
+ if (asprintf(&peekActionLua,
493
+ "%skeys[1] = \"%s\"\n"
494
+ "reviewers[1] = function(message, frame, index)\n"
495
+ " if not message then\n"
496
+ " return true\n"
497
+ " end\n"
498
+ " return false\n"
499
+ "end\n"
500
+ "primary_frame_keys[1] = function(frame)\n"
501
+ " return \"\"\n"
502
+ "end\n",
503
+ peekActionLua,
504
+ currentKey) == -1) {
505
+ return ERR_OUT_OF_MEMORY;
506
+ }
507
+ free(oldPeekActionLua);
508
+ // not (expr->not)
509
+ } else {
510
+ if (asprintf(&packFrameLua,
511
+ " message = frame[\"%s\"]\n"
512
+ " if full_encode and not message[\"$f\"] then\n"
513
+ " result[1] = message\n"
514
+ " else\n"
515
+ " result[1] = message[\"id\"]\n"
516
+ " end\n",
517
+ currentAlias) == -1) {
518
+ return ERR_OUT_OF_MEMORY;
519
+ }
520
+
521
+ if (asprintf(&unpackFrameLua,
522
+ " message = fetch_message(frame[1])\n"
523
+ " if not message then\n"
524
+ " return nil\n"
525
+ " end\n"
526
+ " result[\"%s\"] = message\n",
527
+ currentAlias) == -1) {
528
+ return ERR_OUT_OF_MEMORY;
529
+ }
530
+
531
+
532
+
533
+ oldLua = lua;
534
+ if (asprintf(&lua,
535
+ "%sif input_keys[\"%s\"] %s then\n"
536
+ " toggle = true\n"
537
+ " context_directory[\"%s\"] = context\n"
538
+ " keys[1] = \"%s\"\n"
539
+ " inverse_directory[1] = true\n"
540
+ " directory[\"%s\"] = 1\n"
541
+ " reviewers[1] = function(message, frame, index)\n"
542
+ " if message then\n"
543
+ " frame[\"%s\"] = message\n"
544
+ " return true\n"
545
+ " end\n"
546
+ " return false\n"
547
+ " end\n"
548
+ " frame_packers[1] = function(frame, full_encode)\n"
549
+ " local result = {}\n"
550
+ " local message\n%s"
551
+ " return cmsgpack.pack(result)\n"
552
+ " end\n"
553
+ " frame_unpackers[1] = function(packed_frame)\n"
554
+ " local frame = cmsgpack.unpack(packed_frame)\n"
555
+ " local result = {}\n"
556
+ " local message\n%s"
557
+ " return result\n"
558
+ " end\n"
559
+ " primary_message_keys[1] = function(message)\n"
560
+ " return \"\"\n"
561
+ " end\n"
562
+ " primary_frame_keys[1] = function(frame)\n"
563
+ " return \"\"\n"
564
+ " end\n"
565
+ "end\n",
566
+ lua,
567
+ currentKey,
568
+ nextKeyTest,
569
+ currentKey,
570
+ currentKey,
571
+ currentKey,
572
+ currentAlias,
573
+ packFrameLua,
574
+ unpackFrameLua) == -1) {
575
+ return ERR_OUT_OF_MEMORY;
576
+ }
577
+ free(oldLua);
578
+ }
579
+ // not (iii == 0)
580
+ } else {
581
+ char *test = NULL;
582
+ char *primaryKeyLua = NULL;
583
+ char *primaryFrameKeyLua = NULL;
584
+ unsigned int result = createTest(tree, expr, &test, &primaryKeyLua, &primaryFrameKeyLua);
585
+ if (result != RULES_OK) {
586
+ return result;
587
+ }
588
+
589
+ oldAddMessageLua = addMessageLua;
590
+ if (asprintf(&addMessageLua,
591
+ "%sif input_keys[\"%s\"] then\n"
592
+ " primary_message_keys[\"%s\"] = function(message)\n"
593
+ " local result = \"\"\n%s"
594
+ " return result\n"
595
+ " end\n"
596
+ "end\n",
597
+ addMessageLua,
598
+ currentKey,
599
+ currentKey,
600
+ primaryKeyLua) == -1) {
601
+ return ERR_OUT_OF_MEMORY;
602
+ }
603
+ free(oldAddMessageLua);
604
+
605
+ if (expr->not) {
606
+ oldPackFrameLua = packFrameLua;
607
+ if (asprintf(&packFrameLua,
608
+ "%s result[%d] = \"$n\"\n",
609
+ packFrameLua,
610
+ iii + 1) == -1) {
611
+ return ERR_OUT_OF_MEMORY;
612
+ }
613
+ free(oldPackFrameLua);
614
+
615
+ oldUnpackFrameLua = unpackFrameLua;
616
+ if (asprintf(&unpackFrameLua,
617
+ "%s result[\"%s\"] = \"$n\"\n",
618
+ unpackFrameLua,
619
+ currentAlias) == -1) {
620
+ return ERR_OUT_OF_MEMORY;
621
+ }
622
+ free(oldUnpackFrameLua);
623
+
624
+ oldLua = lua;
625
+ if (asprintf(&lua,
626
+ "%sif toggle %s then\n"
627
+ " toggle = true\n"
628
+ " context_directory[\"%s\"] = context\n"
629
+ " keys[%d] = \"%s\"\n"
630
+ " inverse_directory[%d] = true\n"
631
+ " directory[\"%s\"] = %d\n"
632
+ " reviewers[%d] = function(message, frame, index)\n"
633
+ " if not message or not (%s) then\n"
634
+ " frame[\"%s\"] = \"$n\"\n"
635
+ " return true\n"
636
+ " end\n"
637
+ " return false\n"
638
+ " end\n"
639
+ " frame_packers[%d] = function(frame, full_encode)\n"
640
+ " local result = {}\n"
641
+ " local message\n%s"
642
+ " return cmsgpack.pack(result)\n"
643
+ " end\n"
644
+ " frame_unpackers[%d] = function(packed_frame)\n"
645
+ " local frame = cmsgpack.unpack(packed_frame)\n"
646
+ " local result = {}\n"
647
+ " local message\n%s"
648
+ " return result\n"
649
+ " end\n"
650
+ " primary_message_keys[%d] = function(message)\n"
651
+ " local result = \"\"\n%s"
652
+ " return result\n"
653
+ " end\n"
654
+ " primary_frame_keys[%d] = function(frame)\n"
655
+ " local result = \"\"\n%s"
656
+ " return result\n"
657
+ " end\n"
658
+ "end\n",
659
+ lua,
660
+ nextKeyTest,
661
+ currentKey,
662
+ iii + 1,
663
+ currentKey,
664
+ iii + 1,
665
+ currentKey,
666
+ iii + 1,
667
+ iii + 1,
668
+ test,
669
+ currentAlias,
670
+ iii + 1,
671
+ packFrameLua,
672
+ iii + 1,
673
+ unpackFrameLua,
674
+ iii + 1,
675
+ primaryKeyLua,
676
+ iii + 1,
677
+ primaryFrameKeyLua) == -1) {
678
+ return ERR_OUT_OF_MEMORY;
679
+ }
680
+ free(oldLua);
681
+
682
+ oldPeekActionLua = peekActionLua;
683
+ if (asprintf(&peekActionLua,
684
+ "%skeys[%d] = \"%s\"\n"
685
+ "reviewers[%d] = function(message, frame, index)\n"
686
+ " if not message or not (%s) then\n"
687
+ " return true\n"
688
+ " end\n"
689
+ " return false\n"
690
+ "end\n"
691
+ "primary_frame_keys[%d] = function(frame)\n"
692
+ " local result = \"\"\n%s"
693
+ " return result\n"
694
+ "end\n",
695
+ peekActionLua,
696
+ iii + 1,
697
+ currentKey,
698
+ iii + 1,
699
+ test,
700
+ iii + 1,
701
+ primaryFrameKeyLua) == -1) {
702
+ return ERR_OUT_OF_MEMORY;
703
+ }
704
+ free(oldPeekActionLua);
705
+
706
+ // not (expr->not)
707
+ } else {
708
+ oldPackFrameLua = packFrameLua;
709
+ if (asprintf(&packFrameLua,
710
+ "%s message = frame[\"%s\"]\n"
711
+ " if full_encode and not message[\"$f\"] then\n"
712
+ " result[%d] = message\n"
713
+ " else\n"
714
+ " result[%d] = message[\"id\"]\n"
715
+ " end\n",
716
+ packFrameLua,
717
+ currentAlias,
718
+ iii + 1,
719
+ iii + 1) == -1) {
720
+ return ERR_OUT_OF_MEMORY;
721
+ }
722
+ free(oldPackFrameLua);
723
+
724
+ oldUnpackFrameLua = unpackFrameLua;
725
+ if (asprintf(&unpackFrameLua,
726
+ "%s message = fetch_message(frame[%d])\n"
727
+ " if not message then\n"
728
+ " return nil\n"
729
+ " end\n"
730
+ " result[\"%s\"] = message\n",
731
+ unpackFrameLua,
732
+ iii + 1,
733
+ currentAlias) == -1) {
734
+ return ERR_OUT_OF_MEMORY;
735
+ }
736
+ free(oldUnpackFrameLua);
737
+
738
+ oldLua = lua;
739
+ if (asprintf(&lua,
740
+ "%sif toggle %s then\n"
741
+ " toggle = true\n"
742
+ " context_directory[\"%s\"] = context\n"
743
+ " keys[%d] = \"%s\"\n"
744
+ " directory[\"%s\"] = %d\n"
745
+ " reviewers[%d] = function(message, frame, index)\n"
746
+ " if message and %s then\n"
747
+ " frame[\"%s\"] = message\n"
748
+ " return true\n"
749
+ " end\n"
750
+ " return false\n"
751
+ " end\n"
752
+ " frame_packers[%d] = function(frame, full_encode)\n"
753
+ " local result = {}\n"
754
+ " local message\n%s"
755
+ " return cmsgpack.pack(result)\n"
756
+ " end\n"
757
+ " frame_unpackers[%d] = function(packed_frame)\n"
758
+ " local frame = cmsgpack.unpack(packed_frame)\n"
759
+ " local result = {}\n"
760
+ " local message\n%s"
761
+ " return result\n"
762
+ " end\n"
763
+ " primary_message_keys[%d] = function(message)\n"
764
+ " local result = \"\"\n%s"
765
+ " return result\n"
766
+ " end\n"
767
+ " primary_frame_keys[%d] = function(frame)\n"
768
+ " local result = \"\"\n%s"
769
+ " return result\n"
770
+ " end\n"
771
+ "end\n",
772
+ lua,
773
+ nextKeyTest,
774
+ currentKey,
775
+ iii + 1,
776
+ currentKey,
777
+ currentKey,
778
+ iii + 1,
779
+ iii + 1,
780
+ test,
781
+ currentAlias,
782
+ iii + 1,
783
+ packFrameLua,
784
+ iii + 1,
785
+ unpackFrameLua,
786
+ iii + 1,
787
+ primaryKeyLua,
788
+ iii + 1,
789
+ primaryFrameKeyLua) == -1) {
790
+ return ERR_OUT_OF_MEMORY;
791
+ }
792
+ free(oldLua);
793
+ // done not (expr->not)
794
+ }
795
+ free(nextKeyTest);
796
+ free(test);
797
+ free(primaryKeyLua);
798
+ free(primaryFrameKeyLua);
799
+ // done not (iii == 0)
800
+ }
801
+ }
802
+
803
+ oldPeekActionLua = peekActionLua;
804
+ if (asprintf(&peekActionLua,
805
+ "%scontext[\"frame_restore\"] = function(frame, result)\n"
806
+ " local message\n%s"
807
+ " return true\n"
808
+ "end\n"
809
+ "return context\n"
810
+ "end\n",
811
+ peekActionLua,
812
+ unpackFrameLua) == -1) {
813
+ return ERR_OUT_OF_MEMORY;
814
+ }
815
+ free(oldPeekActionLua);
816
+
817
+ if (currentNode->value.c.span > 0) {
818
+ oldLua = lua;
819
+ if (asprintf(&lua,
820
+ "%sif toggle then\n"
821
+ " context[\"process_key\"] = process_key_with_span\n"
822
+ " context[\"process_key_count\"] = %d\n"
823
+ "end\n",
824
+ lua,
825
+ currentNode->value.c.span) == -1) {
826
+ return ERR_OUT_OF_MEMORY;
827
+ }
828
+ free(oldLua);
829
+
830
+ } else if (currentNode->value.c.cap > 0) {
831
+ oldLua = lua;
832
+ if (asprintf(&lua,
833
+ "%sif toggle then\n"
834
+ " context[\"process_key\"] = process_key_with_cap\n"
835
+ " context[\"process_key_count\"] = %d\n"
836
+ "end\n",
837
+ lua,
838
+ currentNode->value.c.cap) == -1) {
839
+ return ERR_OUT_OF_MEMORY;
840
+ }
841
+ free(oldLua);
842
+
843
+ } else {
844
+ oldLua = lua;
845
+ if (asprintf(&lua,
846
+ "%sif toggle then\n"
847
+ " context[\"process_key\"] = process_key_with_window\n"
848
+ " context[\"process_key_count\"] = %d\n"
849
+ "end\n",
850
+ lua,
851
+ currentNode->value.c.count) == -1) {
852
+ return ERR_OUT_OF_MEMORY;
853
+ }
854
+ free(oldLua);
855
+ }
856
+ }
857
+
858
+ free(unpackFrameLua);
859
+ free(packFrameLua);
860
+ }
861
+ }
862
+
863
+ oldLua = lua;
864
+ if (asprintf(&lua,
865
+ "local sid = ARGV[1]\n"
866
+ "local mid = ARGV[2]\n"
867
+ "local score = tonumber(ARGV[3])\n"
868
+ "local assert_fact = tonumber(ARGV[4])\n"
869
+ "local keys_count = tonumber(ARGV[5])\n"
870
+ "local events_hashset = \"%s!e!\" .. sid\n"
871
+ "local facts_hashset = \"%s!f!\" .. sid\n"
872
+ "local visited_hashset = \"%s!v!\" .. sid\n"
873
+ "local actions_key = \"%s!a\"\n"
874
+ "local state_key = \"%s!s\"\n"
875
+ "local queue_action = false\n"
876
+ "local facts_message_cache = {}\n"
877
+ "local events_message_cache = {}\n"
878
+ "local facts_mids_cache = {}\n"
879
+ "local events_mids_cache = {}\n"
880
+ "local context_directory = {}\n"
881
+ "local input_keys = {}\n"
882
+ "local candidate = nil\n"
883
+ "local toggle\n"
884
+ "local expressions_count\n"
885
+ "local results\n"
886
+ "local unpacked_results\n"
887
+ "local context\n"
888
+ "local keys\n"
889
+ "local reviewers\n"
890
+ "local frame_packers\n"
891
+ "local frame_unpackers\n"
892
+ "local primary_message_keys\n"
893
+ "local primary_frame_keys\n"
894
+ "local directory\n"
895
+ "local results_key\n"
896
+ "local inverse_directory\n"
897
+ "local key\n"
898
+ "local cleanup_mids = function(index, frame, events_key, messages_key, mids_cache, message_cache)\n"
899
+ " local event_mids = mids_cache[events_key]\n"
900
+ " local primary_key = primary_frame_keys[index](frame)\n"
901
+ " local new_mids = event_mids[primary_key]\n"
902
+ " local result_mids = {}\n"
903
+ " for i = 1, #new_mids, 1 do\n"
904
+ " local new_mid = new_mids[i]\n"
905
+ " if message_cache[new_mid] ~= false then\n"
906
+ " table.insert(result_mids, new_mid)\n"
907
+ " end\n"
908
+ " end\n"
909
+ " event_mids[primary_key] = result_mids\n"
910
+ " redis.call(\"del\", events_key .. \"!m!\" .. primary_key)\n"
911
+ " for i = 1, #result_mids, 1 do\n"
912
+ " redis.call(\"rpush\", events_key .. \"!m!\" .. primary_key, result_mids[i])\n"
913
+ " end\n"
914
+ "end\n"
915
+ "local get_mids = function(index, frame, events_key, messages_key, mids_cache, message_cache)\n"
916
+ " local event_mids = mids_cache[events_key]\n"
917
+ " local primary_key = primary_frame_keys[index](frame)\n"
918
+ " local new_mids = nil\n"
919
+ " if not event_mids then\n"
920
+ " event_mids = {}\n"
921
+ " mids_cache[events_key] = event_mids\n"
922
+ " else\n"
923
+ " new_mids = event_mids[primary_key]\n"
924
+ " end\n"
925
+ " if not new_mids then\n"
926
+ " new_mids = redis.call(\"lrange\", events_key .. \"!m!\" .. primary_key, 0, -1)\n"
927
+ " event_mids[primary_key] = new_mids\n"
928
+ " end\n"
929
+ " return new_mids\n"
930
+ "end\n"
931
+ "local get_message = function(new_mid, messages_key, message_cache)\n"
932
+ " local message = false\n"
933
+ " new_mid = tostring(new_mid)\n"
934
+ " if message_cache[new_mid] ~= nil then\n"
935
+ " message = message_cache[new_mid]\n"
936
+ " else\n"
937
+ " local packed_message = redis.call(\"hget\", messages_key, new_mid)\n"
938
+ " if packed_message then\n"
939
+ " message = cmsgpack.unpack(packed_message)\n"
940
+ " end\n"
941
+ " message_cache[new_mid] = message\n"
942
+ " end\n"
943
+ " return message\n"
944
+ "end\n"
945
+ "local fetch_message = function(new_mid)\n"
946
+ " local message = get_message(new_mid, events_hashset, events_message_cache)\n"
947
+ " if not message then\n"
948
+ " message = get_message(new_mid, facts_hashset, facts_message_cache)\n"
949
+ " end\n"
950
+ " return message\n"
951
+ "end\n"
952
+ "local save_message = function(index, message, events_key, messages_key)\n"
953
+ " redis.call(\"hsetnx\", messages_key, message[\"id\"], cmsgpack.pack(message))\n"
954
+ " local primary_key = primary_message_keys[index](message)\n"
955
+ " redis.call(\"lpush\", events_key .. \"!m!\" .. primary_key, message[\"id\"])\n"
956
+ "end\n"
957
+ "local save_result = function(frame, index)\n"
958
+ " table.insert(results, 1, frame_packers[index](frame, true))\n"
959
+ " table.insert(unpacked_results, 1, frame)\n"
960
+ " for name, message in pairs(frame) do\n"
961
+ " if message ~= \"$n\" and not message[\"$f\"] then\n"
962
+ " redis.call(\"hdel\", events_hashset, message[\"id\"])\n"
963
+ // message cache always looked up using strings
964
+ " events_message_cache[tostring(message[\"id\"])] = false\n"
965
+ " end\n"
966
+ " end\n"
967
+ "end\n"
968
+ "local is_pure_fact = function(frame, index)\n"
969
+ " local message_count = 0\n"
970
+ " for name, message in pairs(frame) do\n"
971
+ " if message ~= 1 and message[\"$f\"] ~= 1 then\n"
972
+ " return false\n"
973
+ " end\n"
974
+ " message_count = message_count + 1\n"
975
+ " end\n"
976
+ " return (message_count == index - 1)\n"
977
+ "end\n"
978
+ "local process_frame\n"
979
+ "local process_event_and_frame = function(message, frame, index, use_facts)\n"
980
+ " local result = 0\n"
981
+ " local new_frame = {}\n"
982
+ " for name, new_message in pairs(frame) do\n"
983
+ " new_frame[name] = new_message\n"
984
+ " end\n"
985
+ " if reviewers[index](message, new_frame, index) then\n"
986
+ " if (index == expressions_count) then\n"
987
+ " save_result(new_frame, index)\n"
988
+ " return 1\n"
989
+ " else\n"
990
+ " result = process_frame(new_frame, index + 1, use_facts)\n"
991
+ " if result == 0 or use_facts then\n"
992
+ " local frames_key\n"
993
+ " local primary_key = primary_frame_keys[index + 1](new_frame)\n"
994
+ " frames_key = keys[index + 1] .. \"!c!\" .. sid .. \"!\" .. primary_key\n"
995
+ " redis.call(\"rpush\", frames_key, frame_packers[index](new_frame))\n"
996
+ " end\n"
997
+ " end\n"
998
+ " end\n"
999
+ " return result\n"
1000
+ "end\n"
1001
+ "local process_frame_for_key = function(frame, index, events_key, use_facts)\n"
1002
+ " local result = nil\n"
1003
+ " local inverse = inverse_directory[index]\n"
1004
+ " local messages_key = events_hashset\n"
1005
+ " local message_cache = events_message_cache\n"
1006
+ " local mids_cache = events_mids_cache\n"
1007
+ " local cleanup = false\n"
1008
+ " if use_facts then\n"
1009
+ " messages_key = facts_hashset\n"
1010
+ " message_cache = facts_message_cache\n"
1011
+ " mids_cache = facts_mids_cache\n"
1012
+ " end\n"
1013
+ " if inverse then\n"
1014
+ " local new_frame = {}\n"
1015
+ " for name, new_message in pairs(frame) do\n"
1016
+ " new_frame[name] = new_message\n"
1017
+ " end\n"
1018
+ " local new_mids = get_mids(index, frame, events_key, messages_key, mids_cache, message_cache)\n"
1019
+ " for i = 1, #new_mids, 1 do\n"
1020
+ " local message = get_message(new_mids[i], messages_key, message_cache)\n"
1021
+ " if not message then\n"
1022
+ " cleanup = true\n"
1023
+ " elseif not reviewers[index](message, new_frame, index) then\n"
1024
+ " local frames_key = keys[index] .. \"!i!\" .. sid .. \"!\" .. new_mids[i]\n"
1025
+ " redis.call(\"rpush\", frames_key, frame_packers[index - 1](new_frame))\n"
1026
+ " result = 0\n"
1027
+ " break\n"
1028
+ " end\n"
1029
+ " end\n"
1030
+ " else\n"
1031
+ " local new_mids = get_mids(index, frame, events_key, messages_key, mids_cache, message_cache)\n"
1032
+ " for i = 1, #new_mids, 1 do\n"
1033
+ " local message = get_message(new_mids[i], messages_key, message_cache)\n"
1034
+ " if not message then\n"
1035
+ " cleanup = true\n"
1036
+ " else\n"
1037
+ " local count = process_event_and_frame(message, frame, index, use_facts)\n"
1038
+ " if not result then\n"
1039
+ " result = 0\n"
1040
+ " end\n"
1041
+ " result = result + count\n"
1042
+ " if not is_pure_fact(frame, index) then\n"
1043
+ " break\n"
1044
+ " end\n"
1045
+ " end\n"
1046
+ " end\n"
1047
+ " end\n"
1048
+ " if cleanup then\n"
1049
+ " cleanup_mids(index, frame, events_key, messages_key, mids_cache, message_cache)\n"
1050
+ " end\n"
1051
+ " return result\n"
1052
+ "end\n"
1053
+ "process_frame = function(frame, index, use_facts)\n"
1054
+ " local first_result = process_frame_for_key(frame, index, keys[index] .. \"!e!\" .. sid, false)\n"
1055
+ " local second_result = process_frame_for_key(frame, index, keys[index] .. \"!f!\" .. sid, true)\n"
1056
+ " if not first_result and not second_result then\n"
1057
+ " return process_event_and_frame(nil, frame, index, use_facts)\n"
1058
+ " elseif not first_result then\n"
1059
+ " return second_result\n"
1060
+ " elseif not second_result then\n"
1061
+ " return first_result\n"
1062
+ " else\n"
1063
+ " return first_result + second_result\n"
1064
+ " end\n"
1065
+ "end\n"
1066
+ "local process_inverse_event = function(message, index, events_key, use_facts)\n"
1067
+ " local result = 0\n"
1068
+ " local messages_key = events_hashset\n"
1069
+ " if use_facts then\n"
1070
+ " messages_key = facts_hashset\n"
1071
+ " end\n"
1072
+ " redis.call(\"hdel\", messages_key, mid)\n"
1073
+ " if index == 1 then\n"
1074
+ " result = process_frame({}, 1, use_facts)\n"
1075
+ " else\n"
1076
+ " local frames_key = keys[index] .. \"!i!\" .. sid .. \"!\" .. mid\n"
1077
+ " local packed_frames_len = redis.call(\"llen\", frames_key)\n"
1078
+ " for i = 1, packed_frames_len, 1 do\n"
1079
+ " local packed_frame = redis.call(\"rpop\", frames_key)\n"
1080
+ " local frame = frame_unpackers[index - 1](packed_frame)\n"
1081
+ " if frame then\n"
1082
+ " result = result + process_frame(frame, index, use_facts)\n"
1083
+ " end\n"
1084
+ " end\n"
1085
+ " end\n"
1086
+ " return result\n"
1087
+ "end\n"
1088
+ "local process_event = function(message, index, events_key, use_facts)\n"
1089
+ " local result = 0\n"
1090
+ " local messages_key = events_hashset\n"
1091
+ " if use_facts then\n"
1092
+ " messages_key = facts_hashset\n"
1093
+ " end\n"
1094
+ " if index == 1 then\n"
1095
+ " result = process_event_and_frame(message, {}, 1, use_facts)\n"
1096
+ " else\n"
1097
+ " local frames_key\n"
1098
+ " local primary_key = primary_message_keys[index](message)\n"
1099
+ " if primary_key then\n"
1100
+ " frames_key = keys[index] .. \"!c!\" .. sid .. \"!\" .. primary_key\n"
1101
+ " else\n"
1102
+ " frames_key = keys[index] .. \"!c!\" .. sid\n"
1103
+ " end\n"
1104
+ " local packed_frames_len = redis.call(\"llen\", frames_key)\n"
1105
+ " for i = 1, packed_frames_len, 1 do\n"
1106
+ " local packed_frame = redis.call(\"rpop\", frames_key)\n"
1107
+ " local frame = frame_unpackers[index - 1](packed_frame)\n"
1108
+ " if frame then\n"
1109
+ " local count = process_event_and_frame(message, frame, index, use_facts)\n"
1110
+ " result = result + count\n"
1111
+ " if count == 0 or use_facts then\n"
1112
+ " redis.call(\"lpush\", frames_key, packed_frame)\n"
1113
+ " else\n"
1114
+ " break\n"
1115
+ " end\n"
1116
+ " end\n"
1117
+ " end\n"
1118
+ " end\n"
1119
+ " if result == 0 or use_facts then\n"
1120
+ " save_message(index, message, events_key, messages_key)\n"
1121
+ " end\n"
1122
+ " return result\n"
1123
+ "end\n"
1124
+ "local process_key_with_span = function(message, span)\n"
1125
+ " local index = directory[key]\n"
1126
+ " local next_result = nil\n"
1127
+ " local queue_lock = false\n"
1128
+ " if index then\n"
1129
+ " local last_score = redis.call(\"get\", results_key .. \"!d\")\n"
1130
+ " if not last_score then\n"
1131
+ " redis.call(\"set\", results_key .. \"!d\", score)\n"
1132
+ " else\n"
1133
+ " local new_score = last_score + span\n"
1134
+ " if score > new_score then\n"
1135
+ " redis.call(\"rpush\", results_key, 0)\n"
1136
+ " redis.call(\"rpush\", actions_key .. \"!\" .. sid, results_key)\n"
1137
+ " redis.call(\"rpush\", actions_key .. \"!\" .. sid, 0)\n"
1138
+ " local span_count, span_remain = math.modf((score - new_score) / span)\n"
1139
+ " last_score = new_score + span_count * span\n"
1140
+ " redis.call(\"set\", results_key .. \"!d\", last_score)\n"
1141
+ " queue_lock = true\n"
1142
+ " end\n"
1143
+ " end\n"
1144
+ " local count = 0\n"
1145
+ " if not message then\n"
1146
+ " if assert_fact == 0 then\n"
1147
+ " count = process_inverse_event(message, index, keys[index] .. \"!e!\" .. sid, false)\n"
1148
+ " else\n"
1149
+ " count = process_inverse_event(message, index, keys[index] .. \"!f!\" .. sid, true)\n"
1150
+ " end\n"
1151
+ " else\n"
1152
+ " if assert_fact == 0 then\n"
1153
+ " count = process_event(message, index, keys[index] .. \"!e!\" .. sid, false)\n"
1154
+ " else\n"
1155
+ " count = process_event(message, index, keys[index] .. \"!f!\" .. sid, true)\n"
1156
+ " end\n"
1157
+ " end\n"
1158
+ " if (count > 0) then\n"
1159
+ " for i = 1, #results, 1 do\n"
1160
+ " redis.call(\"rpush\", results_key, results[i])\n"
1161
+ " end\n"
1162
+ " end\n"
1163
+ " end\n"
1164
+ " return queue_lock, next_result\n"
1165
+ "end\n"
1166
+ "local process_key_with_cap = function(message, cap)\n"
1167
+ " local index = directory[key]\n"
1168
+ " local next_result = nil\n"
1169
+ " local queue_lock = false\n"
1170
+ " if index then\n"
1171
+ " local count = 0\n"
1172
+ " if not message then\n"
1173
+ " if assert_fact == 0 then\n"
1174
+ " count = process_inverse_event(message, index, keys[index] .. \"!e!\" .. sid, false)\n"
1175
+ " else\n"
1176
+ " count = process_inverse_event(message, index, keys[index] .. \"!f!\" .. sid, true)\n"
1177
+ " end\n"
1178
+ " else\n"
1179
+ " if assert_fact == 0 then\n"
1180
+ " count = process_event(message, index, keys[index] .. \"!e!\" .. sid, false)\n"
1181
+ " else\n"
1182
+ " count = process_event(message, index, keys[index] .. \"!f!\" .. sid, true)\n"
1183
+ " end\n"
1184
+ " end\n"
1185
+ " if (count > 0) then\n"
1186
+ " for i = #results, 1, -1 do\n"
1187
+ " redis.call(\"lpush\", results_key, results[i])\n"
1188
+ " end\n"
1189
+ " local diff\n"
1190
+ " local new_count, new_remain = math.modf(#results / cap)\n"
1191
+ " local new_remain = #results %% cap\n"
1192
+ " if redis.call(\"llen\", actions_key .. \"!\" .. sid) == 2 then\n"
1193
+ " local frames = {}\n"
1194
+ " if new_count > 0 then\n"
1195
+ " for i = 0, cap - 1, 1 do\n"
1196
+ " table.insert(frames, unpacked_results[#unpacked_results - i])\n"
1197
+ " end\n"
1198
+ " else\n"
1199
+ " for i = 0, new_remain - 1, 1 do\n"
1200
+ " table.insert(frames, unpacked_results[#unpacked_results - i])\n"
1201
+ " end\n"
1202
+ " end\n"
1203
+ " local last_name = string.find(results_key, \"!\") - 1\n"
1204
+ " next_result = {[string.sub(results_key, 1, last_name)] = frames}\n"
1205
+ " end\n"
1206
+ " if new_count > 0 then\n"
1207
+ " for i = 1, new_count, 1 do\n"
1208
+ " redis.call(\"rpush\", actions_key .. \"!\" .. sid, results_key)\n"
1209
+ " redis.call(\"rpush\", actions_key .. \"!\" .. sid, cap)\n"
1210
+ " end\n"
1211
+ " end\n"
1212
+ " if new_remain > 0 then\n"
1213
+ " redis.call(\"rpush\", actions_key .. \"!\" .. sid, results_key)\n"
1214
+ " redis.call(\"rpush\", actions_key .. \"!\" .. sid, new_remain)\n"
1215
+ " end\n"
1216
+ " if new_count > 0 or new_remain > 0 then\n"
1217
+ " queue_lock = true\n"
1218
+ " end\n"
1219
+ " end\n"
1220
+ " end\n"
1221
+ " return queue_lock, next_result\n"
1222
+ "end\n"
1223
+ "local process_key_with_window = function(message, window)\n"
1224
+ " local index = directory[key]\n"
1225
+ " local next_result = nil\n"
1226
+ " local queue_lock = false\n"
1227
+ " if index then\n"
1228
+ " local count = 0\n"
1229
+ " if not message then\n"
1230
+ " if assert_fact == 0 then\n"
1231
+ " count = process_inverse_event(message, index, keys[index] .. \"!e!\" .. sid, false)\n"
1232
+ " else\n"
1233
+ " count = process_inverse_event(message, index, keys[index] .. \"!f!\" .. sid, true)\n"
1234
+ " end\n"
1235
+ " else\n"
1236
+ " if assert_fact == 0 then\n"
1237
+ " count = process_event(message, index, keys[index] .. \"!e!\" .. sid, false)\n"
1238
+ " else\n"
1239
+ " count = process_event(message, index, keys[index] .. \"!f!\" .. sid, true)\n"
1240
+ " end\n"
1241
+ " end\n"
1242
+ " if (count > 0) then\n"
1243
+ " for i = #results, 1, -1 do\n"
1244
+ " redis.call(\"lpush\", results_key, results[i])\n"
1245
+ " end\n"
1246
+ " local diff\n"
1247
+ " local length = redis.call(\"llen\", results_key)\n"
1248
+ " local prev_count, prev_remain = math.modf((length - count) / window)\n"
1249
+ " local new_count, prev_remain = math.modf(length / window)\n"
1250
+ " diff = new_count - prev_count\n"
1251
+ " if diff > 0 then\n"
1252
+ " if redis.call(\"llen\", actions_key .. \"!\" .. sid) == 2 then\n"
1253
+ " local frames = {}\n"
1254
+ " for i = 0, window - 1, 1 do\n"
1255
+ " table.insert(frames, unpacked_results[#unpacked_results - i])\n"
1256
+ " end\n"
1257
+ " local last_name = string.find(results_key, \"!\") - 1\n"
1258
+ " if window == 1 then\n"
1259
+ " next_result = {[string.sub(results_key, 1, last_name)] = frames[1]}\n"
1260
+ " else\n"
1261
+ " next_result = {[string.sub(results_key, 1, last_name)] = frames}\n"
1262
+ " end\n"
1263
+ " end\n"
1264
+ " for i = 0, diff - 1, 1 do\n"
1265
+ " redis.call(\"rpush\", actions_key .. \"!\" .. sid, results_key)\n"
1266
+ " if window == 1 then\n"
1267
+ " redis.call(\"rpush\", actions_key .. \"!\" .. sid, \"single\")\n"
1268
+ " else\n"
1269
+ " redis.call(\"rpush\", actions_key .. \"!\" .. sid, window)\n"
1270
+ " end\n"
1271
+ " end\n"
1272
+ " queue_lock = true\n"
1273
+ " end\n"
1274
+ " end\n"
1275
+ " end\n"
1276
+ " return queue_lock, next_result\n"
1277
+ "end\n"
1278
+ "local message = nil\n"
1279
+ "if #ARGV > (6 + keys_count) then\n"
1280
+ " message = {}\n"
1281
+ " for index = 6 + keys_count, #ARGV, 3 do\n"
1282
+ " if ARGV[index + 2] == \"1\" then\n"
1283
+ " message[ARGV[index]] = ARGV[index + 1]\n"
1284
+ " elseif ARGV[index + 2] == \"2\" or ARGV[index + 2] == \"3\" then\n"
1285
+ " message[ARGV[index]] = tonumber(ARGV[index + 1])\n"
1286
+ " elseif ARGV[index + 2] == \"4\" then\n"
1287
+ " if ARGV[index + 1] == \"true\" then\n"
1288
+ " message[ARGV[index]] = true\n"
1289
+ " else\n"
1290
+ " message[ARGV[index]] = false\n"
1291
+ " end\n"
1292
+ " end\n"
1293
+ " end\n"
1294
+ " if assert_fact == 1 then\n"
1295
+ " message[\"$f\"] = 1\n"
1296
+ " end\n"
1297
+ "end\n"
1298
+ "if redis.call(\"hsetnx\", visited_hashset, mid, 1) == 0 then\n"
1299
+ " if assert_fact == 0 then\n"
1300
+ " if message and redis.call(\"hexists\", events_hashset, mid) == 0 then\n"
1301
+ " return false\n"
1302
+ " end\n"
1303
+ " else\n"
1304
+ " if message and redis.call(\"hexists\", facts_hashset, mid) == 0 then\n"
1305
+ " return false\n"
1306
+ " end\n"
1307
+ " end\n"
1308
+ "end\n"
1309
+ "for index = 6, 5 + keys_count, 1 do\n"
1310
+ " input_keys[ARGV[index]] = true\n"
1311
+ "end\n"
1312
+ "%sfor index = 6, 5 + keys_count, 1 do\n"
1313
+ " results = {}\n"
1314
+ " unpacked_results = {}\n"
1315
+ " key = ARGV[index]\n"
1316
+ " context = context_directory[key]\n"
1317
+ " keys = context[\"keys\"]\n"
1318
+ " reviewers = context[\"reviewers\"]\n"
1319
+ " frame_packers = context[\"frame_packers\"]\n"
1320
+ " frame_unpackers = context[\"frame_unpackers\"]\n"
1321
+ " primary_message_keys = context[\"primary_message_keys\"]\n"
1322
+ " primary_frame_keys = context[\"primary_frame_keys\"]\n"
1323
+ " directory = context[\"directory\"]\n"
1324
+ " results_key = context[\"results_key\"]\n"
1325
+ " inverse_directory = context[\"inverse_directory\"]\n"
1326
+ " expressions_count = context[\"expressions_count\"]\n"
1327
+ " local process_key = context[\"process_key\"]\n"
1328
+ " local process_key_count = context[\"process_key_count\"]\n"
1329
+ " local new_candidate\n"
1330
+ " queue_action, new_candidate = process_key(message, process_key_count)\n"
1331
+ " if new_candidate then\n"
1332
+ " candidate = new_candidate\n"
1333
+ " end\n"
1334
+ " if assert_fact == 0 and events_message_cache[tostring(message[\"id\"])] == false then\n"
1335
+ " break\n"
1336
+ " end\n"
1337
+ "end\n"
1338
+ "if queue_action then\n"
1339
+ " if not redis.call(\"zscore\", actions_key, sid) then\n"
1340
+ " redis.call(\"zadd\", actions_key , score, sid)\n"
1341
+ " end\n"
1342
+ "end\n"
1343
+ "if not candidate then\n"
1344
+ " if redis.call(\"llen\", actions_key .. \"!\" .. sid) > 2 then\n"
1345
+ " end\n"
1346
+ " return nil\n"
1347
+ "else\n"
1348
+ " redis.call(\"set\", \"skip\", \"yes\")\n"
1349
+ " return {sid, cjson.encode(candidate)}\n"
1350
+ "end\n",
1351
+ name,
1352
+ name,
1353
+ name,
1354
+ name,
1355
+ name,
1356
+ lua) == -1) {
1357
+ return ERR_OUT_OF_MEMORY;
1358
+ }
1359
+ free(oldLua);
1360
+ redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
1361
+ redisGetReply(reContext, (void**)&reply);
1362
+ if (reply->type == REDIS_REPLY_ERROR) {
1363
+ printf("%s\n", reply->str);
1364
+ freeReplyObject(reply);
1365
+ free(lua);
1366
+ return ERR_REDIS_ERROR;
1367
+ }
1368
+
1369
+ strncpy(rulesBinding->evalMessageHash, reply->str, 40);
1370
+ rulesBinding->evalMessageHash[40] = '\0';
1371
+ freeReplyObject(reply);
1372
+ free(lua);
1373
+
1374
+ if (asprintf(&lua,
1375
+ "if redis.call(\"get\", \"skip\") == \"yes\" then\n"
1376
+ " redis.call(\"set\", \"skip\", \"no\")\n"
1377
+ " return nil\n"
1378
+ "end\n"
1379
+ "local facts_key = \"%s!f!\"\n"
1380
+ "local events_key = \"%s!e!\"\n"
1381
+ "local action_key = \"%s!a\"\n"
1382
+ "local state_key = \"%s!s\"\n"
1383
+ "local timers_key = \"%s!t\"\n"
1384
+ "local context_directory = {}\n"
1385
+ "local keys\n"
1386
+ "local reviewers\n"
1387
+ "local primary_frame_keys\n"
1388
+ "local facts_hashset\n"
1389
+ "local events_hashset\n"
1390
+ "local events_message_cache = {}\n"
1391
+ "local facts_message_cache = {}\n"
1392
+ "local facts_mids_cache = {}\n"
1393
+ "local events_mids_cache = {}\n"
1394
+ "local get_context\n"
1395
+ "local get_mids = function(index, frame, events_key, messages_key, mids_cache, message_cache)\n"
1396
+ " local event_mids = mids_cache[events_key]\n"
1397
+ " local primary_key = primary_frame_keys[index](frame)\n"
1398
+ " local new_mids = nil\n"
1399
+ " if not event_mids then\n"
1400
+ " event_mids = {}\n"
1401
+ " mids_cache[events_key] = event_mids\n"
1402
+ " else\n"
1403
+ " new_mids = event_mids[primary_key]\n"
1404
+ " end\n"
1405
+ " if not new_mids then\n"
1406
+ " new_mids = redis.call(\"lrange\", events_key .. \"!m!\" .. primary_key, 0, -1)\n"
1407
+ " event_mids[primary_key] = new_mids\n"
1408
+ " end\n"
1409
+ " return new_mids\n"
1410
+ "end\n"
1411
+ "local get_message = function(new_mid, messages_key, message_cache)\n"
1412
+ " local message = false\n"
1413
+ " new_mid = tostring(new_mid)\n"
1414
+ " if message_cache[new_mid] ~= nil then\n"
1415
+ " message = message_cache[new_mid]\n"
1416
+ " else\n"
1417
+ " local packed_message = redis.call(\"hget\", messages_key, new_mid)\n"
1418
+ " if packed_message then\n"
1419
+ " message = cmsgpack.unpack(packed_message)\n"
1420
+ " end\n"
1421
+ " message_cache[new_mid] = message\n"
1422
+ " end\n"
1423
+ " return message\n"
1424
+ "end\n"
1425
+ "local fetch_message = function(new_mid)\n"
1426
+ " if type(new_mid) == \"table\" then\n"
1427
+ " return new_mid\n"
1428
+ " end\n"
1429
+ " return get_message(new_mid, facts_hashset, facts_message_cache)\n"
1430
+ "end\n"
1431
+ "local validate_frame_for_key = function(packed_frame, frame, index, events_list_key, messages_key, mids_cache, message_cache, sid)\n"
1432
+ " local new_mids = get_mids(index, frame, events_list_key, messages_key, mids_cache, message_cache)\n"
1433
+ " for i = 1, #new_mids, 1 do\n"
1434
+ " local message = get_message(new_mids[i], messages_key, message_cache)\n"
1435
+ " if message and not reviewers[index](message, frame, index) then\n"
1436
+ " local frames_key = keys[index] .. \"!i!\" .. sid .. \"!\" .. new_mids[i]\n"
1437
+ " redis.call(\"rpush\", frames_key, packed_frame)\n"
1438
+ " return false\n"
1439
+ " end\n"
1440
+ " end\n"
1441
+ " return true\n"
1442
+ "end\n"
1443
+ "local validate_frame = function(packed_frame, frame, index, sid)\n"
1444
+ " local first_result = validate_frame_for_key(packed_frame, frame, index, keys[index] .. \"!e!\" .. sid, events_hashset, events_mids_cache, events_message_cache, sid)\n"
1445
+ " local second_result = validate_frame_for_key(packed_frame, frame, index, keys[index] ..\"!f!\" .. sid, facts_hashset, facts_mids_cache, facts_message_cache, sid)\n"
1446
+ " return first_result and second_result\n"
1447
+ "end\n"
1448
+ "local review_frame = function(frame, rule_action_key, sid, max_score)\n"
1449
+ " local indexes = {}\n"
1450
+ " local action_id = string.sub(rule_action_key, 1, (string.len(sid) + 1) * -1)\n"
1451
+ " local context = get_context(action_id)\n"
1452
+ " local full_frame = {}\n"
1453
+ " local cancel = false\n"
1454
+ " events_hashset = events_key .. sid\n"
1455
+ " facts_hashset = facts_key .. sid\n"
1456
+ " keys = context[\"keys\"]\n"
1457
+ " reviewers = context[\"reviewers\"]\n"
1458
+ " primary_frame_keys = context[\"primary_frame_keys\"]\n"
1459
+ " if not context[\"frame_restore\"](frame, full_frame) then\n"
1460
+ " cancel = true\n"
1461
+ " else\n"
1462
+ " for i = 1, #frame, 1 do\n"
1463
+ " if frame[i] == \"$n\" then\n"
1464
+ " if not validate_frame(frame, full_frame, i, sid) then\n"
1465
+ " cancel = true\n"
1466
+ " break\n"
1467
+ " end\n"
1468
+ " end\n"
1469
+ " end\n"
1470
+ " end\n"
1471
+ " if cancel then\n"
1472
+ " for i = 1, #frame, 1 do\n"
1473
+ " if type(frame[i]) == \"table\" then\n"
1474
+ " redis.call(\"hsetnx\", events_hashset, frame[i][\"id\"], cmsgpack.pack(frame[i]))\n"
1475
+ " redis.call(\"zadd\", timers_key, max_score, cjson.encode(frame[i]))\n"
1476
+ " end\n"
1477
+ " end\n"
1478
+ " full_frame = nil\n"
1479
+ " end\n"
1480
+ " return full_frame\n"
1481
+ "end\n"
1482
+ "local load_frame_from_rule = function(rule_action_key, raw_count, sid, max_score)\n"
1483
+ " local frames = {}\n"
1484
+ " local packed_frames = {}\n"
1485
+ " local unwrap = true\n"
1486
+ " local count = 1\n"
1487
+ " if raw_count ~= \"single\" then\n"
1488
+ " count = tonumber(raw_count)\n"
1489
+ " unwrap = false\n"
1490
+ " end\n"
1491
+ " if count == 0 then\n"
1492
+ " local packed_frame = redis.call(\"lpop\", rule_action_key)\n"
1493
+ " while packed_frame ~= \"0\" do\n"
1494
+ " local frame = review_frame(cmsgpack.unpack(packed_frame), rule_action_key, sid, max_score)\n"
1495
+ " if frame then\n"
1496
+ " table.insert(frames, frame)\n"
1497
+ " table.insert(packed_frames, packed_frame)\n"
1498
+ " end\n"
1499
+ " packed_frame = redis.call(\"lpop\", rule_action_key)\n"
1500
+ " end\n"
1501
+ " if #packed_frames > 0 then\n"
1502
+ " redis.call(\"lpush\", rule_action_key, 0)\n"
1503
+ " end\n"
1504
+ " else\n"
1505
+ " while count > 0 do\n"
1506
+ " local packed_frame = redis.call(\"rpop\", rule_action_key)\n"
1507
+ " if not packed_frame then\n"
1508
+ " break\n"
1509
+ " else\n"
1510
+ " local frame = review_frame(cmsgpack.unpack(packed_frame), rule_action_key, sid, max_score)\n"
1511
+ " if frame then\n"
1512
+ " table.insert(frames, frame)\n"
1513
+ " table.insert(packed_frames, packed_frame)\n"
1514
+ " count = count - 1\n"
1515
+ " end\n"
1516
+ " end\n"
1517
+ " end\n"
1518
+ " end\n"
1519
+ " for i = #packed_frames, 1, -1 do\n"
1520
+ " redis.call(\"rpush\", rule_action_key, packed_frames[i])\n"
1521
+ " end\n"
1522
+ " if #packed_frames == 0 then\n"
1523
+ " return nil, nil\n"
1524
+ " end\n"
1525
+ " local last_name = string.find(rule_action_key, \"!\") - 1\n"
1526
+ " if unwrap then\n"
1527
+ " return string.sub(rule_action_key, 1, last_name), frames[1]\n"
1528
+ " else\n"
1529
+ " return string.sub(rule_action_key, 1, last_name), frames\n"
1530
+ " end\n"
1531
+ "end\n"
1532
+ "local load_frame_from_sid = function(sid, max_score)\n"
1533
+ " local action_list = action_key .. \"!\" .. sid\n"
1534
+ " local rule_action_key = redis.call(\"lpop\", action_list)\n"
1535
+ " if not rule_action_key then\n"
1536
+ " return nil, nil\n"
1537
+ " end\n"
1538
+ " local count = redis.call(\"lpop\", action_list)\n"
1539
+ " local name, frame = load_frame_from_rule(rule_action_key, count, sid, max_score)\n"
1540
+ " while not frame do\n"
1541
+ " rule_action_key = redis.call(\"lpop\", action_list)\n"
1542
+ " if not rule_action_key then\n"
1543
+ " return nil, nil\n"
1544
+ " end\n"
1545
+ " count = redis.call(\"lpop\", action_list)\n"
1546
+ " name, frame = load_frame_from_rule(rule_action_key, count, sid, max_score)\n"
1547
+ " end\n"
1548
+ " redis.call(\"lpush\", action_list, count)\n"
1549
+ " redis.call(\"lpush\", action_list, rule_action_key)\n"
1550
+ " return name, frame\n"
1551
+ "end\n"
1552
+ "local load_frame = function(max_score)\n"
1553
+ " local current_action = redis.call(\"zrange\", action_key, 0, 0, \"withscores\")\n"
1554
+ " if #current_action == 0 or (tonumber(current_action[2]) > (max_score + 5)) then\n"
1555
+ " return nil, nil, nil\n"
1556
+ " end\n"
1557
+ " local sid = current_action[1]\n"
1558
+ " local name, frame = load_frame_from_sid(sid, max_score)\n"
1559
+ " while not frame do\n"
1560
+ " redis.call(\"zremrangebyrank\", action_key, 0, 0)\n"
1561
+ " current_action = redis.call(\"zrange\", action_key, 0, 0, \"withscores\")\n"
1562
+ " if #current_action == 0 or (tonumber(current_action[2]) > (max_score + 5)) then\n"
1563
+ " return nil, nil, nil\n"
1564
+ " end\n"
1565
+ " sid = current_action[1]\n"
1566
+ " name, frame = load_frame_from_sid(sid, max_score)\n"
1567
+ " end\n"
1568
+ " return sid, name, frame\n"
1569
+ "end\n"
1570
+ "get_context = function(action_key)\n"
1571
+ " if context_directory[action_key] then\n"
1572
+ " return context_directory[action_key]\n"
1573
+ " end\n"
1574
+ " local input_keys = {[action_key] = true}\n%s"
1575
+ "end\n"
1576
+ "local new_sid, action_name, frame\n"
1577
+ "if #ARGV == 3 then\n"
1578
+ " new_sid = ARGV[3]\n"
1579
+ " action_name, frame = load_frame_from_sid(new_sid, ARGV[2])\n"
1580
+ "else\n"
1581
+ " new_sid, action_name, frame = load_frame(tonumber(ARGV[2]))\n"
1582
+ "end\n"
1583
+ "if frame then\n"
1584
+ " if #ARGV == 2 then\n"
1585
+ " redis.call(\"zincrby\", action_key, tonumber(ARGV[1]), new_sid)\n"
1586
+ " local state = redis.call(\"hget\", state_key, new_sid)\n"
1587
+ " return {new_sid, state, cjson.encode({[action_name] = frame})}\n"
1588
+ " else\n"
1589
+ " return {new_sid, cjson.encode({[action_name] = frame})}\n"
1590
+ " end\n"
1591
+ "end\n",
1592
+ name,
1593
+ name,
1594
+ name,
1595
+ name,
1596
+ name,
1597
+ peekActionLua) == -1) {
1598
+ return ERR_OUT_OF_MEMORY;
1599
+ }
1600
+ free(peekActionLua);
1601
+ redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
1602
+ redisGetReply(reContext, (void**)&reply);
1603
+ if (reply->type == REDIS_REPLY_ERROR) {
1604
+ printf("%s\n", reply->str);
1605
+ freeReplyObject(reply);
1606
+ free(lua);
1607
+ return ERR_REDIS_ERROR;
1608
+ }
1609
+
1610
+ strncpy(rulesBinding->peekActionHash, reply->str, 40);
1611
+ rulesBinding->peekActionHash[40] = '\0';
1612
+ freeReplyObject(reply);
1613
+ free(lua);
1614
+
1615
+ if (asprintf(&lua,
1616
+ "local sid = ARGV[1]\n"
1617
+ "local assert_fact = tonumber(ARGV[2])\n"
1618
+ "local keys_count = tonumber(ARGV[3])\n"
1619
+ "local events_hashset = \"%s!e!\" .. sid\n"
1620
+ "local facts_hashset = \"%s!f!\" .. sid\n"
1621
+ "local visited_hashset = \"%s!v!\" .. sid\n"
1622
+ "local message = {}\n"
1623
+ "local primary_message_keys = {}\n"
1624
+ "local input_keys = {}\n"
1625
+ "local save_message = function(current_key, message, events_key, messages_key)\n"
1626
+ " redis.call(\"hsetnx\", messages_key, message[\"id\"], cmsgpack.pack(message))\n"
1627
+ " local primary_key = primary_message_keys[current_key](message)\n"
1628
+ " redis.call(\"lpush\", events_key .. \"!m!\" .. primary_key, message[\"id\"])\n"
1629
+ "end\n"
1630
+ "for index = 4 + keys_count, #ARGV, 3 do\n"
1631
+ " if ARGV[index + 2] == \"1\" then\n"
1632
+ " message[ARGV[index]] = ARGV[index + 1]\n"
1633
+ " elseif ARGV[index + 2] == \"2\" or ARGV[index + 2] == \"3\" then\n"
1634
+ " message[ARGV[index]] = tonumber(ARGV[index + 1])\n"
1635
+ " elseif ARGV[index + 2] == \"4\" then\n"
1636
+ " if ARGV[index + 1] == \"true\" then\n"
1637
+ " message[ARGV[index]] = true\n"
1638
+ " else\n"
1639
+ " message[ARGV[index]] = false\n"
1640
+ " end\n"
1641
+ " end\n"
1642
+ "end\n"
1643
+ "local mid = message[\"id\"]\n"
1644
+ "if redis.call(\"hsetnx\", visited_hashset, message[\"id\"], 1) == 0 then\n"
1645
+ " if assert_fact == 0 then\n"
1646
+ " if not redis.call(\"hget\", events_hashset, mid) then\n"
1647
+ " return false\n"
1648
+ " end\n"
1649
+ " else\n"
1650
+ " if not redis.call(\"hget\", facts_hashset, mid) then\n"
1651
+ " return false\n"
1652
+ " end\n"
1653
+ " end\n"
1654
+ "end\n"
1655
+ "for index = 4, 3 + keys_count, 1 do\n"
1656
+ " input_keys[ARGV[index]] = true\n"
1657
+ "end\n"
1658
+ "%sif assert_fact == 1 then\n"
1659
+ " message[\"$f\"] = 1\n"
1660
+ " for index = 4, 3 + keys_count, 1 do\n"
1661
+ " local key = ARGV[index]\n"
1662
+ " save_message(key, message, key .. \"!f!\" .. sid, facts_hashset)\n"
1663
+ " end\n"
1664
+ "else\n"
1665
+ " for index = 4, 3 + keys_count, 1 do\n"
1666
+ " local key = ARGV[index]\n"
1667
+ " save_message(key, message, key .. \"!e!\" .. sid, events_hashset)\n"
1668
+ " end\n"
1669
+ "end\n",
1670
+ name,
1671
+ name,
1672
+ name,
1673
+ addMessageLua) == -1) {
1674
+ return ERR_OUT_OF_MEMORY;
1675
+ }
1676
+
1677
+ free(addMessageLua);
1678
+ redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
1679
+ redisGetReply(reContext, (void**)&reply);
1680
+ if (reply->type == REDIS_REPLY_ERROR) {
1681
+ printf("%s\n", reply->str);
1682
+ freeReplyObject(reply);
1683
+ free(lua);
1684
+ return ERR_REDIS_ERROR;
1685
+ }
1686
+
1687
+ strncpy(rulesBinding->addMessageHash, reply->str, 40);
1688
+ rulesBinding->addMessageHash[40] = '\0';
1689
+ freeReplyObject(reply);
1690
+ free(lua);
1691
+
1692
+ if (asprintf(&lua,
1693
+ "local delete_frame = function(key)\n"
1694
+ " local rule_action_key = redis.call(\"lpop\", key)\n"
1695
+ " local raw_count = redis.call(\"lpop\", key)\n"
1696
+ " local count = 1\n"
1697
+ " if raw_count ~= \"single\" then\n"
1698
+ " count = tonumber(raw_count)\n"
1699
+ " end\n"
1700
+ " if count == 0 then\n"
1701
+ " local packed_frame = redis.call(\"lpop\", rule_action_key)\n"
1702
+ " while packed_frame ~= \"0\" do\n"
1703
+ " packed_frame = redis.call(\"lpop\", rule_action_key)\n"
1704
+ " end\n"
1705
+ " else\n"
1706
+ " for i = 0, count - 1, 1 do\n"
1707
+ " redis.call(\"lpop\", rule_action_key)\n"
1708
+ " end\n"
1709
+ " end\n"
1710
+ " return (redis.call(\"llen\", key) > 0)\n"
1711
+ "end\n"
1712
+ "local sid = ARGV[1]\n"
1713
+ "local max_score = tonumber(ARGV[2])\n"
1714
+ "local action_key = \"%s!a\"\n"
1715
+ "if delete_frame(action_key .. \"!\" .. sid) then\n"
1716
+ //" if redis.call(\"get\", \"skip\") ~= \"yes\" then\n"
1717
+ " redis.call(\"zadd\", action_key, max_score, sid)\n"
1718
+ //" end\n"
1719
+ "else\n"
1720
+ //" if redis.call(\"get\", \"skip\") ~= \"yes\" then\n"
1721
+ " redis.call(\"zrem\", action_key, sid)\n"
1722
+ //" end\n"
1723
+ "end\n", name) == -1) {
1724
+ return ERR_OUT_OF_MEMORY;
1725
+ }
1726
+
1727
+ redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
1728
+ redisGetReply(reContext, (void**)&reply);
1729
+ if (reply->type == REDIS_REPLY_ERROR) {
1730
+ printf("%s\n", reply->str);
1731
+ freeReplyObject(reply);
1732
+ free(lua);
1733
+ return ERR_REDIS_ERROR;
1734
+ }
1735
+
1736
+ strncpy(rulesBinding->removeActionHash, reply->str, 40);
1737
+ rulesBinding->removeActionHash[40] = '\0';
1738
+ freeReplyObject(reply);
1739
+ free(lua);
1740
+
1741
+ if (asprintf(&lua,
1742
+ "local partition_key = \"%s!p\"\n"
1743
+ "local res = redis.call(\"hget\", partition_key, ARGV[1])\n"
1744
+ "if (not res) then\n"
1745
+ " res = redis.call(\"hincrby\", partition_key, \"index\", 1)\n"
1746
+ " res = res %% tonumber(ARGV[2])\n"
1747
+ " redis.call(\"hset\", partition_key, ARGV[1], res)\n"
1748
+ "end\n"
1749
+ "return tonumber(res)\n", name) == -1) {
1750
+ return ERR_OUT_OF_MEMORY;
1751
+ }
1752
+
1753
+ redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
1754
+ redisGetReply(reContext, (void**)&reply);
1755
+ if (reply->type == REDIS_REPLY_ERROR) {
1756
+ printf("%s\n", reply->str);
1757
+ freeReplyObject(reply);
1758
+ return ERR_REDIS_ERROR;
1759
+ }
1760
+
1761
+ strncpy(rulesBinding->partitionHash, reply->str, 40);
1762
+ rulesBinding->partitionHash[40] = '\0';
1763
+ freeReplyObject(reply);
1764
+ free(lua);
1765
+
1766
+ if (asprintf(&lua,
1767
+ "local timer_key = \"%s!t\"\n"
1768
+ "local timestamp = tonumber(ARGV[1])\n"
1769
+ "local res = redis.call(\"zrangebyscore\", timer_key, 0, timestamp, \"limit\", 0, 10)\n"
1770
+ "if #res > 0 then\n"
1771
+ " for i = 0, #res, 1 do\n"
1772
+ " redis.call(\"zincrby\", timer_key, 10, res[i])\n"
1773
+ " end\n"
1774
+ " return res\n"
1775
+ "end\n"
1776
+ "return 0\n", name) == -1) {
1777
+ return ERR_OUT_OF_MEMORY;
1778
+ }
1779
+
1780
+ redisAppendCommand(reContext, "SCRIPT LOAD %s", lua);
1781
+ redisGetReply(reContext, (void**)&reply);
1782
+ if (reply->type == REDIS_REPLY_ERROR) {
1783
+ printf("%s\n", reply->str);
1784
+ freeReplyObject(reply);
1785
+ return ERR_REDIS_ERROR;
1786
+ }
1787
+
1788
+ strncpy(rulesBinding->timersHash, reply->str, 40);
1789
+ rulesBinding->timersHash[40] = '\0';
1790
+ freeReplyObject(reply);
1791
+ free(lua);
1792
+
1793
+ char *sessionHashset = malloc((nameLength + 3) * sizeof(char));
1794
+ if (!sessionHashset) {
1795
+ return ERR_OUT_OF_MEMORY;
1796
+ }
1797
+
1798
+ strncpy(sessionHashset, name, nameLength);
1799
+ sessionHashset[nameLength] = '!';
1800
+ sessionHashset[nameLength + 1] = 's';
1801
+ sessionHashset[nameLength + 2] = '\0';
1802
+ rulesBinding->sessionHashset = sessionHashset;
1803
+
1804
+ char *factsHashset = malloc((nameLength + 3) * sizeof(char));
1805
+ if (!factsHashset) {
1806
+ return ERR_OUT_OF_MEMORY;
1807
+ }
1808
+
1809
+ strncpy(factsHashset, name, nameLength);
1810
+ factsHashset[nameLength] = '!';
1811
+ factsHashset[nameLength + 1] = 'f';
1812
+ factsHashset[nameLength + 2] = '\0';
1813
+ rulesBinding->factsHashset = factsHashset;
1814
+
1815
+ char *eventsHashset = malloc((nameLength + 3) * sizeof(char));
1816
+ if (!eventsHashset) {
1817
+ return ERR_OUT_OF_MEMORY;
1818
+ }
1819
+
1820
+ strncpy(eventsHashset, name, nameLength);
1821
+ eventsHashset[nameLength] = '!';
1822
+ eventsHashset[nameLength + 1] = 'e';
1823
+ eventsHashset[nameLength + 2] = '\0';
1824
+ rulesBinding->eventsHashset = eventsHashset;
1825
+
1826
+ char *timersSortedset = malloc((nameLength + 3) * sizeof(char));
1827
+ if (!timersSortedset) {
1828
+ return ERR_OUT_OF_MEMORY;
1829
+ }
1830
+
1831
+ strncpy(timersSortedset, name, nameLength);
1832
+ timersSortedset[nameLength] = '!';
1833
+ timersSortedset[nameLength + 1] = 't';
1834
+ timersSortedset[nameLength + 2] = '\0';
1835
+ rulesBinding->timersSortedset = timersSortedset;
1836
+
1837
+ return RULES_OK;
1838
+ }
1839
+
1840
+ unsigned int bindRuleset(void *handle,
1841
+ char *host,
1842
+ unsigned int port,
1843
+ char *password) {
1844
+ ruleset *tree = (ruleset*)handle;
1845
+ bindingsList *list;
1846
+ if (tree->bindingsList) {
1847
+ list = tree->bindingsList;
1848
+ }
1849
+ else {
1850
+ list = malloc(sizeof(bindingsList));
1851
+ if (!list) {
1852
+ return ERR_OUT_OF_MEMORY;
1853
+ }
1854
+
1855
+ list->bindings = NULL;
1856
+ list->bindingsLength = 0;
1857
+ list->lastBinding = 0;
1858
+ list->lastTimersBinding = 0;
1859
+ tree->bindingsList = list;
1860
+ }
1861
+
1862
+ redisContext *reContext;
1863
+ if (port == 0) {
1864
+ reContext = redisConnectUnix(host);
1865
+ } else {
1866
+ reContext = redisConnect(host, port);
1867
+ }
1868
+
1869
+ if (reContext->err) {
1870
+ redisFree(reContext);
1871
+ return ERR_CONNECT_REDIS;
1872
+ }
1873
+
1874
+ if (password != NULL) {
1875
+ int result = redisAppendCommand(reContext, "auth %s", password);
1876
+ if (result != REDIS_OK) {
1877
+ return ERR_REDIS_ERROR;
1878
+ }
1879
+
1880
+ redisReply *reply;
1881
+ result = redisGetReply(reContext, (void**)&reply);
1882
+ if (result != REDIS_OK) {
1883
+ return ERR_REDIS_ERROR;
1884
+ }
1885
+
1886
+ if (reply->type == REDIS_REPLY_ERROR) {
1887
+ freeReplyObject(reply);
1888
+ return ERR_REDIS_ERROR;
1889
+ }
1890
+
1891
+ freeReplyObject(reply);
1892
+ }
1893
+
1894
+ if (!list->bindings) {
1895
+ list->bindings = malloc(sizeof(binding));
1896
+ }
1897
+ else {
1898
+ list->bindings = realloc(list->bindings, sizeof(binding) * (list->bindingsLength + 1));
1899
+ }
1900
+
1901
+ if (!list->bindings) {
1902
+ redisFree(reContext);
1903
+ return ERR_OUT_OF_MEMORY;
1904
+ }
1905
+ list->bindings[list->bindingsLength].reContext = reContext;
1906
+ ++list->bindingsLength;
1907
+ return loadCommands(tree, &list->bindings[list->bindingsLength -1]);
1908
+ }
1909
+
1910
+ unsigned int deleteBindingsList(ruleset *tree) {
1911
+ bindingsList *list = tree->bindingsList;
1912
+ if (tree->bindingsList != NULL) {
1913
+ for (unsigned int i = 0; i < list->bindingsLength; ++i) {
1914
+ binding *currentBinding = &list->bindings[i];
1915
+ redisFree(currentBinding->reContext);
1916
+ free(currentBinding->timersSortedset);
1917
+ free(currentBinding->sessionHashset);
1918
+ free(currentBinding->factsHashset);
1919
+ free(currentBinding->eventsHashset);
1920
+ }
1921
+
1922
+ free(list->bindings);
1923
+ free(list);
1924
+ }
1925
+ return RULES_OK;
1926
+ }
1927
+
1928
+ unsigned int getBindingIndex(ruleset *tree, unsigned int sidHash, unsigned int *bindingIndex) {
1929
+ bindingsList *list = tree->bindingsList;
1930
+ binding *firstBinding = &list->bindings[0];
1931
+ redisContext *reContext = firstBinding->reContext;
1932
+
1933
+ int result = redisAppendCommand(reContext,
1934
+ "evalsha %s 0 %d %d",
1935
+ firstBinding->partitionHash,
1936
+ sidHash,
1937
+ list->bindingsLength);
1938
+ if (result != REDIS_OK) {
1939
+ return ERR_REDIS_ERROR;
1940
+ }
1941
+
1942
+ redisReply *reply;
1943
+ result = redisGetReply(reContext, (void**)&reply);
1944
+ if (result != REDIS_OK) {
1945
+ return ERR_REDIS_ERROR;
1946
+ }
1947
+
1948
+ if (reply->type == REDIS_REPLY_ERROR) {
1949
+ freeReplyObject(reply);
1950
+ return ERR_REDIS_ERROR;
1951
+ }
1952
+
1953
+ *bindingIndex = reply->integer;
1954
+ freeReplyObject(reply);
1955
+ return RULES_OK;
1956
+ }
1957
+
1958
+ unsigned int formatEvalMessage(void *rulesBinding,
1959
+ char *sid,
1960
+ char *mid,
1961
+ char *message,
1962
+ jsonProperty *allProperties,
1963
+ unsigned int propertiesLength,
1964
+ unsigned char actionType,
1965
+ char **keys,
1966
+ unsigned int keysLength,
1967
+ char **command) {
1968
+ if (actionType == ACTION_RETRACT_FACT || actionType == ACTION_RETRACT_EVENT) {
1969
+ propertiesLength = 0;
1970
+ }
1971
+
1972
+ binding *bindingContext = (binding*)rulesBinding;
1973
+ time_t currentTime = time(NULL);
1974
+ char score[11];
1975
+ char keysLengthString[5];
1976
+ #ifdef _WIN32
1977
+ sprintf_s(keysLengthString, 5, "%d", keysLength);
1978
+ sprintf_s(score, 11, "%ld", currentTime);
1979
+ char **argv = (char **)_alloca(sizeof(char*)*(8 + keysLength + propertiesLength * 3));
1980
+ size_t *argvl = (size_t *)_alloca(sizeof(size_t)*(8 + keysLength + propertiesLength * 3));
1981
+ #else
1982
+ snprintf(keysLengthString, 5, "%d", keysLength);
1983
+ snprintf(score, 11, "%ld", currentTime);
1984
+ char *argv[8 + keysLength + propertiesLength * 3];
1985
+ size_t argvl[8 + keysLength + propertiesLength * 3];
1986
+ #endif
1987
+
1988
+ argv[0] = "evalsha";
1989
+ argvl[0] = 7;
1990
+ argv[1] = bindingContext->evalMessageHash;
1991
+ argvl[1] = 40;
1992
+ argv[2] = "0";
1993
+ argvl[2] = 1;
1994
+ argv[3] = sid;
1995
+ argvl[3] = strlen(sid);
1996
+ argv[4] = mid;
1997
+ argvl[4] = strlen(mid);
1998
+ argv[5] = score;
1999
+ argvl[5] = 10;
2000
+ argv[6] = (actionType == ACTION_ASSERT_FACT || actionType == ACTION_RETRACT_FACT) ? "1" : "0";
2001
+ argvl[6] = 1;
2002
+ argv[7] = keysLengthString;
2003
+ argvl[7] = strlen(keysLengthString);
2004
+
2005
+ for (unsigned int i = 0; i < keysLength; ++i) {
2006
+ argv[8 + i] = keys[i];
2007
+ argvl[8 + i] = strlen(keys[i]);
2008
+ }
2009
+
2010
+ unsigned int offset = 8 + keysLength;
2011
+ for (unsigned int i = 0; i < propertiesLength; ++i) {
2012
+ argv[offset + i * 3] = message + allProperties[i].nameOffset;
2013
+ argvl[offset + i * 3] = allProperties[i].nameLength;
2014
+ argv[offset + i * 3 + 1] = message + allProperties[i].valueOffset;
2015
+ if (allProperties[i].type == JSON_STRING) {
2016
+ argvl[offset + i * 3 + 1] = allProperties[i].valueLength;
2017
+ } else {
2018
+ argvl[offset + i * 3 + 1] = allProperties[i].valueLength + 1;
2019
+ }
2020
+
2021
+ switch(allProperties[i].type) {
2022
+ case JSON_STRING:
2023
+ argv[offset + i * 3 + 2] = "1";
2024
+ break;
2025
+ case JSON_INT:
2026
+ argv[offset + i * 3 + 2] = "2";
2027
+ break;
2028
+ case JSON_DOUBLE:
2029
+ argv[offset + i * 3 + 2] = "3";
2030
+ break;
2031
+ case JSON_BOOL:
2032
+ argv[offset + i * 3 + 2] = "4";
2033
+ break;
2034
+ case JSON_ARRAY:
2035
+ argv[offset + i * 3 + 2] = "5";
2036
+ break;
2037
+ case JSON_NIL:
2038
+ argv[offset + i * 3 + 2] = "7";
2039
+ break;
2040
+ }
2041
+ argvl[offset + i * 3 + 2] = 1;
2042
+ }
2043
+
2044
+ int result = redisFormatCommandArgv(command, offset + propertiesLength * 3, (const char**)argv, argvl);
2045
+ if (result == 0) {
2046
+ return ERR_OUT_OF_MEMORY;
2047
+ }
2048
+ return RULES_OK;
2049
+ }
2050
+
2051
+ unsigned int formatStoreMessage(void *rulesBinding,
2052
+ char *sid,
2053
+ char *message,
2054
+ jsonProperty *allProperties,
2055
+ unsigned int propertiesLength,
2056
+ unsigned char storeFact,
2057
+ char **keys,
2058
+ unsigned int keysLength,
2059
+ char **command) {
2060
+ binding *bindingContext = (binding*)rulesBinding;
2061
+ char keysLengthString[5];
2062
+ #ifdef _WIN32
2063
+ sprintf_s(keysLengthString, 5, "%d", keysLength);
2064
+ char **argv = (char **)_alloca(sizeof(char*)*(6 + keysLength + propertiesLength * 3));
2065
+ size_t *argvl = (size_t *)_alloca(sizeof(size_t)*(6 + keysLength + propertiesLength * 3));
2066
+ #else
2067
+ snprintf(keysLengthString, 5, "%d", keysLength);
2068
+ char *argv[6 + keysLength + propertiesLength * 3];
2069
+ size_t argvl[6 + keysLength + propertiesLength * 3];
2070
+ #endif
2071
+
2072
+ argv[0] = "evalsha";
2073
+ argvl[0] = 7;
2074
+ argv[1] = bindingContext->addMessageHash;
2075
+ argvl[1] = 40;
2076
+ argv[2] = "0";
2077
+ argvl[2] = 1;
2078
+ argv[3] = sid;
2079
+ argvl[3] = strlen(sid);
2080
+ argv[4] = storeFact ? "1" : "0";
2081
+ argvl[4] = 1;
2082
+ argv[5] = keysLengthString;
2083
+ argvl[5] = strlen(keysLengthString);
2084
+
2085
+ for (unsigned int i = 0; i < keysLength; ++i) {
2086
+ argv[6 + i] = keys[i];
2087
+ argvl[6 + i] = strlen(keys[i]);
2088
+ }
2089
+
2090
+ unsigned int offset = 6 + keysLength;
2091
+ for (unsigned int i = 0; i < propertiesLength; ++i) {
2092
+ argv[offset + i * 3] = message + allProperties[i].nameOffset;
2093
+ argvl[offset + i * 3] = allProperties[i].nameLength;
2094
+ argv[offset + i * 3 + 1] = message + allProperties[i].valueOffset;
2095
+ if (allProperties[i].type == JSON_STRING) {
2096
+ argvl[offset + i * 3 + 1] = allProperties[i].valueLength;
2097
+ } else {
2098
+ argvl[offset + i * 3 + 1] = allProperties[i].valueLength + 1;
2099
+ }
2100
+
2101
+ switch(allProperties[i].type) {
2102
+ case JSON_STRING:
2103
+ argv[offset + i * 3 + 2] = "1";
2104
+ break;
2105
+ case JSON_INT:
2106
+ argv[offset + i * 3 + 2] = "2";
2107
+ break;
2108
+ case JSON_DOUBLE:
2109
+ argv[offset + i * 3 + 2] = "3";
2110
+ break;
2111
+ case JSON_BOOL:
2112
+ argv[offset + i * 3 + 2] = "4";
2113
+ break;
2114
+ case JSON_ARRAY:
2115
+ argv[offset + i * 3 + 2] = "5";
2116
+ break;
2117
+ case JSON_NIL:
2118
+ argv[offset + i * 3 + 2] = "7";
2119
+ break;
2120
+ }
2121
+ argvl[offset + i * 3 + 2] = 1;
2122
+ }
2123
+
2124
+ int result = redisFormatCommandArgv(command, offset + propertiesLength * 3, (const char**)argv, argvl);
2125
+ if (result == 0) {
2126
+ return ERR_OUT_OF_MEMORY;
2127
+ }
2128
+ return RULES_OK;
2129
+ }
2130
+
2131
+ unsigned int formatStoreSession(void *rulesBinding,
2132
+ char *sid,
2133
+ char *state,
2134
+ unsigned char tryExists,
2135
+ char **command) {
2136
+ binding *currentBinding = (binding*)rulesBinding;
2137
+
2138
+ int result;
2139
+ if (tryExists) {
2140
+ result = redisFormatCommand(command,
2141
+ "hsetnx %s %s %s",
2142
+ currentBinding->sessionHashset,
2143
+ sid,
2144
+ state);
2145
+ } else {
2146
+ result = redisFormatCommand(command,
2147
+ "hset %s %s %s",
2148
+ currentBinding->sessionHashset,
2149
+ sid,
2150
+ state);
2151
+ }
2152
+
2153
+ if (result == 0) {
2154
+ return ERR_OUT_OF_MEMORY;
2155
+ }
2156
+ return RULES_OK;
2157
+ }
2158
+
2159
+ unsigned int formatStoreSessionFact(void *rulesBinding,
2160
+ char *sid,
2161
+ char *message,
2162
+ unsigned char tryExists,
2163
+ char **command) {
2164
+ binding *currentBinding = (binding*)rulesBinding;
2165
+
2166
+ int result;
2167
+ if (tryExists) {
2168
+ result = redisFormatCommand(command,
2169
+ "hsetnx %s %s!f %s",
2170
+ currentBinding->sessionHashset,
2171
+ sid,
2172
+ message);
2173
+ } else {
2174
+ result = redisFormatCommand(command,
2175
+ "hset %s %s!f %s",
2176
+ currentBinding->sessionHashset,
2177
+ sid,
2178
+ message);
2179
+ }
2180
+
2181
+ if (result == 0) {
2182
+ return ERR_OUT_OF_MEMORY;
2183
+ }
2184
+ return RULES_OK;
2185
+ }
2186
+
2187
+ unsigned int formatRemoveTimer(void *rulesBinding,
2188
+ char *timer,
2189
+ char **command) {
2190
+ binding *currentBinding = (binding*)rulesBinding;
2191
+ int result = redisFormatCommand(command,
2192
+ "zrem %s %s",
2193
+ currentBinding->timersSortedset,
2194
+ timer);
2195
+ if (result == 0) {
2196
+ return ERR_OUT_OF_MEMORY;
2197
+ }
2198
+ return RULES_OK;
2199
+ }
2200
+
2201
+ unsigned int formatRemoveAction(void *rulesBinding,
2202
+ char *sid,
2203
+ char **command) {
2204
+ binding *bindingContext = (binding*)rulesBinding;
2205
+ time_t currentTime = time(NULL);
2206
+
2207
+ int result = redisFormatCommand(command,
2208
+ "evalsha %s 0 %s %ld",
2209
+ bindingContext->removeActionHash,
2210
+ sid,
2211
+ currentTime);
2212
+ if (result == 0) {
2213
+ return ERR_OUT_OF_MEMORY;
2214
+ }
2215
+ return RULES_OK;
2216
+ }
2217
+
2218
+ unsigned int formatRemoveMessage(void *rulesBinding,
2219
+ char *sid,
2220
+ char *mid,
2221
+ unsigned char removeFact,
2222
+ char **command) {
2223
+ binding *currentBinding = (binding*)rulesBinding;
2224
+
2225
+ int result = 0;
2226
+ if (removeFact) {
2227
+ result = redisFormatCommand(command,
2228
+ "hdel %s!%s %s",
2229
+ currentBinding->factsHashset,
2230
+ sid,
2231
+ mid);
2232
+ } else {
2233
+ result = redisFormatCommand(command,
2234
+ "hdel %s!%s %s",
2235
+ currentBinding->eventsHashset,
2236
+ sid,
2237
+ mid);
2238
+ }
2239
+
2240
+ if (result == 0) {
2241
+ return ERR_OUT_OF_MEMORY;
2242
+ }
2243
+ return RULES_OK;
2244
+ }
2245
+
2246
+ unsigned int formatPeekAction(void *rulesBinding,
2247
+ char *sid,
2248
+ char **command) {
2249
+ binding *currentBinding = (binding*)rulesBinding;
2250
+
2251
+ time_t currentTime = time(NULL);
2252
+ int result = redisFormatCommand(command,
2253
+ "evalsha %s 0 %d %ld %s",
2254
+ currentBinding->peekActionHash,
2255
+ 60,
2256
+ currentTime,
2257
+ sid);
2258
+ if (result == 0) {
2259
+ return ERR_OUT_OF_MEMORY;
2260
+ }
2261
+
2262
+ return RULES_OK;
2263
+ }
2264
+
2265
+
2266
+ unsigned int startNonBlockingBatch(void *rulesBinding,
2267
+ char **commands,
2268
+ unsigned int commandCount,
2269
+ unsigned int *replyCount) {
2270
+ *replyCount = commandCount;
2271
+ if (commandCount == 0) {
2272
+ return RULES_OK;
2273
+ }
2274
+
2275
+ unsigned int result = RULES_OK;
2276
+ binding *currentBinding = (binding*)rulesBinding;
2277
+ redisContext *reContext = currentBinding->reContext;
2278
+
2279
+ for (unsigned int i = 0; i < commandCount; ++i) {
2280
+ sds newbuf;
2281
+ newbuf = sdscatlen(reContext->obuf, commands[i], strlen(commands[i]));
2282
+ if (newbuf == NULL) {
2283
+ return ERR_OUT_OF_MEMORY;
2284
+ }
2285
+
2286
+ reContext->obuf = newbuf;
2287
+ free(commands[i]);
2288
+ }
2289
+
2290
+ int wdone = 0;
2291
+ do {
2292
+ if (redisBufferWrite(reContext, &wdone) == REDIS_ERR) {
2293
+ printf("error %u %s\n", reContext->err, reContext->errstr);
2294
+ return ERR_REDIS_ERROR;
2295
+ }
2296
+ } while (!wdone);
2297
+
2298
+ return result;
2299
+ }
2300
+
2301
+ unsigned int completeNonBlockingBatch(void *rulesBinding,
2302
+ unsigned int replyCount) {
2303
+ if (replyCount == 0) {
2304
+ return RULES_OK;
2305
+ }
2306
+
2307
+ unsigned int result = RULES_OK;
2308
+ binding *currentBinding = (binding*)rulesBinding;
2309
+ redisContext *reContext = currentBinding->reContext;
2310
+ redisReply *reply;
2311
+ for (unsigned int i = 0; i < replyCount; ++i) {
2312
+ result = redisGetReply(reContext, (void**)&reply);
2313
+ if (result != REDIS_OK) {
2314
+ result = ERR_REDIS_ERROR;
2315
+ } else {
2316
+ if (reply->type == REDIS_REPLY_ERROR) {
2317
+ printf("error %d %s\n", i, reply->str);
2318
+ result = ERR_REDIS_ERROR;
2319
+ }
2320
+
2321
+ freeReplyObject(reply);
2322
+ }
2323
+ }
2324
+
2325
+ return result;
2326
+ }
2327
+
2328
+ unsigned int executeBatch(void *rulesBinding,
2329
+ char **commands,
2330
+ unsigned int commandCount) {
2331
+ return executeBatchWithReply(rulesBinding, 0, commands, commandCount, NULL);
2332
+ }
2333
+
2334
+ unsigned int executeBatchWithReply(void *rulesBinding,
2335
+ unsigned int expectedReplies,
2336
+ char **commands,
2337
+ unsigned int commandCount,
2338
+ redisReply **lastReply) {
2339
+ if (commandCount == 0) {
2340
+ return RULES_OK;
2341
+ }
2342
+
2343
+ unsigned int result = RULES_OK;
2344
+ unsigned int replyCount = commandCount + expectedReplies;
2345
+ binding *currentBinding = (binding*)rulesBinding;
2346
+ redisContext *reContext = currentBinding->reContext;
2347
+ if (lastReply) {
2348
+ *lastReply = NULL;
2349
+ }
2350
+
2351
+ for (unsigned int i = 0; i < commandCount; ++i) {
2352
+ sds newbuf;
2353
+ newbuf = sdscatlen(reContext->obuf, commands[i], strlen(commands[i]));
2354
+ if (newbuf == NULL) {
2355
+ return ERR_OUT_OF_MEMORY;
2356
+ }
2357
+
2358
+ reContext->obuf = newbuf;
2359
+ free(commands[i]);
2360
+ }
2361
+
2362
+ redisReply *reply;
2363
+ for (unsigned int i = 0; i < replyCount; ++i) {
2364
+ result = redisGetReply(reContext, (void**)&reply);
2365
+ if (result != REDIS_OK) {
2366
+ result = ERR_REDIS_ERROR;
2367
+ } else {
2368
+ if (reply->type == REDIS_REPLY_ERROR) {
2369
+ printf("%s\n", reply->str);
2370
+ freeReplyObject(reply);
2371
+ result = ERR_REDIS_ERROR;
2372
+ } else if (reply->type == REDIS_REPLY_ARRAY) {
2373
+ if (lastReply == NULL) {
2374
+ freeReplyObject(reply);
2375
+ } else {
2376
+ if (*lastReply != NULL) {
2377
+ freeReplyObject(*lastReply);
2378
+ }
2379
+
2380
+ *lastReply = reply;
2381
+ }
2382
+ } else {
2383
+ freeReplyObject(reply);
2384
+ }
2385
+ }
2386
+ }
2387
+
2388
+ return result;
2389
+ }
2390
+
2391
+ unsigned int removeMessage(void *rulesBinding, char *sid, char *mid) {
2392
+ binding *currentBinding = (binding*)rulesBinding;
2393
+ redisContext *reContext = currentBinding->reContext;
2394
+ int result = redisAppendCommand(reContext,
2395
+ "hdel %s!%s %s",
2396
+ currentBinding->factsHashset,
2397
+ sid,
2398
+ mid);
2399
+ if (result != REDIS_OK) {
2400
+ return ERR_REDIS_ERROR;
2401
+ }
2402
+
2403
+ redisReply *reply;
2404
+ result = redisGetReply(reContext, (void**)&reply);
2405
+ if (result != REDIS_OK) {
2406
+ return ERR_REDIS_ERROR;
2407
+ }
2408
+
2409
+ if (reply->type == REDIS_REPLY_ERROR) {
2410
+ freeReplyObject(reply);
2411
+ return ERR_REDIS_ERROR;
2412
+ }
2413
+
2414
+ freeReplyObject(reply);
2415
+ return RULES_OK;
2416
+ }
2417
+
2418
+ unsigned int peekAction(ruleset *tree, void **bindingContext, redisReply **reply) {
2419
+ bindingsList *list = tree->bindingsList;
2420
+ for (unsigned int i = 0; i < list->bindingsLength; ++i) {
2421
+ binding *currentBinding = &list->bindings[list->lastBinding % list->bindingsLength];
2422
+ ++list->lastBinding;
2423
+ redisContext *reContext = currentBinding->reContext;
2424
+ time_t currentTime = time(NULL);
2425
+
2426
+ int result = redisAppendCommand(reContext,
2427
+ "evalsha %s 0 %d %ld",
2428
+ currentBinding->peekActionHash,
2429
+ 60,
2430
+ currentTime);
2431
+ if (result != REDIS_OK) {
2432
+ continue;
2433
+ }
2434
+
2435
+ result = redisGetReply(reContext, (void**)reply);
2436
+ if (result != REDIS_OK) {
2437
+ return ERR_REDIS_ERROR;
2438
+ }
2439
+
2440
+ if ((*reply)->type == REDIS_REPLY_ERROR) {
2441
+ printf("%s\n", (*reply)->str);
2442
+ freeReplyObject(*reply);
2443
+ return ERR_REDIS_ERROR;
2444
+ }
2445
+
2446
+ if ((*reply)->type == REDIS_REPLY_ARRAY) {
2447
+ *bindingContext = currentBinding;
2448
+ return RULES_OK;
2449
+ } else {
2450
+ freeReplyObject(*reply);
2451
+ }
2452
+ }
2453
+
2454
+ return ERR_NO_ACTION_AVAILABLE;
2455
+ }
2456
+
2457
+ unsigned int peekTimers(ruleset *tree, void **bindingContext, redisReply **reply) {
2458
+ bindingsList *list = tree->bindingsList;
2459
+ for (unsigned int i = 0; i < list->bindingsLength; ++i) {
2460
+ binding *currentBinding = &list->bindings[list->lastTimersBinding % list->bindingsLength];
2461
+ ++list->lastTimersBinding;
2462
+ redisContext *reContext = currentBinding->reContext;
2463
+ time_t currentTime = time(NULL);
2464
+
2465
+ int result = redisAppendCommand(reContext,
2466
+ "evalsha %s 0 %ld",
2467
+ currentBinding->timersHash,
2468
+ currentTime);
2469
+ if (result != REDIS_OK) {
2470
+ continue;
2471
+ }
2472
+
2473
+ result = redisGetReply(reContext, (void**)reply);
2474
+ if (result != REDIS_OK) {
2475
+ return ERR_REDIS_ERROR;
2476
+ }
2477
+
2478
+ if ((*reply)->type == REDIS_REPLY_ERROR) {
2479
+ freeReplyObject(*reply);
2480
+ return ERR_REDIS_ERROR;
2481
+ }
2482
+
2483
+ if ((*reply)->type == REDIS_REPLY_ARRAY) {
2484
+ *bindingContext = currentBinding;
2485
+ return RULES_OK;
2486
+ } else {
2487
+ freeReplyObject(*reply);
2488
+ }
2489
+ }
2490
+
2491
+ return ERR_NO_TIMERS_AVAILABLE;
2492
+ }
2493
+
2494
+ unsigned int registerTimer(void *rulesBinding, unsigned int duration, char *timer) {
2495
+ binding *currentBinding = (binding*)rulesBinding;
2496
+ redisContext *reContext = currentBinding->reContext;
2497
+ time_t currentTime = time(NULL);
2498
+
2499
+ int result = redisAppendCommand(reContext,
2500
+ "zadd %s %ld %s",
2501
+ currentBinding->timersSortedset,
2502
+ currentTime + duration,
2503
+ timer);
2504
+ if (result != REDIS_OK) {
2505
+ return ERR_REDIS_ERROR;
2506
+ }
2507
+
2508
+ redisReply *reply;
2509
+ result = redisGetReply(reContext, (void**)&reply);
2510
+ if (result != REDIS_OK) {
2511
+ return ERR_REDIS_ERROR;
2512
+ }
2513
+
2514
+ if (reply->type == REDIS_REPLY_ERROR) {
2515
+ freeReplyObject(reply);
2516
+ return ERR_REDIS_ERROR;
2517
+ }
2518
+
2519
+ freeReplyObject(reply);
2520
+ return RULES_OK;
2521
+ }
2522
+
2523
+ unsigned int getSession(void *rulesBinding, char *sid, char **state) {
2524
+ binding *currentBinding = (binding*)rulesBinding;
2525
+ redisContext *reContext = currentBinding->reContext;
2526
+ unsigned int result = redisAppendCommand(reContext,
2527
+ "hget %s %s",
2528
+ currentBinding->sessionHashset,
2529
+ sid);
2530
+ if (result != REDIS_OK) {
2531
+ return ERR_REDIS_ERROR;
2532
+ }
2533
+
2534
+ redisReply *reply;
2535
+ result = redisGetReply(reContext, (void**)&reply);
2536
+ if (result != REDIS_OK) {
2537
+ return ERR_REDIS_ERROR;
2538
+ }
2539
+
2540
+ if (reply->type == REDIS_REPLY_ERROR) {
2541
+ freeReplyObject(reply);
2542
+ return ERR_REDIS_ERROR;
2543
+ }
2544
+
2545
+ if (reply->type != REDIS_REPLY_STRING) {
2546
+ freeReplyObject(reply);
2547
+ return ERR_NEW_SESSION;
2548
+ }
2549
+
2550
+ *state = malloc(strlen(reply->str) * sizeof(char));
2551
+ if (!*state) {
2552
+ return ERR_OUT_OF_MEMORY;
2553
+ }
2554
+ strcpy(*state, reply->str);
2555
+ freeReplyObject(reply);
2556
+ return REDIS_OK;
2557
+ }
2558
+
2559
+