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/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
+ }