farce 0.1.0-aarch64-linux-gnu → 0.2.0-aarch64-linux-gnu

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.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +80 -1
  3. data/README.md +72 -16
  4. data/docs/gems/dry-types.md +8 -8
  5. data/docs/modes.md +67 -10
  6. data/ext/farce/atom.c +37 -22
  7. data/ext/farce/bounded_map.c +17 -13
  8. data/ext/farce/containers.h +15 -0
  9. data/ext/farce/counter.c +13 -18
  10. data/ext/farce/exchanger.c +5 -1
  11. data/ext/farce/farce.c +1 -1
  12. data/ext/farce/flag.c +39 -3
  13. data/ext/farce/io.c +3 -3
  14. data/ext/farce/lock.c +17 -23
  15. data/ext/farce/map.c +88 -66
  16. data/ext/farce/priority_queue.c +55 -43
  17. data/ext/farce/queue.c +10 -4
  18. data/ext/farce/reactor.c +52 -25
  19. data/ext/farce/reactor.h +1 -1
  20. data/ext/farce/signal.c +5 -1
  21. data/ext/farce/transaction.c +20 -24
  22. data/ext/farce/tree_map.c +33 -18
  23. data/ext/farce/trie.c +81 -43
  24. data/ext/farce/unshareable.c +168 -19
  25. data/ext/farce/unshared_io_pool.h +21 -12
  26. data/ext/farce/unshared_signal.c +13 -2
  27. data/ext/farce/unshared_wait.h +3 -0
  28. data/ext/farce/vector.c +82 -18
  29. data/ext/farce/weak_map.c +75 -63
  30. data/lib/farce/_yard/macros.rb +18 -3
  31. data/lib/farce/abstract/flag.rb +73 -0
  32. data/lib/farce/abstract/queue.rb +4 -3
  33. data/lib/farce/abstract/scheduler.rb +3 -3
  34. data/lib/farce/abstract/set.rb +17 -5
  35. data/lib/farce/abstract/sorted_set.rb +2 -2
  36. data/lib/farce/abstract/timer_queue.rb +49 -10
  37. data/lib/farce/abstract/vector.rb +510 -28
  38. data/lib/farce/atom.rb +1 -1
  39. data/lib/farce/class_mirror.rb +20 -7
  40. data/lib/farce/config.rb +7 -1
  41. data/lib/farce/engine/jvm/flag.rb +15 -3
  42. data/lib/farce/engine/ruby/3.4/farce.so +0 -0
  43. data/lib/farce/engine/ruby/4.0/farce.so +0 -0
  44. data/lib/farce/engine/ruby/shared/require_hook.rb +18 -0
  45. data/lib/farce/engine/ruby.rb +7 -0
  46. data/lib/farce/engine/shared/unshared_vector.rb +31 -4
  47. data/lib/farce/engine/shared/vector.rb +8 -0
  48. data/lib/farce/engine/shared/weak_map/index.rb +14 -1
  49. data/lib/farce/engine/truffleruby/native/flag.rb +15 -3
  50. data/lib/farce/envelope.rb +54 -7
  51. data/lib/farce/exchanger.rb +1 -1
  52. data/lib/farce/flag.rb +2 -0
  53. data/lib/farce/integrations/active_support.rb +1 -1
  54. data/lib/farce/integrations/bson.rb +1 -1
  55. data/lib/farce/integrations/cbor.rb +1 -1
  56. data/lib/farce/integrations/concurrent.rb +3 -3
  57. data/lib/farce/integrations/date.rb +8 -0
  58. data/lib/farce/integrations/dry_types.rb +7 -4
  59. data/lib/farce/integrations/json.rb +2 -2
  60. data/lib/farce/integrations/msgpack.rb +1 -1
  61. data/lib/farce/integrations/oj.rb +3 -4
  62. data/lib/farce/integrations/psych.rb +1 -1
  63. data/lib/farce/integrations/ractor_sharing.rb +1 -1
  64. data/lib/farce/integrations/sorted_set.rb +1 -1
  65. data/lib/farce/integrations/weakref.rb +9 -1
  66. data/lib/farce/integrations/yajl.rb +3 -3
  67. data/lib/farce/integrations.rb +33 -23
  68. data/lib/farce/internal/flag_waiting.rb +30 -0
  69. data/lib/farce/internal/freeze.rb +8 -3
  70. data/lib/farce/internal/map_value_modes.rb +1 -1
  71. data/lib/farce/internal/scheduler_lifecycle.rb +1 -1
  72. data/lib/farce/lazy.rb +4 -4
  73. data/lib/farce/lazy_ref.rb +3 -3
  74. data/lib/farce/lfu_map.rb +1 -1
  75. data/lib/farce/local/flag.rb +4 -0
  76. data/lib/farce/local/scoped.rb +1 -1
  77. data/lib/farce/lru_map.rb +1 -1
  78. data/lib/farce/map.rb +2 -2
  79. data/lib/farce/mode_manager/smart_rule.rb +138 -0
  80. data/lib/farce/mode_manager.rb +12 -3
  81. data/lib/farce/molecule.rb +2 -2
  82. data/lib/farce/pool.rb +1 -1
  83. data/lib/farce/port.rb +4 -4
  84. data/lib/farce/priority_queue.rb +1 -1
  85. data/lib/farce/proxy/register.rb +6 -0
  86. data/lib/farce/proxy/supervisor.rb +1 -1
  87. data/lib/farce/proxy.rb +7 -2
  88. data/lib/farce/queue.rb +1 -1
  89. data/lib/farce/scheduler.rb +2 -2
  90. data/lib/farce/set.rb +1 -1
  91. data/lib/farce/sorted_set.rb +1 -1
  92. data/lib/farce/timer_queue.rb +20 -6
  93. data/lib/farce/transaction/vector.rb +43 -9
  94. data/lib/farce/transaction/wrapper.rb +14 -0
  95. data/lib/farce/tree_map.rb +1 -1
  96. data/lib/farce/unsafe/lfu_map.rb +2 -0
  97. data/lib/farce/unsafe/lru_map.rb +2 -0
  98. data/lib/farce/unsafe/tree_map.rb +2 -0
  99. data/lib/farce/unshareable.rb +8 -19
  100. data/lib/farce/unshared/atom.rb +3 -0
  101. data/lib/farce/unshared/lfu_map.rb +2 -0
  102. data/lib/farce/unshared/lru_map.rb +2 -0
  103. data/lib/farce/unshared/map.rb +3 -0
  104. data/lib/farce/unshared/set.rb +1 -0
  105. data/lib/farce/unshared/sorted_set.rb +1 -0
  106. data/lib/farce/unshared/tree_map.rb +5 -1
  107. data/lib/farce/unshared/vector.rb +3 -0
  108. data/lib/farce/unshared/weak_atom.rb +5 -1
  109. data/lib/farce/unshared/weak_key_map.rb +3 -0
  110. data/lib/farce/unshared/weak_map.rb +3 -0
  111. data/lib/farce/unshared/weak_set.rb +1 -0
  112. data/lib/farce/unshared/weak_value_map.rb +3 -0
  113. data/lib/farce/vector.rb +44 -19
  114. data/lib/farce/version.rb +1 -1
  115. data/lib/farce.rb +11 -8
  116. metadata +5 -1
@@ -4,36 +4,29 @@
4
4
 
5
5
  static VALUE cTransactionEntry;
6
6
 
7
- static void
8
- transaction_entry_mark(void *pointer)
9
- {
10
- farce_transaction_entry_t *entry = pointer;
11
- rb_gc_mark_movable(entry->source);
12
- rb_gc_mark_movable(entry->working);
13
- }
14
-
15
- static void
16
- transaction_entry_compact(void *pointer)
17
- {
18
- farce_transaction_entry_t *entry = pointer;
19
- entry->source = rb_gc_location(entry->source);
20
- entry->working = rb_gc_location(entry->working);
21
- }
22
-
23
7
  static size_t
24
8
  transaction_entry_memsize(const void *pointer)
25
9
  {
26
10
  return pointer ? sizeof(farce_transaction_entry_t) : 0;
27
11
  }
28
12
 
13
+ RUBY_REFERENCES(transaction_entry_references) = {
14
+ RUBY_REF_EDGE(farce_transaction_entry_t, source),
15
+ RUBY_REF_EDGE(farce_transaction_entry_t, working),
16
+ RUBY_REF_END,
17
+ };
18
+
29
19
  static const rb_data_type_t transaction_entry_type = {
30
20
  .wrap_struct_name = "Farce::Internal::NativeTransactionEntry",
31
21
  .function = {
32
- .dmark = transaction_entry_mark,
22
+ .dmark = RUBY_REFS_LIST_PTR(transaction_entry_references),
33
23
  .dfree = RUBY_TYPED_DEFAULT_FREE,
34
24
  .dsize = transaction_entry_memsize,
35
- .dcompact = transaction_entry_compact,
36
25
  },
26
+ .flags =
27
+ RUBY_TYPED_DECL_MARKING |
28
+ RUBY_TYPED_FREE_IMMEDIATELY |
29
+ FARCE_TYPED_THREAD_SAFE_FREE,
37
30
  };
38
31
 
39
32
  VALUE
@@ -51,6 +44,9 @@ farce_transaction_entry_new(
51
44
  .source_data = source_data, .working_data = working_data,
52
45
  .lock = lock, .ops = ops,
53
46
  };
47
+ /* Attempt-local copies are rescanned by every GC. They do not need the
48
+ * barriers or ownership scans used by long-lived containers. */
49
+ if (!NIL_P(working)) RB_OBJ_WB_UNPROTECT(working);
54
50
  return object;
55
51
  }
56
52
 
@@ -91,13 +87,13 @@ transaction_commit(VALUE namespace, VALUE entries, VALUE guards)
91
87
  Check_Type(guards, T_ARRAY);
92
88
  /* Type-check guards before acquiring participant locks. */
93
89
  for (long i = 0; i < RARRAY_LEN(guards); i++) {
94
- if (farce_transaction_flag_set(RARRAY_AREF(guards, i))) return Qfalse;
90
+ if (farce_transaction_flag_set(rb_ary_entry(guards, i))) return Qfalse;
95
91
  }
96
92
  long count = RARRAY_LEN(entries);
97
93
  VALUE buffer = 0;
98
94
  farce_transaction_entry_t **ordered = ALLOCV_N(farce_transaction_entry_t *, buffer, count);
99
95
  for (long i = 0; i < count; i++) {
100
- ordered[i] = get_transaction_entry(RARRAY_AREF(entries, i));
96
+ ordered[i] = get_transaction_entry(rb_ary_entry(entries, i));
101
97
  }
102
98
  qsort(ordered, (size_t)count, sizeof(*ordered), transaction_entry_order);
103
99
  for (long i = 1; i < count; i++) {
@@ -128,7 +124,7 @@ transaction_commit(VALUE namespace, VALUE entries, VALUE guards)
128
124
  }
129
125
  if (valid) {
130
126
  for (long i = 0; i < RARRAY_LEN(guards); i++) {
131
- if (farce_transaction_flag_set(RARRAY_AREF(guards, i))) {
127
+ if (farce_transaction_flag_set(rb_ary_entry(guards, i))) {
132
128
  valid = false;
133
129
  break;
134
130
  }
@@ -233,7 +229,7 @@ transaction_reservation_body(VALUE opaque)
233
229
  }
234
230
  if (valid) {
235
231
  for (long i = 0; i < RARRAY_LEN(reservation->guards); i++) {
236
- if (farce_transaction_flag_set(RARRAY_AREF(reservation->guards, i))) {
232
+ if (farce_transaction_flag_set(rb_ary_entry(reservation->guards, i))) {
237
233
  valid = false;
238
234
  break;
239
235
  }
@@ -308,13 +304,13 @@ transaction_reserve(VALUE namespace, VALUE entries, VALUE guards, VALUE context)
308
304
  Check_Type(entries, T_ARRAY);
309
305
  Check_Type(guards, T_ARRAY);
310
306
  for (long i = 0; i < RARRAY_LEN(guards); i++) {
311
- (void)farce_transaction_flag_set(RARRAY_AREF(guards, i));
307
+ (void)farce_transaction_flag_set(rb_ary_entry(guards, i));
312
308
  }
313
309
  long count = RARRAY_LEN(entries);
314
310
  VALUE buffer = 0;
315
311
  farce_transaction_entry_t **ordered = ALLOCV_N(farce_transaction_entry_t *, buffer, count);
316
312
  for (long i = 0; i < count; i++) {
317
- ordered[i] = get_transaction_entry(RARRAY_AREF(entries, i));
313
+ ordered[i] = get_transaction_entry(rb_ary_entry(entries, i));
318
314
  if (!ordered[i]->ops->reserve || !ordered[i]->ops->release) {
319
315
  ALLOCV_END(buffer);
320
316
  rb_raise(rb_eTypeError, "participant does not support commit reservations");
data/ext/farce/tree_map.c CHANGED
@@ -471,8 +471,8 @@ tc_map_compact_node(tc_link *link)
471
471
 
472
472
  if (link == NULL) return;
473
473
  node = TC_MAP_NODE(link);
474
- node->key = rb_gc_location(node->key);
475
- node->value = rb_gc_location(node->value);
474
+ containers_gc_update(&node->key);
475
+ containers_gc_update(&node->value);
476
476
  tc_map_compact_node(link->left);
477
477
  tc_map_compact_node(link->right);
478
478
  }
@@ -605,12 +605,10 @@ tc_core_map_compact(void *opaque)
605
605
  tc_core_map *core = opaque;
606
606
  if (core == NULL) return;
607
607
  tc_map_compact_node(core->map.root);
608
- core->owner_fiber = rb_gc_location(core->owner_fiber);
609
- core->owner_ruby_thread = rb_gc_location(core->owner_ruby_thread);
610
- core->publication_owner_fiber =
611
- rb_gc_location(core->publication_owner_fiber);
612
- core->publication_owner_ruby_thread =
613
- rb_gc_location(core->publication_owner_ruby_thread);
608
+ containers_gc_update(&core->owner_fiber);
609
+ containers_gc_update(&core->owner_ruby_thread);
610
+ containers_gc_update(&core->publication_owner_fiber);
611
+ containers_gc_update(&core->publication_owner_ruby_thread);
614
612
  }
615
613
 
616
614
  static void
@@ -642,7 +640,7 @@ static const rb_data_type_t tc_unsafe_map_type = {
642
640
  .dsize = tc_core_map_memsize,
643
641
  .dcompact = tc_core_map_compact,
644
642
  },
645
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
643
+ .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | FARCE_TYPED_THREAD_SAFE_FREE,
646
644
  };
647
645
 
648
646
  static const rb_data_type_t tc_map_type = {
@@ -653,7 +651,7 @@ static const rb_data_type_t tc_map_type = {
653
651
  .dsize = tc_core_map_memsize,
654
652
  .dcompact = tc_core_map_compact,
655
653
  },
656
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
654
+ .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | FARCE_TYPED_THREAD_SAFE_FREE,
657
655
  };
658
656
 
659
657
  static const rb_data_type_t tc_shared_map_type = {
@@ -664,7 +662,11 @@ static const rb_data_type_t tc_shared_map_type = {
664
662
  .dsize = tc_core_map_memsize,
665
663
  .dcompact = tc_core_map_compact,
666
664
  },
667
- .flags = RUBY_TYPED_FROZEN_SHAREABLE,
665
+ .flags =
666
+ RUBY_TYPED_WB_PROTECTED |
667
+ RUBY_TYPED_FROZEN_SHAREABLE |
668
+ RUBY_TYPED_FREE_IMMEDIATELY |
669
+ FARCE_TYPED_THREAD_SAFE_FREE,
668
670
  };
669
671
 
670
672
  static VALUE
@@ -925,8 +927,8 @@ tc_core_map_lock(VALUE self, tc_core_map *core)
925
927
  rb_raise(rb_eThreadError, "deadlock; recursive tree map access");
926
928
  }
927
929
  if (NIL_P(core->owner_fiber) && !publication_busy) {
928
- core->owner_fiber = current;
929
- core->owner_ruby_thread = current_thread;
930
+ RB_OBJ_WRITE(self, &core->owner_fiber, current);
931
+ RB_OBJ_WRITE(self, &core->owner_ruby_thread, current_thread);
930
932
  pthread_mutex_unlock(&core->lock);
931
933
  return;
932
934
  }
@@ -1731,8 +1733,8 @@ tc_core_map_each(VALUE self)
1731
1733
  entry_count = RARRAY_LEN(entries);
1732
1734
  for (index = 0; index < entry_count; index += 2) {
1733
1735
  rb_yield(rb_assoc_new(
1734
- RARRAY_AREF(entries, index),
1735
- RARRAY_AREF(entries, index + 1)
1736
+ rb_ary_entry(entries, index),
1737
+ rb_ary_entry(entries, index + 1)
1736
1738
  ));
1737
1739
  }
1738
1740
  RB_GC_GUARD(entries);
@@ -1787,6 +1789,18 @@ tc_transaction_valid(farce_transaction_entry_t *entry)
1787
1789
  source->map.revision == entry->version;
1788
1790
  }
1789
1791
 
1792
+ /* A transaction transfers the tree's reference edges to a different owner. */
1793
+ static void
1794
+ tc_map_written_node(VALUE self, tc_link *link)
1795
+ {
1796
+ if (!link) return;
1797
+ tc_map_node *node = (tc_map_node *)link;
1798
+ RB_OBJ_WRITTEN(self, Qundef, node->key);
1799
+ RB_OBJ_WRITTEN(self, Qundef, node->value);
1800
+ tc_map_written_node(self, link->left);
1801
+ tc_map_written_node(self, link->right);
1802
+ }
1803
+
1790
1804
  static void
1791
1805
  tc_transaction_apply(farce_transaction_entry_t *entry)
1792
1806
  {
@@ -1800,6 +1814,7 @@ tc_transaction_apply(farce_transaction_entry_t *entry)
1800
1814
  working->map.root = root;
1801
1815
  working->map.size = size;
1802
1816
  working->map.revision++;
1817
+ tc_map_written_node(entry->source, source->map.root);
1803
1818
  }
1804
1819
 
1805
1820
  static void
@@ -1813,8 +1828,8 @@ tc_transaction_reserve(farce_transaction_entry_t *entry, VALUE fiber, VALUE thre
1813
1828
  {
1814
1829
  tc_core_map *core = entry->source_data;
1815
1830
  core->transaction_reserved = true;
1816
- core->owner_fiber = fiber;
1817
- core->owner_ruby_thread = thread;
1831
+ RB_OBJ_WRITE(entry->source, &core->owner_fiber, fiber);
1832
+ RB_OBJ_WRITE(entry->source, &core->owner_ruby_thread, thread);
1818
1833
  }
1819
1834
 
1820
1835
  static void
@@ -1882,7 +1897,7 @@ tc_transaction_snapshot(VALUE self)
1882
1897
  };
1883
1898
  VALUE pairs = tc_core_map_call_locked(self, source, tc_transaction_snapshot_body, &snapshot);
1884
1899
  for (long i = 0; i < RARRAY_LEN(pairs); i += 2) {
1885
- tc_core_map_store(working, RARRAY_AREF(pairs, i), RARRAY_AREF(pairs, i + 1));
1900
+ tc_core_map_store(working, rb_ary_entry(pairs, i), rb_ary_entry(pairs, i + 1));
1886
1901
  }
1887
1902
  RB_GC_GUARD(pairs);
1888
1903
  return result;
data/ext/farce/trie.c CHANGED
@@ -120,7 +120,7 @@ typedef struct {
120
120
  trie_event_kind_t kind;
121
121
  long begin;
122
122
  long end;
123
- VALUE name;
123
+ const VALUE *name;
124
124
  long match_index;
125
125
  } trie_capture_event_t;
126
126
 
@@ -180,6 +180,7 @@ typedef struct {
180
180
  } trie_search_t;
181
181
 
182
182
  typedef struct {
183
+ VALUE self;
183
184
  const unsigned char *bytes;
184
185
  size_t length;
185
186
  size_t position;
@@ -213,8 +214,8 @@ trie_check_interrupts(void)
213
214
  #ifdef FARCE_TRIE_FAILURE_INJECTION
214
215
  if (!NIL_P(trie_interrupt_queue)) {
215
216
  VALUE queues = trie_interrupt_queue;
216
- VALUE ready = RARRAY_AREF(queues, 0);
217
- VALUE release = RARRAY_AREF(queues, 1);
217
+ VALUE ready = rb_ary_entry(queues, 0);
218
+ VALUE release = rb_ary_entry(queues, 1);
218
219
  trie_interrupt_queue = Qnil;
219
220
  rb_funcall(ready, rb_intern("<<"), 1, Qtrue);
220
221
  rb_funcall(release, rb_intern("pop"), 0);
@@ -277,7 +278,7 @@ static const rb_data_type_t trie_scratch_type = {
277
278
  },
278
279
  .parent = NULL,
279
280
  .data = NULL,
280
- .flags = RUBY_TYPED_FREE_IMMEDIATELY,
281
+ .flags = RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | FARCE_TYPED_THREAD_SAFE_FREE,
281
282
  };
282
283
 
283
284
  static void *
@@ -350,20 +351,20 @@ trie_mark(void *pointer)
350
351
  size_t program_index;
351
352
  if (!trie) return;
352
353
  if (!NIL_P(trie->owner)) {
353
- rb_gc_mark(trie->owner);
354
+ rb_gc_mark_movable(trie->owner);
354
355
  return;
355
356
  }
356
- rb_gc_mark(trie->tokens);
357
+ rb_gc_mark_movable(trie->tokens);
357
358
  for (node_index = 0; node_index < trie->node_count; node_index++) {
358
359
  trie_node_t *node = trie->nodes[node_index];
359
360
  size_t index;
360
361
  for (index = 0; index < node->static_count; index++) {
361
- rb_gc_mark(node->static_edges[index].label);
362
+ rb_gc_mark_movable(node->static_edges[index].label);
362
363
  }
363
364
  for (index = 0; index < node->regexp_count; index++) {
364
- rb_gc_mark(node->regexp_edges[index].source);
365
- rb_gc_mark(node->regexp_edges[index].anchored);
366
- rb_gc_mark(node->regexp_edges[index].timeout);
365
+ rb_gc_mark_movable(node->regexp_edges[index].source);
366
+ rb_gc_mark_movable(node->regexp_edges[index].anchored);
367
+ rb_gc_mark_movable(node->regexp_edges[index].timeout);
367
368
  }
368
369
  }
369
370
  for (program_index = 0; program_index < trie->program_count; program_index++) {
@@ -371,7 +372,38 @@ trie_mark(void *pointer)
371
372
  size_t index;
372
373
  for (index = 0; index < program->instruction_count; index++) {
373
374
  if (program->instructions[index].opcode != TRIE_LITERAL_ALTERNATIVES) {
374
- rb_gc_mark(program->instructions[index].name);
375
+ rb_gc_mark_movable(program->instructions[index].name);
376
+ }
377
+ }
378
+ }
379
+ }
380
+
381
+ static void
382
+ trie_compact(void *pointer)
383
+ {
384
+ trie_t *trie = pointer;
385
+ if (!trie) return;
386
+ if (!NIL_P(trie->owner)) {
387
+ containers_gc_update(&trie->owner);
388
+ return;
389
+ }
390
+ containers_gc_update(&trie->tokens);
391
+ for (size_t n = 0; n < trie->node_count; n++) {
392
+ trie_node_t *node = trie->nodes[n];
393
+ for (size_t i = 0; i < node->static_count; i++) {
394
+ containers_gc_update(&node->static_edges[i].label);
395
+ }
396
+ for (size_t i = 0; i < node->regexp_count; i++) {
397
+ containers_gc_update(&node->regexp_edges[i].source);
398
+ containers_gc_update(&node->regexp_edges[i].anchored);
399
+ containers_gc_update(&node->regexp_edges[i].timeout);
400
+ }
401
+ }
402
+ for (size_t p = 0; p < trie->program_count; p++) {
403
+ trie_program_t *program = trie->programs[p];
404
+ for (size_t i = 0; i < program->instruction_count; i++) {
405
+ if (program->instructions[i].opcode != TRIE_LITERAL_ALTERNATIVES) {
406
+ containers_gc_update(&program->instructions[i].name);
375
407
  }
376
408
  }
377
409
  }
@@ -478,10 +510,15 @@ static const rb_data_type_t trie_type = {
478
510
  .wrap_struct_name = "Farce::Internal::Trie",
479
511
  .function = {
480
512
  .dmark = trie_mark,
513
+ .dcompact = trie_compact,
481
514
  .dfree = trie_free,
482
515
  .dsize = trie_memsize,
483
516
  },
484
- .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE,
517
+ .flags =
518
+ RUBY_TYPED_WB_PROTECTED |
519
+ RUBY_TYPED_FREE_IMMEDIATELY |
520
+ RUBY_TYPED_FROZEN_SHAREABLE |
521
+ FARCE_TYPED_THREAD_SAFE_FREE,
485
522
  };
486
523
 
487
524
  static VALUE
@@ -846,8 +883,8 @@ trie_parse_capture(trie_parser_t *parser)
846
883
  instruction.name = Qnil;
847
884
  if (!trie_program_add_instruction(parser->program, instruction)) return false;
848
885
  name = rb_enc_str_new((const char *)parser->bytes + name_start, (long)name_length, rb_usascii_encoding());
849
- rb_obj_freeze(name);
850
- parser->program->instructions[parser->program->instruction_count - 1].name = name;
886
+ name = rb_str_to_interned_str(name);
887
+ RB_OBJ_WRITE(parser->self, &parser->program->instructions[parser->program->instruction_count - 1].name, name);
851
888
  parser->program->capture_count++;
852
889
  if (opcode == TRIE_CAPTURE_DIGIT || opcode == TRIE_CAPTURE_WORD) {
853
890
  parser->program->requires_ascii_input = true;
@@ -1028,7 +1065,7 @@ trie_program_cache_rebuild(trie_t *trie, size_t capacity)
1028
1065
  }
1029
1066
 
1030
1067
  static trie_program_t *
1031
- trie_compile_program(trie_t *trie, VALUE source, int options, VALUE timeout)
1068
+ trie_compile_program(VALUE self, trie_t *trie, VALUE source, int options, VALUE timeout)
1032
1069
  {
1033
1070
  size_t length;
1034
1071
  int encoding;
@@ -1068,6 +1105,7 @@ trie_compile_program(trie_t *trie, VALUE source, int options, VALUE timeout)
1068
1105
  program->key_options = options;
1069
1106
 
1070
1107
  memset(&parser, 0, sizeof(parser));
1108
+ parser.self = self;
1071
1109
  parser.bytes = program->key;
1072
1110
  parser.length = length;
1073
1111
  parser.program = program;
@@ -1199,7 +1237,7 @@ trie_regexp_edge_equal(const trie_regexp_edge_t *edge, VALUE source,
1199
1237
  }
1200
1238
 
1201
1239
  static trie_node_t *
1202
- trie_insert_string(trie_t *trie, trie_node_t *node, VALUE string)
1240
+ trie_insert_string(VALUE self, trie_t *trie, trie_node_t *node, VALUE string)
1203
1241
  {
1204
1242
  size_t offset = 0;
1205
1243
  size_t length = (size_t)RSTRING_LEN(string);
@@ -1210,7 +1248,7 @@ trie_insert_string(trie_t *trie, trie_node_t *node, VALUE string)
1210
1248
  VALUE label = trie_frozen_string(string, (long)offset, (long)(length - offset));
1211
1249
  size_t edge_index = node->static_count;
1212
1250
  trie_ensure_static_edges(node, node->static_count + 1);
1213
- node->static_edges[edge_index].label = label;
1251
+ RB_OBJ_WRITE(self, &node->static_edges[edge_index].label, label);
1214
1252
  node->static_edges[edge_index].child = child;
1215
1253
  node->static_edges[edge_index].encoding = rb_enc_get_index(label);
1216
1254
  node->static_count++;
@@ -1233,13 +1271,13 @@ trie_insert_string(trie_t *trie, trie_node_t *node, VALUE string)
1233
1271
  VALUE prefix = trie_frozen_string(old_label, 0, (long)shared);
1234
1272
  VALUE old_suffix = trie_frozen_string(old_label, (long)shared, (long)(edge_length - shared));
1235
1273
  trie_ensure_static_edges(middle, shared == length - offset ? 1 : 2);
1236
- middle->static_edges[0].label = old_suffix;
1274
+ RB_OBJ_WRITE(self, &middle->static_edges[0].label, old_suffix);
1237
1275
  middle->static_edges[0].child = edge->child;
1238
1276
  middle->static_edges[0].encoding = rb_enc_get_index(old_suffix);
1239
1277
  middle->static_count = 1;
1240
1278
  trie->static_edge_count++;
1241
1279
  trie_index_static_edge(trie, middle, 0);
1242
- edge->label = prefix;
1280
+ RB_OBJ_WRITE(self, &edge->label, prefix);
1243
1281
  edge->child = middle;
1244
1282
  edge->encoding = rb_enc_get_index(prefix);
1245
1283
  offset += shared;
@@ -1251,7 +1289,7 @@ trie_insert_string(trie_t *trie, trie_node_t *node, VALUE string)
1251
1289
  }
1252
1290
  trie_node_t *child = trie_node_new(trie);
1253
1291
  VALUE new_suffix = trie_frozen_string(string, (long)offset, (long)(length - offset));
1254
- middle->static_edges[1].label = new_suffix;
1292
+ RB_OBJ_WRITE(self, &middle->static_edges[1].label, new_suffix);
1255
1293
  middle->static_edges[1].child = child;
1256
1294
  middle->static_edges[1].encoding = rb_enc_get_index(new_suffix);
1257
1295
  middle->static_count = 2;
@@ -1267,7 +1305,7 @@ trie_insert_string(trie_t *trie, trie_node_t *node, VALUE string)
1267
1305
  }
1268
1306
 
1269
1307
  static trie_node_t *
1270
- trie_insert_regexp(trie_t *trie, trie_node_t *node, VALUE regexp)
1308
+ trie_insert_regexp(VALUE self, trie_t *trie, trie_node_t *node, VALUE regexp)
1271
1309
  {
1272
1310
  VALUE source = RREGEXP_SRC(regexp);
1273
1311
  VALUE source_copy = rb_str_dup(source);
@@ -1287,13 +1325,13 @@ trie_insert_regexp(trie_t *trie, trie_node_t *node, VALUE regexp)
1287
1325
  }
1288
1326
  }
1289
1327
  VALUE anchored = trie_anchored_regexp(source_copy, options, timeout);
1290
- trie_program_t *program = trie_compile_program(trie, source_copy, options, timeout);
1328
+ trie_program_t *program = trie_compile_program(self, trie, source_copy, options, timeout);
1291
1329
  trie_node_t *child = trie_node_new(trie);
1292
1330
  trie_ensure_regexp_edges(node, node->regexp_count + 1);
1293
1331
  trie_regexp_edge_t *edge = &node->regexp_edges[node->regexp_count++];
1294
- edge->source = source_copy;
1295
- edge->anchored = anchored;
1296
- edge->timeout = timeout;
1332
+ RB_OBJ_WRITE(self, &edge->source, source_copy);
1333
+ RB_OBJ_WRITE(self, &edge->anchored, anchored);
1334
+ RB_OBJ_WRITE(self, &edge->timeout, timeout);
1297
1335
  edge->options = options;
1298
1336
  edge->program = program;
1299
1337
  edge->child = child;
@@ -1306,23 +1344,23 @@ trie_insert_regexp(trie_t *trie, trie_node_t *node, VALUE regexp)
1306
1344
  }
1307
1345
 
1308
1346
  static void
1309
- trie_insert_entry(trie_t *trie, VALUE parts, uint32_t token)
1347
+ trie_insert_entry(VALUE self, trie_t *trie, VALUE parts, uint32_t token)
1310
1348
  {
1311
1349
  trie_node_t *node = trie->root;
1312
1350
  long index;
1313
1351
  Check_Type(parts, T_ARRAY);
1314
1352
  for (index = 0; index < RARRAY_LEN(parts); index++) {
1315
- VALUE part = RARRAY_AREF(parts, index);
1353
+ VALUE part = rb_ary_entry(parts, index);
1316
1354
  if (RB_TYPE_P(part, T_STRING)) {
1317
1355
  VALUE stable_part = rb_str_new_frozen(part);
1318
1356
  if (rb_enc_str_coderange(stable_part) == ENC_CODERANGE_BROKEN) {
1319
1357
  rb_raise(rb_eArgError, "trie literal has invalid encoding");
1320
1358
  }
1321
- node = trie_insert_string(trie, node, stable_part);
1359
+ node = trie_insert_string(self, trie, node, stable_part);
1322
1360
  RB_GC_GUARD(stable_part);
1323
1361
  }
1324
1362
  else if (rb_obj_is_kind_of(part, rb_cRegexp)) {
1325
- node = trie_insert_regexp(trie, node, part);
1363
+ node = trie_insert_regexp(self, trie, node, part);
1326
1364
  }
1327
1365
  else {
1328
1366
  rb_raise(rb_eTypeError, "trie parts must be strings or regular expressions");
@@ -1386,7 +1424,7 @@ trie_initialize(VALUE self, VALUE entries, VALUE tokens)
1386
1424
  token_identities = rb_hash_new();
1387
1425
  rb_funcall(token_identities, rb_intern("compare_by_identity"), 0);
1388
1426
  for (index = 0; index < RARRAY_LEN(tokens); index++) {
1389
- VALUE token = RARRAY_AREF(tokens, index);
1427
+ VALUE token = rb_ary_entry(tokens, index);
1390
1428
  containers_check_shareable(token);
1391
1429
  if (rb_hash_lookup2(token_identities, token, Qundef) != Qundef) {
1392
1430
  rb_raise(rb_eArgError, "trie tokens must have unique identities");
@@ -1396,24 +1434,24 @@ trie_initialize(VALUE self, VALUE entries, VALUE tokens)
1396
1434
  if ((index & (TRIE_INTERRUPT_INTERVAL - 1)) == TRIE_INTERRUPT_INTERVAL - 1) trie_check_interrupts();
1397
1435
  }
1398
1436
  rb_obj_freeze(token_copy);
1399
- trie->tokens = token_copy;
1437
+ RB_OBJ_WRITE(self, &trie->tokens, token_copy);
1400
1438
  trie->root = trie_node_new(trie);
1401
1439
 
1402
1440
  for (index = 0; index < RARRAY_LEN(entries); index++) {
1403
- VALUE entry = RARRAY_AREF(entries, index);
1441
+ VALUE entry = rb_ary_entry(entries, index);
1404
1442
  VALUE parts;
1405
1443
  VALUE identifier;
1406
1444
  unsigned long id;
1407
1445
  Check_Type(entry, T_ARRAY);
1408
1446
  if (RARRAY_LEN(entry) != 2) rb_raise(rb_eArgError, "trie entry must contain parts and token id");
1409
- parts = RARRAY_AREF(entry, 0);
1410
- identifier = RARRAY_AREF(entry, 1);
1447
+ parts = rb_ary_entry(entry, 0);
1448
+ identifier = rb_ary_entry(entry, 1);
1411
1449
  if (!RB_INTEGER_TYPE_P(identifier)) rb_raise(rb_eTypeError, "trie token id must be an integer");
1412
1450
  id = NUM2ULONG(identifier);
1413
1451
  if (id >= (unsigned long)RARRAY_LEN(token_copy) || id > UINT32_MAX) {
1414
1452
  rb_raise(rb_eArgError, "trie token id is out of range");
1415
1453
  }
1416
- trie_insert_entry(trie, parts, (uint32_t)id);
1454
+ trie_insert_entry(self, trie, parts, (uint32_t)id);
1417
1455
  if ((index & (TRIE_INTERRUPT_INTERVAL - 1)) == TRIE_INTERRUPT_INTERVAL - 1) trie_check_interrupts();
1418
1456
  }
1419
1457
  trie->initialized = true;
@@ -1477,7 +1515,7 @@ trie_search_ensure_events(trie_search_t *search, size_t minimum)
1477
1515
  }
1478
1516
 
1479
1517
  static void
1480
- trie_search_append_native(trie_search_t *search, long begin, long end, VALUE name)
1518
+ trie_search_append_native(trie_search_t *search, long begin, long end, const VALUE *name)
1481
1519
  {
1482
1520
  trie_capture_event_t *event;
1483
1521
  trie_search_ensure_events(search, search->event_count + 1);
@@ -1500,7 +1538,7 @@ trie_search_append_ruby(trie_search_t *search, VALUE match)
1500
1538
  event = &search->events[search->event_count++];
1501
1539
  event->kind = TRIE_EVENT_RUBY;
1502
1540
  event->begin = event->end = 0;
1503
- event->name = Qnil;
1541
+ event->name = NULL;
1504
1542
  event->match_index = match_index;
1505
1543
  }
1506
1544
 
@@ -1536,7 +1574,7 @@ trie_program_match(const trie_program_t *program, VALUE input, long cursor,
1536
1574
  trie_search_work_checkpoint(search);
1537
1575
  }
1538
1576
  if (cursor == begin) return false;
1539
- trie_search_append_native(search, begin, cursor, instruction->name);
1577
+ trie_search_append_native(search, begin, cursor, &instruction->name);
1540
1578
  }
1541
1579
  }
1542
1580
  if (program->end_anchor && cursor != length) return false;
@@ -1633,17 +1671,17 @@ static void
1633
1671
  trie_materialize_captures(trie_search_t *search, VALUE *captures, VALUE *named)
1634
1672
  {
1635
1673
  size_t index;
1636
- *captures = rb_ary_new();
1674
+ *captures = rb_ary_new_capa((long)search->event_count);
1637
1675
  *named = rb_hash_new();
1638
1676
  for (index = 0; index < search->event_count; index++) {
1639
1677
  trie_capture_event_t *event = &search->events[index];
1640
1678
  if (event->kind == TRIE_EVENT_NATIVE) {
1641
1679
  VALUE value = rb_str_subseq(search->input, event->begin, event->end - event->begin);
1642
1680
  rb_ary_push(*captures, value);
1643
- trie_merge_named(*named, event->name, value);
1681
+ trie_merge_named(*named, *event->name, value);
1644
1682
  }
1645
1683
  else {
1646
- VALUE match = RARRAY_AREF(search->matches, event->match_index);
1684
+ VALUE match = rb_ary_entry(search->matches, event->match_index);
1647
1685
  VALUE edge_captures = rb_funcall(match, id_captures, 0);
1648
1686
  VALUE edge_named = rb_funcall(match, id_named_captures, 0);
1649
1687
  trie_named_merge_t merge = {.destination = *named};
@@ -1743,7 +1781,7 @@ trie_emit_terminal(trie_search_t *search, trie_frame_t *frame)
1743
1781
  if (trie_seen(search, id)) continue;
1744
1782
  trie_materialize_captures(search, &captures, &named);
1745
1783
  suffix = rb_str_subseq(search->input, frame->cursor, search->input_length - frame->cursor);
1746
- token = RARRAY_AREF(search->trie->tokens, (long)id);
1784
+ token = rb_ary_entry(search->trie->tokens, (long)id);
1747
1785
  if (search->stream) {
1748
1786
  rb_yield_values(4, token, captures, named, suffix);
1749
1787
  continue;
@@ -1930,7 +1968,7 @@ trie_initialize_copy(VALUE self, VALUE other)
1930
1968
  trie_release_contents(destination);
1931
1969
  memset(destination, 0, sizeof(*destination));
1932
1970
  destination->tokens = Qnil;
1933
- destination->owner = owner;
1971
+ RB_OBJ_WRITE(self, &destination->owner, owner);
1934
1972
  destination->initialized = true;
1935
1973
  rb_obj_freeze(self);
1936
1974
  containers_publish_native_with_references(self, trie_validate_references);