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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -0
- data/docs/modes.md +1 -1
- data/docs/utilities.md +1 -0
- data/ext/farce/atom.c +23 -7
- data/ext/farce/containers.h +3 -0
- data/ext/farce/counter.c +4 -6
- data/ext/farce/depend +3 -1
- data/ext/farce/extconf.rb +19 -14
- data/ext/farce/farce.c +101 -3
- data/ext/farce/map.c +1 -4
- data/ext/farce/trie.c +140 -72
- data/ext/farce/trie_search.h +114 -0
- data/ext/farce/vector.c +50 -18
- data/ext/farce/weak_map.c +4 -8
- data/ext/farce/weak_references.h +23 -0
- data/ext/java/org/farce/FiberScheduler.java +8 -6
- data/lib/farce/abstract/atom.rb +5 -1
- data/lib/farce/abstract/counter.rb +5 -4
- data/lib/farce/abstract/molecule.rb +14 -14
- data/lib/farce/abstract/vector.rb +1 -1
- data/lib/farce/atom.rb +6 -0
- data/lib/farce/clock.rb +8 -7
- data/lib/farce/counter.rb +6 -0
- data/lib/farce/deduper.rb +12 -4
- data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
- data/lib/farce/engine/jvm/counter.rb +0 -2
- data/lib/farce/engine/ruby/3.4/farce.so +0 -0
- data/lib/farce/engine/ruby/4.0/farce.so +0 -0
- data/lib/farce/engine/ruby/shared/weak_cache_mirror.rb +56 -0
- data/lib/farce/engine/ruby.rb +5 -1
- data/lib/farce/engine/shared/trie/ignore_case.rb +136 -0
- data/lib/farce/engine/shared/trie.rb +14 -1
- data/lib/farce/engine/shared/trie_builder.rb +19 -8
- data/lib/farce/engine/shared/unshared_vector.rb +3 -1
- data/lib/farce/engine/shared/vector.rb +2 -0
- data/lib/farce/engine/shared.rb +2 -0
- data/lib/farce/engine/truffleruby/native/counter.rb +0 -2
- data/lib/farce/engine/truffleruby.rb +9 -0
- data/lib/farce/extension.rb +5 -3
- data/lib/farce/integrations/sorted_set.rb +1 -0
- data/lib/farce/internal/fake_ractor.rb +1 -1
- data/lib/farce/internal/freeze.rb +1 -5
- data/lib/farce/internal/map_value_modes.rb +8 -7
- data/lib/farce/internal.rb +11 -0
- data/lib/farce/local/counter.rb +4 -10
- data/lib/farce/map.rb +8 -1
- data/lib/farce/memoize.rb +10 -5
- data/lib/farce/shareable.rb +3 -0
- data/lib/farce/strict/weak_cache.rb +199 -0
- data/lib/farce/vector.rb +8 -7
- data/lib/farce/version.rb +1 -1
- data/lib/farce/walker/definitions.rb +18 -0
- data/lib/farce/walker/modification.rb +28 -14
- data/lib/farce/walker.rb +9 -1
- data/lib/farce/weak_key_map.rb +1 -1
- data/lib/farce/weak_map.rb +1 -1
- data/lib/farce/weak_value_map.rb +1 -1
- 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(
|
|
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
|
-
|
|
1800
|
-
trie_search_body
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
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(
|
|
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,
|
|
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
|
-
|
|
394
|
+
vector_normalize_source(VALUE klass, VALUE source)
|
|
385
395
|
{
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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 (
|
|
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
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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
|
|
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 ||
|
|
223
|
-
bool value_alive = !map->weak_values ||
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
klass.
|
|
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().
|
|
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().
|
|
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");
|
data/lib/farce/abstract/atom.rb
CHANGED
|
@@ -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, &)
|
|
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
|
-
#
|
|
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 [
|
|
21
|
+
# @return [Integer] The value produced by this atomic update.
|
|
21
22
|
#
|
|
22
23
|
# @!method decrement(by = 1)
|
|
23
|
-
#
|
|
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 [
|
|
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
|