durable_rules 0.34.29 → 0.34.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/src/rules/json.c CHANGED
@@ -395,7 +395,7 @@ static unsigned int getString(char *start, char **first, char **last) {
395
395
  static unsigned int getStringAndHash(char *start, char **first, char **last, unsigned int *hash) {
396
396
  unsigned char state = ST_STRING_SEEK;
397
397
  char delimiter = '\0';
398
- unsigned int currentHash = 5381;
398
+ unsigned int currentHash = FNV_32_OFFSET_BASIS;
399
399
  while(start[0] != '\0') {
400
400
  switch (state) {
401
401
  case ST_STRING_SEEK:
@@ -417,11 +417,14 @@ static unsigned int getStringAndHash(char *start, char **first, char **last, uns
417
417
  state = ST_STRING_ESC;
418
418
  }
419
419
 
420
- currentHash = ((currentHash << 5) + currentHash) + (*start);
420
+ currentHash ^= (*start);
421
+ currentHash *= FNV_32_PRIME;
421
422
  break;
422
423
  case ST_STRING_ESC:
423
424
  state = ST_STRING_PARSE;
424
- currentHash = ((currentHash << 5) + currentHash) + (*start);
425
+
426
+ currentHash ^= (*start);
427
+ currentHash *= FNV_32_PRIME;
425
428
  break;
426
429
  }
427
430
 
@@ -430,3 +433,4 @@ static unsigned int getStringAndHash(char *start, char **first, char **last, uns
430
433
 
431
434
  return ERR_PARSE_STRING;
432
435
  }
436
+
data/src/rules/json.h CHANGED
@@ -17,6 +17,9 @@
17
17
  #define JSON_REGEX 0x0C
18
18
  #define JSON_IREGEX 0x0D
19
19
 
20
+ #define FNV_32_OFFSET_BASIS 0x811c9dc5
21
+ #define FNV_32_PRIME 16777619
22
+
20
23
  unsigned int readNextName(char *start, char **first, char **last, unsigned int *hash);
21
24
  unsigned int readNextValue(char *start, char **first, char **last, unsigned char *type);
22
25
  unsigned int readNextArrayValue(char *start, char **first, char **last, unsigned char *type);
data/src/rules/net.c CHANGED
@@ -686,7 +686,12 @@ static unsigned int loadAddMessageCommand(ruleset *tree, binding *rulesBinding)
686
686
  if (asprintf(&lua,
687
687
  "local sid = ARGV[1]\n"
688
688
  "local assert_fact = tonumber(ARGV[2])\n"
689
- "local keys_count = tonumber(ARGV[3])\n"
689
+ // when mark visited is set to 0, it means eval will be called immedately
690
+ // with the same message. In that case, the function will avid setting
691
+ // the visited bit (for events), storing the message and increasing the
692
+ // mid count (for the case of auto generated mid)
693
+ "local mark_visited = tonumber(ARGV[3])\n"
694
+ "local keys_count = tonumber(ARGV[4])\n"
690
695
  "local events_hashset = \"%s!e!\" .. sid\n"
691
696
  "local facts_hashset = \"%s!f!\" .. sid\n"
692
697
  "local visited_hashset = \"%s!v!\" .. sid\n"
@@ -694,12 +699,14 @@ static unsigned int loadAddMessageCommand(ruleset *tree, binding *rulesBinding)
694
699
  "local message = {}\n"
695
700
  "local primary_message_keys = {}\n"
696
701
  "local input_keys = {}\n"
697
- "local save_message = function(current_key, message, events_key, messages_key)\n"
698
- " redis.call(\"hsetnx\", messages_key, message[\"id\"], cmsgpack.pack(message))\n"
702
+ "local save_message = function(current_key, message, events_key, messages_key, save_hashset)\n"
703
+ " if save_hashset == 1 then\n"
704
+ " redis.call(\"hsetnx\", messages_key, message[\"id\"], cmsgpack.pack(message))\n"
705
+ " end\n"
699
706
  " local primary_key = primary_message_keys[current_key](message)\n"
700
707
  " redis.call(\"lpush\", events_key .. \"!m!\" .. primary_key, message[\"id\"])\n"
701
708
  "end\n"
702
- "for index = 4 + keys_count, #ARGV, 3 do\n"
709
+ "for index = 5 + keys_count, #ARGV, 3 do\n"
703
710
  " if ARGV[index + 2] == \"1\" then\n"
704
711
  " message[ARGV[index]] = ARGV[index + 1]\n"
705
712
  " elseif ARGV[index + 2] == \"2\" or ARGV[index + 2] == \"3\" then\n"
@@ -717,35 +724,41 @@ static unsigned int loadAddMessageCommand(ruleset *tree, binding *rulesBinding)
717
724
  " end\n"
718
725
  "end\n"
719
726
  "local mid = message[\"id\"]\n"
720
- "if not mid then\n"
721
- " mid = \"$m-\" .. redis.call(\"hincrby\", mid_count_hashset, sid, 1)\n"
727
+ "if mid == \"\" then\n"
728
+ " if mark_visited == 0 then\n"
729
+ " mid = \"$m-\" .. redis.call(\"hget\", mid_count_hashset, sid) + 1\n"
730
+ " else\n"
731
+ " mid = \"$m-\" .. redis.call(\"hincrby\", mid_count_hashset, sid, 1)\n"
732
+ " end\n"
722
733
  " message[\"id\"] = mid\n"
723
734
  "else\n"
724
- " if redis.call(\"hsetnx\", visited_hashset, mid, 1) == 0 then\n"
725
- " if assert_fact == 0 then\n"
726
- " if not redis.call(\"hget\", events_hashset, mid) then\n"
727
- " return %d\n"
728
- " end\n"
729
- " else\n"
730
- " if not redis.call(\"hget\", facts_hashset, mid) then\n"
735
+ " if assert_fact == 1 then\n"
736
+ " if redis.call(\"hexists\", facts_hashset, mid) == 1 then\n"
737
+ " return %d\n"
738
+ " end\n"
739
+ " else\n"
740
+ " if mark_visited == 1 then\n"
741
+ " if redis.call(\"hsetnx\", visited_hashset, mid, 1) == 0 then\n"
731
742
  " return %d\n"
732
743
  " end\n"
744
+ " elseif redis.call(\"hexists\", visited_hashset, mid) == 1 then \n"
745
+ " return %d\n"
733
746
  " end\n"
734
747
  " end\n"
735
748
  "end\n"
736
- "for index = 4, 3 + keys_count, 1 do\n"
749
+ "for index = 5, 4 + keys_count, 1 do\n"
737
750
  " input_keys[ARGV[index]] = true\n"
738
751
  "end\n"
739
752
  "%sif assert_fact == 1 then\n"
740
753
  " message[\"$f\"] = 1\n"
741
- " for index = 4, 3 + keys_count, 1 do\n"
754
+ " for index = 5, 4 + keys_count, 1 do\n"
742
755
  " local key = ARGV[index]\n"
743
- " save_message(key, message, key .. \"!f!\" .. sid, facts_hashset)\n"
756
+ " save_message(key, message, key .. \"!f!\" .. sid, facts_hashset, mark_visited)\n"
744
757
  " end\n"
745
758
  "else\n"
746
- " for index = 4, 3 + keys_count, 1 do\n"
759
+ " for index = 5, 4 + keys_count, 1 do\n"
747
760
  " local key = ARGV[index]\n"
748
- " save_message(key, message, key .. \"!e!\" .. sid, events_hashset)\n"
761
+ " save_message(key, message, key .. \"!e!\" .. sid, events_hashset, mark_visited)\n"
749
762
  " end\n"
750
763
  "end\n",
751
764
  name,
@@ -754,6 +767,7 @@ static unsigned int loadAddMessageCommand(ruleset *tree, binding *rulesBinding)
754
767
  name,
755
768
  ERR_EVENT_OBSERVED,
756
769
  ERR_EVENT_OBSERVED,
770
+ ERR_EVENT_OBSERVED,
757
771
  addMessageLua) == -1) {
758
772
  return ERR_OUT_OF_MEMORY;
759
773
  }
@@ -947,6 +961,7 @@ static unsigned int loadPeekActionCommand(ruleset *tree, binding *rulesBinding)
947
961
  if (asprintf(&lua,
948
962
  "local facts_key = \"%s!f!\"\n"
949
963
  "local events_key = \"%s!e!\"\n"
964
+ "local visited_key = \"%s!v!\"\n"
950
965
  "local action_key = \"%s!a\"\n"
951
966
  "local state_key = \"%s!s\"\n"
952
967
  "local timers_key = \"%s!t\"\n"
@@ -1040,7 +1055,7 @@ static unsigned int loadPeekActionCommand(ruleset *tree, binding *rulesBinding)
1040
1055
  " if cancel then\n"
1041
1056
  " for i = 1, #frame, 1 do\n"
1042
1057
  " if type(frame[i]) == \"table\" then\n"
1043
- " redis.call(\"hsetnx\", events_hashset, frame[i][\"id\"], cmsgpack.pack(frame[i]))\n"
1058
+ " redis.call(\"hdel\", visited_key .. sid, frame[i][\"id\"])\n"
1044
1059
  " redis.call(\"zadd\", timers_key, max_score, \"p:\" .. cjson.encode(frame[i]))\n"
1045
1060
  " end\n"
1046
1061
  " end\n"
@@ -1211,6 +1226,7 @@ static unsigned int loadPeekActionCommand(ruleset *tree, binding *rulesBinding)
1211
1226
  name,
1212
1227
  name,
1213
1228
  name,
1229
+ name,
1214
1230
  peekActionLua) == -1) {
1215
1231
  return ERR_OUT_OF_MEMORY;
1216
1232
  }
@@ -2095,15 +2111,13 @@ static unsigned int loadEvalMessageCommand(ruleset *tree, binding *rulesBinding)
2095
2111
  " message[\"id\"] = mid\n"
2096
2112
  " end\n"
2097
2113
  "else\n"
2098
- " if redis.call(\"hsetnx\", visited_hashset, mid, 1) == 0 then\n"
2099
- " if assert_fact == 0 then\n"
2100
- " if message and redis.call(\"hexists\", events_hashset, mid) == 0 then\n"
2101
- " return %d\n"
2102
- " end\n"
2103
- " else\n"
2104
- " if message and redis.call(\"hexists\", facts_hashset, mid) == 0 then\n"
2105
- " return %d\n"
2106
- " end\n"
2114
+ " if assert_fact == 1 then\n"
2115
+ " if message and redis.call(\"hexists\", facts_hashset, mid) == 1 then\n"
2116
+ " return %d\n"
2117
+ " end\n"
2118
+ " else\n"
2119
+ " if message and redis.call(\"hsetnx\", visited_hashset, mid, 1) == 0 then\n"
2120
+ " return %d\n"
2107
2121
  " end\n"
2108
2122
  " end\n"
2109
2123
  "end\n"
@@ -2354,12 +2368,12 @@ unsigned int formatEvalMessage(void *rulesBinding,
2354
2368
  char *sid,
2355
2369
  char *mid,
2356
2370
  char *message,
2357
- jsonProperty *allProperties,
2358
- unsigned int propertiesLength,
2371
+ jsonObject *jo,
2359
2372
  unsigned char actionType,
2360
2373
  char **keys,
2361
2374
  unsigned int keysLength,
2362
2375
  char **command) {
2376
+ unsigned int propertiesLength = jo->propertiesLength;
2363
2377
  if (actionType == ACTION_RETRACT_FACT || actionType == ACTION_RETRACT_EVENT) {
2364
2378
  propertiesLength = 0;
2365
2379
  }
@@ -2404,16 +2418,16 @@ unsigned int formatEvalMessage(void *rulesBinding,
2404
2418
 
2405
2419
  unsigned int offset = 8 + keysLength;
2406
2420
  for (unsigned int i = 0; i < propertiesLength; ++i) {
2407
- argv[offset + i * 3] = allProperties[i].name;
2408
- argvl[offset + i * 3] = allProperties[i].nameLength;
2409
- argv[offset + i * 3 + 1] = message + allProperties[i].valueOffset;
2410
- if (allProperties[i].type == JSON_STRING) {
2411
- argvl[offset + i * 3 + 1] = allProperties[i].valueLength;
2421
+ argv[offset + i * 3] = jo->properties[i].name;
2422
+ argvl[offset + i * 3] = jo->properties[i].nameLength;
2423
+ argv[offset + i * 3 + 1] = jo->properties[i].valueString;
2424
+ if (jo->properties[i].type == JSON_STRING) {
2425
+ argvl[offset + i * 3 + 1] = jo->properties[i].valueLength;
2412
2426
  } else {
2413
- argvl[offset + i * 3 + 1] = allProperties[i].valueLength + 1;
2427
+ argvl[offset + i * 3 + 1] = jo->properties[i].valueLength + 1;
2414
2428
  }
2415
2429
 
2416
- switch(allProperties[i].type) {
2430
+ switch(jo->properties[i].type) {
2417
2431
  case JSON_STRING:
2418
2432
  argv[offset + i * 3 + 2] = "1";
2419
2433
  break;
@@ -2446,22 +2460,23 @@ unsigned int formatEvalMessage(void *rulesBinding,
2446
2460
  unsigned int formatStoreMessage(void *rulesBinding,
2447
2461
  char *sid,
2448
2462
  char *message,
2449
- jsonProperty *allProperties,
2450
- unsigned int propertiesLength,
2463
+ jsonObject *jo,
2451
2464
  unsigned char storeFact,
2465
+ unsigned char markVisited,
2452
2466
  char **keys,
2453
2467
  unsigned int keysLength,
2454
2468
  char **command) {
2469
+ unsigned int propertiesLength = jo->propertiesLength;
2455
2470
  binding *bindingContext = (binding*)rulesBinding;
2456
2471
  char keysLengthString[5];
2457
2472
  #ifdef _WIN32
2458
2473
  sprintf_s(keysLengthString, sizeof(char) * 5, "%d", keysLength);
2459
- char **argv = (char **)_alloca(sizeof(char*)*(6 + keysLength + propertiesLength * 3));
2460
- size_t *argvl = (size_t *)_alloca(sizeof(size_t)*(6 + keysLength + propertiesLength * 3));
2474
+ char **argv = (char **)_alloca(sizeof(char*)*(7 + keysLength + propertiesLength * 3));
2475
+ size_t *argvl = (size_t *)_alloca(sizeof(size_t)*(7 + keysLength + propertiesLength * 3));
2461
2476
  #else
2462
2477
  snprintf(keysLengthString, sizeof(char) * 5, "%d", keysLength);
2463
- char *argv[6 + keysLength + propertiesLength * 3];
2464
- size_t argvl[6 + keysLength + propertiesLength * 3];
2478
+ char *argv[7 + keysLength + propertiesLength * 3];
2479
+ size_t argvl[7 + keysLength + propertiesLength * 3];
2465
2480
  #endif
2466
2481
 
2467
2482
  argv[0] = "evalsha";
@@ -2474,26 +2489,28 @@ unsigned int formatStoreMessage(void *rulesBinding,
2474
2489
  argvl[3] = strlen(sid);
2475
2490
  argv[4] = storeFact ? "1" : "0";
2476
2491
  argvl[4] = 1;
2477
- argv[5] = keysLengthString;
2478
- argvl[5] = strlen(keysLengthString);
2492
+ argv[5] = markVisited ? "1" : "0";
2493
+ argvl[5] = 1;
2494
+ argv[6] = keysLengthString;
2495
+ argvl[6] = strlen(keysLengthString);
2479
2496
 
2480
2497
  for (unsigned int i = 0; i < keysLength; ++i) {
2481
- argv[6 + i] = keys[i];
2482
- argvl[6 + i] = strlen(keys[i]);
2498
+ argv[7 + i] = keys[i];
2499
+ argvl[7 + i] = strlen(keys[i]);
2483
2500
  }
2484
2501
 
2485
- unsigned int offset = 6 + keysLength;
2502
+ unsigned int offset = 7 + keysLength;
2486
2503
  for (unsigned int i = 0; i < propertiesLength; ++i) {
2487
- argv[offset + i * 3] = allProperties[i].name;
2488
- argvl[offset + i * 3] = allProperties[i].nameLength;
2489
- argv[offset + i * 3 + 1] = message + allProperties[i].valueOffset;
2490
- if (allProperties[i].type == JSON_STRING) {
2491
- argvl[offset + i * 3 + 1] = allProperties[i].valueLength;
2504
+ argv[offset + i * 3] = jo->properties[i].name;
2505
+ argvl[offset + i * 3] = jo->properties[i].nameLength;
2506
+ argv[offset + i * 3 + 1] = jo->properties[i].valueString;
2507
+ if (jo->properties[i].type == JSON_STRING) {
2508
+ argvl[offset + i * 3 + 1] = jo->properties[i].valueLength;
2492
2509
  } else {
2493
- argvl[offset + i * 3 + 1] = allProperties[i].valueLength + 1;
2510
+ argvl[offset + i * 3 + 1] = jo->properties[i].valueLength + 1;
2494
2511
  }
2495
2512
 
2496
- switch(allProperties[i].type) {
2513
+ switch(jo->properties[i].type) {
2497
2514
  case JSON_STRING:
2498
2515
  argv[offset + i * 3 + 2] = "1";
2499
2516
  break;
data/src/rules/net.h CHANGED
@@ -54,8 +54,7 @@ unsigned int formatEvalMessage(void *rulesBinding,
54
54
  char *sid,
55
55
  char *mid,
56
56
  char *message,
57
- jsonProperty *allProperties,
58
- unsigned int propertiesLength,
57
+ jsonObject *jo,
59
58
  unsigned char actionType,
60
59
  char **keys,
61
60
  unsigned int keysLength,
@@ -64,9 +63,9 @@ unsigned int formatEvalMessage(void *rulesBinding,
64
63
  unsigned int formatStoreMessage(void *rulesBinding,
65
64
  char *sid,
66
65
  char *message,
67
- jsonProperty *allProperties,
68
- unsigned int propertiesLength,
66
+ jsonObject *jo,
69
67
  unsigned char storeFact,
68
+ unsigned char markVisited,
70
69
  char **keys,
71
70
  unsigned int keysLength,
72
71
  char **command);
data/src/rules/rete.c CHANGED
@@ -8,34 +8,32 @@
8
8
  #include "json.h"
9
9
  #include "regex.h"
10
10
 
11
- #define HASH_ALL 193486302 // all
12
- #define HASH_ANY 193486381 // any
13
- #define HASH_PRI 193502832 // pri
14
- #define HASH_COUNT 255678574 // count
15
- #define HASH_SPAN 2090731639 // span
16
- #define HASH_CAP 193488121 // cap
17
- #define HASH_BY 5863264 // by
18
- #define HASH_LT 193419881 // $lt
19
- #define HASH_LTE 2087888878 // $lte
20
- #define HASH_GT 193419716 // $gt
21
- #define HASH_GTE 2087883433 // $gte
22
- #define HASH_EQ 193419647 // $eq
23
- #define HASH_NEQ 2087890573 // $neq
24
- #define HASH_MT 193419914 // $mt
25
- #define HASH_IMT 2087885395 // $imt
26
- #define HASH_EX 193419654 // $ex
27
- #define HASH_NEX 2087890580 // $nex
28
- #define HASH_OR 193419978 // $or
29
- #define HASH_AND 2087876700 // $and
30
- #define HASH_S 5861212 // $s
31
- #define HASH_NAME 2090536006 // name
32
- #define HASH_ADD 2087876370 // $add
33
- #define HASH_SUB 2087896531 // $sub
34
- #define HASH_MUL 2087890007 // $mul
35
- #define HASH_DIV 2087879820 // $div
36
- #define HASH_L 5861205 // $l
37
- #define HASH_R 5861211 //$r
38
- #define HASH_FORWARD 1810358942 // $forward
11
+ #define HASH_ALL 321211332 // all
12
+ #define HASH_ANY 740945997 // any
13
+ #define HASH_PRI 1450887882 // pri
14
+ #define HASH_COUNT 967958004 // count
15
+ #define HASH_CAP 41178555 // cap
16
+ #define HASH_LT 542787579 // $lt
17
+ #define HASH_LTE 2350824890 // $lte
18
+ #define HASH_GT 507407960 // $gt
19
+ #define HASH_GTE 3645344263 // $gte
20
+ #define HASH_EQ 256037865 // $eq
21
+ #define HASH_NEQ 2488026869 // $neq
22
+ #define HASH_MT 576092554 // $mt
23
+ #define HASH_IMT 1472564215 // $imt
24
+ #define HASH_EX 373481198 // $ex
25
+ #define HASH_NEX 2605470202 // $nex
26
+ #define HASH_OR 340911698 // $or
27
+ #define HASH_AND 3746487396 // $and
28
+ #define HASH_S 1186729920 // $s
29
+ #define HASH_NAME 2369371622 // name
30
+ #define HASH_ADD 4081054038 // $add
31
+ #define HASH_SUB 1040718071 // $sub
32
+ #define HASH_MUL 304370403 // $mul
33
+ #define HASH_DIV 130502 // $div
34
+ #define HASH_L 1706836109 // $l
35
+ #define HASH_R 1203507539 //$r
36
+ #define HASH_FORWARD 739185624 // $forward
39
37
 
40
38
  typedef struct path {
41
39
  unsigned char operator;
@@ -746,26 +744,15 @@ static unsigned int validateRuleset(char *rules) {
746
744
  return countResult;
747
745
  }
748
746
 
749
- unsigned int spanResult = validateSetting(HASH_SPAN, first, JSON_INT);
750
- if (spanResult != PARSE_OK && spanResult != ERR_SETTING_NOT_FOUND) {
751
- return spanResult;
752
- }
753
-
754
747
  unsigned int capResult = validateSetting(HASH_CAP, first, JSON_INT);
755
748
  if (capResult != PARSE_OK && capResult != ERR_SETTING_NOT_FOUND) {
756
749
  return capResult;
757
750
  }
758
751
 
759
- if ((spanResult == PARSE_OK && (countResult == PARSE_OK || capResult == PARSE_OK)) ||
760
- (countResult == PARSE_OK && capResult == PARSE_OK)) {
752
+ if (countResult == PARSE_OK && capResult == PARSE_OK) {
761
753
  return ERR_UNEXPECTED_NAME;
762
754
  }
763
755
 
764
- result = validateSetting(HASH_BY, first, JSON_STRING);
765
- if (result != PARSE_OK && result != ERR_SETTING_NOT_FOUND) {
766
- return result;
767
- }
768
-
769
756
  result = validateSetting(HASH_PRI, first, JSON_INT);
770
757
  if (result != PARSE_OK && result != ERR_SETTING_NOT_FOUND) {
771
758
  return result;
@@ -783,7 +770,7 @@ static unsigned int validateRuleset(char *rules) {
783
770
  if (result != RULES_OK && result != PARSE_END) {
784
771
  return result;
785
772
  }
786
- } else if (hash != HASH_COUNT && hash != HASH_PRI && hash != HASH_SPAN && hash != HASH_CAP && hash != HASH_BY) {
773
+ } else if (hash != HASH_COUNT && hash != HASH_PRI && hash != HASH_CAP) {
787
774
  return ERR_UNEXPECTED_NAME;
788
775
  }
789
776
 
@@ -1289,7 +1276,7 @@ static unsigned int createBetaConnector(ruleset *tree,
1289
1276
 
1290
1277
  if (operator == OP_ALL || operator == OP_ANY || operator == OP_NOT) {
1291
1278
  nameLength = nameLength - 4;
1292
- hash = djbHash(first, nameLength);
1279
+ hash = fnv1Hash32(first, nameLength);
1293
1280
  }
1294
1281
  }
1295
1282
 
@@ -1694,7 +1681,6 @@ static unsigned int createTree(ruleset *tree, char *rules) {
1694
1681
  ruleAction->value.c.cap = 0;
1695
1682
  getSetting(HASH_PRI, first, &ruleAction->value.c.priority);
1696
1683
  getSetting(HASH_COUNT, first, &ruleAction->value.c.count);
1697
- getSetting(HASH_SPAN, first, &ruleAction->value.c.span);
1698
1684
  getSetting(HASH_CAP, first, &ruleAction->value.c.cap);
1699
1685
  if (!ruleAction->value.c.count && !ruleAction->value.c.span && !ruleAction->value.c.cap) {
1700
1686
  ruleAction->value.c.count = 1;
data/src/rules/state.c CHANGED
@@ -6,15 +6,13 @@
6
6
  #include "json.h"
7
7
  #include "net.h"
8
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;
9
+ unsigned int fnv1Hash32(char *str, unsigned int len) {
10
+ unsigned int hash = FNV_32_OFFSET_BASIS;
11
+ for(unsigned int i = 0; i < len; str++, i++) {
12
+ hash ^= (*str);
13
+ hash *= FNV_32_PRIME;
14
+ }
15
+ return hash;
18
16
  }
19
17
 
20
18
  static unsigned int evictEntry(ruleset *tree) {
@@ -43,7 +41,7 @@ static unsigned int evictEntry(ruleset *tree) {
43
41
  current->sidHash = 0;
44
42
  current->bindingIndex = 0;
45
43
  current->lastRefresh = 0;
46
- current->propertiesLength = 0;
44
+ current->jo.propertiesLength = 0;
47
45
  found = 1;
48
46
  }
49
47
  }
@@ -164,15 +162,70 @@ static stateEntry *getEntry(ruleset *tree, char *sid, unsigned int sidHash) {
164
162
  return NULL;
165
163
  }
166
164
 
165
+ static unsigned int fixupIds(jsonObject *jo) {
166
+ jsonProperty *property;
167
+ // id and sid are coerced to strings
168
+ // to avoid unnecessary conversions
169
+ if (jo->sidIndex != UNDEFINED_INDEX && jo->properties[jo->sidIndex].type != JSON_NIL) {
170
+ //coerce value to string
171
+ property = &jo->properties[jo->sidIndex];
172
+ if (property->type == JSON_DOUBLE || property->type == JSON_INT) {
173
+ ++property->valueLength;
174
+ }
175
+
176
+ property->type = JSON_STRING;
177
+ } else {
178
+ property = &jo->properties[jo->propertiesLength];
179
+ jo->sidIndex = jo->propertiesLength;
180
+ ++jo->propertiesLength;
181
+ if (jo->propertiesLength == MAX_OBJECT_PROPERTIES) {
182
+ return ERR_EVENT_MAX_PROPERTIES;
183
+ }
184
+
185
+ strncpy(jo->sidBuffer, "0", 1);
186
+ property->hash = HASH_SID;
187
+ property->isMaterial = 1;
188
+ property->valueString = jo->sidBuffer;
189
+ property->valueLength = 1;
190
+ strncpy(property->name, "sid", 3);
191
+ property->nameLength = 3;
192
+ property->type = JSON_STRING;
193
+ }
194
+
195
+ if (jo->idIndex != UNDEFINED_INDEX && jo->properties[jo->idIndex].type != JSON_NIL) {
196
+ //coerce value to string
197
+ property = &jo->properties[jo->idIndex];
198
+ if (property->type == JSON_DOUBLE || property->type == JSON_INT) {
199
+ ++property->valueLength;
200
+ }
201
+
202
+ property->type = JSON_STRING;
203
+ } else {
204
+ property = &jo->properties[jo->propertiesLength];
205
+ jo->idIndex = jo->propertiesLength;
206
+ ++jo->propertiesLength;
207
+ if (jo->propertiesLength == MAX_OBJECT_PROPERTIES) {
208
+ return ERR_EVENT_MAX_PROPERTIES;
209
+ }
210
+
211
+ jo->idBuffer[0] = 0;
212
+ property->hash = HASH_ID;
213
+ property->isMaterial = 1;
214
+ property->valueString = jo->idBuffer;
215
+ property->valueLength = 0;
216
+ strncpy(property->name, "id", 2);
217
+ property->nameLength = 2;
218
+ property->type = JSON_STRING;
219
+ }
220
+
221
+ return RULES_OK;
222
+ }
223
+
167
224
  unsigned int constructObject(char *root,
168
225
  char *parentName,
169
226
  char *object,
170
- char createHashtable,
171
- unsigned int maxProperties,
172
- jsonProperty *properties,
173
- unsigned int *propertiesLength,
174
- unsigned int *midIndex,
175
- unsigned int *sidIndex,
227
+ char layout,
228
+ jsonObject *jo,
176
229
  char **next) {
177
230
  char *firstName;
178
231
  char *lastName;
@@ -180,7 +233,16 @@ unsigned int constructObject(char *root,
180
233
  char *last;
181
234
  unsigned char type;
182
235
  unsigned int hash;
183
- int parentNameLength = (parentName ? strlen(parentName): 0);
236
+ int parentNameLength;
237
+ if (parentName) {
238
+ parentNameLength = strlen(parentName);
239
+ } else {
240
+ parentNameLength = 0;
241
+ jo->idIndex = UNDEFINED_INDEX;
242
+ jo->sidIndex = UNDEFINED_INDEX;
243
+ jo->propertiesLength = 0;
244
+ }
245
+
184
246
  object = (object ? object : root);
185
247
  unsigned int result = readNextName(object, &firstName, &lastName, &hash);
186
248
  while (result == PARSE_OK) {
@@ -191,35 +253,40 @@ unsigned int constructObject(char *root,
191
253
 
192
254
  jsonProperty *property = NULL;
193
255
  if (type != JSON_OBJECT) {
194
- if (!createHashtable) {
195
- property = &properties[*propertiesLength];
196
- if (hash == HASH_ID) {
197
- *midIndex = *propertiesLength;
198
- } else if (hash == HASH_SID) {
199
- *sidIndex = *propertiesLength;
200
- }
201
- } else {
202
- unsigned int candidate = hash % maxProperties;
203
- while (properties[candidate].type != 0) {
204
- candidate = (candidate + 1) % maxProperties;
205
- }
206
-
207
- if (hash == HASH_ID) {
208
- *midIndex = candidate;
209
- } else if (hash == HASH_SID) {
210
- *sidIndex = candidate;
256
+ switch (layout) {
257
+ case JSON_OBJECT_SEQUENCED:
258
+ property = &jo->properties[jo->propertiesLength];
259
+ if (hash == HASH_ID) {
260
+ jo->idIndex = jo->propertiesLength;
261
+ } else if (hash == HASH_SID) {
262
+ jo->sidIndex = jo->propertiesLength;
263
+ }
264
+ break;
265
+ case JSON_OBJECT_HASHED:
266
+ {
267
+ unsigned int candidate = hash % MAX_OBJECT_PROPERTIES;
268
+ while (jo->properties[candidate].type != 0) {
269
+ candidate = (candidate + 1) % MAX_OBJECT_PROPERTIES;
270
+ }
271
+
272
+ if (hash == HASH_ID) {
273
+ jo->idIndex = candidate;
274
+ } else if (hash == HASH_SID) {
275
+ jo->sidIndex = candidate;
276
+ }
277
+
278
+ property = &jo->properties[candidate];
211
279
  }
212
-
213
- property = &properties[candidate];
214
- }
215
-
216
- *propertiesLength = *propertiesLength + 1;
217
- if (*propertiesLength == maxProperties) {
280
+ break;
281
+ }
282
+
283
+ ++jo->propertiesLength;
284
+ if (jo->propertiesLength == MAX_OBJECT_PROPERTIES) {
218
285
  return ERR_EVENT_MAX_PROPERTIES;
219
286
  }
220
287
 
221
288
  property->isMaterial = 0;
222
- property->valueOffset = first - root;
289
+ property->valueString = first;
223
290
  property->valueLength = last - first;
224
291
  property->type = type;
225
292
  }
@@ -244,15 +311,11 @@ unsigned int constructObject(char *root,
244
311
  strncpy(newParent, firstName, nameLength);
245
312
  newParent[nameLength] = '\0';
246
313
  result = constructObject(root,
247
- newParent,
248
- first,
249
- createHashtable,
250
- maxProperties,
251
- properties,
252
- propertiesLength,
253
- midIndex,
254
- sidIndex,
255
- next);
314
+ newParent,
315
+ first,
316
+ layout,
317
+ jo,
318
+ next);
256
319
  if (result != RULES_OK) {
257
320
  return result;
258
321
  }
@@ -269,7 +332,7 @@ unsigned int constructObject(char *root,
269
332
  property->name[parentNameLength] = '.';
270
333
  strncpy(&property->name[parentNameLength + 1], firstName, nameLength);
271
334
  property->nameLength = fullNameLength;
272
- property->hash = djbHash(property->name, fullNameLength);
335
+ property->hash = fnv1Hash32(property->name, fullNameLength);
273
336
  } else {
274
337
  #ifdef _WIN32
275
338
  char *fullName = (char *)_alloca(sizeof(char)*(fullNameLength + 1));
@@ -281,15 +344,11 @@ unsigned int constructObject(char *root,
281
344
  strncpy(&fullName[parentNameLength + 1], firstName, nameLength);
282
345
  fullName[fullNameLength] = '\0';
283
346
  result = constructObject(root,
284
- fullName,
285
- first,
286
- createHashtable,
287
- maxProperties,
288
- properties,
289
- propertiesLength,
290
- midIndex,
291
- sidIndex,
292
- next);
347
+ fullName,
348
+ first,
349
+ layout,
350
+ jo,
351
+ next);
293
352
  if (result != RULES_OK) {
294
353
  return result;
295
354
  }
@@ -300,15 +359,20 @@ unsigned int constructObject(char *root,
300
359
  result = readNextName(last, &firstName, &lastName, &hash);
301
360
  }
302
361
 
362
+ if (!parentName) {
363
+ int idResult = fixupIds(jo);
364
+ if (idResult != RULES_OK) {
365
+ return idResult;
366
+ }
367
+ }
368
+
303
369
  return (result == PARSE_END ? RULES_OK: result);
304
370
  }
305
371
 
306
372
  void rehydrateProperty(jsonProperty *property, char *state) {
307
- // ID and SID are treated as strings regardless of type
308
- // to avoid unnecessary conversions
309
- if (!property->isMaterial && property->hash != HASH_ID && property->hash != HASH_SID) {
373
+ if (!property->isMaterial) {
310
374
  unsigned short propertyLength = property->valueLength + 1;
311
- char *propertyFirst = state + property->valueOffset;
375
+ char *propertyFirst = property->valueString;
312
376
  unsigned char propertyType = property->type;
313
377
  unsigned char b = 0;
314
378
  char temp;
@@ -343,7 +407,7 @@ static unsigned int resolveBindingAndEntry(ruleset *tree,
343
407
  char *sid,
344
408
  stateEntry **entry,
345
409
  void **rulesBinding) {
346
- unsigned int sidHash = djbHash(sid, strlen(sid));
410
+ unsigned int sidHash = fnv1Hash32(sid, strlen(sid));
347
411
  if (!ensureEntry(tree, sid, sidHash, entry)) {
348
412
  unsigned int result = getBindingIndex(tree, sidHash, &(*entry)->bindingIndex);
349
413
  if (result != RULES_OK) {
@@ -386,20 +450,14 @@ unsigned int refreshState(void *handle,
386
450
  return result;
387
451
  }
388
452
 
389
- memset(entry->properties, 0, MAX_STATE_PROPERTIES * sizeof(jsonProperty));
390
- entry->propertiesLength = 0;
453
+ memset(entry->jo.properties, 0, MAX_OBJECT_PROPERTIES * sizeof(jsonProperty));
454
+ entry->jo.propertiesLength = 0;
391
455
  char *next;
392
- unsigned int midIndex = UNDEFINED_INDEX;
393
- unsigned int sidIndex = UNDEFINED_INDEX;
394
456
  result = constructObject(entry->state,
395
457
  NULL,
396
458
  NULL,
397
- 1,
398
- MAX_STATE_PROPERTIES,
399
- entry->properties,
400
- &entry->propertiesLength,
401
- &midIndex,
402
- &sidIndex,
459
+ JSON_OBJECT_HASHED,
460
+ &entry->jo,
403
461
  &next);
404
462
  if (result != RULES_OK) {
405
463
  return result;
@@ -416,7 +474,7 @@ unsigned int fetchStateProperty(void *tree,
416
474
  unsigned char ignoreStaleState,
417
475
  char **state,
418
476
  jsonProperty **property) {
419
- unsigned int sidHash = djbHash(sid, strlen(sid));
477
+ unsigned int sidHash = fnv1Hash32(sid, strlen(sid));
420
478
  stateEntry *entry = getEntry(tree, sid, sidHash);
421
479
  if (entry == NULL || entry->lastRefresh == 0) {
422
480
  return ERR_STATE_NOT_LOADED;
@@ -426,11 +484,11 @@ unsigned int fetchStateProperty(void *tree,
426
484
  return ERR_STALE_STATE;
427
485
  }
428
486
 
429
- unsigned int propertyIndex = propertyHash % MAX_STATE_PROPERTIES;
430
- jsonProperty *result = &entry->properties[propertyIndex];
487
+ unsigned int propertyIndex = propertyHash % MAX_OBJECT_PROPERTIES;
488
+ jsonProperty *result = &entry->jo.properties[propertyIndex];
431
489
  while (result->type != 0 && result->hash != propertyHash) {
432
- propertyIndex = (propertyIndex + 1) % MAX_STATE_PROPERTIES;
433
- result = &entry->properties[propertyIndex];
490
+ propertyIndex = (propertyIndex + 1) % MAX_OBJECT_PROPERTIES;
491
+ result = &entry->jo.properties[propertyIndex];
434
492
  }
435
493
 
436
494
  if (!result->type) {