durable_rules 0.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/src/rules/rete.h ADDED
@@ -0,0 +1,133 @@
1
+ #include "state.h"
2
+
3
+ #define OP_NOP 0
4
+ #define OP_LT 0x01
5
+ #define OP_LTE 0x02
6
+ #define OP_GT 0x03
7
+ #define OP_GTE 0x04
8
+ #define OP_EQ 0x05
9
+ #define OP_NEQ 0x06
10
+ #define OP_EX 0x07
11
+ #define OP_NEX 0x08
12
+ #define OP_ALL 0x09
13
+ #define OP_ANY 0x0A
14
+ #define OP_OR 0x0B
15
+ #define OP_AND 0x0C
16
+ #define OP_END 0x0D
17
+ #define OP_ADD 0x0E
18
+ #define OP_SUB 0x0F
19
+ #define OP_MUL 0x10
20
+ #define OP_DIV 0x11
21
+ #define OP_TYPE 0x12
22
+ #define OP_NOT 0x13
23
+
24
+ #define NODE_ALPHA 0
25
+ #define NODE_BETA_CONNECTOR 1
26
+ #define NODE_ACTION 2
27
+ #define NODE_M_OFFSET 0
28
+
29
+ typedef struct reference {
30
+ unsigned int hash;
31
+ unsigned int nameOffset;
32
+ unsigned int idOffset;
33
+ } reference;
34
+
35
+ typedef struct jsonValue {
36
+ unsigned char type;
37
+ union {
38
+ long i;
39
+ double d;
40
+ unsigned char b;
41
+ unsigned int stringOffset;
42
+ unsigned int idiomOffset;
43
+ reference property;
44
+ } value;
45
+ } jsonValue;
46
+
47
+ typedef struct idiom {
48
+ unsigned char operator;
49
+ jsonValue left;
50
+ jsonValue right;
51
+ } idiom;
52
+
53
+ typedef struct expression {
54
+ unsigned int nameOffset;
55
+ unsigned int aliasOffset;
56
+ unsigned short termsLength;
57
+ unsigned char not;
58
+ union {
59
+ unsigned int termsOffset;
60
+ unsigned int *termsPointer;
61
+ } t;
62
+ } expression;
63
+
64
+ typedef struct join {
65
+ unsigned int expressionsOffset;
66
+ unsigned short expressionsLength;
67
+ } join;
68
+
69
+ typedef struct alpha {
70
+ unsigned int hash;
71
+ unsigned char operator;
72
+ unsigned int betaListOffset;
73
+ unsigned int nextListOffset;
74
+ unsigned int nextOffset;
75
+ jsonValue right;
76
+ } alpha;
77
+
78
+ typedef struct betaConnector {
79
+ unsigned int hash;
80
+ unsigned int nextOffset;
81
+ unsigned char not;
82
+ } betaConnector;
83
+
84
+ typedef struct action {
85
+ unsigned int index;
86
+ unsigned short span;
87
+ unsigned short count;
88
+ unsigned short cap;
89
+ unsigned int partitionBy;
90
+ unsigned short priority;
91
+ unsigned int joinsOffset;
92
+ unsigned short joinsLength;
93
+ } action;
94
+
95
+ typedef struct node {
96
+ unsigned int nameOffset;
97
+ unsigned char type;
98
+ union {
99
+ alpha a;
100
+ betaConnector b;
101
+ action c;
102
+ } value;
103
+ } node;
104
+
105
+ typedef struct ruleset {
106
+ unsigned int nameOffset;
107
+ node *nodePool;
108
+ unsigned int nodeOffset;
109
+ unsigned int *nextPool;
110
+ unsigned int nextOffset;
111
+ char *stringPool;
112
+ unsigned int stringPoolLength;
113
+ expression *expressionPool;
114
+ unsigned int expressionOffset;
115
+ idiom *idiomPool;
116
+ unsigned int idiomOffset;
117
+ join *joinPool;
118
+ unsigned int joinOffset;
119
+ unsigned int actionCount;
120
+ void *bindingsList;
121
+ unsigned int *stateBuckets;
122
+ unsigned int stateBucketsLength;
123
+ stateEntry *state;
124
+ unsigned int maxStateLength;
125
+ unsigned int stateLength;
126
+ unsigned int lruStateOffset;
127
+ unsigned int mruStateOffset;
128
+ unsigned int orNodeOffset;
129
+ unsigned int andNodeOffset;
130
+ unsigned int endNodeOffset;
131
+ } ruleset;
132
+
133
+
data/src/rules/rules.h ADDED
@@ -0,0 +1,171 @@
1
+
2
+ #define RULES_OK 0
3
+ #define ERR_OUT_OF_MEMORY 1
4
+ #define ERR_UNEXPECTED_TYPE 2
5
+ #define ERR_IDENTIFIER_LENGTH 3
6
+ #define ERR_UNEXPECTED_VALUE 4
7
+ #define ERR_UNEXPECTED_NAME 5
8
+ #define ERR_RULE_LIMIT_EXCEEDED 6
9
+ #define ERR_RULE_EXISTS_LIMIT_EXCEEDED 7
10
+ #define ERR_RULE_BETA_LIMIT_EXCEEDED 8
11
+ #define ERR_RULE_WITHOUT_QUALIFIER 9
12
+ #define ERR_SETTING_NOT_FOUND 10
13
+ #define ERR_PARSE_VALUE 101
14
+ #define ERR_PARSE_STRING 102
15
+ #define ERR_PARSE_NUMBER 103
16
+ #define ERR_PARSE_OBJECT 104
17
+ #define ERR_PARSE_ARRAY 105
18
+ #define ERR_EVENT_NOT_HANDLED 201
19
+ #define ERR_EVENT_MAX_PROPERTIES 202
20
+ #define ERR_MAX_STACK_SIZE 203
21
+ #define ERR_NO_ID_DEFINED 204
22
+ #define ERR_INVALID_ID 205
23
+ #define ERR_PARSE_PATH 206
24
+ #define ERR_MAX_NODE_RESULTS 207
25
+ #define ERR_MAX_RESULT_NODES 208
26
+ #define ERR_MAX_COMMAND_COUNT 209
27
+ #define ERR_MAX_ADD_COUNT 210
28
+ #define ERR_MAX_EVAL_COUNT 211
29
+ #define ERR_CONNECT_REDIS 301
30
+ #define ERR_REDIS_ERROR 302
31
+ #define ERR_NO_ACTION_AVAILABLE 303
32
+ #define ERR_NO_TIMERS_AVAILABLE 304
33
+ #define ERR_NEW_SESSION 305
34
+ #define ERR_STATE_CACHE_FULL 401
35
+ #define ERR_BINDING_NOT_MAPPED 402
36
+ #define ERR_STATE_NOT_LOADED 403
37
+ #define ERR_STALE_STATE 404
38
+ #define ERR_PROPERTY_NOT_FOUND 405
39
+
40
+ #define NEXT_BUCKET_LENGTH 512
41
+ #define NEXT_LIST_LENGTH 32
42
+ #define BETA_LIST_LENGTH 16
43
+ #define HASH_MASK 0x1F
44
+
45
+ #ifdef __cplusplus
46
+ extern "C" {
47
+ #endif
48
+
49
+ unsigned int createRuleset(void **handle,
50
+ char *name,
51
+ char *rules,
52
+ unsigned int stateCaheSize);
53
+
54
+ unsigned int deleteRuleset(void *handle);
55
+
56
+ unsigned int bindRuleset(void *handle,
57
+ char *host,
58
+ unsigned int port,
59
+ char *password);
60
+
61
+ unsigned int complete(void *rulesBinding,
62
+ unsigned int replyCount);
63
+
64
+ unsigned int assertEvent(void *handle,
65
+ char *message);
66
+
67
+ unsigned int startAssertEvent(void *handle,
68
+ char *message,
69
+ void **rulesBinding,
70
+ unsigned int *replyCount);
71
+
72
+ unsigned int assertEvents(void *handle,
73
+ char *messages,
74
+ unsigned int *resultsLength,
75
+ unsigned int **results);
76
+
77
+ unsigned int startAssertEvents(void *handle,
78
+ char *messages,
79
+ unsigned int *resultsLength,
80
+ unsigned int **results,
81
+ void **rulesBinding,
82
+ unsigned int *replyCount);
83
+
84
+ unsigned int retractEvent(void *handle,
85
+ char *message);
86
+
87
+ unsigned int startAssertFact(void *handle,
88
+ char *message,
89
+ void **rulesBinding,
90
+ unsigned int *replyCount);
91
+
92
+ unsigned int assertFact(void *handle,
93
+ char *message);
94
+
95
+ unsigned int startAssertFacts(void *handle,
96
+ char *messages,
97
+ unsigned int *resultsLength,
98
+ unsigned int **results,
99
+ void **rulesBinding,
100
+ unsigned int *replyCount);
101
+
102
+ unsigned int assertFacts(void *handle,
103
+ char *messages,
104
+ unsigned int *resultsLength,
105
+ unsigned int **results);
106
+
107
+ unsigned int retractFact(void *handle,
108
+ char *message);
109
+
110
+ unsigned int startRetractFact(void *handle,
111
+ char *message,
112
+ void **rulesBinding,
113
+ unsigned int *replyCount);
114
+
115
+ unsigned int retractFacts(void *handle,
116
+ char *messages,
117
+ unsigned int *resultsLength,
118
+ unsigned int **results);
119
+
120
+ unsigned int startRetractFacts(void *handle,
121
+ char *messages,
122
+ unsigned int *resultsLength,
123
+ unsigned int **results,
124
+ void **rulesBinding,
125
+ unsigned int *replyCount);
126
+
127
+ unsigned int startUpdateState(void *handle,
128
+ void *actionHandle,
129
+ char *state,
130
+ void **rulesBinding,
131
+ unsigned int *replyCount);
132
+
133
+ unsigned int assertState(void *handle,
134
+ char *state);
135
+
136
+ unsigned int startAction(void *handle,
137
+ char **state,
138
+ char **messages,
139
+ void **actionHandle,
140
+ void **actionBinding);
141
+
142
+ unsigned int completeAction(void *handle,
143
+ void *actionHandle,
144
+ char *state);
145
+
146
+ unsigned int completeAndStartAction(void *handle,
147
+ unsigned int expectedReplies,
148
+ void *actionHandle,
149
+ char **messages);
150
+
151
+ unsigned int abandonAction(void *handle,
152
+ void *actionHandle);
153
+
154
+ unsigned int startTimer(void *handle,
155
+ char *sid,
156
+ unsigned int duration,
157
+ char *timer);
158
+
159
+ unsigned int assertTimers(void *handle);
160
+
161
+ unsigned int getState(void *handle,
162
+ char *sid,
163
+ char **state);
164
+
165
+ #ifdef _WIN32
166
+ int asprintf(char** ret, char* format, ...);
167
+ #endif
168
+
169
+ #ifdef __cplusplus
170
+ }
171
+ #endif
data/src/rules/state.c ADDED
@@ -0,0 +1,412 @@
1
+ #include <stdio.h>
2
+ #include <stdlib.h>
3
+ #include <string.h>
4
+ #include <time.h>
5
+ #include "rules.h"
6
+ #include "json.h"
7
+ #include "net.h"
8
+
9
+ unsigned int djbHash(char *str, unsigned int len) {
10
+ unsigned int hash = 5381;
11
+ unsigned int i = 0;
12
+
13
+ for(i = 0; i < len; str++, i++) {
14
+ hash = ((hash << 5) + hash) + (*str);
15
+ }
16
+
17
+ return hash;
18
+ }
19
+
20
+ static unsigned int evictEntry(ruleset *tree) {
21
+ stateEntry *lruEntry = &tree->state[tree->lruStateOffset];
22
+ unsigned int lruBucket = lruEntry->sidHash % tree->stateBucketsLength;
23
+ unsigned int offset = tree->stateBuckets[lruBucket];
24
+ unsigned int lastOffset = UNDEFINED_HASH_OFFSET;
25
+ unsigned char found = 0;
26
+ while (!found) {
27
+ stateEntry *current = &tree->state[offset];
28
+ if (current->sidHash == lruEntry->sidHash) {
29
+ if (!strcmp(current->sid, lruEntry->sid)) {
30
+ if (lastOffset == UNDEFINED_HASH_OFFSET) {
31
+ tree->stateBuckets[lruBucket] = current->nextHashOffset;
32
+ } else {
33
+ tree->state[lastOffset].nextHashOffset = current->nextHashOffset;
34
+ }
35
+
36
+ if (current->state) {
37
+ free(current->state);
38
+ current->state = NULL;
39
+ }
40
+
41
+ free(current->sid);
42
+ current->sid = NULL;
43
+ found = 1;
44
+ }
45
+ }
46
+
47
+ lastOffset = offset;
48
+ offset = current->nextHashOffset;
49
+ }
50
+
51
+ unsigned int result = tree->lruStateOffset;
52
+ tree->lruStateOffset = lruEntry->nextLruOffset;
53
+ tree->state[tree->lruStateOffset].prevLruOffset = UNDEFINED_HASH_OFFSET;
54
+ return result;
55
+ }
56
+
57
+ static unsigned int addEntry(ruleset *tree, char *sid, unsigned int sidHash) {
58
+ unsigned newOffset;
59
+ if (tree->stateLength == tree->maxStateLength) {
60
+ newOffset = evictEntry(tree);
61
+ } else {
62
+ newOffset = tree->stateLength;
63
+ ++tree->stateLength;
64
+ }
65
+
66
+ stateEntry *current = &tree->state[newOffset];
67
+ current->prevLruOffset = UNDEFINED_HASH_OFFSET;
68
+ current->nextLruOffset = UNDEFINED_HASH_OFFSET;
69
+ current->nextHashOffset = UNDEFINED_HASH_OFFSET;
70
+ current->sid = malloc(strlen(sid) + 1);
71
+ strcpy(current->sid, sid);
72
+ current->sidHash = sidHash;
73
+ unsigned int bucket = sidHash % tree->stateBucketsLength;
74
+ unsigned int offset = tree->stateBuckets[bucket];
75
+ if (offset == UNDEFINED_HASH_OFFSET) {
76
+ tree->stateBuckets[bucket] = newOffset;
77
+ }
78
+ else {
79
+ while (1) {
80
+ current = &tree->state[offset];
81
+ if (current->nextHashOffset == UNDEFINED_HASH_OFFSET) {
82
+ current->nextHashOffset = newOffset;
83
+ break;
84
+ }
85
+
86
+ offset = current->nextHashOffset;
87
+ }
88
+ }
89
+
90
+ return newOffset;
91
+ }
92
+
93
+ static unsigned char ensureEntry(ruleset *tree, char *sid, unsigned int sidHash, stateEntry **result) {
94
+ unsigned int bucket = sidHash % tree->stateBucketsLength;
95
+ unsigned int offset = tree->stateBuckets[bucket];
96
+ stateEntry *current = NULL;
97
+ unsigned char found = 0;
98
+ while (offset != UNDEFINED_HASH_OFFSET) {
99
+ current = &tree->state[offset];
100
+ if (current->sidHash == sidHash) {
101
+ if (!strcmp(current->sid, sid)) {
102
+ found = 1;
103
+ break;
104
+ }
105
+ }
106
+
107
+ offset = current->nextHashOffset;
108
+ }
109
+ if (offset == UNDEFINED_HASH_OFFSET) {
110
+ offset = addEntry(tree, sid, sidHash);
111
+ current = &tree->state[offset];
112
+ }
113
+
114
+ if (current->prevLruOffset != UNDEFINED_HASH_OFFSET) {
115
+ tree->state[current->prevLruOffset].nextLruOffset = current->nextLruOffset;
116
+ }
117
+
118
+ if (current->nextLruOffset != UNDEFINED_HASH_OFFSET) {
119
+ tree->state[current->nextLruOffset].prevLruOffset = current->prevLruOffset;
120
+ }
121
+
122
+ current->prevLruOffset = tree->mruStateOffset;
123
+ current->nextLruOffset = UNDEFINED_HASH_OFFSET;
124
+ if (tree->mruStateOffset == UNDEFINED_HASH_OFFSET) {
125
+ tree->lruStateOffset = offset;
126
+ } else {
127
+ tree->state[tree->mruStateOffset].nextLruOffset = offset;
128
+ }
129
+
130
+ tree->mruStateOffset = offset;
131
+ *result = current;
132
+ return found;
133
+ }
134
+
135
+ static stateEntry *getEntry(ruleset *tree, char *sid, unsigned int sidHash) {
136
+ unsigned int bucket = sidHash % tree->stateBucketsLength;
137
+ unsigned int offset = tree->stateBuckets[bucket];
138
+ while (offset != UNDEFINED_HASH_OFFSET) {
139
+ stateEntry *current = &tree->state[offset];
140
+ if (current->sidHash == sidHash) {
141
+ if (!strcmp(current->sid, sid)) {
142
+ return current;
143
+ }
144
+ }
145
+
146
+ offset = current->nextHashOffset;
147
+ }
148
+
149
+ return NULL;
150
+ }
151
+
152
+ unsigned int constructObject(char *parentName,
153
+ char *object,
154
+ char createHashtable,
155
+ unsigned int maxProperties,
156
+ jsonProperty *properties,
157
+ unsigned int *propertiesLength,
158
+ unsigned int *midIndex,
159
+ unsigned int *sidIndex,
160
+ char **next) {
161
+ char *firstName;
162
+ char *lastName;
163
+ char *first;
164
+ char *last;
165
+ unsigned char type;
166
+ unsigned int hash;
167
+ int parentNameLength = (parentName ? strlen(parentName): 0);
168
+ unsigned int result = readNextName(object, &firstName, &lastName, &hash);
169
+ while (result == PARSE_OK) {
170
+ result = readNextValue(lastName, &first, &last, &type);
171
+ if (result != PARSE_OK) {
172
+ return result;
173
+ }
174
+
175
+ if (!parentName) {
176
+ if (type == JSON_OBJECT) {
177
+ int nameLength = lastName - firstName;
178
+ #ifdef _WIN32
179
+ char *newParent = (char *)_alloca(sizeof(char)*(nameLength + 1));
180
+ #else
181
+ char newParent[nameLength + 1];
182
+ #endif
183
+ strncpy(newParent, firstName, nameLength);
184
+ newParent[nameLength] = '\0';
185
+ return constructObject(newParent,
186
+ first,
187
+ createHashtable,
188
+ maxProperties,
189
+ properties,
190
+ propertiesLength,
191
+ midIndex,
192
+ sidIndex,
193
+ next);
194
+ }
195
+ } else {
196
+ int nameLength = lastName - firstName;
197
+ int fullNameLength = nameLength + parentNameLength + 1;
198
+ #ifdef _WIN32
199
+ char *fullName = (char *)_alloca(sizeof(char)*(fullNameLength + 1));
200
+ #else
201
+ char fullName[fullNameLength + 1];
202
+ #endif
203
+ strncpy(fullName, firstName, nameLength);
204
+ fullName[nameLength] = '.';
205
+ strncpy(&fullName[nameLength + 1], parentName, parentNameLength);
206
+ fullName[fullNameLength] = '\0';
207
+ hash = djbHash(fullName, fullNameLength);
208
+ if (type == JSON_OBJECT) {
209
+ return constructObject(fullName,
210
+ first,
211
+ createHashtable,
212
+ maxProperties,
213
+ properties,
214
+ propertiesLength,
215
+ midIndex,
216
+ sidIndex,
217
+ next);
218
+ }
219
+ }
220
+
221
+ jsonProperty *property = NULL;
222
+ if (!createHashtable) {
223
+ property = &properties[*propertiesLength];
224
+ if (hash == HASH_ID) {
225
+ *midIndex = *propertiesLength;
226
+ } else if (hash == HASH_SID) {
227
+ *sidIndex = *propertiesLength;
228
+ }
229
+ } else {
230
+ unsigned int candidate = hash % maxProperties;
231
+ while (properties[candidate].type != 0) {
232
+ candidate = (candidate + 1) % maxProperties;
233
+ }
234
+
235
+ if (hash == HASH_ID) {
236
+ *midIndex = candidate;
237
+ } else if (hash == HASH_SID) {
238
+ *sidIndex = candidate;
239
+ }
240
+
241
+ property = &properties[candidate];
242
+ }
243
+
244
+
245
+
246
+ *propertiesLength = *propertiesLength + 1;
247
+ if (*propertiesLength == maxProperties) {
248
+ return ERR_EVENT_MAX_PROPERTIES;
249
+ }
250
+
251
+ property->isMaterial = 0;
252
+ property->hash = hash;
253
+ property->valueOffset = first - object;
254
+ property->valueLength = last - first;
255
+ property->nameOffset = firstName - object;
256
+ property->nameLength = lastName - firstName;
257
+ property->type = type;
258
+ *next = last;
259
+ result = readNextName(last, &firstName, &lastName, &hash);
260
+ }
261
+
262
+ return (result == PARSE_END ? RULES_OK: result);
263
+ }
264
+
265
+ void rehydrateProperty(jsonProperty *property, char *state) {
266
+ // ID and SID are treated as strings regardless of type
267
+ // to avoid unnecessary conversions
268
+ if (!property->isMaterial && property->hash != HASH_ID && property->hash != HASH_SID) {
269
+ unsigned short propertyLength = property->valueLength + 1;
270
+ char *propertyFirst = state + property->valueOffset;
271
+ unsigned char propertyType = property->type;
272
+ unsigned char b = 0;
273
+ char temp;
274
+
275
+ switch(propertyType) {
276
+ case JSON_INT:
277
+ temp = propertyFirst[propertyLength];
278
+ propertyFirst[propertyLength] = '\0';
279
+ property->value.i = atol(propertyFirst);
280
+ propertyFirst[propertyLength] = temp;
281
+ break;
282
+ case JSON_DOUBLE:
283
+ temp = propertyFirst[propertyLength];
284
+ propertyFirst[propertyLength] = '\0';
285
+ property->value.d = atof(propertyFirst);
286
+ propertyFirst[propertyLength] = temp;
287
+ break;
288
+ case JSON_BOOL:
289
+ if (propertyLength == 4 && strncmp("true", propertyFirst, 4) == 0) {
290
+ b = 1;
291
+ }
292
+
293
+ property->value.b = b;
294
+ break;
295
+ }
296
+
297
+ property->isMaterial = 1;
298
+ }
299
+ }
300
+
301
+ static unsigned int resolveBindingAndEntry(ruleset *tree,
302
+ char *sid,
303
+ stateEntry **entry,
304
+ void **rulesBinding) {
305
+ unsigned int sidHash = djbHash(sid, strlen(sid));
306
+ if (!ensureEntry(tree, sid, sidHash, entry)) {
307
+ unsigned int result = getBindingIndex(tree, sidHash, &(*entry)->bindingIndex);
308
+ if (result != RULES_OK) {
309
+ return result;
310
+ }
311
+ }
312
+
313
+ bindingsList *list = tree->bindingsList;
314
+ *rulesBinding = &list->bindings[(*entry)->bindingIndex];
315
+ return RULES_OK;
316
+ }
317
+
318
+ unsigned int resolveBinding(void *handle,
319
+ char *sid,
320
+ void **rulesBinding) {
321
+ stateEntry *entry = NULL;
322
+ return resolveBindingAndEntry(handle, sid, &entry, rulesBinding);
323
+ }
324
+
325
+ unsigned int refreshState(void *handle,
326
+ char *sid) {
327
+ unsigned int result;
328
+ stateEntry *entry = NULL;
329
+ void *rulesBinding;
330
+ result = resolveBindingAndEntry(handle, sid, &entry, &rulesBinding);
331
+ if (result != RULES_OK) {
332
+ return result;
333
+ }
334
+
335
+ if (entry->state) {
336
+ free(entry->state);
337
+ entry->state = NULL;
338
+ }
339
+
340
+ result = getSession(rulesBinding, sid, &entry->state);
341
+ if (result != RULES_OK) {
342
+ if (result == ERR_NEW_SESSION) {
343
+ entry->lastRefresh = time(NULL);
344
+ }
345
+ return result;
346
+ }
347
+
348
+ memset(entry->properties, 0, MAX_STATE_PROPERTIES * sizeof(jsonProperty));
349
+ entry->propertiesLength = 0;
350
+ char *next;
351
+ unsigned int midIndex = UNDEFINED_INDEX;
352
+ unsigned int sidIndex = UNDEFINED_INDEX;
353
+ result = constructObject(NULL,
354
+ entry->state,
355
+ 1,
356
+ MAX_STATE_PROPERTIES,
357
+ entry->properties,
358
+ &entry->propertiesLength,
359
+ &midIndex,
360
+ &sidIndex,
361
+ &next);
362
+ if (result != RULES_OK) {
363
+ return result;
364
+ }
365
+
366
+ entry->lastRefresh = time(NULL);
367
+ return RULES_OK;
368
+ }
369
+
370
+ unsigned int fetchStateProperty(void *tree,
371
+ char *sid,
372
+ unsigned int propertyHash,
373
+ unsigned int maxTime,
374
+ unsigned char ignoreStaleState,
375
+ char **state,
376
+ jsonProperty **property) {
377
+ unsigned int sidHash = djbHash(sid, strlen(sid));
378
+ stateEntry *entry = getEntry(tree, sid, sidHash);
379
+ if (entry == NULL || entry->lastRefresh == 0) {
380
+ return ERR_STATE_NOT_LOADED;
381
+ }
382
+
383
+ if (!ignoreStaleState && (time(NULL) - entry->lastRefresh > maxTime)) {
384
+ return ERR_STALE_STATE;
385
+ }
386
+
387
+ unsigned int propertyIndex = propertyHash % MAX_STATE_PROPERTIES;
388
+ jsonProperty *result = &entry->properties[propertyIndex];
389
+ while (result->type != 0 && result->hash != propertyHash) {
390
+ propertyIndex = (propertyIndex + 1) % MAX_STATE_PROPERTIES;
391
+ result = &entry->properties[propertyIndex];
392
+ }
393
+
394
+ if (!result->type) {
395
+ return ERR_PROPERTY_NOT_FOUND;
396
+ }
397
+
398
+ *state = entry->state;
399
+ rehydrateProperty(result, *state);
400
+ *property = result;
401
+ return RULES_OK;
402
+ }
403
+
404
+ unsigned int getState(void *handle, char *sid, char **state) {
405
+ void *rulesBinding = NULL;
406
+ unsigned int result = resolveBinding(handle, sid, &rulesBinding);
407
+ if (result != RULES_OK) {
408
+ return result;
409
+ }
410
+
411
+ return getSession(rulesBinding, sid, state);
412
+ }