farce 0.5.0-aarch64-linux-gnu → 0.6.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/docs/modes.md +1 -1
  4. data/docs/utilities.md +1 -0
  5. data/ext/farce/atom.c +23 -7
  6. data/ext/farce/containers.h +3 -0
  7. data/ext/farce/counter.c +4 -6
  8. data/ext/farce/depend +3 -1
  9. data/ext/farce/extconf.rb +19 -14
  10. data/ext/farce/farce.c +101 -3
  11. data/ext/farce/map.c +1 -4
  12. data/ext/farce/trie.c +140 -72
  13. data/ext/farce/trie_search.h +114 -0
  14. data/ext/farce/vector.c +50 -18
  15. data/ext/farce/weak_map.c +4 -8
  16. data/ext/farce/weak_references.h +23 -0
  17. data/ext/java/org/farce/FiberScheduler.java +8 -6
  18. data/lib/farce/abstract/atom.rb +5 -1
  19. data/lib/farce/abstract/counter.rb +5 -4
  20. data/lib/farce/abstract/molecule.rb +14 -14
  21. data/lib/farce/abstract/vector.rb +1 -1
  22. data/lib/farce/atom.rb +6 -0
  23. data/lib/farce/clock.rb +8 -7
  24. data/lib/farce/counter.rb +6 -0
  25. data/lib/farce/deduper.rb +12 -4
  26. data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
  27. data/lib/farce/engine/jvm/counter.rb +0 -2
  28. data/lib/farce/engine/ruby/3.4/farce.so +0 -0
  29. data/lib/farce/engine/ruby/4.0/farce.so +0 -0
  30. data/lib/farce/engine/ruby/shared/weak_cache_mirror.rb +56 -0
  31. data/lib/farce/engine/ruby.rb +5 -1
  32. data/lib/farce/engine/shared/trie/ignore_case.rb +136 -0
  33. data/lib/farce/engine/shared/trie.rb +14 -1
  34. data/lib/farce/engine/shared/trie_builder.rb +19 -8
  35. data/lib/farce/engine/shared/unshared_vector.rb +3 -1
  36. data/lib/farce/engine/shared/vector.rb +2 -0
  37. data/lib/farce/engine/shared.rb +2 -0
  38. data/lib/farce/engine/truffleruby/native/counter.rb +0 -2
  39. data/lib/farce/engine/truffleruby.rb +9 -0
  40. data/lib/farce/extension.rb +5 -3
  41. data/lib/farce/integrations/sorted_set.rb +1 -0
  42. data/lib/farce/internal/fake_ractor.rb +1 -1
  43. data/lib/farce/internal/freeze.rb +1 -5
  44. data/lib/farce/internal/map_value_modes.rb +8 -7
  45. data/lib/farce/internal.rb +11 -0
  46. data/lib/farce/local/counter.rb +4 -10
  47. data/lib/farce/map.rb +8 -1
  48. data/lib/farce/memoize.rb +10 -5
  49. data/lib/farce/shareable.rb +3 -0
  50. data/lib/farce/strict/weak_cache.rb +199 -0
  51. data/lib/farce/vector.rb +8 -7
  52. data/lib/farce/version.rb +1 -1
  53. data/lib/farce/walker/definitions.rb +18 -0
  54. data/lib/farce/walker/modification.rb +28 -14
  55. data/lib/farce/walker.rb +9 -1
  56. data/lib/farce/weak_key_map.rb +1 -1
  57. data/lib/farce/weak_map.rb +1 -1
  58. data/lib/farce/weak_value_map.rb +1 -1
  59. metadata +6 -1
data/ext/farce/trie.c CHANGED
@@ -111,6 +111,7 @@ typedef struct {
111
111
  size_t static_index_count;
112
112
  size_t static_index_bytes;
113
113
  size_t regexp_edge_count;
114
+ VALUE (*search_body)(VALUE);
114
115
  bool initialized;
115
116
  } trie_t;
116
117
 
@@ -131,6 +132,7 @@ typedef struct {
131
132
  size_t static_index;
132
133
  size_t regexp_index;
133
134
  unsigned phase;
135
+ unsigned lowercase_offset;
134
136
  } trie_frame_t;
135
137
 
136
138
  /* A suspended Enumerator or user Fiber can be collected without unwinding the
@@ -191,8 +193,12 @@ static VALUE cTrie;
191
193
  static ID id_captures;
192
194
  static ID id_named_captures;
193
195
  static ID id_timeout;
196
+ static ID id_downcase;
194
197
  static bool regexp_timeout_supported;
195
198
 
199
+ static VALUE trie_search_body(VALUE opaque);
200
+ static VALUE trie_search_body_ignore_case(VALUE opaque);
201
+
196
202
  #ifdef FARCE_TRIE_FAILURE_INJECTION
197
203
  static _Thread_local long trie_failure_countdown = -1;
198
204
  static VALUE trie_interrupt_queue = Qnil;
@@ -1356,6 +1362,14 @@ trie_insert_entry(VALUE self, trie_t *trie, VALUE parts, uint32_t token)
1356
1362
  if (rb_enc_str_coderange(stable_part) == ENC_CODERANGE_BROKEN) {
1357
1363
  rb_raise(rb_eArgError, "trie literal has invalid encoding");
1358
1364
  }
1365
+ if (trie->search_body == trie_search_body_ignore_case) {
1366
+ VALUE lowercase = rb_funcall(stable_part, id_downcase, 0);
1367
+ Check_Type(lowercase, T_STRING);
1368
+ stable_part = rb_str_new_frozen(lowercase);
1369
+ if (rb_enc_str_coderange(stable_part) == ENC_CODERANGE_BROKEN) {
1370
+ rb_raise(rb_eArgError, "trie literal has invalid encoding");
1371
+ }
1372
+ }
1359
1373
  node = trie_insert_string(self, trie, node, stable_part);
1360
1374
  RB_GC_GUARD(stable_part);
1361
1375
  }
@@ -1407,16 +1421,25 @@ trie_validate_references(VALUE self)
1407
1421
  }
1408
1422
 
1409
1423
  static VALUE
1410
- trie_initialize(VALUE self, VALUE entries, VALUE tokens)
1424
+ trie_initialize(int argc, VALUE *argv, VALUE self)
1411
1425
  {
1412
1426
  trie_t *trie;
1427
+ VALUE entries, tokens, options;
1428
+ VALUE ignore_case = Qfalse;
1413
1429
  VALUE token_copy;
1414
1430
  VALUE token_identities;
1415
1431
  long index;
1432
+ rb_scan_args(argc, argv, "2:", &entries, &tokens, &options);
1433
+ if (!NIL_P(options)) {
1434
+ ID keyword = rb_intern("ignore_case");
1435
+ rb_get_kwargs(options, &keyword, 0, 1, &ignore_case);
1436
+ if (ignore_case == Qundef) ignore_case = Qfalse;
1437
+ }
1416
1438
  TypedData_Get_Struct(self, trie_t, &trie_type, trie);
1417
1439
  if (trie->initialized || trie->node_count || !NIL_P(trie->tokens)) {
1418
1440
  rb_raise(rb_eRuntimeError, "trie initialization already attempted");
1419
1441
  }
1442
+ trie->search_body = RTEST(ignore_case) ? trie_search_body_ignore_case : trie_search_body;
1420
1443
  Check_Type(entries, T_ARRAY);
1421
1444
  Check_Type(tokens, T_ARRAY);
1422
1445
  if ((unsigned long)RARRAY_LEN(tokens) > UINT32_MAX) rb_raise(rb_eArgError, "too many trie tokens");
@@ -1616,6 +1639,107 @@ trie_static_match(VALUE input, long cursor, const trie_static_edge_t *edge,
1616
1639
  return true;
1617
1640
  }
1618
1641
 
1642
+ typedef struct {
1643
+ rb_encoding *encoding;
1644
+ bool ascii_compatible;
1645
+ } trie_case_mapping_t;
1646
+
1647
+ static unsigned char
1648
+ trie_lowercase_ascii(unsigned char byte)
1649
+ {
1650
+ return byte >= 'A' && byte <= 'Z' ? byte + ('a' - 'A') : byte;
1651
+ }
1652
+
1653
+ static int
1654
+ trie_lowercase_character(const trie_case_mapping_t *mapping, const unsigned char **cursor,
1655
+ const unsigned char *end, unsigned char *output)
1656
+ {
1657
+ int bytes = rb_enc_mbclen((const char *)*cursor, (const char *)end, mapping->encoding);
1658
+ OnigCaseFoldType flags = ONIGENC_CASE_DOWNCASE;
1659
+ int length = mapping->encoding->case_map(&flags, cursor, *cursor + bytes,
1660
+ output, output + ONIGENC_MBC_CASE_FOLD_MAXLEN,
1661
+ mapping->encoding);
1662
+ if (length <= 0) rb_raise(rb_eArgError, "cannot lowercase trie input character");
1663
+ return length;
1664
+ }
1665
+
1666
+ static void
1667
+ trie_search_push_frame_ignore_case(trie_search_t *search, trie_node_t *node, long cursor,
1668
+ size_t restore_events, unsigned lowercase_offset,
1669
+ const trie_case_mapping_t *mapping)
1670
+ {
1671
+ trie_search_ensure_frames(search, search->frame_count + 1);
1672
+ trie_frame_t *frame = &search->frames[search->frame_count++];
1673
+ memset(frame, 0, sizeof(*frame));
1674
+ frame->node = node;
1675
+ frame->cursor = cursor;
1676
+ frame->restore_events = restore_events;
1677
+ frame->lowercase_offset = lowercase_offset;
1678
+ if (node->static_index && cursor < search->input_length) {
1679
+ const unsigned char *bytes = (const unsigned char *)RSTRING_PTR(search->input) + cursor;
1680
+ unsigned char first;
1681
+ if (*bytes < 128 && mapping->ascii_compatible) first = trie_lowercase_ascii(*bytes);
1682
+ else {
1683
+ unsigned char lowercase[ONIGENC_MBC_CASE_FOLD_MAXLEN];
1684
+ const unsigned char *end = (const unsigned char *)RSTRING_END(search->input);
1685
+ trie_lowercase_character(mapping, &bytes, end, lowercase);
1686
+ first = lowercase[lowercase_offset];
1687
+ }
1688
+ const uint32_t *slot = trie_static_index_slot_const(node->static_index, first);
1689
+ frame->static_index = slot ? *slot : 0;
1690
+ }
1691
+ }
1692
+
1693
+ static bool
1694
+ trie_static_match_ignore_case(trie_search_t *search, const trie_frame_t *frame,
1695
+ const trie_static_edge_t *edge, const trie_case_mapping_t *mapping,
1696
+ long *end_cursor, unsigned *end_offset)
1697
+ {
1698
+ VALUE input = search->input;
1699
+ VALUE label = edge->label;
1700
+ if (edge->encoding != search->input_encoding && !rb_enc_compatible(label, input)) return false;
1701
+ long cursor = frame->cursor;
1702
+ unsigned offset = frame->lowercase_offset;
1703
+ long index = 0;
1704
+ long length = RSTRING_LEN(label);
1705
+ long checkpoint = 16384;
1706
+ while (index < length) {
1707
+ if (cursor >= search->input_length) return false;
1708
+ const unsigned char *bytes = (const unsigned char *)RSTRING_PTR(input) + cursor;
1709
+ const unsigned char *literal = (const unsigned char *)RSTRING_PTR(label);
1710
+ if (*bytes < 128 && mapping->ascii_compatible) {
1711
+ if (trie_lowercase_ascii(*bytes) != literal[index]) return false;
1712
+ cursor++;
1713
+ index++;
1714
+ }
1715
+ else {
1716
+ unsigned char lowercase[ONIGENC_MBC_CASE_FOLD_MAXLEN];
1717
+ const unsigned char *begin = bytes;
1718
+ const unsigned char *end = (const unsigned char *)RSTRING_END(input);
1719
+ int count = trie_lowercase_character(mapping, &bytes, end, lowercase);
1720
+ while (offset < (unsigned)count && index < length) {
1721
+ if (lowercase[offset++] != literal[index++]) return false;
1722
+ }
1723
+ /* A radix split can fall inside one character's lowercase expansion.
1724
+ * Retain its original cursor until every lowercase byte is consumed. */
1725
+ if (offset == (unsigned)count) {
1726
+ cursor += bytes - begin;
1727
+ offset = 0;
1728
+ }
1729
+ }
1730
+ if (index >= checkpoint) {
1731
+ trie_check_interrupts();
1732
+ checkpoint = index + 16384;
1733
+ }
1734
+ }
1735
+ *end_cursor = cursor;
1736
+ *end_offset = offset;
1737
+ return true;
1738
+ }
1739
+
1740
+ /* Keep the original traversal's inlining when compiling both search modes. */
1741
+ ALWAYS_INLINE(static bool trie_regexp_match(const trie_regexp_edge_t *edge, trie_search_t *search,
1742
+ long cursor, long *end_cursor));
1619
1743
  static bool
1620
1744
  trie_regexp_match(const trie_regexp_edge_t *edge, trie_search_t *search,
1621
1745
  long cursor, long *end_cursor)
@@ -1765,6 +1889,7 @@ trie_seen(trie_search_t *search, uint32_t token)
1765
1889
  return false;
1766
1890
  }
1767
1891
 
1892
+ ALWAYS_INLINE(static bool trie_emit_terminal(trie_search_t *search, trie_frame_t *frame));
1768
1893
  static bool
1769
1894
  trie_emit_terminal(trie_search_t *search, trie_frame_t *frame)
1770
1895
  {
@@ -1796,75 +1921,17 @@ trie_emit_terminal(trie_search_t *search, trie_frame_t *frame)
1796
1921
  return false;
1797
1922
  }
1798
1923
 
1799
- static VALUE
1800
- trie_search_body(VALUE opaque)
1801
- {
1802
- trie_search_t *search = (trie_search_t *)(uintptr_t)opaque;
1803
- trie_search_push_frame(search, search->trie->root, 0, 0);
1804
- while (search->frame_count) {
1805
- trie_frame_t *frame = &search->frames[search->frame_count - 1];
1806
- trie_search_work_checkpoint(search);
1807
- if (frame->phase == 0) {
1808
- bool descended = false;
1809
- if (frame->node->static_index) {
1810
- while (frame->static_index) {
1811
- trie_static_edge_t *edge = &frame->node->static_edges[frame->static_index - 1];
1812
- long label_length = RSTRING_LEN(edge->label);
1813
- frame->static_index = edge->next_same_byte;
1814
- if (!trie_static_match(search->input, frame->cursor, edge,
1815
- label_length, search->input_encoding)) continue;
1816
- trie_search_push_frame(search, edge->child, frame->cursor + label_length,
1817
- search->event_count);
1818
- descended = true;
1819
- break;
1820
- }
1821
- }
1822
- else {
1823
- while (frame->static_index < frame->node->static_count) {
1824
- trie_static_edge_t *edge = &frame->node->static_edges[frame->static_index++];
1825
- long label_length = RSTRING_LEN(edge->label);
1826
- if (!trie_static_match(search->input, frame->cursor, edge,
1827
- label_length, search->input_encoding)) continue;
1828
- trie_search_push_frame(search, edge->child, frame->cursor + label_length,
1829
- search->event_count);
1830
- descended = true;
1831
- break;
1832
- }
1833
- }
1834
- if (descended) continue;
1835
- frame->phase = 1;
1836
- }
1837
- if (frame->phase == 1) {
1838
- bool descended = false;
1839
- while (frame->regexp_index < frame->node->regexp_count) {
1840
- trie_regexp_edge_t *edge = &frame->node->regexp_edges[frame->regexp_index++];
1841
- size_t restore = search->event_count;
1842
- long end_cursor;
1843
- if (!trie_regexp_match(edge, search, frame->cursor, &end_cursor)) {
1844
- search->event_count = restore;
1845
- continue;
1846
- }
1847
- trie_search_push_frame(search, edge->child, end_cursor, restore);
1848
- descended = true;
1849
- break;
1850
- }
1851
- if (descended) continue;
1852
- frame->phase = 2;
1853
- }
1854
- if (frame->phase == 2) {
1855
- frame->phase = 3;
1856
- if ((search->peek || frame->cursor == search->input_length) &&
1857
- trie_emit_terminal(search, frame)) break;
1858
- }
1859
- if (frame->phase == 3) {
1860
- size_t restore = frame->restore_events;
1861
- search->frame_count--;
1862
- search->event_count = restore;
1863
- }
1864
- }
1865
- if (search->stream) return search->self;
1866
- return search->first_only ? search->first : search->results;
1867
- }
1924
+ #define TRIE_IGNORE_CASE 0
1925
+ #define TRIE_SEARCH_BODY trie_search_body
1926
+ #include "trie_search.h"
1927
+ #undef TRIE_SEARCH_BODY
1928
+ #undef TRIE_IGNORE_CASE
1929
+
1930
+ #define TRIE_IGNORE_CASE 1
1931
+ #define TRIE_SEARCH_BODY trie_search_body_ignore_case
1932
+ #include "trie_search.h"
1933
+ #undef TRIE_SEARCH_BODY
1934
+ #undef TRIE_IGNORE_CASE
1868
1935
 
1869
1936
  static VALUE
1870
1937
  trie_search_cleanup(VALUE opaque)
@@ -1916,7 +1983,7 @@ trie_search(VALUE self, VALUE input, bool peek, bool first_only, bool stream)
1916
1983
  encoding == rb_usascii_encindex() ||
1917
1984
  encoding == rb_ascii8bit_encindex();
1918
1985
  }
1919
- VALUE result = rb_ensure(trie_search_body, (VALUE)(uintptr_t)&search,
1986
+ VALUE result = rb_ensure(search.trie->search_body, (VALUE)(uintptr_t)&search,
1920
1987
  trie_search_cleanup, (VALUE)(uintptr_t)&search);
1921
1988
  RB_GC_GUARD(self);
1922
1989
  RB_GC_GUARD(input);
@@ -2035,7 +2102,7 @@ containers_init_trie(VALUE namespace)
2035
2102
  (void)namespace;
2036
2103
  cTrie = rb_define_class_under(strict, "Trie", rb_cObject);
2037
2104
  rb_define_alloc_func(cTrie, trie_allocate);
2038
- rb_define_private_method(cTrie, "initialize", trie_initialize, 2);
2105
+ rb_define_private_method(cTrie, "initialize", trie_initialize, -1);
2039
2106
  rb_define_private_method(cTrie, "initialize_copy", trie_initialize_copy, 1);
2040
2107
  rb_define_method(cTrie, "match", trie_match, 1);
2041
2108
  rb_define_method(cTrie, "match_all", trie_match_all, 1);
@@ -2054,5 +2121,6 @@ containers_init_trie(VALUE namespace)
2054
2121
  id_captures = rb_intern("captures");
2055
2122
  id_named_captures = rb_intern("named_captures");
2056
2123
  id_timeout = rb_intern("timeout");
2124
+ id_downcase = rb_intern("downcase");
2057
2125
  regexp_timeout_supported = rb_respond_to(rb_cRegexp, id_timeout);
2058
2126
  }
@@ -0,0 +1,114 @@
1
+ /* Included twice to specialize static matching without a mode branch in the
2
+ * traversal loop. Keep both modes on the same capture and backtracking logic. */
3
+
4
+ static VALUE
5
+ TRIE_SEARCH_BODY(VALUE opaque)
6
+ {
7
+ trie_search_t *search = (trie_search_t *)(uintptr_t)opaque;
8
+ #if TRIE_IGNORE_CASE
9
+ trie_case_mapping_t mapping = { .encoding = rb_enc_from_index(search->input_encoding) };
10
+ mapping.ascii_compatible = rb_enc_asciicompat(mapping.encoding);
11
+ trie_search_push_frame_ignore_case(search, search->trie->root, 0, 0, 0, &mapping);
12
+ #else
13
+ trie_search_push_frame(search, search->trie->root, 0, 0);
14
+ #endif
15
+ while (search->frame_count) {
16
+ trie_frame_t *frame = &search->frames[search->frame_count - 1];
17
+ trie_search_work_checkpoint(search);
18
+ if (frame->phase == 0) {
19
+ bool descended = false;
20
+ if (frame->node->static_index) {
21
+ while (frame->static_index) {
22
+ trie_static_edge_t *edge = &frame->node->static_edges[frame->static_index - 1];
23
+ #if TRIE_IGNORE_CASE
24
+ long end_cursor;
25
+ unsigned end_offset;
26
+ #else
27
+ long label_length = RSTRING_LEN(edge->label);
28
+ #endif
29
+ frame->static_index = edge->next_same_byte;
30
+ #if TRIE_IGNORE_CASE
31
+ if (!trie_static_match_ignore_case(search, frame, edge, &mapping,
32
+ &end_cursor, &end_offset)) continue;
33
+ trie_search_push_frame_ignore_case(search, edge->child, end_cursor,
34
+ search->event_count, end_offset, &mapping);
35
+ #else
36
+ if (!trie_static_match(search->input, frame->cursor, edge,
37
+ label_length, search->input_encoding)) continue;
38
+ trie_search_push_frame(search, edge->child, frame->cursor + label_length,
39
+ search->event_count);
40
+ #endif
41
+ descended = true;
42
+ break;
43
+ }
44
+ }
45
+ else {
46
+ while (frame->static_index < frame->node->static_count) {
47
+ trie_static_edge_t *edge = &frame->node->static_edges[frame->static_index++];
48
+ #if TRIE_IGNORE_CASE
49
+ long end_cursor;
50
+ unsigned end_offset;
51
+ #else
52
+ long label_length = RSTRING_LEN(edge->label);
53
+ #endif
54
+ #if TRIE_IGNORE_CASE
55
+ if (!trie_static_match_ignore_case(search, frame, edge, &mapping,
56
+ &end_cursor, &end_offset)) continue;
57
+ trie_search_push_frame_ignore_case(search, edge->child, end_cursor,
58
+ search->event_count, end_offset, &mapping);
59
+ #else
60
+ if (!trie_static_match(search->input, frame->cursor, edge,
61
+ label_length, search->input_encoding)) continue;
62
+ trie_search_push_frame(search, edge->child, frame->cursor + label_length,
63
+ search->event_count);
64
+ #endif
65
+ descended = true;
66
+ break;
67
+ }
68
+ }
69
+ if (descended) continue;
70
+ frame->phase = 1;
71
+ }
72
+ if (frame->phase == 1) {
73
+ bool descended = false;
74
+ while (frame->regexp_index < frame->node->regexp_count) {
75
+ #if TRIE_IGNORE_CASE
76
+ if (frame->lowercase_offset) break;
77
+ #endif
78
+ trie_regexp_edge_t *edge = &frame->node->regexp_edges[frame->regexp_index++];
79
+ size_t restore = search->event_count;
80
+ long end_cursor;
81
+ if (!trie_regexp_match(edge, search, frame->cursor, &end_cursor)) {
82
+ search->event_count = restore;
83
+ continue;
84
+ }
85
+ #if TRIE_IGNORE_CASE
86
+ trie_search_push_frame_ignore_case(search, edge->child, end_cursor, restore, 0, &mapping);
87
+ #else
88
+ trie_search_push_frame(search, edge->child, end_cursor, restore);
89
+ #endif
90
+ descended = true;
91
+ break;
92
+ }
93
+ if (descended) continue;
94
+ frame->phase = 2;
95
+ }
96
+ if (frame->phase == 2) {
97
+ frame->phase = 3;
98
+ if (
99
+ #if TRIE_IGNORE_CASE
100
+ !frame->lowercase_offset &&
101
+ #endif
102
+ (search->peek || frame->cursor == search->input_length) &&
103
+ trie_emit_terminal(search, frame)) break;
104
+ }
105
+ if (frame->phase == 3) {
106
+ size_t restore = frame->restore_events;
107
+ search->frame_count--;
108
+ search->event_count = restore;
109
+ }
110
+ }
111
+ if (search->stream) return search->self;
112
+ return search->first_only ? search->first : search->results;
113
+ }
114
+
data/ext/farce/vector.c CHANGED
@@ -4,6 +4,7 @@
4
4
  #include "ruby/fiber/scheduler.h"
5
5
 
6
6
  #include <errno.h>
7
+ #include <limits.h>
7
8
  #include <math.h>
8
9
  #include <stdint.h>
9
10
  #include <stdlib.h>
@@ -380,22 +381,26 @@ vector_assignment_index(vector_t *vector, long long raw)
380
381
  return (size_t)raw;
381
382
  }
382
383
 
384
+ static size_t
385
+ vector_initial_size(VALUE source)
386
+ {
387
+ long size = NUM2LONG(source);
388
+ if (size < 0) rb_raise(rb_eArgError, "negative array size");
389
+ if (size > LONG_MAX / (long)sizeof(VALUE)) rb_raise(rb_eArgError, "array size too big");
390
+ return (size_t)size;
391
+ }
392
+
383
393
  static VALUE
384
- vector_initialize(int argc, VALUE *argv, VALUE self)
394
+ vector_normalize_source(VALUE klass, VALUE source)
385
395
  {
386
- VALUE source = Qnil;
387
- VALUE keywords = Qnil;
388
- VALUE identity = Qundef;
389
- ID keyword_ids[] = {rb_intern("compare_by_identity")};
390
- VALUE keyword_values[1];
391
- vector_t *vector;
396
+ if (RB_INTEGER_TYPE_P(source)) vector_initial_size(source);
397
+ return source;
398
+ }
392
399
 
393
- rb_check_frozen(self);
394
- rb_scan_args(argc, argv, "01:", &source, &keywords);
395
- if (!NIL_P(keywords)) {
396
- rb_get_kwargs(keywords, keyword_ids, 0, 1, keyword_values);
397
- identity = keyword_values[0];
398
- }
400
+ static VALUE
401
+ vector_initialize_with_identity(VALUE self, VALUE source, VALUE identity)
402
+ {
403
+ vector_t *vector;
399
404
  TypedData_Get_Struct(self, vector_t, &vector_type, vector);
400
405
  if (vector->initialized) rb_raise(rb_eRuntimeError, "Vector is already initialized");
401
406
 
@@ -403,8 +408,12 @@ vector_initialize(int argc, VALUE *argv, VALUE self)
403
408
  ? false
404
409
  : containers_strict_bool(identity, "compare_by_identity");
405
410
 
411
+ bool sized = RB_INTEGER_TYPE_P(source);
406
412
  size_t size = 0;
407
- if (!NIL_P(source)) {
413
+ if (sized) {
414
+ size = vector_initial_size(source);
415
+ }
416
+ else if (!NIL_P(source)) {
408
417
  Check_Type(source, T_ARRAY);
409
418
  size = (size_t)RARRAY_LEN(source);
410
419
  }
@@ -413,16 +422,37 @@ vector_initialize(int argc, VALUE *argv, VALUE self)
413
422
  if (!vector->values) rb_memerror();
414
423
  vector->capacity = capacity;
415
424
 
416
- for (size_t index = 0; index < size; index++) {
417
- VALUE value = rb_ary_entry(source, (long)index);
418
- containers_check_shareable(value);
419
- RB_OBJ_WRITE(self, &vector->values[index], value);
425
+ if (!sized) {
426
+ for (size_t index = 0; index < size; index++) {
427
+ VALUE value = rb_ary_entry(source, (long)index);
428
+ containers_check_shareable(value);
429
+ RB_OBJ_WRITE(self, &vector->values[index], value);
430
+ }
420
431
  }
421
432
  vector->size = size;
422
433
  vector->initialized = true;
434
+ if (sized) return containers_publish_native_reference_free(self);
423
435
  return containers_publish_native_with_references(self, vector_validate_references);
424
436
  }
425
437
 
438
+ static VALUE
439
+ vector_initialize(int argc, VALUE *argv, VALUE self)
440
+ {
441
+ ID keyword_ids[] = {rb_intern("compare_by_identity")};
442
+ VALUE keyword_values[1];
443
+
444
+ rb_check_frozen(self);
445
+ VALUE source = containers_parse_constructor(argc, argv, keyword_ids, 1, keyword_values);
446
+ return vector_initialize_with_identity(self, source, keyword_values[0]);
447
+ }
448
+
449
+ static VALUE
450
+ vector_new_for_modes(VALUE klass, VALUE source)
451
+ {
452
+ VALUE self = rb_obj_alloc(klass);
453
+ return vector_initialize_with_identity(self, source, Qtrue);
454
+ }
455
+
426
456
  static VALUE
427
457
  vector_snapshot_locked(VALUE self)
428
458
  {
@@ -1185,6 +1215,8 @@ containers_init_vector(VALUE namespace)
1185
1215
 
1186
1216
  cVector = rb_define_class_under(namespace, "Vector", rb_cObject);
1187
1217
  rb_define_alloc_func(cVector, vector_allocate);
1218
+ rb_define_singleton_method(cVector, "normalize_source", vector_normalize_source, 1);
1219
+ rb_define_singleton_method(cVector, "new_for_modes", vector_new_for_modes, 1);
1188
1220
  rb_define_method(cVector, "freeze", vector_freeze, 0);
1189
1221
  rb_define_method(cVector, "initialize", vector_initialize, -1);
1190
1222
  rb_define_method(cVector, "size", vector_size, 0);
data/ext/farce/weak_map.c CHANGED
@@ -1,4 +1,5 @@
1
1
  #include "containers.h"
2
+ #include "weak_references.h"
2
3
 
3
4
  #ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
4
5
 
@@ -15,11 +16,6 @@
15
16
 
16
17
  #define WEAK_MAP_INITIAL_CAPACITY 16
17
18
 
18
- /* These functions are exported by Rubies with the probed typed-data weak
19
- * reference callback, but their declarations intentionally remain internal. */
20
- void rb_gc_declare_weak_references(VALUE object);
21
- bool rb_gc_handle_weak_references_alive_p(VALUE object);
22
-
23
19
  static VALUE cWeakKeyMap;
24
20
  static VALUE weak_map_keep;
25
21
  static VALUE weak_map_delete_token;
@@ -209,7 +205,7 @@ weak_map_compact(void *pointer)
209
205
  * mutex here could deadlock if GC stopped a mutator at a safepoint while it
210
206
  * owned the mutex, so cleanup deliberately runs lock-free. */
211
207
  static void
212
- weak_map_handle_weak_references(void *pointer)
208
+ FARCE_WEAK_REFERENCES_CALLBACK(weak_map_handle_weak_references)
213
209
  {
214
210
  weak_map_t *map = pointer;
215
211
  if (!map->slots) return;
@@ -219,8 +215,8 @@ weak_map_handle_weak_references(void *pointer)
219
215
  weak_map_slot_t *slot = &map->slots[index];
220
216
  if (slot->state != WEAK_MAP_OCCUPIED) continue;
221
217
 
222
- bool key_alive = !map->weak_keys || rb_gc_handle_weak_references_alive_p(slot->key);
223
- bool value_alive = !map->weak_values || rb_gc_handle_weak_references_alive_p(slot->value);
218
+ bool key_alive = !map->weak_keys || FARCE_WEAK_REFERENCE_ALIVE_P(slot->key);
219
+ bool value_alive = !map->weak_values || FARCE_WEAK_REFERENCE_ALIVE_P(slot->value);
224
220
  if (key_alive && value_alive) continue;
225
221
 
226
222
  slot->key = Qnil;
@@ -0,0 +1,23 @@
1
+ #ifndef FARCE_WEAK_REFERENCES_H
2
+ #define FARCE_WEAK_REFERENCES_H
3
+
4
+ #include "ruby.h"
5
+ #include <stdbool.h>
6
+
7
+ #ifdef RC_HAVE_NATIVE_WEAK_REFERENCES
8
+ /* These symbols are exported, but their declarations remain internal. The
9
+ * callback and liveness function gained the same opaque GC context together. */
10
+ void rb_gc_declare_weak_references(VALUE object);
11
+
12
+ #ifdef FARCE_WEAK_REFERENCES_HAVE_OBJSPACE
13
+ bool rb_gc_handle_weak_references_alive_p(void *objspace, VALUE object);
14
+ # define FARCE_WEAK_REFERENCES_CALLBACK(name) name(void *pointer, void *objspace)
15
+ # define FARCE_WEAK_REFERENCE_ALIVE_P(object) rb_gc_handle_weak_references_alive_p(objspace, object)
16
+ #else
17
+ bool rb_gc_handle_weak_references_alive_p(VALUE object);
18
+ # define FARCE_WEAK_REFERENCES_CALLBACK(name) name(void *pointer)
19
+ # define FARCE_WEAK_REFERENCE_ALIVE_P(object) rb_gc_handle_weak_references_alive_p(object)
20
+ #endif
21
+ #endif
22
+
23
+ #endif
@@ -17,6 +17,7 @@ import org.jruby.RubyModule;
17
17
  import org.jruby.RubyObject;
18
18
  import org.jruby.RubyThread;
19
19
  import org.jruby.anno.JRubyMethod;
20
+ import org.jruby.api.Access;
20
21
  import org.jruby.runtime.ThreadContext;
21
22
  import org.jruby.runtime.builtin.IRubyObject;
22
23
  import org.jruby.util.io.SelectExecutor;
@@ -30,9 +31,10 @@ public final class FiberScheduler extends RubyObject {
30
31
  public FiberScheduler(Ruby runtime, RubyClass klass) { super(runtime, klass); }
31
32
 
32
33
  public static void load(Ruby runtime) {
33
- RubyModule internal = (RubyModule) runtime.getModule("Farce").getConstantAt("Internal");
34
- RubyClass klass = internal.defineClassUnder("FiberScheduler", runtime.getObject(), FiberScheduler::new);
35
- klass.defineAnnotatedMethods(FiberScheduler.class);
34
+ ThreadContext context = runtime.getCurrentContext();
35
+ RubyModule internal = (RubyModule) Access.getModule(context, "Farce").getConstantAt(context, "Internal");
36
+ RubyClass klass = internal.defineClassUnder(context, "FiberScheduler", runtime.getObject(), FiberScheduler::new);
37
+ klass.defineMethods(context, FiberScheduler.class);
36
38
  }
37
39
 
38
40
  @JRubyMethod(name = "nio_initialize")
@@ -59,14 +61,14 @@ public final class FiberScheduler extends RubyObject {
59
61
  checkOwner(context);
60
62
  if (selector == null) throw context.runtime.newIOError("scheduler is closed");
61
63
  RubyArray groups = (RubyArray) groupValue;
62
- Long milliseconds = timeout.isNil() ? null : Math.max(0, (long)Math.ceil(timeout.convertToFloat().getDoubleValue() * 1000));
64
+ Long milliseconds = timeout.isNil() ? null : Math.max(0, (long)Math.ceil(timeout.convertToFloat().asDouble(context) * 1000));
63
65
  IRubyObject immediate = snapshot(context, groups, 0L);
64
66
  if (!immediate.isNil() || (milliseconds != null && milliseconds == 0)) return immediate;
65
67
  HashMap<SelectableChannel, Integer> interests = new HashMap<>();
66
68
  boolean compatible = ((RubyArray)groups.entry(2)).isEmpty();
67
69
  for (int i = 0; i < 2; i++) {
68
70
  RubyArray list = (RubyArray)groups.entry(i);
69
- for (Object object : list.toJavaArray()) {
71
+ for (Object object : list.toJavaArray(context)) {
70
72
  RubyIO io = RubyIO.convertToIO(context, (IRubyObject)object);
71
73
  SelectableChannel channel = io.getOpenFileChecked().fd().chSelect;
72
74
  if (channel == null || channel.provider() != selector.provider()) { compatible = false; continue; }
@@ -123,7 +125,7 @@ public final class FiberScheduler extends RubyObject {
123
125
  public IRubyObject hookBuffer(ThreadContext context, IRubyObject value) {
124
126
  RubyIOBuffer buffer = (RubyIOBuffer)value;
125
127
  if (!buffer.locked_p(context).isTrue()) return value;
126
- if (buffer.size(context).convertToInteger().getLongValue() != 0) return value;
128
+ if (buffer.size(context).convertToInteger().asLong(context) != 0) return value;
127
129
  try {
128
130
  Field field = RubyIOBuffer.class.getDeclaredField("base");
129
131
  if (!field.trySetAccessible()) throw context.runtime.newRuntimeError("Farce cannot access the JRuby hook buffer layout");
@@ -59,7 +59,11 @@ module Farce
59
59
  # @yield computes the value to store when the current value is nil
60
60
  # @yieldreturn [BasicObject, nil] the value to store
61
61
  # @return [BasicObject, nil] the current or newly stored value, or nil when the timeout expires
62
- def store_if_absent(timeout: nil, &) = internal_atom.store_if_absent(timeout:, &)
62
+ def store_if_absent(timeout: nil, &)
63
+ return internal_atom.store_if_absent(&) if timeout.nil?
64
+
65
+ internal_atom.store_if_absent(timeout:, &)
66
+ end
63
67
 
64
68
  # Atomically replace the current value if it matches the expected value.
65
69
  # @param expected [BasicObject, nil] the value to compare with the current value
@@ -15,14 +15,15 @@ module Farce
15
15
  # @return [Integer] The current value of the counter.
16
16
  #
17
17
  # @!method increment(by = 1)
18
- # Increment the counter by the given amount (default is 1).
18
+ # Atomically increment the counter by the given amount (default is 1).
19
+ # Use the returned integer to allocate a sequence number without a separate read.
19
20
  # @param [Numeric, String, #to_int] by The amount, converted to an Integer.
20
- # @return [self] Returns self for chaining.
21
+ # @return [Integer] The value produced by this atomic update.
21
22
  #
22
23
  # @!method decrement(by = 1)
23
- # Decrement the counter by the given amount (default is 1).
24
+ # Atomically decrement the counter by the given amount (default is 1).
24
25
  # @param [Numeric, String, #to_int] by The amount, converted to an Integer.
25
- # @return [self] Returns self for chaining.
26
+ # @return [Integer] The value produced by this atomic update.
26
27
  module Counter
27
28
  include Internal::MarshalSupport::Counter
28
29
  include Internal::Copyable