durable_rules 0.31.1

Sign up to get free protection for your applications and to get access to all the features.
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
+