farce 0.0.1.alpha2-x86_64-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 +7 -0
- data/CODE_OF_CONDUCT.md +26 -0
- data/CONTRIBUTING.md +71 -0
- data/MIT-LICENSE +20 -0
- data/README.md +1524 -0
- data/SECURITY.md +10 -0
- data/docs/benchmarks.md +185 -0
- data/docs/gems/dry-types.md +290 -0
- data/docs/gems/msgpack.md +70 -0
- data/docs/gems/ractor-shim.md +63 -0
- data/docs/modes.md +628 -0
- data/docs/scopes.md +649 -0
- data/docs/variants.md +346 -0
- data/ext/ext_helper.rb +20 -0
- data/ext/farce/README.md +21 -0
- data/ext/farce/atom.c +1023 -0
- data/ext/farce/bounded_map.c +1683 -0
- data/ext/farce/containers.h +77 -0
- data/ext/farce/counter.c +399 -0
- data/ext/farce/darwin.c +100 -0
- data/ext/farce/depend +12 -0
- data/ext/farce/dict.c +1523 -0
- data/ext/farce/dict.h +152 -0
- data/ext/farce/drivers.c +237 -0
- data/ext/farce/exchanger.c +299 -0
- data/ext/farce/extconf.rb +99 -0
- data/ext/farce/farce.c +359 -0
- data/ext/farce/flag.c +288 -0
- data/ext/farce/io.c +338 -0
- data/ext/farce/lock.c +510 -0
- data/ext/farce/map.c +2240 -0
- data/ext/farce/priority_queue.c +2056 -0
- data/ext/farce/queue.c +1059 -0
- data/ext/farce/reactor.c +820 -0
- data/ext/farce/reactor.h +103 -0
- data/ext/farce/shareable.h +31 -0
- data/ext/farce/signal.c +350 -0
- data/ext/farce/transaction.c +354 -0
- data/ext/farce/transaction.h +40 -0
- data/ext/farce/tree_map.c +1953 -0
- data/ext/farce/trie.c +2020 -0
- data/ext/farce/unshareable.c +155 -0
- data/ext/farce/unshared_io_pool.h +234 -0
- data/ext/farce/unshared_signal.c +263 -0
- data/ext/farce/unshared_wait.h +193 -0
- data/ext/farce/unsupported.c +6 -0
- data/ext/farce/vector.c +1195 -0
- data/ext/farce/weak_map.c +1714 -0
- data/ext/java/org/farce/BoundedMap.java +394 -0
- data/ext/java/org/farce/FiberScheduler.java +141 -0
- data/ext/java/org/farce/PriorityKey.java +45 -0
- data/ext/java/org/farce/PriorityQueue.java +373 -0
- data/ext/java/org/farce/QueueSignal.java +28 -0
- data/ext/rebind/README.md +14 -0
- data/ext/rebind/extconf.rb +10 -0
- data/ext/rebind/rebind.c +186 -0
- data/lib/farce/_yard/internal.rb +13 -0
- data/lib/farce/_yard/macros.rb +61 -0
- data/lib/farce/_yard/ractor.rb +46 -0
- data/lib/farce/abstract/atom.rb +186 -0
- data/lib/farce/abstract/bounded_map.rb +232 -0
- data/lib/farce/abstract/collection.rb +151 -0
- data/lib/farce/abstract/concurrent_map.rb +381 -0
- data/lib/farce/abstract/counter.rb +193 -0
- data/lib/farce/abstract/duplicable_map.rb +229 -0
- data/lib/farce/abstract/exchanger.rb +26 -0
- data/lib/farce/abstract/flag.rb +71 -0
- data/lib/farce/abstract/lazy.rb +115 -0
- data/lib/farce/abstract/lease.rb +104 -0
- data/lib/farce/abstract/lease_map.rb +261 -0
- data/lib/farce/abstract/lease_pool.rb +91 -0
- data/lib/farce/abstract/lfu_map.rb +20 -0
- data/lib/farce/abstract/lru_map.rb +25 -0
- data/lib/farce/abstract/map.rb +345 -0
- data/lib/farce/abstract/molecule.rb +245 -0
- data/lib/farce/abstract/port.rb +74 -0
- data/lib/farce/abstract/priority_queue.rb +117 -0
- data/lib/farce/abstract/queue.rb +269 -0
- data/lib/farce/abstract/scheduler.rb +111 -0
- data/lib/farce/abstract/set.rb +910 -0
- data/lib/farce/abstract/sorted_set.rb +172 -0
- data/lib/farce/abstract/timer_queue.rb +136 -0
- data/lib/farce/abstract/tree_map.rb +269 -0
- data/lib/farce/abstract/value.rb +68 -0
- data/lib/farce/abstract/vector.rb +1126 -0
- data/lib/farce/abstract/weak_atom.rb +32 -0
- data/lib/farce/abstract/weak_key_map.rb +12 -0
- data/lib/farce/abstract/weak_map.rb +13 -0
- data/lib/farce/abstract/weak_set.rb +12 -0
- data/lib/farce/abstract/weak_value_map.rb +12 -0
- data/lib/farce/abstract.rb +17 -0
- data/lib/farce/atom.rb +293 -0
- data/lib/farce/class_mirror.rb +87 -0
- data/lib/farce/clock.rb +121 -0
- data/lib/farce/config.rb +229 -0
- data/lib/farce/counter.rb +71 -0
- data/lib/farce/deduper.rb +122 -0
- data/lib/farce/engine/jruby/bounded_map.rb +314 -0
- data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
- data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
- data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
- data/lib/farce/engine/jruby/map.rb +505 -0
- data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
- data/lib/farce/engine/jruby/signal.rb +147 -0
- data/lib/farce/engine/jruby.rb +63 -0
- data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
- data/lib/farce/engine/jvm/counter.rb +102 -0
- data/lib/farce/engine/jvm/extension.rb +32 -0
- data/lib/farce/engine/jvm/farce.jar +0 -0
- data/lib/farce/engine/jvm/flag.rb +79 -0
- data/lib/farce/engine/jvm/priority_queue.rb +217 -0
- data/lib/farce/engine/jvm/tree_map.rb +350 -0
- data/lib/farce/engine/jvm/types.rb +180 -0
- data/lib/farce/engine/jvm.rb +19 -0
- data/lib/farce/engine/ruby/3.4/farce.so +0 -0
- data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
- data/lib/farce/engine/ruby/3.4/port.rb +186 -0
- data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
- data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
- data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
- data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
- data/lib/farce/engine/ruby/4.0/farce.so +0 -0
- data/lib/farce/engine/ruby/4.0/port.rb +19 -0
- data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
- data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
- data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
- data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
- data/lib/farce/engine/ruby/4.1/port.rb +21 -0
- data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
- data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
- data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
- data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
- data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
- data/lib/farce/engine/ruby/shared/lease.rb +26 -0
- data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
- data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
- data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
- data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
- data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
- data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
- data/lib/farce/engine/ruby/shared/vault.rb +144 -0
- data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
- data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
- data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
- data/lib/farce/engine/ruby.rb +130 -0
- data/lib/farce/engine/shared/atom.rb +10 -0
- data/lib/farce/engine/shared/exchanger.rb +130 -0
- data/lib/farce/engine/shared/identity_key.rb +22 -0
- data/lib/farce/engine/shared/lease.rb +10 -0
- data/lib/farce/engine/shared/lease_pool.rb +10 -0
- data/lib/farce/engine/shared/main_scheduler.rb +20 -0
- data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
- data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
- data/lib/farce/engine/shared/port.rb +44 -0
- data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
- data/lib/farce/engine/shared/proxy_owner.rb +43 -0
- data/lib/farce/engine/shared/queue.rb +233 -0
- data/lib/farce/engine/shared/ractor_methods.rb +73 -0
- data/lib/farce/engine/shared/rebindable.rb +15 -0
- data/lib/farce/engine/shared/strict_atom.rb +73 -0
- data/lib/farce/engine/shared/strict_map.rb +117 -0
- data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
- data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
- data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
- data/lib/farce/engine/shared/trie.rb +317 -0
- data/lib/farce/engine/shared/trie_builder.rb +199 -0
- data/lib/farce/engine/shared/unshareable.rb +14 -0
- data/lib/farce/engine/shared/unshared_atom.rb +256 -0
- data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
- data/lib/farce/engine/shared/unshared_queue.rb +18 -0
- data/lib/farce/engine/shared/unshared_signal.rb +21 -0
- data/lib/farce/engine/shared/unshared_vector.rb +377 -0
- data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
- data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
- data/lib/farce/engine/shared/vault.rb +31 -0
- data/lib/farce/engine/shared/vector.rb +83 -0
- data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
- data/lib/farce/engine/shared/weak_atom.rb +20 -0
- data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
- data/lib/farce/engine/shared/weak_map/index.rb +249 -0
- data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
- data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
- data/lib/farce/engine/shared.rb +63 -0
- data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
- data/lib/farce/engine/truffleruby/lock.rb +121 -0
- data/lib/farce/engine/truffleruby/map.rb +656 -0
- data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
- data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
- data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
- data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
- data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
- data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
- data/lib/farce/engine/truffleruby/signal.rb +77 -0
- data/lib/farce/engine/truffleruby.rb +148 -0
- data/lib/farce/envelope.rb +335 -0
- data/lib/farce/error.rb +35 -0
- data/lib/farce/exchanger.rb +41 -0
- data/lib/farce/flag.rb +27 -0
- data/lib/farce/integrations/active_support/blank.rb +68 -0
- data/lib/farce/integrations/active_support/clock.rb +17 -0
- data/lib/farce/integrations/active_support/duplicable.rb +78 -0
- data/lib/farce/integrations/active_support/map.rb +175 -0
- data/lib/farce/integrations/active_support/set.rb +60 -0
- data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
- data/lib/farce/integrations/active_support/vector.rb +231 -0
- data/lib/farce/integrations/active_support.rb +9 -0
- data/lib/farce/integrations/activesupport.rb +5 -0
- data/lib/farce/integrations/bson.rb +108 -0
- data/lib/farce/integrations/cbor.rb +51 -0
- data/lib/farce/integrations/concurrent.rb +112 -0
- data/lib/farce/integrations/dry-types.rb +5 -0
- data/lib/farce/integrations/dry_types.rb +694 -0
- data/lib/farce/integrations/json.rb +7 -0
- data/lib/farce/integrations/msgpack.rb +112 -0
- data/lib/farce/integrations/oj.rb +69 -0
- data/lib/farce/integrations/psych.rb +191 -0
- data/lib/farce/integrations/ractor-sharing.rb +5 -0
- data/lib/farce/integrations/ractor-tmvar.rb +5 -0
- data/lib/farce/integrations/ractor_sharing.rb +172 -0
- data/lib/farce/integrations/ractor_tmvar.rb +64 -0
- data/lib/farce/integrations/shared/to_json.rb +42 -0
- data/lib/farce/integrations/sorted_set.rb +11 -0
- data/lib/farce/integrations/weakref.rb +38 -0
- data/lib/farce/integrations/yajl.rb +10 -0
- data/lib/farce/integrations.rb +156 -0
- data/lib/farce/internal/_frozen_config.rb +11 -0
- data/lib/farce/internal/autoloads.rb +47 -0
- data/lib/farce/internal/blocking_priority_queue.rb +122 -0
- data/lib/farce/internal/converter.rb +106 -0
- data/lib/farce/internal/copyable.rb +31 -0
- data/lib/farce/internal/delegation.rb +20 -0
- data/lib/farce/internal/external_transaction.rb +59 -0
- data/lib/farce/internal/fake_ractor.rb +179 -0
- data/lib/farce/internal/freeze.rb +118 -0
- data/lib/farce/internal/inspect.rb +157 -0
- data/lib/farce/internal/key_lock_map.rb +29 -0
- data/lib/farce/internal/key_normalizer.rb +289 -0
- data/lib/farce/internal/lease_initialization.rb +115 -0
- data/lib/farce/internal/lease_map.rb +441 -0
- data/lib/farce/internal/lease_pool_state.rb +228 -0
- data/lib/farce/internal/lease_state.rb +342 -0
- data/lib/farce/internal/lease_waiting.rb +13 -0
- data/lib/farce/internal/managed_queue.rb +26 -0
- data/lib/farce/internal/map_value_modes.rb +227 -0
- data/lib/farce/internal/marshal_support.rb +227 -0
- data/lib/farce/internal/mixin.rb +20 -0
- data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
- data/lib/farce/internal/noncopyable.rb +17 -0
- data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
- data/lib/farce/internal/pool_supervisor.rb +41 -0
- data/lib/farce/internal/pool_worker.rb +61 -0
- data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
- data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
- data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
- data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
- data/lib/farce/internal/portable_transaction.rb +164 -0
- data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
- data/lib/farce/internal/reservation_waiting.rb +24 -0
- data/lib/farce/internal/scheduled_task.rb +48 -0
- data/lib/farce/internal/scheduler_io.rb +171 -0
- data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
- data/lib/farce/internal/select_scheduler.rb +235 -0
- data/lib/farce/internal/storage.rb +162 -0
- data/lib/farce/internal/strict_lease.rb +30 -0
- data/lib/farce/internal/strict_lease_map.rb +20 -0
- data/lib/farce/internal/strict_lease_pool.rb +29 -0
- data/lib/farce/internal/thread_pool.rb +166 -0
- data/lib/farce/internal/transaction_conflict.rb +9 -0
- data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
- data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
- data/lib/farce/internal/undefined.rb +22 -0
- data/lib/farce/internal/unshared_lease.rb +29 -0
- data/lib/farce/internal/unshared_lease_pool.rb +34 -0
- data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
- data/lib/farce/internal/value_serialization.rb +13 -0
- data/lib/farce/internal/weak_map_value_modes.rb +58 -0
- data/lib/farce/internal/weak_mode_manager.rb +26 -0
- data/lib/farce/internal.rb +129 -0
- data/lib/farce/lazy.rb +100 -0
- data/lib/farce/lazy_ref.rb +40 -0
- data/lib/farce/lease.rb +28 -0
- data/lib/farce/lease_map.rb +34 -0
- data/lib/farce/lease_pool.rb +29 -0
- data/lib/farce/lfu_map.rb +53 -0
- data/lib/farce/local/atom.rb +23 -0
- data/lib/farce/local/counter.rb +88 -0
- data/lib/farce/local/flag.rb +65 -0
- data/lib/farce/local/lazy.rb +38 -0
- data/lib/farce/local/lazy_ref.rb +36 -0
- data/lib/farce/local/lease.rb +77 -0
- data/lib/farce/local/lease_map.rb +80 -0
- data/lib/farce/local/lease_pool.rb +38 -0
- data/lib/farce/local/lfu_map.rb +55 -0
- data/lib/farce/local/lru_map.rb +62 -0
- data/lib/farce/local/map.rb +47 -0
- data/lib/farce/local/molecule.rb +45 -0
- data/lib/farce/local/priority_queue.rb +53 -0
- data/lib/farce/local/queue.rb +32 -0
- data/lib/farce/local/scoped.rb +166 -0
- data/lib/farce/local/set.rb +18 -0
- data/lib/farce/local/sorted_set.rb +50 -0
- data/lib/farce/local/timer_queue.rb +41 -0
- data/lib/farce/local/tree_map.rb +47 -0
- data/lib/farce/local/vector.rb +30 -0
- data/lib/farce/local/weak_atom.rb +23 -0
- data/lib/farce/local/weak_key_map.rb +39 -0
- data/lib/farce/local/weak_map.rb +39 -0
- data/lib/farce/local/weak_set.rb +18 -0
- data/lib/farce/local/weak_value_map.rb +39 -0
- data/lib/farce/local.rb +35 -0
- data/lib/farce/lock.rb +54 -0
- data/lib/farce/lru_map.rb +63 -0
- data/lib/farce/map.rb +58 -0
- data/lib/farce/mode_manager.rb +142 -0
- data/lib/farce/molecule.rb +60 -0
- data/lib/farce/mutable.rb +171 -0
- data/lib/farce/pool.rb +332 -0
- data/lib/farce/port.rb +134 -0
- data/lib/farce/priority_queue.rb +70 -0
- data/lib/farce/proxy/register.rb +101 -0
- data/lib/farce/proxy/supervisor.rb +137 -0
- data/lib/farce/proxy/wrapper.rb +49 -0
- data/lib/farce/proxy.rb +291 -0
- data/lib/farce/queue.rb +60 -0
- data/lib/farce/ractor.rb +269 -0
- data/lib/farce/read_write_lock.rb +256 -0
- data/lib/farce/reference.rb +158 -0
- data/lib/farce/resolv/dns.rb +42 -0
- data/lib/farce/resolv.rb +85 -0
- data/lib/farce/scheduler.rb +536 -0
- data/lib/farce/set.rb +39 -0
- data/lib/farce/shareable.rb +150 -0
- data/lib/farce/signal.rb +97 -0
- data/lib/farce/sorted_set.rb +36 -0
- data/lib/farce/strict/atom.rb +34 -0
- data/lib/farce/strict/counter.rb +10 -0
- data/lib/farce/strict/exchanger.rb +25 -0
- data/lib/farce/strict/flag.rb +10 -0
- data/lib/farce/strict/lazy.rb +26 -0
- data/lib/farce/strict/lazy_ref.rb +26 -0
- data/lib/farce/strict/lease.rb +27 -0
- data/lib/farce/strict/lease_map.rb +33 -0
- data/lib/farce/strict/lease_pool.rb +28 -0
- data/lib/farce/strict/lfu_map.rb +27 -0
- data/lib/farce/strict/lru_map.rb +28 -0
- data/lib/farce/strict/map.rb +54 -0
- data/lib/farce/strict/molecule.rb +16 -0
- data/lib/farce/strict/port.rb +40 -0
- data/lib/farce/strict/priority_queue.rb +20 -0
- data/lib/farce/strict/queue.rb +17 -0
- data/lib/farce/strict/set.rb +14 -0
- data/lib/farce/strict/sorted_set.rb +19 -0
- data/lib/farce/strict/timer_queue.rb +13 -0
- data/lib/farce/strict/tree_map.rb +27 -0
- data/lib/farce/strict/vector.rb +24 -0
- data/lib/farce/strict/weak_atom.rb +31 -0
- data/lib/farce/strict/weak_key_map.rb +49 -0
- data/lib/farce/strict/weak_map.rb +49 -0
- data/lib/farce/strict/weak_set.rb +14 -0
- data/lib/farce/strict/weak_value_map.rb +48 -0
- data/lib/farce/strict.rb +28 -0
- data/lib/farce/system.rb +40 -0
- data/lib/farce/thread_scheduler.rb +70 -0
- data/lib/farce/timer_queue.rb +66 -0
- data/lib/farce/transaction/atom.rb +73 -0
- data/lib/farce/transaction/map.rb +25 -0
- data/lib/farce/transaction/map_operations.rb +143 -0
- data/lib/farce/transaction/molecule.rb +90 -0
- data/lib/farce/transaction/mutable.rb +61 -0
- data/lib/farce/transaction/set.rb +17 -0
- data/lib/farce/transaction/set_operations.rb +48 -0
- data/lib/farce/transaction/sorted_set.rb +18 -0
- data/lib/farce/transaction/tree_map.rb +78 -0
- data/lib/farce/transaction/vector.rb +139 -0
- data/lib/farce/transaction/wrapper.rb +202 -0
- data/lib/farce/transaction.rb +343 -0
- data/lib/farce/tree_map.rb +56 -0
- data/lib/farce/unsafe/lfu_map.rb +36 -0
- data/lib/farce/unsafe/lru_map.rb +36 -0
- data/lib/farce/unsafe/tree_map.rb +21 -0
- data/lib/farce/unsafe.rb +29 -0
- data/lib/farce/unshareable.rb +67 -0
- data/lib/farce/unshared/atom.rb +24 -0
- data/lib/farce/unshared/counter.rb +10 -0
- data/lib/farce/unshared/flag.rb +10 -0
- data/lib/farce/unshared/lazy.rb +38 -0
- data/lib/farce/unshared/lazy_ref.rb +26 -0
- data/lib/farce/unshared/lease.rb +27 -0
- data/lib/farce/unshared/lease_map.rb +26 -0
- data/lib/farce/unshared/lease_pool.rb +25 -0
- data/lib/farce/unshared/lfu_map.rb +20 -0
- data/lib/farce/unshared/lru_map.rb +20 -0
- data/lib/farce/unshared/map.rb +49 -0
- data/lib/farce/unshared/molecule.rb +16 -0
- data/lib/farce/unshared/priority_queue.rb +16 -0
- data/lib/farce/unshared/queue.rb +27 -0
- data/lib/farce/unshared/set.rb +14 -0
- data/lib/farce/unshared/sorted_set.rb +28 -0
- data/lib/farce/unshared/timer_queue.rb +16 -0
- data/lib/farce/unshared/tree_map.rb +21 -0
- data/lib/farce/unshared/vector.rb +19 -0
- data/lib/farce/unshared/weak_atom.rb +31 -0
- data/lib/farce/unshared/weak_key_map.rb +44 -0
- data/lib/farce/unshared/weak_map.rb +44 -0
- data/lib/farce/unshared/weak_set.rb +14 -0
- data/lib/farce/unshared/weak_value_map.rb +43 -0
- data/lib/farce/unshared.rb +28 -0
- data/lib/farce/vector.rb +228 -0
- data/lib/farce/version.rb +8 -0
- data/lib/farce/walker/definitions.rb +186 -0
- data/lib/farce/walker/modification.rb +355 -0
- data/lib/farce/walker.rb +319 -0
- data/lib/farce/weak_atom.rb +121 -0
- data/lib/farce/weak_key_map.rb +31 -0
- data/lib/farce/weak_map.rb +33 -0
- data/lib/farce/weak_ref.rb +67 -0
- data/lib/farce/weak_set.rb +82 -0
- data/lib/farce/weak_value.rb +195 -0
- data/lib/farce/weak_value_map.rb +32 -0
- data/lib/farce.rb +519 -0
- metadata +468 -0
data/ext/farce/trie.c
ADDED
|
@@ -0,0 +1,2020 @@
|
|
|
1
|
+
#include "containers.h"
|
|
2
|
+
|
|
3
|
+
#include "ruby/atomic.h"
|
|
4
|
+
#include "ruby/encoding.h"
|
|
5
|
+
#include "ruby/internal/core/rregexp.h"
|
|
6
|
+
#include "ruby/re.h"
|
|
7
|
+
|
|
8
|
+
#include <limits.h>
|
|
9
|
+
#include <stddef.h>
|
|
10
|
+
#include <stdbool.h>
|
|
11
|
+
#include <stdint.h>
|
|
12
|
+
#include <stdlib.h>
|
|
13
|
+
#include <string.h>
|
|
14
|
+
|
|
15
|
+
#define TRIE_INLINE_FRAMES 64
|
|
16
|
+
#define TRIE_INLINE_EVENTS 32
|
|
17
|
+
#define TRIE_INTERRUPT_INTERVAL 1024
|
|
18
|
+
#define TRIE_PROGRAM_INSTRUCTIONS 16
|
|
19
|
+
#define TRIE_PROGRAM_CAPTURES 8
|
|
20
|
+
#define TRIE_PROGRAM_SOURCE_BYTES 4096
|
|
21
|
+
#define TRIE_STATIC_INDEX_THRESHOLD 8
|
|
22
|
+
|
|
23
|
+
typedef struct trie_node trie_node_t;
|
|
24
|
+
|
|
25
|
+
typedef enum {
|
|
26
|
+
TRIE_CAPTURE_DIGIT,
|
|
27
|
+
TRIE_CAPTURE_WORD,
|
|
28
|
+
TRIE_CAPTURE_NOT_SLASH,
|
|
29
|
+
TRIE_CAPTURE_NOT_PATH_SEPARATOR,
|
|
30
|
+
TRIE_LITERAL_ALTERNATIVES,
|
|
31
|
+
} trie_opcode_t;
|
|
32
|
+
|
|
33
|
+
typedef struct {
|
|
34
|
+
unsigned char *bytes;
|
|
35
|
+
size_t length;
|
|
36
|
+
} trie_literal_t;
|
|
37
|
+
|
|
38
|
+
typedef struct {
|
|
39
|
+
trie_opcode_t opcode;
|
|
40
|
+
VALUE name;
|
|
41
|
+
trie_literal_t *alternatives;
|
|
42
|
+
size_t alternative_count;
|
|
43
|
+
} trie_instruction_t;
|
|
44
|
+
|
|
45
|
+
typedef struct trie_program {
|
|
46
|
+
unsigned char *key;
|
|
47
|
+
size_t key_length;
|
|
48
|
+
uint64_t key_hash;
|
|
49
|
+
int key_encoding;
|
|
50
|
+
int key_options;
|
|
51
|
+
trie_instruction_t *instructions;
|
|
52
|
+
size_t instruction_count;
|
|
53
|
+
size_t instruction_capacity;
|
|
54
|
+
size_t capture_count;
|
|
55
|
+
bool end_anchor;
|
|
56
|
+
bool requires_ascii_input;
|
|
57
|
+
} trie_program_t;
|
|
58
|
+
|
|
59
|
+
typedef struct {
|
|
60
|
+
VALUE label;
|
|
61
|
+
trie_node_t *child;
|
|
62
|
+
int encoding;
|
|
63
|
+
uint32_t next_same_byte;
|
|
64
|
+
} trie_static_edge_t;
|
|
65
|
+
|
|
66
|
+
typedef struct {
|
|
67
|
+
uint16_t span;
|
|
68
|
+
unsigned char minimum;
|
|
69
|
+
unsigned char reserved;
|
|
70
|
+
uint32_t slots[];
|
|
71
|
+
} trie_static_index_t;
|
|
72
|
+
|
|
73
|
+
_Static_assert(sizeof(trie_static_index_t) == sizeof(uint32_t),
|
|
74
|
+
"static index header must occupy one slot");
|
|
75
|
+
|
|
76
|
+
typedef struct {
|
|
77
|
+
VALUE source;
|
|
78
|
+
VALUE anchored;
|
|
79
|
+
VALUE timeout;
|
|
80
|
+
int options;
|
|
81
|
+
trie_program_t *program;
|
|
82
|
+
trie_node_t *child;
|
|
83
|
+
} trie_regexp_edge_t;
|
|
84
|
+
|
|
85
|
+
struct trie_node {
|
|
86
|
+
trie_static_edge_t *static_edges;
|
|
87
|
+
size_t static_count;
|
|
88
|
+
size_t static_capacity;
|
|
89
|
+
trie_static_index_t *static_index;
|
|
90
|
+
trie_regexp_edge_t *regexp_edges;
|
|
91
|
+
size_t regexp_count;
|
|
92
|
+
size_t regexp_capacity;
|
|
93
|
+
uint32_t *tokens;
|
|
94
|
+
size_t token_count;
|
|
95
|
+
size_t token_capacity;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
typedef struct {
|
|
99
|
+
trie_node_t *root;
|
|
100
|
+
trie_node_t **nodes;
|
|
101
|
+
size_t node_count;
|
|
102
|
+
size_t node_capacity;
|
|
103
|
+
trie_program_t **programs;
|
|
104
|
+
size_t program_count;
|
|
105
|
+
size_t program_capacity;
|
|
106
|
+
trie_program_t **program_cache;
|
|
107
|
+
size_t program_cache_capacity;
|
|
108
|
+
VALUE tokens;
|
|
109
|
+
VALUE owner;
|
|
110
|
+
size_t static_edge_count;
|
|
111
|
+
size_t static_index_count;
|
|
112
|
+
size_t static_index_bytes;
|
|
113
|
+
size_t regexp_edge_count;
|
|
114
|
+
bool initialized;
|
|
115
|
+
} trie_t;
|
|
116
|
+
|
|
117
|
+
typedef enum { TRIE_EVENT_NATIVE, TRIE_EVENT_RUBY } trie_event_kind_t;
|
|
118
|
+
|
|
119
|
+
typedef struct {
|
|
120
|
+
trie_event_kind_t kind;
|
|
121
|
+
long begin;
|
|
122
|
+
long end;
|
|
123
|
+
VALUE name;
|
|
124
|
+
long match_index;
|
|
125
|
+
} trie_capture_event_t;
|
|
126
|
+
|
|
127
|
+
typedef struct {
|
|
128
|
+
trie_node_t *node;
|
|
129
|
+
long cursor;
|
|
130
|
+
size_t restore_events;
|
|
131
|
+
size_t static_index;
|
|
132
|
+
size_t regexp_index;
|
|
133
|
+
unsigned phase;
|
|
134
|
+
} trie_frame_t;
|
|
135
|
+
|
|
136
|
+
/* A suspended Enumerator or user Fiber can be collected without unwinding the
|
|
137
|
+
* native stack. Keep every spilled search buffer behind a Ruby object so GC can
|
|
138
|
+
* reclaim it even when trie_search_cleanup never runs. */
|
|
139
|
+
typedef struct {
|
|
140
|
+
void *pointer;
|
|
141
|
+
size_t bytes;
|
|
142
|
+
} trie_scratch_t;
|
|
143
|
+
|
|
144
|
+
typedef struct {
|
|
145
|
+
VALUE self;
|
|
146
|
+
VALUE input;
|
|
147
|
+
VALUE results;
|
|
148
|
+
VALUE matches;
|
|
149
|
+
VALUE previous_backref;
|
|
150
|
+
VALUE frame_scratch;
|
|
151
|
+
VALUE event_scratch;
|
|
152
|
+
VALUE seen_scratch;
|
|
153
|
+
VALUE seen_building_scratch;
|
|
154
|
+
trie_t *trie;
|
|
155
|
+
long input_length;
|
|
156
|
+
int input_encoding;
|
|
157
|
+
trie_frame_t *frames;
|
|
158
|
+
size_t frame_count;
|
|
159
|
+
size_t frame_capacity;
|
|
160
|
+
trie_capture_event_t *events;
|
|
161
|
+
size_t event_count;
|
|
162
|
+
size_t event_capacity;
|
|
163
|
+
uint32_t *seen;
|
|
164
|
+
uint32_t *seen_building;
|
|
165
|
+
size_t seen_count;
|
|
166
|
+
size_t seen_capacity;
|
|
167
|
+
bool seen_hashed;
|
|
168
|
+
size_t work;
|
|
169
|
+
bool peek;
|
|
170
|
+
bool first_only;
|
|
171
|
+
bool stream;
|
|
172
|
+
bool utf8_input;
|
|
173
|
+
bool ascii_input;
|
|
174
|
+
bool native_segment_input;
|
|
175
|
+
bool regexp_timeout_checked;
|
|
176
|
+
VALUE first;
|
|
177
|
+
trie_frame_t inline_frames[TRIE_INLINE_FRAMES];
|
|
178
|
+
trie_capture_event_t inline_events[TRIE_INLINE_EVENTS];
|
|
179
|
+
uint32_t inline_seen[16];
|
|
180
|
+
} trie_search_t;
|
|
181
|
+
|
|
182
|
+
typedef struct {
|
|
183
|
+
const unsigned char *bytes;
|
|
184
|
+
size_t length;
|
|
185
|
+
size_t position;
|
|
186
|
+
trie_program_t *program;
|
|
187
|
+
} trie_parser_t;
|
|
188
|
+
|
|
189
|
+
static VALUE cTrie;
|
|
190
|
+
static ID id_captures;
|
|
191
|
+
static ID id_named_captures;
|
|
192
|
+
static ID id_timeout;
|
|
193
|
+
static bool regexp_timeout_supported;
|
|
194
|
+
|
|
195
|
+
#ifdef FARCE_TRIE_FAILURE_INJECTION
|
|
196
|
+
static _Thread_local long trie_failure_countdown = -1;
|
|
197
|
+
static VALUE trie_interrupt_queue = Qnil;
|
|
198
|
+
static rb_atomic_t trie_scratch_live_count;
|
|
199
|
+
|
|
200
|
+
static void
|
|
201
|
+
trie_failure_checkpoint(void)
|
|
202
|
+
{
|
|
203
|
+
if (trie_failure_countdown < 0) return;
|
|
204
|
+
if (trie_failure_countdown-- == 0) rb_memerror();
|
|
205
|
+
}
|
|
206
|
+
#else
|
|
207
|
+
# define trie_failure_checkpoint() ((void)0)
|
|
208
|
+
#endif
|
|
209
|
+
|
|
210
|
+
static void
|
|
211
|
+
trie_check_interrupts(void)
|
|
212
|
+
{
|
|
213
|
+
#ifdef FARCE_TRIE_FAILURE_INJECTION
|
|
214
|
+
if (!NIL_P(trie_interrupt_queue)) {
|
|
215
|
+
VALUE queues = trie_interrupt_queue;
|
|
216
|
+
VALUE ready = RARRAY_AREF(queues, 0);
|
|
217
|
+
VALUE release = RARRAY_AREF(queues, 1);
|
|
218
|
+
trie_interrupt_queue = Qnil;
|
|
219
|
+
rb_funcall(ready, rb_intern("<<"), 1, Qtrue);
|
|
220
|
+
rb_funcall(release, rb_intern("pop"), 0);
|
|
221
|
+
RB_GC_GUARD(queues);
|
|
222
|
+
RB_GC_GUARD(ready);
|
|
223
|
+
RB_GC_GUARD(release);
|
|
224
|
+
}
|
|
225
|
+
#endif
|
|
226
|
+
rb_thread_check_ints();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
static void
|
|
230
|
+
trie_search_work_checkpoint(trie_search_t *search)
|
|
231
|
+
{
|
|
232
|
+
if ((++search->work & (TRIE_INTERRUPT_INTERVAL - 1)) == 0) trie_check_interrupts();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
static void *
|
|
236
|
+
trie_malloc(size_t count, size_t element)
|
|
237
|
+
{
|
|
238
|
+
trie_failure_checkpoint();
|
|
239
|
+
return ruby_xmalloc2(count, element);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
static void *
|
|
243
|
+
trie_realloc(void *pointer, size_t count, size_t element)
|
|
244
|
+
{
|
|
245
|
+
trie_failure_checkpoint();
|
|
246
|
+
return ruby_xrealloc2(pointer, count, element);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
static void
|
|
250
|
+
trie_scratch_free(void *opaque)
|
|
251
|
+
{
|
|
252
|
+
trie_scratch_t *scratch = opaque;
|
|
253
|
+
if (!scratch) return;
|
|
254
|
+
if (scratch->pointer) {
|
|
255
|
+
xfree(scratch->pointer);
|
|
256
|
+
#ifdef FARCE_TRIE_FAILURE_INJECTION
|
|
257
|
+
RUBY_ATOMIC_DEC(trie_scratch_live_count);
|
|
258
|
+
#endif
|
|
259
|
+
}
|
|
260
|
+
xfree(scratch);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
static size_t
|
|
264
|
+
trie_scratch_memsize(const void *opaque)
|
|
265
|
+
{
|
|
266
|
+
const trie_scratch_t *scratch = opaque;
|
|
267
|
+
return scratch ? sizeof(*scratch) + scratch->bytes : 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
static const rb_data_type_t trie_scratch_type = {
|
|
271
|
+
.wrap_struct_name = "Farce::Strict::Trie::SearchScratch",
|
|
272
|
+
.function = {
|
|
273
|
+
.dmark = NULL,
|
|
274
|
+
.dfree = trie_scratch_free,
|
|
275
|
+
.dsize = trie_scratch_memsize,
|
|
276
|
+
.dcompact = NULL,
|
|
277
|
+
},
|
|
278
|
+
.parent = NULL,
|
|
279
|
+
.data = NULL,
|
|
280
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
static void *
|
|
284
|
+
trie_scratch_allocate(VALUE *owner, size_t count, size_t element)
|
|
285
|
+
{
|
|
286
|
+
trie_scratch_t *scratch;
|
|
287
|
+
VALUE wrapper = TypedData_Make_Struct(rb_cObject, trie_scratch_t,
|
|
288
|
+
&trie_scratch_type, scratch);
|
|
289
|
+
*owner = wrapper;
|
|
290
|
+
scratch->pointer = NULL;
|
|
291
|
+
scratch->bytes = 0;
|
|
292
|
+
scratch->pointer = trie_malloc(count, element);
|
|
293
|
+
scratch->bytes = count * element;
|
|
294
|
+
#ifdef FARCE_TRIE_FAILURE_INJECTION
|
|
295
|
+
RUBY_ATOMIC_INC(trie_scratch_live_count);
|
|
296
|
+
#endif
|
|
297
|
+
RB_GC_GUARD(wrapper);
|
|
298
|
+
return scratch->pointer;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
static void *
|
|
302
|
+
trie_scratch_resize(VALUE owner, size_t count, size_t element)
|
|
303
|
+
{
|
|
304
|
+
trie_scratch_t *scratch = RTYPEDDATA_DATA(owner);
|
|
305
|
+
void *pointer = trie_realloc(scratch->pointer, count, element);
|
|
306
|
+
scratch->pointer = pointer;
|
|
307
|
+
scratch->bytes = count * element;
|
|
308
|
+
RB_GC_GUARD(owner);
|
|
309
|
+
return pointer;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
static void
|
|
313
|
+
trie_scratch_release(VALUE owner)
|
|
314
|
+
{
|
|
315
|
+
trie_scratch_t *scratch;
|
|
316
|
+
if (owner == 0) return;
|
|
317
|
+
scratch = RTYPEDDATA_DATA(owner);
|
|
318
|
+
if (!scratch->pointer) return;
|
|
319
|
+
xfree(scratch->pointer);
|
|
320
|
+
scratch->pointer = NULL;
|
|
321
|
+
scratch->bytes = 0;
|
|
322
|
+
#ifdef FARCE_TRIE_FAILURE_INJECTION
|
|
323
|
+
RUBY_ATOMIC_DEC(trie_scratch_live_count);
|
|
324
|
+
#endif
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
#define TRIE_ALLOC(type) ((type *)trie_malloc(1, sizeof(type)))
|
|
328
|
+
#define TRIE_ALLOC_N(type, count) ((type *)trie_malloc((count), sizeof(type)))
|
|
329
|
+
#define TRIE_REALLOC_N(pointer, type, count) \
|
|
330
|
+
((pointer) = (type *)trie_realloc((pointer), (count), sizeof(type)))
|
|
331
|
+
|
|
332
|
+
static bool
|
|
333
|
+
trie_size_grow(size_t current, size_t minimum, size_t element, size_t *result)
|
|
334
|
+
{
|
|
335
|
+
size_t capacity = current ? current : 4;
|
|
336
|
+
while (capacity < minimum) {
|
|
337
|
+
if (capacity > SIZE_MAX / 2) return false;
|
|
338
|
+
capacity *= 2;
|
|
339
|
+
}
|
|
340
|
+
if (capacity > SIZE_MAX / element) return false;
|
|
341
|
+
*result = capacity;
|
|
342
|
+
return true;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
static void
|
|
346
|
+
trie_mark(void *pointer)
|
|
347
|
+
{
|
|
348
|
+
trie_t *trie = pointer;
|
|
349
|
+
size_t node_index;
|
|
350
|
+
size_t program_index;
|
|
351
|
+
if (!trie) return;
|
|
352
|
+
if (!NIL_P(trie->owner)) {
|
|
353
|
+
rb_gc_mark(trie->owner);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
rb_gc_mark(trie->tokens);
|
|
357
|
+
for (node_index = 0; node_index < trie->node_count; node_index++) {
|
|
358
|
+
trie_node_t *node = trie->nodes[node_index];
|
|
359
|
+
size_t index;
|
|
360
|
+
for (index = 0; index < node->static_count; index++) {
|
|
361
|
+
rb_gc_mark(node->static_edges[index].label);
|
|
362
|
+
}
|
|
363
|
+
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);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
for (program_index = 0; program_index < trie->program_count; program_index++) {
|
|
370
|
+
trie_program_t *program = trie->programs[program_index];
|
|
371
|
+
size_t index;
|
|
372
|
+
for (index = 0; index < program->instruction_count; index++) {
|
|
373
|
+
if (program->instructions[index].opcode != TRIE_LITERAL_ALTERNATIVES) {
|
|
374
|
+
rb_gc_mark(program->instructions[index].name);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
static void
|
|
381
|
+
trie_program_free(trie_program_t *program)
|
|
382
|
+
{
|
|
383
|
+
size_t index;
|
|
384
|
+
if (!program) return;
|
|
385
|
+
for (index = 0; index < program->instruction_count; index++) {
|
|
386
|
+
trie_instruction_t *instruction = &program->instructions[index];
|
|
387
|
+
size_t alternative;
|
|
388
|
+
for (alternative = 0; alternative < instruction->alternative_count; alternative++) {
|
|
389
|
+
xfree(instruction->alternatives[alternative].bytes);
|
|
390
|
+
}
|
|
391
|
+
xfree(instruction->alternatives);
|
|
392
|
+
}
|
|
393
|
+
xfree(program->instructions);
|
|
394
|
+
xfree(program->key);
|
|
395
|
+
xfree(program);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
static void
|
|
399
|
+
trie_release_contents(trie_t *trie)
|
|
400
|
+
{
|
|
401
|
+
size_t index;
|
|
402
|
+
for (index = 0; index < trie->node_count; index++) {
|
|
403
|
+
trie_node_t *node = trie->nodes[index];
|
|
404
|
+
xfree(node->static_edges);
|
|
405
|
+
xfree(node->static_index);
|
|
406
|
+
xfree(node->regexp_edges);
|
|
407
|
+
xfree(node->tokens);
|
|
408
|
+
xfree(node);
|
|
409
|
+
}
|
|
410
|
+
for (index = 0; index < trie->program_count; index++) {
|
|
411
|
+
trie_program_free(trie->programs[index]);
|
|
412
|
+
}
|
|
413
|
+
xfree(trie->nodes);
|
|
414
|
+
xfree(trie->programs);
|
|
415
|
+
xfree(trie->program_cache);
|
|
416
|
+
trie->root = NULL;
|
|
417
|
+
trie->nodes = NULL;
|
|
418
|
+
trie->node_count = trie->node_capacity = 0;
|
|
419
|
+
trie->programs = NULL;
|
|
420
|
+
trie->program_count = trie->program_capacity = 0;
|
|
421
|
+
trie->program_cache = NULL;
|
|
422
|
+
trie->program_cache_capacity = 0;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
static void
|
|
426
|
+
trie_free(void *pointer)
|
|
427
|
+
{
|
|
428
|
+
trie_t *trie = pointer;
|
|
429
|
+
if (!trie) return;
|
|
430
|
+
if (NIL_P(trie->owner)) trie_release_contents(trie);
|
|
431
|
+
xfree(trie);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
static size_t
|
|
435
|
+
trie_program_memsize(const trie_program_t *program)
|
|
436
|
+
{
|
|
437
|
+
size_t size = sizeof(*program) + program->key_length;
|
|
438
|
+
size_t index;
|
|
439
|
+
size += program->instruction_capacity * sizeof(*program->instructions);
|
|
440
|
+
for (index = 0; index < program->instruction_count; index++) {
|
|
441
|
+
const trie_instruction_t *instruction = &program->instructions[index];
|
|
442
|
+
size_t alternative;
|
|
443
|
+
size += instruction->alternative_count * sizeof(*instruction->alternatives);
|
|
444
|
+
for (alternative = 0; alternative < instruction->alternative_count; alternative++) {
|
|
445
|
+
size += instruction->alternatives[alternative].length;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
return size;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
static size_t
|
|
452
|
+
trie_memsize(const void *pointer)
|
|
453
|
+
{
|
|
454
|
+
const trie_t *trie = pointer;
|
|
455
|
+
size_t size;
|
|
456
|
+
size_t index;
|
|
457
|
+
if (!trie) return 0;
|
|
458
|
+
if (!NIL_P(trie->owner)) return sizeof(*trie);
|
|
459
|
+
size = sizeof(*trie);
|
|
460
|
+
size += trie->node_capacity * sizeof(*trie->nodes);
|
|
461
|
+
size += trie->program_capacity * sizeof(*trie->programs);
|
|
462
|
+
size += trie->program_cache_capacity * sizeof(*trie->program_cache);
|
|
463
|
+
for (index = 0; index < trie->node_count; index++) {
|
|
464
|
+
const trie_node_t *node = trie->nodes[index];
|
|
465
|
+
size += sizeof(*node);
|
|
466
|
+
size += node->static_capacity * sizeof(*node->static_edges);
|
|
467
|
+
size += node->regexp_capacity * sizeof(*node->regexp_edges);
|
|
468
|
+
size += node->token_capacity * sizeof(*node->tokens);
|
|
469
|
+
}
|
|
470
|
+
for (index = 0; index < trie->program_count; index++) {
|
|
471
|
+
size += trie_program_memsize(trie->programs[index]);
|
|
472
|
+
}
|
|
473
|
+
size += trie->static_index_bytes;
|
|
474
|
+
return size;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
static const rb_data_type_t trie_type = {
|
|
478
|
+
.wrap_struct_name = "Farce::Internal::Trie",
|
|
479
|
+
.function = {
|
|
480
|
+
.dmark = trie_mark,
|
|
481
|
+
.dfree = trie_free,
|
|
482
|
+
.dsize = trie_memsize,
|
|
483
|
+
},
|
|
484
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_FROZEN_SHAREABLE,
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
static VALUE
|
|
488
|
+
trie_allocate(VALUE klass)
|
|
489
|
+
{
|
|
490
|
+
trie_t *trie;
|
|
491
|
+
VALUE object = TypedData_Make_Struct(klass, trie_t, &trie_type, trie);
|
|
492
|
+
memset(trie, 0, sizeof(*trie));
|
|
493
|
+
trie->tokens = Qnil;
|
|
494
|
+
trie->owner = Qnil;
|
|
495
|
+
return object;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
static trie_t *
|
|
499
|
+
trie_get(VALUE self)
|
|
500
|
+
{
|
|
501
|
+
trie_t *trie;
|
|
502
|
+
TypedData_Get_Struct(self, trie_t, &trie_type, trie);
|
|
503
|
+
if (!NIL_P(trie->owner)) {
|
|
504
|
+
TypedData_Get_Struct(trie->owner, trie_t, &trie_type, trie);
|
|
505
|
+
}
|
|
506
|
+
if (!trie->initialized) rb_raise(rb_eRuntimeError, "uninitialized trie");
|
|
507
|
+
return trie;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
static void
|
|
511
|
+
trie_ensure_node_inventory(trie_t *trie, size_t minimum)
|
|
512
|
+
{
|
|
513
|
+
size_t capacity;
|
|
514
|
+
if (minimum <= trie->node_capacity) return;
|
|
515
|
+
if (!trie_size_grow(trie->node_capacity, minimum, sizeof(*trie->nodes), &capacity)) rb_memerror();
|
|
516
|
+
TRIE_REALLOC_N(trie->nodes, trie_node_t *, capacity);
|
|
517
|
+
trie->node_capacity = capacity;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
static trie_node_t *
|
|
521
|
+
trie_node_new(trie_t *trie)
|
|
522
|
+
{
|
|
523
|
+
trie_node_t *node;
|
|
524
|
+
trie_ensure_node_inventory(trie, trie->node_count + 1);
|
|
525
|
+
node = TRIE_ALLOC(trie_node_t);
|
|
526
|
+
memset(node, 0, sizeof(*node));
|
|
527
|
+
trie->nodes[trie->node_count++] = node;
|
|
528
|
+
return node;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
static void
|
|
532
|
+
trie_ensure_static_edges(trie_node_t *node, size_t minimum)
|
|
533
|
+
{
|
|
534
|
+
size_t capacity;
|
|
535
|
+
if (minimum <= node->static_capacity) return;
|
|
536
|
+
if (!trie_size_grow(node->static_capacity, minimum, sizeof(*node->static_edges), &capacity)) rb_memerror();
|
|
537
|
+
TRIE_REALLOC_N(node->static_edges, trie_static_edge_t, capacity);
|
|
538
|
+
node->static_capacity = capacity;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
static void
|
|
542
|
+
trie_ensure_regexp_edges(trie_node_t *node, size_t minimum)
|
|
543
|
+
{
|
|
544
|
+
size_t capacity;
|
|
545
|
+
if (minimum <= node->regexp_capacity) return;
|
|
546
|
+
if (!trie_size_grow(node->regexp_capacity, minimum, sizeof(*node->regexp_edges), &capacity)) rb_memerror();
|
|
547
|
+
TRIE_REALLOC_N(node->regexp_edges, trie_regexp_edge_t, capacity);
|
|
548
|
+
node->regexp_capacity = capacity;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
static void
|
|
552
|
+
trie_ensure_tokens(trie_node_t *node, size_t minimum)
|
|
553
|
+
{
|
|
554
|
+
size_t capacity;
|
|
555
|
+
if (minimum <= node->token_capacity) return;
|
|
556
|
+
if (!trie_size_grow(node->token_capacity, minimum, sizeof(*node->tokens), &capacity)) rb_memerror();
|
|
557
|
+
TRIE_REALLOC_N(node->tokens, uint32_t, capacity);
|
|
558
|
+
node->token_capacity = capacity;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
static VALUE
|
|
562
|
+
trie_frozen_string(VALUE source, long offset, long length)
|
|
563
|
+
{
|
|
564
|
+
VALUE string = rb_str_subseq(source, offset, length);
|
|
565
|
+
rb_obj_freeze(string);
|
|
566
|
+
return string;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
static size_t
|
|
570
|
+
trie_common_prefix(VALUE left, VALUE right, size_t right_offset)
|
|
571
|
+
{
|
|
572
|
+
size_t left_length = (size_t)RSTRING_LEN(left);
|
|
573
|
+
size_t right_length = (size_t)RSTRING_LEN(right) - right_offset;
|
|
574
|
+
size_t limit = left_length < right_length ? left_length : right_length;
|
|
575
|
+
size_t index = 0;
|
|
576
|
+
while (index < limit) {
|
|
577
|
+
size_t length = limit - index;
|
|
578
|
+
if (length > 16384) length = 16384;
|
|
579
|
+
const unsigned char *left_bytes = (const unsigned char *)RSTRING_PTR(left) + index;
|
|
580
|
+
const unsigned char *right_bytes = (const unsigned char *)RSTRING_PTR(right) + right_offset + index;
|
|
581
|
+
if (memcmp(left_bytes, right_bytes, length) != 0) {
|
|
582
|
+
size_t offset = 0;
|
|
583
|
+
while (offset < length && left_bytes[offset] == right_bytes[offset]) offset++;
|
|
584
|
+
return index + offset;
|
|
585
|
+
}
|
|
586
|
+
index += length;
|
|
587
|
+
if (index < limit) trie_check_interrupts();
|
|
588
|
+
}
|
|
589
|
+
return index;
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
static size_t
|
|
593
|
+
trie_static_index_size(size_t span)
|
|
594
|
+
{
|
|
595
|
+
return sizeof(trie_static_index_t) + span * sizeof(uint32_t);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
static trie_static_index_t *
|
|
599
|
+
trie_static_index_new(unsigned char minimum, unsigned char maximum)
|
|
600
|
+
{
|
|
601
|
+
size_t span = (size_t)maximum - minimum + 1;
|
|
602
|
+
size_t bytes = trie_static_index_size(span);
|
|
603
|
+
trie_static_index_t *table = (trie_static_index_t *)TRIE_ALLOC_N(unsigned char, bytes);
|
|
604
|
+
table->span = (uint16_t)span;
|
|
605
|
+
table->minimum = minimum;
|
|
606
|
+
table->reserved = 0;
|
|
607
|
+
memset(table->slots, 0, span * sizeof(*table->slots));
|
|
608
|
+
return table;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
static uint32_t *
|
|
612
|
+
trie_static_index_slot(trie_static_index_t *table, unsigned char first)
|
|
613
|
+
{
|
|
614
|
+
if (first < table->minimum) return NULL;
|
|
615
|
+
size_t offset = (size_t)first - table->minimum;
|
|
616
|
+
if (offset >= table->span) return NULL;
|
|
617
|
+
return &table->slots[offset];
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
static const uint32_t *
|
|
621
|
+
trie_static_index_slot_const(const trie_static_index_t *table, unsigned char first)
|
|
622
|
+
{
|
|
623
|
+
if (first < table->minimum) return NULL;
|
|
624
|
+
size_t offset = (size_t)first - table->minimum;
|
|
625
|
+
if (offset >= table->span) return NULL;
|
|
626
|
+
return &table->slots[offset];
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
static void
|
|
630
|
+
trie_enable_static_index(trie_t *trie, trie_node_t *node)
|
|
631
|
+
{
|
|
632
|
+
unsigned char minimum = UCHAR_MAX;
|
|
633
|
+
unsigned char maximum = 0;
|
|
634
|
+
size_t index;
|
|
635
|
+
for (index = 0; index < node->static_count; index++) {
|
|
636
|
+
unsigned char first = (unsigned char)RSTRING_PTR(node->static_edges[index].label)[0];
|
|
637
|
+
if (first < minimum) minimum = first;
|
|
638
|
+
if (first > maximum) maximum = first;
|
|
639
|
+
}
|
|
640
|
+
trie_static_index_t *table = trie_static_index_new(minimum, maximum);
|
|
641
|
+
for (index = node->static_count; index > 0; index--) {
|
|
642
|
+
trie_static_edge_t *edge = &node->static_edges[index - 1];
|
|
643
|
+
unsigned char first = (unsigned char)RSTRING_PTR(edge->label)[0];
|
|
644
|
+
uint32_t *slot = trie_static_index_slot(table, first);
|
|
645
|
+
edge->next_same_byte = *slot;
|
|
646
|
+
*slot = (uint32_t)index;
|
|
647
|
+
}
|
|
648
|
+
node->static_index = table;
|
|
649
|
+
trie->static_index_count++;
|
|
650
|
+
trie->static_index_bytes += trie_static_index_size(table->span);
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
static void
|
|
654
|
+
trie_expand_static_index(trie_t *trie, trie_node_t *node, unsigned char first)
|
|
655
|
+
{
|
|
656
|
+
trie_static_index_t *old = node->static_index;
|
|
657
|
+
if (trie_static_index_slot(old, first)) return;
|
|
658
|
+
unsigned char minimum = first < old->minimum ? first : old->minimum;
|
|
659
|
+
unsigned char old_maximum = (unsigned char)(old->minimum + old->span - 1);
|
|
660
|
+
unsigned char maximum = first > old_maximum ? first : old_maximum;
|
|
661
|
+
trie_static_index_t *replacement = trie_static_index_new(minimum, maximum);
|
|
662
|
+
size_t offset = (size_t)old->minimum - replacement->minimum;
|
|
663
|
+
memcpy(replacement->slots + offset, old->slots, old->span * sizeof(*old->slots));
|
|
664
|
+
node->static_index = replacement;
|
|
665
|
+
trie->static_index_bytes += trie_static_index_size(replacement->span) -
|
|
666
|
+
trie_static_index_size(old->span);
|
|
667
|
+
xfree(old);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
static void
|
|
671
|
+
trie_index_static_edge(trie_t *trie, trie_node_t *node, size_t index)
|
|
672
|
+
{
|
|
673
|
+
if (index >= UINT32_MAX) rb_raise(rb_eArgError, "too many static edges at one trie node");
|
|
674
|
+
trie_static_edge_t *edge = &node->static_edges[index];
|
|
675
|
+
unsigned char first = (unsigned char)RSTRING_PTR(edge->label)[0];
|
|
676
|
+
size_t work = 0;
|
|
677
|
+
edge->next_same_byte = 0;
|
|
678
|
+
if (!node->static_index) {
|
|
679
|
+
if (node->static_count >= TRIE_STATIC_INDEX_THRESHOLD) trie_enable_static_index(trie, node);
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
trie_expand_static_index(trie, node, first);
|
|
683
|
+
uint32_t *next = trie_static_index_slot(node->static_index, first);
|
|
684
|
+
while (*next) {
|
|
685
|
+
next = &node->static_edges[*next - 1].next_same_byte;
|
|
686
|
+
if ((++work & (TRIE_INTERRUPT_INTERVAL - 1)) == 0) trie_check_interrupts();
|
|
687
|
+
}
|
|
688
|
+
*next = (uint32_t)(index + 1);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
static long
|
|
692
|
+
trie_find_static_edge(const trie_node_t *node, VALUE string, size_t offset)
|
|
693
|
+
{
|
|
694
|
+
unsigned char first = (unsigned char)RSTRING_PTR(string)[offset];
|
|
695
|
+
size_t index;
|
|
696
|
+
if (node->static_index) {
|
|
697
|
+
const uint32_t *slot = trie_static_index_slot_const(node->static_index, first);
|
|
698
|
+
uint32_t current = slot ? *slot : 0;
|
|
699
|
+
size_t work = 0;
|
|
700
|
+
while (current) {
|
|
701
|
+
const trie_static_edge_t *edge = &node->static_edges[current - 1];
|
|
702
|
+
if (rb_enc_compatible(edge->label, string)) return (long)(current - 1);
|
|
703
|
+
current = edge->next_same_byte;
|
|
704
|
+
if ((++work & (TRIE_INTERRUPT_INTERVAL - 1)) == 0) trie_check_interrupts();
|
|
705
|
+
}
|
|
706
|
+
return -1;
|
|
707
|
+
}
|
|
708
|
+
for (index = 0; index < node->static_count; index++) {
|
|
709
|
+
VALUE label = node->static_edges[index].label;
|
|
710
|
+
if (RSTRING_LEN(label) > 0 && (unsigned char)RSTRING_PTR(label)[0] == first &&
|
|
711
|
+
rb_enc_compatible(label, string)) return (long)index;
|
|
712
|
+
}
|
|
713
|
+
return -1;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
static bool
|
|
717
|
+
trie_parser_starts_with(const trie_parser_t *parser, const char *text)
|
|
718
|
+
{
|
|
719
|
+
size_t length = strlen(text);
|
|
720
|
+
return parser->position <= parser->length &&
|
|
721
|
+
length <= parser->length - parser->position &&
|
|
722
|
+
memcmp(parser->bytes + parser->position, text, length) == 0;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
static bool
|
|
726
|
+
trie_parser_consume(trie_parser_t *parser, const char *text)
|
|
727
|
+
{
|
|
728
|
+
size_t length = strlen(text);
|
|
729
|
+
if (!trie_parser_starts_with(parser, text)) return false;
|
|
730
|
+
parser->position += length;
|
|
731
|
+
return true;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
static bool
|
|
735
|
+
trie_program_add_instruction(trie_program_t *program, trie_instruction_t instruction)
|
|
736
|
+
{
|
|
737
|
+
size_t capacity;
|
|
738
|
+
if (program->instruction_count >= TRIE_PROGRAM_INSTRUCTIONS) return false;
|
|
739
|
+
if (program->instruction_count == program->instruction_capacity) {
|
|
740
|
+
if (!trie_size_grow(program->instruction_capacity,
|
|
741
|
+
program->instruction_count + 1,
|
|
742
|
+
sizeof(*program->instructions), &capacity)) return false;
|
|
743
|
+
TRIE_REALLOC_N(program->instructions, trie_instruction_t, capacity);
|
|
744
|
+
program->instruction_capacity = capacity;
|
|
745
|
+
}
|
|
746
|
+
program->instructions[program->instruction_count++] = instruction;
|
|
747
|
+
return true;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
static bool
|
|
751
|
+
trie_capture_accepts(trie_opcode_t opcode, unsigned char byte)
|
|
752
|
+
{
|
|
753
|
+
switch (opcode) {
|
|
754
|
+
case TRIE_CAPTURE_DIGIT:
|
|
755
|
+
return byte >= '0' && byte <= '9';
|
|
756
|
+
case TRIE_CAPTURE_WORD:
|
|
757
|
+
return (byte >= 'a' && byte <= 'z') || (byte >= 'A' && byte <= 'Z') ||
|
|
758
|
+
(byte >= '0' && byte <= '9') || byte == '_';
|
|
759
|
+
case TRIE_CAPTURE_NOT_SLASH:
|
|
760
|
+
return byte != '/';
|
|
761
|
+
case TRIE_CAPTURE_NOT_PATH_SEPARATOR:
|
|
762
|
+
return byte != '/' && byte != '?' && byte != '#';
|
|
763
|
+
default:
|
|
764
|
+
return false;
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
static bool
|
|
769
|
+
trie_name_byte(unsigned char byte, bool first)
|
|
770
|
+
{
|
|
771
|
+
return (byte >= 'a' && byte <= 'z') || (byte >= 'A' && byte <= 'Z') ||
|
|
772
|
+
(!first && ((byte >= '0' && byte <= '9') || byte == '_'));
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
static bool
|
|
776
|
+
trie_parse_negated_delimiters(trie_parser_t *parser, trie_opcode_t *opcode)
|
|
777
|
+
{
|
|
778
|
+
size_t start = parser->position;
|
|
779
|
+
unsigned int delimiters = 0;
|
|
780
|
+
if (!trie_parser_consume(parser, "[^")) return false;
|
|
781
|
+
while (parser->position < parser->length && parser->bytes[parser->position] != ']') {
|
|
782
|
+
unsigned char byte = parser->bytes[parser->position++];
|
|
783
|
+
unsigned int delimiter;
|
|
784
|
+
if (byte == '\\') {
|
|
785
|
+
if (parser->position >= parser->length) goto fail;
|
|
786
|
+
byte = parser->bytes[parser->position++];
|
|
787
|
+
}
|
|
788
|
+
if (byte == '/') delimiter = 1;
|
|
789
|
+
else if (byte == '?') delimiter = 2;
|
|
790
|
+
else if (byte == '#') delimiter = 4;
|
|
791
|
+
else goto fail;
|
|
792
|
+
if (delimiters & delimiter) goto fail;
|
|
793
|
+
delimiters |= delimiter;
|
|
794
|
+
}
|
|
795
|
+
if (parser->position >= parser->length || parser->bytes[parser->position++] != ']' ||
|
|
796
|
+
parser->position >= parser->length || parser->bytes[parser->position++] != '+') goto fail;
|
|
797
|
+
if (delimiters == 1) *opcode = TRIE_CAPTURE_NOT_SLASH;
|
|
798
|
+
else if (delimiters == 7) *opcode = TRIE_CAPTURE_NOT_PATH_SEPARATOR;
|
|
799
|
+
else goto fail;
|
|
800
|
+
return true;
|
|
801
|
+
|
|
802
|
+
fail:
|
|
803
|
+
parser->position = start;
|
|
804
|
+
return false;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
static bool
|
|
808
|
+
trie_parse_capture(trie_parser_t *parser)
|
|
809
|
+
{
|
|
810
|
+
size_t name_start;
|
|
811
|
+
size_t name_length;
|
|
812
|
+
trie_opcode_t opcode;
|
|
813
|
+
trie_instruction_t instruction;
|
|
814
|
+
VALUE name;
|
|
815
|
+
size_t index;
|
|
816
|
+
if (!trie_parser_consume(parser, "(?<")) return false;
|
|
817
|
+
name_start = parser->position;
|
|
818
|
+
while (parser->position < parser->length &&
|
|
819
|
+
trie_name_byte(parser->bytes[parser->position], parser->position == name_start)) {
|
|
820
|
+
parser->position++;
|
|
821
|
+
}
|
|
822
|
+
name_length = parser->position - name_start;
|
|
823
|
+
if (name_length == 0 || parser->position >= parser->length ||
|
|
824
|
+
parser->bytes[parser->position++] != '>') return false;
|
|
825
|
+
|
|
826
|
+
if (trie_parser_consume(parser, "\\d+")) opcode = TRIE_CAPTURE_DIGIT;
|
|
827
|
+
else if (trie_parser_consume(parser, "[0-9]+")) opcode = TRIE_CAPTURE_DIGIT;
|
|
828
|
+
else if (trie_parser_consume(parser, "\\w+")) opcode = TRIE_CAPTURE_WORD;
|
|
829
|
+
else if (trie_parser_starts_with(parser, "[^")) {
|
|
830
|
+
if (!trie_parse_negated_delimiters(parser, &opcode)) return false;
|
|
831
|
+
}
|
|
832
|
+
else if (trie_parser_consume(parser, "(?-mix:\\d+)")) opcode = TRIE_CAPTURE_DIGIT;
|
|
833
|
+
else if (trie_parser_consume(parser, "(?-mix:\\w+)")) opcode = TRIE_CAPTURE_WORD;
|
|
834
|
+
else return false;
|
|
835
|
+
if (parser->position >= parser->length || parser->bytes[parser->position++] != ')') return false;
|
|
836
|
+
if (parser->program->capture_count >= TRIE_PROGRAM_CAPTURES) return false;
|
|
837
|
+
for (index = 0; index < parser->program->instruction_count; index++) {
|
|
838
|
+
trie_instruction_t *existing = &parser->program->instructions[index];
|
|
839
|
+
if (existing->opcode == TRIE_LITERAL_ALTERNATIVES) continue;
|
|
840
|
+
if ((size_t)RSTRING_LEN(existing->name) == name_length &&
|
|
841
|
+
memcmp(RSTRING_PTR(existing->name), parser->bytes + name_start, name_length) == 0) return false;
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
memset(&instruction, 0, sizeof(instruction));
|
|
845
|
+
instruction.opcode = opcode;
|
|
846
|
+
instruction.name = Qnil;
|
|
847
|
+
if (!trie_program_add_instruction(parser->program, instruction)) return false;
|
|
848
|
+
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;
|
|
851
|
+
parser->program->capture_count++;
|
|
852
|
+
if (opcode == TRIE_CAPTURE_DIGIT || opcode == TRIE_CAPTURE_WORD) {
|
|
853
|
+
parser->program->requires_ascii_input = true;
|
|
854
|
+
}
|
|
855
|
+
RB_GC_GUARD(name);
|
|
856
|
+
return true;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
static bool
|
|
860
|
+
trie_literal_meta(unsigned char byte)
|
|
861
|
+
{
|
|
862
|
+
switch (byte) {
|
|
863
|
+
case '.': case '^': case '$': case '*': case '+': case '?':
|
|
864
|
+
case '{': case '}': case '[': case ']': case '(': case ')':
|
|
865
|
+
case '|': case '\\':
|
|
866
|
+
return true;
|
|
867
|
+
default:
|
|
868
|
+
return false;
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
static bool
|
|
873
|
+
trie_add_literal_alternative(trie_instruction_t *instruction,
|
|
874
|
+
const unsigned char *bytes, size_t length)
|
|
875
|
+
{
|
|
876
|
+
trie_literal_t *alternatives;
|
|
877
|
+
unsigned char *copy;
|
|
878
|
+
size_t count = instruction->alternative_count;
|
|
879
|
+
if (count == SIZE_MAX || count + 1 > SIZE_MAX / sizeof(*alternatives)) return false;
|
|
880
|
+
alternatives = instruction->alternatives;
|
|
881
|
+
TRIE_REALLOC_N(alternatives, trie_literal_t, count + 1);
|
|
882
|
+
instruction->alternatives = alternatives;
|
|
883
|
+
alternatives[count].bytes = NULL;
|
|
884
|
+
alternatives[count].length = 0;
|
|
885
|
+
instruction->alternative_count++;
|
|
886
|
+
copy = TRIE_ALLOC_N(unsigned char, length);
|
|
887
|
+
memcpy(copy, bytes, length);
|
|
888
|
+
alternatives[count].bytes = copy;
|
|
889
|
+
alternatives[count].length = length;
|
|
890
|
+
return true;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
static void
|
|
894
|
+
trie_instruction_clear(trie_instruction_t *instruction)
|
|
895
|
+
{
|
|
896
|
+
size_t index;
|
|
897
|
+
for (index = 0; index < instruction->alternative_count; index++) {
|
|
898
|
+
xfree(instruction->alternatives[index].bytes);
|
|
899
|
+
}
|
|
900
|
+
xfree(instruction->alternatives);
|
|
901
|
+
memset(instruction, 0, sizeof(*instruction));
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
static bool
|
|
905
|
+
trie_parse_literal_alternatives(trie_parser_t *parser, trie_opcode_t preceding)
|
|
906
|
+
{
|
|
907
|
+
trie_instruction_t empty;
|
|
908
|
+
trie_instruction_t *instruction;
|
|
909
|
+
bool closed = false;
|
|
910
|
+
if (!trie_parser_consume(parser, "(?:")) return false;
|
|
911
|
+
memset(&empty, 0, sizeof(empty));
|
|
912
|
+
empty.opcode = TRIE_LITERAL_ALTERNATIVES;
|
|
913
|
+
if (!trie_program_add_instruction(parser->program, empty)) return false;
|
|
914
|
+
instruction = &parser->program->instructions[parser->program->instruction_count - 1];
|
|
915
|
+
while (parser->position < parser->length) {
|
|
916
|
+
unsigned char buffer[32];
|
|
917
|
+
size_t length = 0;
|
|
918
|
+
while (parser->position < parser->length) {
|
|
919
|
+
unsigned char byte = parser->bytes[parser->position++];
|
|
920
|
+
if (byte == '|' || byte == ')') {
|
|
921
|
+
closed = byte == ')';
|
|
922
|
+
break;
|
|
923
|
+
}
|
|
924
|
+
if (byte == '\\') {
|
|
925
|
+
if (parser->position >= parser->length) goto fail;
|
|
926
|
+
byte = parser->bytes[parser->position++];
|
|
927
|
+
if (!trie_literal_meta(byte) && byte != '/' && byte != '-' && byte != '#') goto fail;
|
|
928
|
+
}
|
|
929
|
+
else if (trie_literal_meta(byte)) goto fail;
|
|
930
|
+
if (length == sizeof(buffer)) goto fail;
|
|
931
|
+
buffer[length++] = byte;
|
|
932
|
+
}
|
|
933
|
+
if (length == 0 || trie_capture_accepts(preceding, buffer[0]) ||
|
|
934
|
+
!trie_add_literal_alternative(instruction, buffer, length)) goto fail;
|
|
935
|
+
if (closed) break;
|
|
936
|
+
}
|
|
937
|
+
if (!closed || instruction->alternative_count < 2) goto fail;
|
|
938
|
+
/* Alternatives must be prefix-free or the native first choice could hide
|
|
939
|
+
* a longer Ruby choice. */
|
|
940
|
+
for (size_t left = 0; left < instruction->alternative_count; left++) {
|
|
941
|
+
for (size_t right = left + 1; right < instruction->alternative_count; right++) {
|
|
942
|
+
trie_literal_t *a = &instruction->alternatives[left];
|
|
943
|
+
trie_literal_t *b = &instruction->alternatives[right];
|
|
944
|
+
size_t limit = a->length < b->length ? a->length : b->length;
|
|
945
|
+
if (memcmp(a->bytes, b->bytes, limit) == 0) goto fail;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
return true;
|
|
949
|
+
fail:
|
|
950
|
+
return false;
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
static bool
|
|
954
|
+
trie_parse_literal(trie_parser_t *parser, trie_opcode_t preceding)
|
|
955
|
+
{
|
|
956
|
+
trie_instruction_t empty;
|
|
957
|
+
trie_instruction_t *instruction;
|
|
958
|
+
unsigned char buffer[64];
|
|
959
|
+
size_t length = 0;
|
|
960
|
+
if (trie_parser_starts_with(parser, "(?:")) {
|
|
961
|
+
return trie_parse_literal_alternatives(parser, preceding);
|
|
962
|
+
}
|
|
963
|
+
while (parser->position < parser->length && !trie_parser_starts_with(parser, "(?<")) {
|
|
964
|
+
unsigned char byte = parser->bytes[parser->position++];
|
|
965
|
+
if (byte == '\\') {
|
|
966
|
+
if (parser->position >= parser->length) return false;
|
|
967
|
+
byte = parser->bytes[parser->position++];
|
|
968
|
+
if (!trie_literal_meta(byte) && byte != '/' && byte != '-' && byte != '#') return false;
|
|
969
|
+
}
|
|
970
|
+
else if (trie_literal_meta(byte)) return false;
|
|
971
|
+
if (length == sizeof(buffer)) return false;
|
|
972
|
+
buffer[length++] = byte;
|
|
973
|
+
}
|
|
974
|
+
if (length == 0 || trie_capture_accepts(preceding, buffer[0])) return false;
|
|
975
|
+
memset(&empty, 0, sizeof(empty));
|
|
976
|
+
empty.opcode = TRIE_LITERAL_ALTERNATIVES;
|
|
977
|
+
if (!trie_program_add_instruction(parser->program, empty)) return false;
|
|
978
|
+
instruction = &parser->program->instructions[parser->program->instruction_count - 1];
|
|
979
|
+
if (!trie_add_literal_alternative(instruction, buffer, length)) return false;
|
|
980
|
+
return true;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
static uint64_t
|
|
984
|
+
trie_hash_source(VALUE source, size_t length, int encoding, int options)
|
|
985
|
+
{
|
|
986
|
+
uint64_t hash = UINT64_C(1469598103934665603);
|
|
987
|
+
size_t index = 0;
|
|
988
|
+
while (index < length) {
|
|
989
|
+
size_t limit = length - index > 16384 ? index + 16384 : length;
|
|
990
|
+
const unsigned char *bytes = (const unsigned char *)RSTRING_PTR(source);
|
|
991
|
+
while (index < limit) {
|
|
992
|
+
hash ^= bytes[index++];
|
|
993
|
+
hash *= UINT64_C(1099511628211);
|
|
994
|
+
}
|
|
995
|
+
if (index < length) trie_check_interrupts();
|
|
996
|
+
}
|
|
997
|
+
hash ^= (uint32_t)encoding;
|
|
998
|
+
hash *= UINT64_C(1099511628211);
|
|
999
|
+
hash ^= (uint32_t)options;
|
|
1000
|
+
return hash ? hash : 1;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
static void
|
|
1004
|
+
trie_ensure_program_inventory(trie_t *trie, size_t minimum)
|
|
1005
|
+
{
|
|
1006
|
+
size_t capacity;
|
|
1007
|
+
if (minimum <= trie->program_capacity) return;
|
|
1008
|
+
if (!trie_size_grow(trie->program_capacity, minimum, sizeof(*trie->programs), &capacity)) rb_memerror();
|
|
1009
|
+
TRIE_REALLOC_N(trie->programs, trie_program_t *, capacity);
|
|
1010
|
+
trie->program_capacity = capacity;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
static void
|
|
1014
|
+
trie_program_cache_rebuild(trie_t *trie, size_t capacity)
|
|
1015
|
+
{
|
|
1016
|
+
trie_program_t **cache = TRIE_ALLOC_N(trie_program_t *, capacity);
|
|
1017
|
+
size_t index;
|
|
1018
|
+
memset(cache, 0, capacity * sizeof(*cache));
|
|
1019
|
+
for (index = 0; index < trie->program_count; index++) {
|
|
1020
|
+
trie_program_t *program = trie->programs[index];
|
|
1021
|
+
size_t slot = (size_t)program->key_hash & (capacity - 1);
|
|
1022
|
+
while (cache[slot]) slot = (slot + 1) & (capacity - 1);
|
|
1023
|
+
cache[slot] = program;
|
|
1024
|
+
}
|
|
1025
|
+
xfree(trie->program_cache);
|
|
1026
|
+
trie->program_cache = cache;
|
|
1027
|
+
trie->program_cache_capacity = capacity;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
static trie_program_t *
|
|
1031
|
+
trie_compile_program(trie_t *trie, VALUE source, int options, VALUE timeout)
|
|
1032
|
+
{
|
|
1033
|
+
size_t length;
|
|
1034
|
+
int encoding;
|
|
1035
|
+
uint64_t hash;
|
|
1036
|
+
size_t slot;
|
|
1037
|
+
trie_program_t *program;
|
|
1038
|
+
trie_parser_t parser;
|
|
1039
|
+
bool success = false;
|
|
1040
|
+
|
|
1041
|
+
if (options != 0) return NULL;
|
|
1042
|
+
if (!NIL_P(timeout)) return NULL;
|
|
1043
|
+
if (!rb_enc_asciicompat(rb_enc_get(source)) || !rb_enc_str_asciionly_p(source)) return NULL;
|
|
1044
|
+
length = (size_t)RSTRING_LEN(source);
|
|
1045
|
+
if (length == 0 || length > TRIE_PROGRAM_SOURCE_BYTES) return NULL;
|
|
1046
|
+
encoding = rb_enc_get_index(source);
|
|
1047
|
+
hash = trie_hash_source(source, length, encoding, options);
|
|
1048
|
+
|
|
1049
|
+
if (trie->program_cache_capacity) {
|
|
1050
|
+
slot = (size_t)hash & (trie->program_cache_capacity - 1);
|
|
1051
|
+
while ((program = trie->program_cache[slot])) {
|
|
1052
|
+
if (program->key_hash == hash && program->key_length == length &&
|
|
1053
|
+
program->key_encoding == encoding && program->key_options == options &&
|
|
1054
|
+
memcmp(program->key, RSTRING_PTR(source), length) == 0) return program;
|
|
1055
|
+
slot = (slot + 1) & (trie->program_cache_capacity - 1);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
trie_ensure_program_inventory(trie, trie->program_count + 1);
|
|
1060
|
+
program = TRIE_ALLOC(trie_program_t);
|
|
1061
|
+
memset(program, 0, sizeof(*program));
|
|
1062
|
+
trie->programs[trie->program_count++] = program;
|
|
1063
|
+
program->key = TRIE_ALLOC_N(unsigned char, length);
|
|
1064
|
+
memcpy(program->key, RSTRING_PTR(source), length);
|
|
1065
|
+
program->key_length = length;
|
|
1066
|
+
program->key_hash = hash;
|
|
1067
|
+
program->key_encoding = encoding;
|
|
1068
|
+
program->key_options = options;
|
|
1069
|
+
|
|
1070
|
+
memset(&parser, 0, sizeof(parser));
|
|
1071
|
+
parser.bytes = program->key;
|
|
1072
|
+
parser.length = length;
|
|
1073
|
+
parser.program = program;
|
|
1074
|
+
if (parser.length >= 6 && memcmp(parser.bytes, "(?:", 3) == 0 &&
|
|
1075
|
+
memcmp(parser.bytes + parser.length - 3, ")\\z", 3) == 0) {
|
|
1076
|
+
parser.position = 3;
|
|
1077
|
+
parser.length -= 3;
|
|
1078
|
+
program->end_anchor = true;
|
|
1079
|
+
}
|
|
1080
|
+
else if (parser.length >= 2 && memcmp(parser.bytes + parser.length - 2, "\\z", 2) == 0) {
|
|
1081
|
+
parser.length -= 2;
|
|
1082
|
+
program->end_anchor = true;
|
|
1083
|
+
}
|
|
1084
|
+
trie_parser_consume(&parser, "\\G");
|
|
1085
|
+
if (!trie_parse_capture(&parser)) goto finish;
|
|
1086
|
+
while (parser.position < parser.length) {
|
|
1087
|
+
trie_opcode_t preceding = program->instructions[program->instruction_count - 1].opcode;
|
|
1088
|
+
if (!trie_parse_literal(&parser, preceding)) goto finish;
|
|
1089
|
+
if (!trie_parse_capture(&parser)) goto finish;
|
|
1090
|
+
}
|
|
1091
|
+
success = program->instruction_count > 0 && program->capture_count > 0;
|
|
1092
|
+
|
|
1093
|
+
finish:
|
|
1094
|
+
if (!success) {
|
|
1095
|
+
trie->program_count--;
|
|
1096
|
+
trie_program_free(program);
|
|
1097
|
+
return NULL;
|
|
1098
|
+
}
|
|
1099
|
+
if (!trie->program_cache_capacity ||
|
|
1100
|
+
trie->program_count * 10 >= trie->program_cache_capacity * 7) {
|
|
1101
|
+
size_t capacity = trie->program_cache_capacity ? trie->program_cache_capacity * 2 : 16;
|
|
1102
|
+
if (capacity < trie->program_cache_capacity) rb_memerror();
|
|
1103
|
+
trie_program_cache_rebuild(trie, capacity);
|
|
1104
|
+
}
|
|
1105
|
+
else {
|
|
1106
|
+
slot = (size_t)hash & (trie->program_cache_capacity - 1);
|
|
1107
|
+
while (trie->program_cache[slot]) slot = (slot + 1) & (trie->program_cache_capacity - 1);
|
|
1108
|
+
trie->program_cache[slot] = program;
|
|
1109
|
+
}
|
|
1110
|
+
RB_GC_GUARD(source);
|
|
1111
|
+
RB_GC_GUARD(timeout);
|
|
1112
|
+
return program;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
typedef struct {
|
|
1116
|
+
VALUE source;
|
|
1117
|
+
VALUE timeout;
|
|
1118
|
+
int options;
|
|
1119
|
+
} trie_regexp_build_t;
|
|
1120
|
+
|
|
1121
|
+
static VALUE
|
|
1122
|
+
trie_build_regexp(VALUE opaque)
|
|
1123
|
+
{
|
|
1124
|
+
trie_regexp_build_t *build = (trie_regexp_build_t *)(uintptr_t)opaque;
|
|
1125
|
+
if (NIL_P(build->timeout)) return rb_reg_new_str(build->source, build->options);
|
|
1126
|
+
VALUE keywords = rb_hash_new();
|
|
1127
|
+
VALUE arguments[3] = {build->source, INT2NUM(build->options), keywords};
|
|
1128
|
+
rb_hash_aset(keywords, ID2SYM(id_timeout), build->timeout);
|
|
1129
|
+
return rb_funcallv_kw(rb_cRegexp, rb_intern("new"), 3, arguments, RB_PASS_KEYWORDS);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
static VALUE
|
|
1133
|
+
trie_anchored_regexp(VALUE source, int options, VALUE timeout)
|
|
1134
|
+
{
|
|
1135
|
+
VALUE wrapped = rb_str_new_cstr("\\G(?:");
|
|
1136
|
+
trie_regexp_build_t build;
|
|
1137
|
+
VALUE regexp;
|
|
1138
|
+
int state = 0;
|
|
1139
|
+
rb_enc_copy(wrapped, source);
|
|
1140
|
+
rb_str_concat(wrapped, source);
|
|
1141
|
+
rb_str_cat_cstr(wrapped, ")");
|
|
1142
|
+
build.source = wrapped;
|
|
1143
|
+
build.timeout = timeout;
|
|
1144
|
+
build.options = options;
|
|
1145
|
+
regexp = rb_protect(trie_build_regexp, (VALUE)(uintptr_t)&build, &state);
|
|
1146
|
+
if (state) {
|
|
1147
|
+
VALUE error = rb_errinfo();
|
|
1148
|
+
if (!rb_obj_is_kind_of(error, rb_eRegexpError)) rb_jump_tag(state);
|
|
1149
|
+
rb_set_errinfo(Qnil);
|
|
1150
|
+
wrapped = rb_str_new_cstr("\\G(?:");
|
|
1151
|
+
rb_enc_copy(wrapped, source);
|
|
1152
|
+
rb_str_concat(wrapped, source);
|
|
1153
|
+
/* A trailing extended-mode comment can consume our close. The newline
|
|
1154
|
+
* is ignored exactly when extended mode remains active at source end. */
|
|
1155
|
+
rb_str_cat_cstr(wrapped, "\n)");
|
|
1156
|
+
build.source = wrapped;
|
|
1157
|
+
regexp = trie_build_regexp((VALUE)(uintptr_t)&build);
|
|
1158
|
+
RB_GC_GUARD(error);
|
|
1159
|
+
}
|
|
1160
|
+
rb_obj_freeze(regexp);
|
|
1161
|
+
RB_GC_GUARD(source);
|
|
1162
|
+
RB_GC_GUARD(timeout);
|
|
1163
|
+
RB_GC_GUARD(wrapped);
|
|
1164
|
+
return regexp;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
static bool
|
|
1168
|
+
trie_string_bytes_equal(VALUE left, VALUE right)
|
|
1169
|
+
{
|
|
1170
|
+
long length = RSTRING_LEN(left);
|
|
1171
|
+
long offset = 0;
|
|
1172
|
+
if (length != RSTRING_LEN(right)) return false;
|
|
1173
|
+
while (offset < length) {
|
|
1174
|
+
long count = length - offset;
|
|
1175
|
+
if (count > 16384) count = 16384;
|
|
1176
|
+
if (memcmp(RSTRING_PTR(left) + offset, RSTRING_PTR(right) + offset,
|
|
1177
|
+
(size_t)count) != 0) return false;
|
|
1178
|
+
offset += count;
|
|
1179
|
+
if (offset < length) trie_check_interrupts();
|
|
1180
|
+
}
|
|
1181
|
+
return true;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
static bool
|
|
1185
|
+
trie_timeout_equal(VALUE left, VALUE right)
|
|
1186
|
+
{
|
|
1187
|
+
if (left == right) return true;
|
|
1188
|
+
if (NIL_P(left) || NIL_P(right)) return false;
|
|
1189
|
+
return RTEST(rb_eql(left, right));
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
static bool
|
|
1193
|
+
trie_regexp_edge_equal(const trie_regexp_edge_t *edge, VALUE source,
|
|
1194
|
+
int options, VALUE timeout)
|
|
1195
|
+
{
|
|
1196
|
+
return edge->options == options && rb_enc_get_index(edge->source) == rb_enc_get_index(source) &&
|
|
1197
|
+
trie_string_bytes_equal(edge->source, source) &&
|
|
1198
|
+
trie_timeout_equal(edge->timeout, timeout);
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
static trie_node_t *
|
|
1202
|
+
trie_insert_string(trie_t *trie, trie_node_t *node, VALUE string)
|
|
1203
|
+
{
|
|
1204
|
+
size_t offset = 0;
|
|
1205
|
+
size_t length = (size_t)RSTRING_LEN(string);
|
|
1206
|
+
while (offset < length) {
|
|
1207
|
+
long found = trie_find_static_edge(node, string, offset);
|
|
1208
|
+
if (found < 0) {
|
|
1209
|
+
trie_node_t *child = trie_node_new(trie);
|
|
1210
|
+
VALUE label = trie_frozen_string(string, (long)offset, (long)(length - offset));
|
|
1211
|
+
size_t edge_index = node->static_count;
|
|
1212
|
+
trie_ensure_static_edges(node, node->static_count + 1);
|
|
1213
|
+
node->static_edges[edge_index].label = label;
|
|
1214
|
+
node->static_edges[edge_index].child = child;
|
|
1215
|
+
node->static_edges[edge_index].encoding = rb_enc_get_index(label);
|
|
1216
|
+
node->static_count++;
|
|
1217
|
+
trie->static_edge_count++;
|
|
1218
|
+
trie_index_static_edge(trie, node, edge_index);
|
|
1219
|
+
RB_GC_GUARD(label);
|
|
1220
|
+
return child;
|
|
1221
|
+
}
|
|
1222
|
+
trie_static_edge_t *edge = &node->static_edges[found];
|
|
1223
|
+
size_t shared = trie_common_prefix(edge->label, string, offset);
|
|
1224
|
+
size_t edge_length = (size_t)RSTRING_LEN(edge->label);
|
|
1225
|
+
if (shared == edge_length) {
|
|
1226
|
+
node = edge->child;
|
|
1227
|
+
offset += shared;
|
|
1228
|
+
continue;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
trie_node_t *middle = trie_node_new(trie);
|
|
1232
|
+
VALUE old_label = edge->label;
|
|
1233
|
+
VALUE prefix = trie_frozen_string(old_label, 0, (long)shared);
|
|
1234
|
+
VALUE old_suffix = trie_frozen_string(old_label, (long)shared, (long)(edge_length - shared));
|
|
1235
|
+
trie_ensure_static_edges(middle, shared == length - offset ? 1 : 2);
|
|
1236
|
+
middle->static_edges[0].label = old_suffix;
|
|
1237
|
+
middle->static_edges[0].child = edge->child;
|
|
1238
|
+
middle->static_edges[0].encoding = rb_enc_get_index(old_suffix);
|
|
1239
|
+
middle->static_count = 1;
|
|
1240
|
+
trie->static_edge_count++;
|
|
1241
|
+
trie_index_static_edge(trie, middle, 0);
|
|
1242
|
+
edge->label = prefix;
|
|
1243
|
+
edge->child = middle;
|
|
1244
|
+
edge->encoding = rb_enc_get_index(prefix);
|
|
1245
|
+
offset += shared;
|
|
1246
|
+
if (offset == length) {
|
|
1247
|
+
RB_GC_GUARD(old_label);
|
|
1248
|
+
RB_GC_GUARD(prefix);
|
|
1249
|
+
RB_GC_GUARD(old_suffix);
|
|
1250
|
+
return middle;
|
|
1251
|
+
}
|
|
1252
|
+
trie_node_t *child = trie_node_new(trie);
|
|
1253
|
+
VALUE new_suffix = trie_frozen_string(string, (long)offset, (long)(length - offset));
|
|
1254
|
+
middle->static_edges[1].label = new_suffix;
|
|
1255
|
+
middle->static_edges[1].child = child;
|
|
1256
|
+
middle->static_edges[1].encoding = rb_enc_get_index(new_suffix);
|
|
1257
|
+
middle->static_count = 2;
|
|
1258
|
+
trie->static_edge_count++;
|
|
1259
|
+
trie_index_static_edge(trie, middle, 1);
|
|
1260
|
+
RB_GC_GUARD(old_label);
|
|
1261
|
+
RB_GC_GUARD(prefix);
|
|
1262
|
+
RB_GC_GUARD(old_suffix);
|
|
1263
|
+
RB_GC_GUARD(new_suffix);
|
|
1264
|
+
return child;
|
|
1265
|
+
}
|
|
1266
|
+
return node;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
static trie_node_t *
|
|
1270
|
+
trie_insert_regexp(trie_t *trie, trie_node_t *node, VALUE regexp)
|
|
1271
|
+
{
|
|
1272
|
+
VALUE source = RREGEXP_SRC(regexp);
|
|
1273
|
+
VALUE source_copy = rb_str_dup(source);
|
|
1274
|
+
VALUE timeout = regexp_timeout_supported ? rb_funcall(regexp, id_timeout, 0) : Qnil;
|
|
1275
|
+
int options = rb_reg_options(regexp);
|
|
1276
|
+
size_t index;
|
|
1277
|
+
if (!NIL_P(timeout) && !RB_FLOAT_TYPE_P(timeout) && !RB_INTEGER_TYPE_P(timeout)) {
|
|
1278
|
+
rb_raise(rb_eTypeError, "regexp timeout must be numeric or nil");
|
|
1279
|
+
}
|
|
1280
|
+
rb_obj_freeze(source_copy);
|
|
1281
|
+
for (index = 0; index < node->regexp_count; index++) {
|
|
1282
|
+
if (trie_regexp_edge_equal(&node->regexp_edges[index], source_copy, options, timeout)) {
|
|
1283
|
+
RB_GC_GUARD(source);
|
|
1284
|
+
RB_GC_GUARD(source_copy);
|
|
1285
|
+
RB_GC_GUARD(timeout);
|
|
1286
|
+
return node->regexp_edges[index].child;
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
VALUE anchored = trie_anchored_regexp(source_copy, options, timeout);
|
|
1290
|
+
trie_program_t *program = trie_compile_program(trie, source_copy, options, timeout);
|
|
1291
|
+
trie_node_t *child = trie_node_new(trie);
|
|
1292
|
+
trie_ensure_regexp_edges(node, node->regexp_count + 1);
|
|
1293
|
+
trie_regexp_edge_t *edge = &node->regexp_edges[node->regexp_count++];
|
|
1294
|
+
edge->source = source_copy;
|
|
1295
|
+
edge->anchored = anchored;
|
|
1296
|
+
edge->timeout = timeout;
|
|
1297
|
+
edge->options = options;
|
|
1298
|
+
edge->program = program;
|
|
1299
|
+
edge->child = child;
|
|
1300
|
+
trie->regexp_edge_count++;
|
|
1301
|
+
RB_GC_GUARD(source);
|
|
1302
|
+
RB_GC_GUARD(source_copy);
|
|
1303
|
+
RB_GC_GUARD(timeout);
|
|
1304
|
+
RB_GC_GUARD(anchored);
|
|
1305
|
+
return child;
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
static void
|
|
1309
|
+
trie_insert_entry(trie_t *trie, VALUE parts, uint32_t token)
|
|
1310
|
+
{
|
|
1311
|
+
trie_node_t *node = trie->root;
|
|
1312
|
+
long index;
|
|
1313
|
+
Check_Type(parts, T_ARRAY);
|
|
1314
|
+
for (index = 0; index < RARRAY_LEN(parts); index++) {
|
|
1315
|
+
VALUE part = RARRAY_AREF(parts, index);
|
|
1316
|
+
if (RB_TYPE_P(part, T_STRING)) {
|
|
1317
|
+
VALUE stable_part = rb_str_new_frozen(part);
|
|
1318
|
+
if (rb_enc_str_coderange(stable_part) == ENC_CODERANGE_BROKEN) {
|
|
1319
|
+
rb_raise(rb_eArgError, "trie literal has invalid encoding");
|
|
1320
|
+
}
|
|
1321
|
+
node = trie_insert_string(trie, node, stable_part);
|
|
1322
|
+
RB_GC_GUARD(stable_part);
|
|
1323
|
+
}
|
|
1324
|
+
else if (rb_obj_is_kind_of(part, rb_cRegexp)) {
|
|
1325
|
+
node = trie_insert_regexp(trie, node, part);
|
|
1326
|
+
}
|
|
1327
|
+
else {
|
|
1328
|
+
rb_raise(rb_eTypeError, "trie parts must be strings or regular expressions");
|
|
1329
|
+
}
|
|
1330
|
+
if ((index & (TRIE_INTERRUPT_INTERVAL - 1)) == TRIE_INTERRUPT_INTERVAL - 1) trie_check_interrupts();
|
|
1331
|
+
}
|
|
1332
|
+
trie_ensure_tokens(node, node->token_count + 1);
|
|
1333
|
+
node->tokens[node->token_count++] = token;
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
static void
|
|
1337
|
+
trie_validate_references(VALUE self)
|
|
1338
|
+
{
|
|
1339
|
+
trie_t *trie;
|
|
1340
|
+
size_t node_index;
|
|
1341
|
+
size_t program_index;
|
|
1342
|
+
TypedData_Get_Struct(self, trie_t, &trie_type, trie);
|
|
1343
|
+
if (!NIL_P(trie->owner)) {
|
|
1344
|
+
containers_check_shareable(trie->owner);
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
containers_check_shareable(trie->tokens);
|
|
1348
|
+
for (node_index = 0; node_index < trie->node_count; node_index++) {
|
|
1349
|
+
trie_node_t *node = trie->nodes[node_index];
|
|
1350
|
+
size_t index;
|
|
1351
|
+
for (index = 0; index < node->static_count; index++) {
|
|
1352
|
+
containers_check_shareable(node->static_edges[index].label);
|
|
1353
|
+
}
|
|
1354
|
+
for (index = 0; index < node->regexp_count; index++) {
|
|
1355
|
+
containers_check_shareable(node->regexp_edges[index].source);
|
|
1356
|
+
containers_check_shareable(node->regexp_edges[index].anchored);
|
|
1357
|
+
containers_check_shareable(node->regexp_edges[index].timeout);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
for (program_index = 0; program_index < trie->program_count; program_index++) {
|
|
1361
|
+
trie_program_t *program = trie->programs[program_index];
|
|
1362
|
+
size_t index;
|
|
1363
|
+
for (index = 0; index < program->instruction_count; index++) {
|
|
1364
|
+
if (program->instructions[index].opcode != TRIE_LITERAL_ALTERNATIVES) {
|
|
1365
|
+
containers_check_shareable(program->instructions[index].name);
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
static VALUE
|
|
1372
|
+
trie_initialize(VALUE self, VALUE entries, VALUE tokens)
|
|
1373
|
+
{
|
|
1374
|
+
trie_t *trie;
|
|
1375
|
+
VALUE token_copy;
|
|
1376
|
+
VALUE token_identities;
|
|
1377
|
+
long index;
|
|
1378
|
+
TypedData_Get_Struct(self, trie_t, &trie_type, trie);
|
|
1379
|
+
if (trie->initialized || trie->node_count || !NIL_P(trie->tokens)) {
|
|
1380
|
+
rb_raise(rb_eRuntimeError, "trie initialization already attempted");
|
|
1381
|
+
}
|
|
1382
|
+
Check_Type(entries, T_ARRAY);
|
|
1383
|
+
Check_Type(tokens, T_ARRAY);
|
|
1384
|
+
if ((unsigned long)RARRAY_LEN(tokens) > UINT32_MAX) rb_raise(rb_eArgError, "too many trie tokens");
|
|
1385
|
+
token_copy = rb_ary_new_capa(RARRAY_LEN(tokens));
|
|
1386
|
+
token_identities = rb_hash_new();
|
|
1387
|
+
rb_funcall(token_identities, rb_intern("compare_by_identity"), 0);
|
|
1388
|
+
for (index = 0; index < RARRAY_LEN(tokens); index++) {
|
|
1389
|
+
VALUE token = RARRAY_AREF(tokens, index);
|
|
1390
|
+
containers_check_shareable(token);
|
|
1391
|
+
if (rb_hash_lookup2(token_identities, token, Qundef) != Qundef) {
|
|
1392
|
+
rb_raise(rb_eArgError, "trie tokens must have unique identities");
|
|
1393
|
+
}
|
|
1394
|
+
rb_hash_aset(token_identities, token, Qtrue);
|
|
1395
|
+
rb_ary_push(token_copy, token);
|
|
1396
|
+
if ((index & (TRIE_INTERRUPT_INTERVAL - 1)) == TRIE_INTERRUPT_INTERVAL - 1) trie_check_interrupts();
|
|
1397
|
+
}
|
|
1398
|
+
rb_obj_freeze(token_copy);
|
|
1399
|
+
trie->tokens = token_copy;
|
|
1400
|
+
trie->root = trie_node_new(trie);
|
|
1401
|
+
|
|
1402
|
+
for (index = 0; index < RARRAY_LEN(entries); index++) {
|
|
1403
|
+
VALUE entry = RARRAY_AREF(entries, index);
|
|
1404
|
+
VALUE parts;
|
|
1405
|
+
VALUE identifier;
|
|
1406
|
+
unsigned long id;
|
|
1407
|
+
Check_Type(entry, T_ARRAY);
|
|
1408
|
+
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);
|
|
1411
|
+
if (!RB_INTEGER_TYPE_P(identifier)) rb_raise(rb_eTypeError, "trie token id must be an integer");
|
|
1412
|
+
id = NUM2ULONG(identifier);
|
|
1413
|
+
if (id >= (unsigned long)RARRAY_LEN(token_copy) || id > UINT32_MAX) {
|
|
1414
|
+
rb_raise(rb_eArgError, "trie token id is out of range");
|
|
1415
|
+
}
|
|
1416
|
+
trie_insert_entry(trie, parts, (uint32_t)id);
|
|
1417
|
+
if ((index & (TRIE_INTERRUPT_INTERVAL - 1)) == TRIE_INTERRUPT_INTERVAL - 1) trie_check_interrupts();
|
|
1418
|
+
}
|
|
1419
|
+
trie->initialized = true;
|
|
1420
|
+
rb_obj_freeze(self);
|
|
1421
|
+
containers_publish_native_with_references(self, trie_validate_references);
|
|
1422
|
+
RB_GC_GUARD(entries);
|
|
1423
|
+
RB_GC_GUARD(tokens);
|
|
1424
|
+
RB_GC_GUARD(token_copy);
|
|
1425
|
+
RB_GC_GUARD(token_identities);
|
|
1426
|
+
return self;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1429
|
+
static void
|
|
1430
|
+
trie_search_ensure_frames(trie_search_t *search, size_t minimum)
|
|
1431
|
+
{
|
|
1432
|
+
size_t capacity;
|
|
1433
|
+
trie_frame_t *frames;
|
|
1434
|
+
if (minimum <= search->frame_capacity) return;
|
|
1435
|
+
if (!trie_size_grow(search->frame_capacity, minimum, sizeof(*frames), &capacity)) rb_memerror();
|
|
1436
|
+
if (search->frame_scratch == 0) {
|
|
1437
|
+
frames = trie_scratch_allocate(&search->frame_scratch, capacity, sizeof(*frames));
|
|
1438
|
+
memcpy(frames, search->frames, search->frame_count * sizeof(*frames));
|
|
1439
|
+
}
|
|
1440
|
+
else frames = trie_scratch_resize(search->frame_scratch, capacity, sizeof(*frames));
|
|
1441
|
+
search->frames = frames;
|
|
1442
|
+
search->frame_capacity = capacity;
|
|
1443
|
+
}
|
|
1444
|
+
|
|
1445
|
+
static void
|
|
1446
|
+
trie_search_push_frame(trie_search_t *search, trie_node_t *node, long cursor,
|
|
1447
|
+
size_t restore_events)
|
|
1448
|
+
{
|
|
1449
|
+
trie_frame_t *frame;
|
|
1450
|
+
trie_search_ensure_frames(search, search->frame_count + 1);
|
|
1451
|
+
frame = &search->frames[search->frame_count++];
|
|
1452
|
+
memset(frame, 0, sizeof(*frame));
|
|
1453
|
+
frame->node = node;
|
|
1454
|
+
frame->cursor = cursor;
|
|
1455
|
+
frame->restore_events = restore_events;
|
|
1456
|
+
if (node->static_index && cursor < search->input_length) {
|
|
1457
|
+
unsigned char first = (unsigned char)RSTRING_PTR(search->input)[cursor];
|
|
1458
|
+
const uint32_t *slot = trie_static_index_slot_const(node->static_index, first);
|
|
1459
|
+
frame->static_index = slot ? *slot : 0;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
static void
|
|
1464
|
+
trie_search_ensure_events(trie_search_t *search, size_t minimum)
|
|
1465
|
+
{
|
|
1466
|
+
size_t capacity;
|
|
1467
|
+
trie_capture_event_t *events;
|
|
1468
|
+
if (minimum <= search->event_capacity) return;
|
|
1469
|
+
if (!trie_size_grow(search->event_capacity, minimum, sizeof(*events), &capacity)) rb_memerror();
|
|
1470
|
+
if (search->event_scratch == 0) {
|
|
1471
|
+
events = trie_scratch_allocate(&search->event_scratch, capacity, sizeof(*events));
|
|
1472
|
+
memcpy(events, search->events, search->event_count * sizeof(*events));
|
|
1473
|
+
}
|
|
1474
|
+
else events = trie_scratch_resize(search->event_scratch, capacity, sizeof(*events));
|
|
1475
|
+
search->events = events;
|
|
1476
|
+
search->event_capacity = capacity;
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
static void
|
|
1480
|
+
trie_search_append_native(trie_search_t *search, long begin, long end, VALUE name)
|
|
1481
|
+
{
|
|
1482
|
+
trie_capture_event_t *event;
|
|
1483
|
+
trie_search_ensure_events(search, search->event_count + 1);
|
|
1484
|
+
event = &search->events[search->event_count++];
|
|
1485
|
+
event->kind = TRIE_EVENT_NATIVE;
|
|
1486
|
+
event->begin = begin;
|
|
1487
|
+
event->end = end;
|
|
1488
|
+
event->name = name;
|
|
1489
|
+
event->match_index = -1;
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
static void
|
|
1493
|
+
trie_search_append_ruby(trie_search_t *search, VALUE match)
|
|
1494
|
+
{
|
|
1495
|
+
trie_capture_event_t *event;
|
|
1496
|
+
if (NIL_P(search->matches)) search->matches = rb_ary_new();
|
|
1497
|
+
long match_index = RARRAY_LEN(search->matches);
|
|
1498
|
+
rb_ary_push(search->matches, match);
|
|
1499
|
+
trie_search_ensure_events(search, search->event_count + 1);
|
|
1500
|
+
event = &search->events[search->event_count++];
|
|
1501
|
+
event->kind = TRIE_EVENT_RUBY;
|
|
1502
|
+
event->begin = event->end = 0;
|
|
1503
|
+
event->name = Qnil;
|
|
1504
|
+
event->match_index = match_index;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
static bool
|
|
1508
|
+
trie_program_match(const trie_program_t *program, VALUE input, long cursor,
|
|
1509
|
+
trie_search_t *search, long *end_cursor)
|
|
1510
|
+
{
|
|
1511
|
+
long length = RSTRING_LEN(input);
|
|
1512
|
+
size_t index;
|
|
1513
|
+
for (index = 0; index < program->instruction_count; index++) {
|
|
1514
|
+
const trie_instruction_t *instruction = &program->instructions[index];
|
|
1515
|
+
if (instruction->opcode == TRIE_LITERAL_ALTERNATIVES) {
|
|
1516
|
+
size_t alternative;
|
|
1517
|
+
bool matched = false;
|
|
1518
|
+
for (alternative = 0; alternative < instruction->alternative_count; alternative++) {
|
|
1519
|
+
const trie_literal_t *literal = &instruction->alternatives[alternative];
|
|
1520
|
+
const unsigned char *bytes = (const unsigned char *)RSTRING_PTR(input);
|
|
1521
|
+
if (literal->length <= (size_t)(length - cursor) &&
|
|
1522
|
+
memcmp(bytes + cursor, literal->bytes, literal->length) == 0) {
|
|
1523
|
+
cursor += (long)literal->length;
|
|
1524
|
+
matched = true;
|
|
1525
|
+
break;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
if (!matched) return false;
|
|
1529
|
+
}
|
|
1530
|
+
else {
|
|
1531
|
+
long begin = cursor;
|
|
1532
|
+
while (cursor < length) {
|
|
1533
|
+
const unsigned char *bytes = (const unsigned char *)RSTRING_PTR(input);
|
|
1534
|
+
if (!trie_capture_accepts(instruction->opcode, bytes[cursor])) break;
|
|
1535
|
+
cursor++;
|
|
1536
|
+
trie_search_work_checkpoint(search);
|
|
1537
|
+
}
|
|
1538
|
+
if (cursor == begin) return false;
|
|
1539
|
+
trie_search_append_native(search, begin, cursor, instruction->name);
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
if (program->end_anchor && cursor != length) return false;
|
|
1543
|
+
*end_cursor = cursor;
|
|
1544
|
+
return true;
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
static bool
|
|
1548
|
+
trie_native_program_allowed(const trie_program_t *program, trie_search_t *search, long cursor)
|
|
1549
|
+
{
|
|
1550
|
+
if (!search->native_segment_input || (program->requires_ascii_input && !search->ascii_input)) return false;
|
|
1551
|
+
if (search->utf8_input && cursor < search->input_length) {
|
|
1552
|
+
const unsigned char *bytes = (const unsigned char *)RSTRING_PTR(search->input);
|
|
1553
|
+
if ((bytes[cursor] & 0xc0) == 0x80) return false;
|
|
1554
|
+
}
|
|
1555
|
+
return true;
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
static bool
|
|
1559
|
+
trie_static_match(VALUE input, long cursor, const trie_static_edge_t *edge,
|
|
1560
|
+
long label_length, int input_encoding)
|
|
1561
|
+
{
|
|
1562
|
+
VALUE label = edge->label;
|
|
1563
|
+
long offset = 0;
|
|
1564
|
+
if (label_length > RSTRING_LEN(input) - cursor) return false;
|
|
1565
|
+
if ((unsigned char)RSTRING_PTR(input)[cursor] != (unsigned char)RSTRING_PTR(label)[0]) return false;
|
|
1566
|
+
if (edge->encoding != input_encoding && !rb_enc_compatible(label, input)) return false;
|
|
1567
|
+
if (label_length <= 16384) {
|
|
1568
|
+
return memcmp(RSTRING_PTR(input) + cursor, RSTRING_PTR(label), (size_t)label_length) == 0;
|
|
1569
|
+
}
|
|
1570
|
+
while (offset < label_length) {
|
|
1571
|
+
long length = label_length - offset;
|
|
1572
|
+
if (length > 16384) length = 16384;
|
|
1573
|
+
if (memcmp(RSTRING_PTR(input) + cursor + offset,
|
|
1574
|
+
RSTRING_PTR(label) + offset, (size_t)length) != 0) return false;
|
|
1575
|
+
offset += length;
|
|
1576
|
+
if (offset < label_length) trie_check_interrupts();
|
|
1577
|
+
}
|
|
1578
|
+
return true;
|
|
1579
|
+
}
|
|
1580
|
+
|
|
1581
|
+
static bool
|
|
1582
|
+
trie_regexp_match(const trie_regexp_edge_t *edge, trie_search_t *search,
|
|
1583
|
+
long cursor, long *end_cursor)
|
|
1584
|
+
{
|
|
1585
|
+
size_t restore = search->event_count;
|
|
1586
|
+
if (edge->program) {
|
|
1587
|
+
if (!search->regexp_timeout_checked) {
|
|
1588
|
+
search->regexp_timeout_checked = true;
|
|
1589
|
+
if (regexp_timeout_supported && !NIL_P(rb_funcall(rb_cRegexp, id_timeout, 0))) {
|
|
1590
|
+
search->native_segment_input = false;
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
if (trie_native_program_allowed(edge->program, search, cursor)) {
|
|
1594
|
+
if (trie_program_match(edge->program, search->input, cursor, search, end_cursor)) return true;
|
|
1595
|
+
search->event_count = restore;
|
|
1596
|
+
return false;
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
long position = rb_reg_search(edge->anchored, search->input, cursor, 0);
|
|
1600
|
+
if (position < 0) return false;
|
|
1601
|
+
VALUE match = rb_backref_get();
|
|
1602
|
+
struct re_registers *registers = RMATCH_REGS(match);
|
|
1603
|
+
if (!registers || registers->num_regs < 1 || registers->end[0] < cursor) return false;
|
|
1604
|
+
*end_cursor = registers->end[0];
|
|
1605
|
+
/* Subsequent regexp searches may otherwise recycle the current backref's
|
|
1606
|
+
* MatchData storage while this branch still retains it. */
|
|
1607
|
+
rb_match_busy(match);
|
|
1608
|
+
trie_search_append_ruby(search, match);
|
|
1609
|
+
RB_GC_GUARD(match);
|
|
1610
|
+
return true;
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
static void
|
|
1614
|
+
trie_merge_named(VALUE named, VALUE key, VALUE value)
|
|
1615
|
+
{
|
|
1616
|
+
VALUE previous = rb_hash_lookup2(named, key, Qundef);
|
|
1617
|
+
if (previous == Qundef) rb_hash_aset(named, key, value);
|
|
1618
|
+
else if (RB_TYPE_P(previous, T_ARRAY)) rb_ary_push(previous, value);
|
|
1619
|
+
else rb_hash_aset(named, key, rb_ary_new_from_args(2, previous, value));
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
typedef struct { VALUE destination; } trie_named_merge_t;
|
|
1623
|
+
|
|
1624
|
+
static int
|
|
1625
|
+
trie_merge_named_i(VALUE key, VALUE value, VALUE opaque)
|
|
1626
|
+
{
|
|
1627
|
+
trie_named_merge_t *merge = (trie_named_merge_t *)(uintptr_t)opaque;
|
|
1628
|
+
trie_merge_named(merge->destination, key, value);
|
|
1629
|
+
return ST_CONTINUE;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
static void
|
|
1633
|
+
trie_materialize_captures(trie_search_t *search, VALUE *captures, VALUE *named)
|
|
1634
|
+
{
|
|
1635
|
+
size_t index;
|
|
1636
|
+
*captures = rb_ary_new();
|
|
1637
|
+
*named = rb_hash_new();
|
|
1638
|
+
for (index = 0; index < search->event_count; index++) {
|
|
1639
|
+
trie_capture_event_t *event = &search->events[index];
|
|
1640
|
+
if (event->kind == TRIE_EVENT_NATIVE) {
|
|
1641
|
+
VALUE value = rb_str_subseq(search->input, event->begin, event->end - event->begin);
|
|
1642
|
+
rb_ary_push(*captures, value);
|
|
1643
|
+
trie_merge_named(*named, event->name, value);
|
|
1644
|
+
}
|
|
1645
|
+
else {
|
|
1646
|
+
VALUE match = RARRAY_AREF(search->matches, event->match_index);
|
|
1647
|
+
VALUE edge_captures = rb_funcall(match, id_captures, 0);
|
|
1648
|
+
VALUE edge_named = rb_funcall(match, id_named_captures, 0);
|
|
1649
|
+
trie_named_merge_t merge = {.destination = *named};
|
|
1650
|
+
rb_ary_concat(*captures, edge_captures);
|
|
1651
|
+
rb_hash_foreach(edge_named, trie_merge_named_i, (VALUE)(uintptr_t)&merge);
|
|
1652
|
+
RB_GC_GUARD(edge_captures);
|
|
1653
|
+
RB_GC_GUARD(edge_named);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
static void
|
|
1659
|
+
trie_seen_rebuild(trie_search_t *search, size_t capacity)
|
|
1660
|
+
{
|
|
1661
|
+
uint32_t *old = search->seen;
|
|
1662
|
+
VALUE old_scratch = search->seen_scratch;
|
|
1663
|
+
size_t old_capacity = search->seen_capacity;
|
|
1664
|
+
size_t source_length = search->seen_hashed ? old_capacity : search->seen_count;
|
|
1665
|
+
uint32_t *table = trie_scratch_allocate(&search->seen_building_scratch,
|
|
1666
|
+
capacity, sizeof(*table));
|
|
1667
|
+
size_t index;
|
|
1668
|
+
|
|
1669
|
+
/* The ensure cleanup owns both tables until the replacement is complete. */
|
|
1670
|
+
search->seen_building = table;
|
|
1671
|
+
for (index = 0; index < capacity; index++) {
|
|
1672
|
+
table[index] = UINT32_MAX;
|
|
1673
|
+
trie_search_work_checkpoint(search);
|
|
1674
|
+
}
|
|
1675
|
+
for (index = 0; index < source_length; index++) {
|
|
1676
|
+
uint32_t existing = old[index];
|
|
1677
|
+
size_t slot;
|
|
1678
|
+
if (search->seen_hashed && existing == UINT32_MAX) {
|
|
1679
|
+
trie_search_work_checkpoint(search);
|
|
1680
|
+
continue;
|
|
1681
|
+
}
|
|
1682
|
+
slot = ((uint64_t)existing * UINT64_C(11400714819323198485)) & (capacity - 1);
|
|
1683
|
+
while (table[slot] != UINT32_MAX) {
|
|
1684
|
+
slot = (slot + 1) & (capacity - 1);
|
|
1685
|
+
trie_search_work_checkpoint(search);
|
|
1686
|
+
}
|
|
1687
|
+
table[slot] = existing;
|
|
1688
|
+
trie_search_work_checkpoint(search);
|
|
1689
|
+
}
|
|
1690
|
+
search->seen = table;
|
|
1691
|
+
search->seen_capacity = capacity;
|
|
1692
|
+
search->seen_hashed = true;
|
|
1693
|
+
search->seen_building = NULL;
|
|
1694
|
+
search->seen_scratch = search->seen_building_scratch;
|
|
1695
|
+
search->seen_building_scratch = 0;
|
|
1696
|
+
trie_scratch_release(old_scratch);
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
static bool
|
|
1700
|
+
trie_seen(trie_search_t *search, uint32_t token)
|
|
1701
|
+
{
|
|
1702
|
+
size_t index;
|
|
1703
|
+
if (search->first_only) return false;
|
|
1704
|
+
if (!search->seen_hashed) {
|
|
1705
|
+
for (index = 0; index < search->seen_count; index++) {
|
|
1706
|
+
if (search->seen[index] == token) return true;
|
|
1707
|
+
}
|
|
1708
|
+
if (search->seen_count < search->seen_capacity) {
|
|
1709
|
+
search->seen[search->seen_count++] = token;
|
|
1710
|
+
return false;
|
|
1711
|
+
}
|
|
1712
|
+
trie_seen_rebuild(search, 32);
|
|
1713
|
+
}
|
|
1714
|
+
if (search->seen_count + 1 >= search->seen_capacity - search->seen_capacity / 4) {
|
|
1715
|
+
size_t capacity = search->seen_capacity * 2;
|
|
1716
|
+
if (capacity < search->seen_capacity || capacity > SIZE_MAX / sizeof(*search->seen)) rb_memerror();
|
|
1717
|
+
trie_seen_rebuild(search, capacity);
|
|
1718
|
+
}
|
|
1719
|
+
size_t slot = ((uint64_t)token * UINT64_C(11400714819323198485)) & (search->seen_capacity - 1);
|
|
1720
|
+
while (search->seen[slot] != UINT32_MAX) {
|
|
1721
|
+
if (search->seen[slot] == token) return true;
|
|
1722
|
+
slot = (slot + 1) & (search->seen_capacity - 1);
|
|
1723
|
+
trie_search_work_checkpoint(search);
|
|
1724
|
+
}
|
|
1725
|
+
search->seen[slot] = token;
|
|
1726
|
+
search->seen_count++;
|
|
1727
|
+
return false;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
static bool
|
|
1731
|
+
trie_emit_terminal(trie_search_t *search, trie_frame_t *frame)
|
|
1732
|
+
{
|
|
1733
|
+
trie_node_t *node = frame->node;
|
|
1734
|
+
size_t index;
|
|
1735
|
+
for (index = 0; index < node->token_count; index++) {
|
|
1736
|
+
uint32_t id = node->tokens[index];
|
|
1737
|
+
VALUE captures;
|
|
1738
|
+
VALUE named;
|
|
1739
|
+
VALUE suffix;
|
|
1740
|
+
VALUE token;
|
|
1741
|
+
VALUE result;
|
|
1742
|
+
trie_search_work_checkpoint(search);
|
|
1743
|
+
if (trie_seen(search, id)) continue;
|
|
1744
|
+
trie_materialize_captures(search, &captures, &named);
|
|
1745
|
+
suffix = rb_str_subseq(search->input, frame->cursor, search->input_length - frame->cursor);
|
|
1746
|
+
token = RARRAY_AREF(search->trie->tokens, (long)id);
|
|
1747
|
+
if (search->stream) {
|
|
1748
|
+
rb_yield_values(4, token, captures, named, suffix);
|
|
1749
|
+
continue;
|
|
1750
|
+
}
|
|
1751
|
+
result = rb_ary_new_from_args(4, token, captures, named, suffix);
|
|
1752
|
+
if (search->first_only) {
|
|
1753
|
+
search->first = result;
|
|
1754
|
+
return true;
|
|
1755
|
+
}
|
|
1756
|
+
rb_ary_push(search->results, result);
|
|
1757
|
+
}
|
|
1758
|
+
return false;
|
|
1759
|
+
}
|
|
1760
|
+
|
|
1761
|
+
static VALUE
|
|
1762
|
+
trie_search_body(VALUE opaque)
|
|
1763
|
+
{
|
|
1764
|
+
trie_search_t *search = (trie_search_t *)(uintptr_t)opaque;
|
|
1765
|
+
trie_search_push_frame(search, search->trie->root, 0, 0);
|
|
1766
|
+
while (search->frame_count) {
|
|
1767
|
+
trie_frame_t *frame = &search->frames[search->frame_count - 1];
|
|
1768
|
+
trie_search_work_checkpoint(search);
|
|
1769
|
+
if (frame->phase == 0) {
|
|
1770
|
+
bool descended = false;
|
|
1771
|
+
if (frame->node->static_index) {
|
|
1772
|
+
while (frame->static_index) {
|
|
1773
|
+
trie_static_edge_t *edge = &frame->node->static_edges[frame->static_index - 1];
|
|
1774
|
+
long label_length = RSTRING_LEN(edge->label);
|
|
1775
|
+
frame->static_index = edge->next_same_byte;
|
|
1776
|
+
if (!trie_static_match(search->input, frame->cursor, edge,
|
|
1777
|
+
label_length, search->input_encoding)) continue;
|
|
1778
|
+
trie_search_push_frame(search, edge->child, frame->cursor + label_length,
|
|
1779
|
+
search->event_count);
|
|
1780
|
+
descended = true;
|
|
1781
|
+
break;
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
else {
|
|
1785
|
+
while (frame->static_index < frame->node->static_count) {
|
|
1786
|
+
trie_static_edge_t *edge = &frame->node->static_edges[frame->static_index++];
|
|
1787
|
+
long label_length = RSTRING_LEN(edge->label);
|
|
1788
|
+
if (!trie_static_match(search->input, frame->cursor, edge,
|
|
1789
|
+
label_length, search->input_encoding)) continue;
|
|
1790
|
+
trie_search_push_frame(search, edge->child, frame->cursor + label_length,
|
|
1791
|
+
search->event_count);
|
|
1792
|
+
descended = true;
|
|
1793
|
+
break;
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
if (descended) continue;
|
|
1797
|
+
frame->phase = 1;
|
|
1798
|
+
}
|
|
1799
|
+
if (frame->phase == 1) {
|
|
1800
|
+
bool descended = false;
|
|
1801
|
+
while (frame->regexp_index < frame->node->regexp_count) {
|
|
1802
|
+
trie_regexp_edge_t *edge = &frame->node->regexp_edges[frame->regexp_index++];
|
|
1803
|
+
size_t restore = search->event_count;
|
|
1804
|
+
long end_cursor;
|
|
1805
|
+
if (!trie_regexp_match(edge, search, frame->cursor, &end_cursor)) {
|
|
1806
|
+
search->event_count = restore;
|
|
1807
|
+
continue;
|
|
1808
|
+
}
|
|
1809
|
+
trie_search_push_frame(search, edge->child, end_cursor, restore);
|
|
1810
|
+
descended = true;
|
|
1811
|
+
break;
|
|
1812
|
+
}
|
|
1813
|
+
if (descended) continue;
|
|
1814
|
+
frame->phase = 2;
|
|
1815
|
+
}
|
|
1816
|
+
if (frame->phase == 2) {
|
|
1817
|
+
frame->phase = 3;
|
|
1818
|
+
if ((search->peek || frame->cursor == search->input_length) &&
|
|
1819
|
+
trie_emit_terminal(search, frame)) break;
|
|
1820
|
+
}
|
|
1821
|
+
if (frame->phase == 3) {
|
|
1822
|
+
size_t restore = frame->restore_events;
|
|
1823
|
+
search->frame_count--;
|
|
1824
|
+
search->event_count = restore;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
if (search->stream) return search->self;
|
|
1828
|
+
return search->first_only ? search->first : search->results;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
static VALUE
|
|
1832
|
+
trie_search_cleanup(VALUE opaque)
|
|
1833
|
+
{
|
|
1834
|
+
trie_search_t *search = (trie_search_t *)(uintptr_t)opaque;
|
|
1835
|
+
trie_scratch_release(search->frame_scratch);
|
|
1836
|
+
trie_scratch_release(search->event_scratch);
|
|
1837
|
+
trie_scratch_release(search->seen_scratch);
|
|
1838
|
+
trie_scratch_release(search->seen_building_scratch);
|
|
1839
|
+
if (search->previous_backref != Qundef) rb_backref_set(search->previous_backref);
|
|
1840
|
+
return Qnil;
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
static VALUE
|
|
1844
|
+
trie_search(VALUE self, VALUE input, bool peek, bool first_only, bool stream)
|
|
1845
|
+
{
|
|
1846
|
+
trie_search_t search;
|
|
1847
|
+
VALUE stable_input;
|
|
1848
|
+
int encoding;
|
|
1849
|
+
int coderange;
|
|
1850
|
+
if (!RB_TYPE_P(input, T_STRING)) rb_raise(rb_eTypeError, "trie input must be a string");
|
|
1851
|
+
stable_input = rb_str_new_frozen(input);
|
|
1852
|
+
coderange = rb_enc_str_coderange(stable_input);
|
|
1853
|
+
if (coderange == ENC_CODERANGE_BROKEN) rb_raise(rb_eArgError, "trie input has invalid encoding");
|
|
1854
|
+
memset(&search, 0, offsetof(trie_search_t, inline_frames));
|
|
1855
|
+
search.self = self;
|
|
1856
|
+
search.input = stable_input;
|
|
1857
|
+
search.trie = trie_get(self);
|
|
1858
|
+
search.input_length = RSTRING_LEN(stable_input);
|
|
1859
|
+
search.frames = search.inline_frames;
|
|
1860
|
+
search.frame_capacity = TRIE_INLINE_FRAMES;
|
|
1861
|
+
search.events = search.inline_events;
|
|
1862
|
+
search.event_capacity = TRIE_INLINE_EVENTS;
|
|
1863
|
+
search.seen = search.inline_seen;
|
|
1864
|
+
search.seen_capacity = sizeof(search.inline_seen) / sizeof(*search.inline_seen);
|
|
1865
|
+
search.results = first_only || stream ? Qnil : rb_ary_new();
|
|
1866
|
+
search.matches = Qnil;
|
|
1867
|
+
search.previous_backref = search.trie->regexp_edge_count ? rb_backref_get() : Qundef;
|
|
1868
|
+
search.first = Qnil;
|
|
1869
|
+
search.peek = peek;
|
|
1870
|
+
search.first_only = first_only;
|
|
1871
|
+
search.stream = stream;
|
|
1872
|
+
encoding = rb_enc_get_index(stable_input);
|
|
1873
|
+
search.input_encoding = encoding;
|
|
1874
|
+
if (search.trie->program_count) {
|
|
1875
|
+
search.utf8_input = encoding == rb_utf8_encindex();
|
|
1876
|
+
search.ascii_input = coderange == ENC_CODERANGE_7BIT;
|
|
1877
|
+
search.native_segment_input = encoding == rb_utf8_encindex() ||
|
|
1878
|
+
encoding == rb_usascii_encindex() ||
|
|
1879
|
+
encoding == rb_ascii8bit_encindex();
|
|
1880
|
+
}
|
|
1881
|
+
VALUE result = rb_ensure(trie_search_body, (VALUE)(uintptr_t)&search,
|
|
1882
|
+
trie_search_cleanup, (VALUE)(uintptr_t)&search);
|
|
1883
|
+
RB_GC_GUARD(self);
|
|
1884
|
+
RB_GC_GUARD(input);
|
|
1885
|
+
RB_GC_GUARD(stable_input);
|
|
1886
|
+
RB_GC_GUARD(search.results);
|
|
1887
|
+
RB_GC_GUARD(search.matches);
|
|
1888
|
+
RB_GC_GUARD(search.previous_backref);
|
|
1889
|
+
RB_GC_GUARD(search.first);
|
|
1890
|
+
RB_GC_GUARD(search.frame_scratch);
|
|
1891
|
+
RB_GC_GUARD(search.event_scratch);
|
|
1892
|
+
RB_GC_GUARD(search.seen_scratch);
|
|
1893
|
+
RB_GC_GUARD(search.seen_building_scratch);
|
|
1894
|
+
return result;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
static VALUE trie_match(VALUE self, VALUE input) { return trie_search(self, input, false, true, false); }
|
|
1898
|
+
static VALUE trie_match_all(VALUE self, VALUE input) { return trie_search(self, input, false, false, false); }
|
|
1899
|
+
static VALUE trie_peek(VALUE self, VALUE input) { return trie_search(self, input, true, true, false); }
|
|
1900
|
+
static VALUE trie_peek_all(VALUE self, VALUE input) { return trie_search(self, input, true, false, false); }
|
|
1901
|
+
|
|
1902
|
+
static VALUE
|
|
1903
|
+
trie_each_match(VALUE self, VALUE input)
|
|
1904
|
+
{
|
|
1905
|
+
RETURN_ENUMERATOR(self, 1, &input);
|
|
1906
|
+
return trie_search(self, input, false, false, true);
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
static VALUE
|
|
1910
|
+
trie_each_peek(VALUE self, VALUE input)
|
|
1911
|
+
{
|
|
1912
|
+
RETURN_ENUMERATOR(self, 1, &input);
|
|
1913
|
+
return trie_search(self, input, true, false, true);
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
static VALUE
|
|
1917
|
+
trie_initialize_copy(VALUE self, VALUE other)
|
|
1918
|
+
{
|
|
1919
|
+
trie_t *destination;
|
|
1920
|
+
trie_t *source;
|
|
1921
|
+
VALUE owner;
|
|
1922
|
+
TypedData_Get_Struct(self, trie_t, &trie_type, destination);
|
|
1923
|
+
TypedData_Get_Struct(other, trie_t, &trie_type, source);
|
|
1924
|
+
if (!source->initialized) rb_raise(rb_eTypeError, "cannot copy an uninitialized trie");
|
|
1925
|
+
if (destination->initialized) rb_raise(rb_eRuntimeError, "cannot overwrite an initialized trie");
|
|
1926
|
+
rb_obj_init_copy(self, other);
|
|
1927
|
+
owner = NIL_P(source->owner) ? other : source->owner;
|
|
1928
|
+
/* A fresh allocation has no graph yet. Keep the root wrapper alive rather
|
|
1929
|
+
* than sharing ownership of raw pointers. */
|
|
1930
|
+
trie_release_contents(destination);
|
|
1931
|
+
memset(destination, 0, sizeof(*destination));
|
|
1932
|
+
destination->tokens = Qnil;
|
|
1933
|
+
destination->owner = owner;
|
|
1934
|
+
destination->initialized = true;
|
|
1935
|
+
rb_obj_freeze(self);
|
|
1936
|
+
containers_publish_native_with_references(self, trie_validate_references);
|
|
1937
|
+
RB_GC_GUARD(other);
|
|
1938
|
+
RB_GC_GUARD(owner);
|
|
1939
|
+
return self;
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1942
|
+
static VALUE
|
|
1943
|
+
trie_stats(VALUE self)
|
|
1944
|
+
{
|
|
1945
|
+
trie_t *trie = trie_get(self);
|
|
1946
|
+
VALUE stats = rb_hash_new();
|
|
1947
|
+
rb_hash_aset(stats, ID2SYM(rb_intern("nodes")), SIZET2NUM(trie->node_count));
|
|
1948
|
+
rb_hash_aset(stats, ID2SYM(rb_intern("static_edges")), SIZET2NUM(trie->static_edge_count));
|
|
1949
|
+
rb_hash_aset(stats, ID2SYM(rb_intern("static_index_nodes")), SIZET2NUM(trie->static_index_count));
|
|
1950
|
+
rb_hash_aset(stats, ID2SYM(rb_intern("static_index_bytes")),
|
|
1951
|
+
SIZET2NUM(trie->static_index_bytes));
|
|
1952
|
+
rb_hash_aset(stats, ID2SYM(rb_intern("dynamic_edges")), SIZET2NUM(trie->regexp_edge_count));
|
|
1953
|
+
rb_hash_aset(stats, ID2SYM(rb_intern("native_programs")), SIZET2NUM(trie->program_count));
|
|
1954
|
+
rb_hash_aset(stats, ID2SYM(rb_intern("native_bytes")), SIZET2NUM(trie_memsize(trie)));
|
|
1955
|
+
return stats;
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
#ifdef FARCE_TRIE_FAILURE_INJECTION
|
|
1959
|
+
static VALUE
|
|
1960
|
+
trie_failure_after(VALUE klass, VALUE count)
|
|
1961
|
+
{
|
|
1962
|
+
(void)klass;
|
|
1963
|
+
if (NIL_P(count)) trie_failure_countdown = -1;
|
|
1964
|
+
else {
|
|
1965
|
+
long value = NUM2LONG(count);
|
|
1966
|
+
if (value < 0) rb_raise(rb_eArgError, "failure countdown must be nonnegative or nil");
|
|
1967
|
+
trie_failure_countdown = value;
|
|
1968
|
+
}
|
|
1969
|
+
return count;
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
static VALUE
|
|
1973
|
+
trie_interrupt_queue_set(VALUE klass, VALUE queue)
|
|
1974
|
+
{
|
|
1975
|
+
(void)klass;
|
|
1976
|
+
if (!NIL_P(queue)) {
|
|
1977
|
+
Check_Type(queue, T_ARRAY);
|
|
1978
|
+
if (RARRAY_LEN(queue) != 2) rb_raise(rb_eArgError, "interrupt queues must contain ready and release");
|
|
1979
|
+
}
|
|
1980
|
+
trie_interrupt_queue = queue;
|
|
1981
|
+
return queue;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
static VALUE
|
|
1985
|
+
trie_search_scratch_count(VALUE klass)
|
|
1986
|
+
{
|
|
1987
|
+
(void)klass;
|
|
1988
|
+
return UINT2NUM(RUBY_ATOMIC_LOAD(trie_scratch_live_count));
|
|
1989
|
+
}
|
|
1990
|
+
#endif
|
|
1991
|
+
|
|
1992
|
+
void
|
|
1993
|
+
containers_init_trie(VALUE namespace)
|
|
1994
|
+
{
|
|
1995
|
+
VALUE farce = rb_const_get(rb_cObject, rb_intern("Farce"));
|
|
1996
|
+
VALUE strict = rb_const_get(farce, rb_intern("Strict"));
|
|
1997
|
+
(void)namespace;
|
|
1998
|
+
cTrie = rb_define_class_under(strict, "Trie", rb_cObject);
|
|
1999
|
+
rb_define_alloc_func(cTrie, trie_allocate);
|
|
2000
|
+
rb_define_private_method(cTrie, "initialize", trie_initialize, 2);
|
|
2001
|
+
rb_define_private_method(cTrie, "initialize_copy", trie_initialize_copy, 1);
|
|
2002
|
+
rb_define_method(cTrie, "match", trie_match, 1);
|
|
2003
|
+
rb_define_method(cTrie, "match_all", trie_match_all, 1);
|
|
2004
|
+
rb_define_method(cTrie, "peek", trie_peek, 1);
|
|
2005
|
+
rb_define_method(cTrie, "peek_all", trie_peek_all, 1);
|
|
2006
|
+
rb_define_method(cTrie, "each_match", trie_each_match, 1);
|
|
2007
|
+
rb_define_method(cTrie, "each_peek", trie_each_peek, 1);
|
|
2008
|
+
rb_define_method(cTrie, "stats", trie_stats, 0);
|
|
2009
|
+
#ifdef FARCE_TRIE_FAILURE_INJECTION
|
|
2010
|
+
rb_define_private_method(rb_singleton_class(cTrie), "__native_failure_after=", trie_failure_after, 1);
|
|
2011
|
+
rb_global_variable(&trie_interrupt_queue);
|
|
2012
|
+
rb_define_private_method(rb_singleton_class(cTrie), "__native_interrupt_queue=", trie_interrupt_queue_set, 1);
|
|
2013
|
+
rb_define_private_method(rb_singleton_class(cTrie), "__native_search_scratch_count", trie_search_scratch_count, 0);
|
|
2014
|
+
#endif
|
|
2015
|
+
|
|
2016
|
+
id_captures = rb_intern("captures");
|
|
2017
|
+
id_named_captures = rb_intern("named_captures");
|
|
2018
|
+
id_timeout = rb_intern("timeout");
|
|
2019
|
+
regexp_timeout_supported = rb_respond_to(rb_cRegexp, id_timeout);
|
|
2020
|
+
}
|