farce 0.0.1.alpha2-x86-linux-musl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (423) hide show
  1. checksums.yaml +7 -0
  2. data/CODE_OF_CONDUCT.md +26 -0
  3. data/CONTRIBUTING.md +71 -0
  4. data/MIT-LICENSE +20 -0
  5. data/README.md +1524 -0
  6. data/SECURITY.md +10 -0
  7. data/docs/benchmarks.md +185 -0
  8. data/docs/gems/dry-types.md +290 -0
  9. data/docs/gems/msgpack.md +70 -0
  10. data/docs/gems/ractor-shim.md +63 -0
  11. data/docs/modes.md +628 -0
  12. data/docs/scopes.md +649 -0
  13. data/docs/variants.md +346 -0
  14. data/ext/ext_helper.rb +20 -0
  15. data/ext/farce/README.md +21 -0
  16. data/ext/farce/atom.c +1023 -0
  17. data/ext/farce/bounded_map.c +1683 -0
  18. data/ext/farce/containers.h +77 -0
  19. data/ext/farce/counter.c +399 -0
  20. data/ext/farce/darwin.c +100 -0
  21. data/ext/farce/depend +12 -0
  22. data/ext/farce/dict.c +1523 -0
  23. data/ext/farce/dict.h +152 -0
  24. data/ext/farce/drivers.c +237 -0
  25. data/ext/farce/exchanger.c +299 -0
  26. data/ext/farce/extconf.rb +99 -0
  27. data/ext/farce/farce.c +359 -0
  28. data/ext/farce/flag.c +288 -0
  29. data/ext/farce/io.c +338 -0
  30. data/ext/farce/lock.c +510 -0
  31. data/ext/farce/map.c +2240 -0
  32. data/ext/farce/priority_queue.c +2056 -0
  33. data/ext/farce/queue.c +1059 -0
  34. data/ext/farce/reactor.c +820 -0
  35. data/ext/farce/reactor.h +103 -0
  36. data/ext/farce/shareable.h +31 -0
  37. data/ext/farce/signal.c +350 -0
  38. data/ext/farce/transaction.c +354 -0
  39. data/ext/farce/transaction.h +40 -0
  40. data/ext/farce/tree_map.c +1953 -0
  41. data/ext/farce/trie.c +2020 -0
  42. data/ext/farce/unshareable.c +155 -0
  43. data/ext/farce/unshared_io_pool.h +234 -0
  44. data/ext/farce/unshared_signal.c +263 -0
  45. data/ext/farce/unshared_wait.h +193 -0
  46. data/ext/farce/unsupported.c +6 -0
  47. data/ext/farce/vector.c +1195 -0
  48. data/ext/farce/weak_map.c +1714 -0
  49. data/ext/java/org/farce/BoundedMap.java +394 -0
  50. data/ext/java/org/farce/FiberScheduler.java +141 -0
  51. data/ext/java/org/farce/PriorityKey.java +45 -0
  52. data/ext/java/org/farce/PriorityQueue.java +373 -0
  53. data/ext/java/org/farce/QueueSignal.java +28 -0
  54. data/ext/rebind/README.md +14 -0
  55. data/ext/rebind/extconf.rb +10 -0
  56. data/ext/rebind/rebind.c +186 -0
  57. data/lib/farce/_yard/internal.rb +13 -0
  58. data/lib/farce/_yard/macros.rb +61 -0
  59. data/lib/farce/_yard/ractor.rb +46 -0
  60. data/lib/farce/abstract/atom.rb +186 -0
  61. data/lib/farce/abstract/bounded_map.rb +232 -0
  62. data/lib/farce/abstract/collection.rb +151 -0
  63. data/lib/farce/abstract/concurrent_map.rb +381 -0
  64. data/lib/farce/abstract/counter.rb +193 -0
  65. data/lib/farce/abstract/duplicable_map.rb +229 -0
  66. data/lib/farce/abstract/exchanger.rb +26 -0
  67. data/lib/farce/abstract/flag.rb +71 -0
  68. data/lib/farce/abstract/lazy.rb +115 -0
  69. data/lib/farce/abstract/lease.rb +104 -0
  70. data/lib/farce/abstract/lease_map.rb +261 -0
  71. data/lib/farce/abstract/lease_pool.rb +91 -0
  72. data/lib/farce/abstract/lfu_map.rb +20 -0
  73. data/lib/farce/abstract/lru_map.rb +25 -0
  74. data/lib/farce/abstract/map.rb +345 -0
  75. data/lib/farce/abstract/molecule.rb +245 -0
  76. data/lib/farce/abstract/port.rb +74 -0
  77. data/lib/farce/abstract/priority_queue.rb +117 -0
  78. data/lib/farce/abstract/queue.rb +269 -0
  79. data/lib/farce/abstract/scheduler.rb +111 -0
  80. data/lib/farce/abstract/set.rb +910 -0
  81. data/lib/farce/abstract/sorted_set.rb +172 -0
  82. data/lib/farce/abstract/timer_queue.rb +136 -0
  83. data/lib/farce/abstract/tree_map.rb +269 -0
  84. data/lib/farce/abstract/value.rb +68 -0
  85. data/lib/farce/abstract/vector.rb +1126 -0
  86. data/lib/farce/abstract/weak_atom.rb +32 -0
  87. data/lib/farce/abstract/weak_key_map.rb +12 -0
  88. data/lib/farce/abstract/weak_map.rb +13 -0
  89. data/lib/farce/abstract/weak_set.rb +12 -0
  90. data/lib/farce/abstract/weak_value_map.rb +12 -0
  91. data/lib/farce/abstract.rb +17 -0
  92. data/lib/farce/atom.rb +293 -0
  93. data/lib/farce/class_mirror.rb +87 -0
  94. data/lib/farce/clock.rb +121 -0
  95. data/lib/farce/config.rb +229 -0
  96. data/lib/farce/counter.rb +71 -0
  97. data/lib/farce/deduper.rb +122 -0
  98. data/lib/farce/engine/jruby/bounded_map.rb +314 -0
  99. data/lib/farce/engine/jruby/fiber_scheduler.jar +0 -0
  100. data/lib/farce/engine/jruby/fiber_scheduler.rb +119 -0
  101. data/lib/farce/engine/jruby/lease_waiting.rb +19 -0
  102. data/lib/farce/engine/jruby/map.rb +505 -0
  103. data/lib/farce/engine/jruby/mutable_numeric_copy.rb +42 -0
  104. data/lib/farce/engine/jruby/signal.rb +147 -0
  105. data/lib/farce/engine/jruby.rb +63 -0
  106. data/lib/farce/engine/jvm/concurrent_weak_registry.rb +55 -0
  107. data/lib/farce/engine/jvm/counter.rb +102 -0
  108. data/lib/farce/engine/jvm/extension.rb +32 -0
  109. data/lib/farce/engine/jvm/farce.jar +0 -0
  110. data/lib/farce/engine/jvm/flag.rb +79 -0
  111. data/lib/farce/engine/jvm/priority_queue.rb +217 -0
  112. data/lib/farce/engine/jvm/tree_map.rb +350 -0
  113. data/lib/farce/engine/jvm/types.rb +180 -0
  114. data/lib/farce/engine/jvm.rb +19 -0
  115. data/lib/farce/engine/ruby/3.4/farce.so +0 -0
  116. data/lib/farce/engine/ruby/3.4/fiber_scheduler.rb +20 -0
  117. data/lib/farce/engine/ruby/3.4/port.rb +186 -0
  118. data/lib/farce/engine/ruby/3.4/ractor_methods.rb +26 -0
  119. data/lib/farce/engine/ruby/3.4/ractor_selector.rb +92 -0
  120. data/lib/farce/engine/ruby/3.4/rebind.so +0 -0
  121. data/lib/farce/engine/ruby/3.4/vault.rb +56 -0
  122. data/lib/farce/engine/ruby/4.0/farce.so +0 -0
  123. data/lib/farce/engine/ruby/4.0/port.rb +19 -0
  124. data/lib/farce/engine/ruby/4.0/ractor_methods.rb +20 -0
  125. data/lib/farce/engine/ruby/4.0/ractor_selector.rb +108 -0
  126. data/lib/farce/engine/ruby/4.0/rebind.so +0 -0
  127. data/lib/farce/engine/ruby/4.0/vault.rb +74 -0
  128. data/lib/farce/engine/ruby/4.1/port.rb +21 -0
  129. data/lib/farce/engine/ruby/4.1/ractor_methods.rb +22 -0
  130. data/lib/farce/engine/ruby/4.1/ractor_selector.rb +25 -0
  131. data/lib/farce/engine/ruby/4.1/vault.rb +5 -0
  132. data/lib/farce/engine/ruby/fiber_scheduler.rb +32 -0
  133. data/lib/farce/engine/ruby/key_lock_map.rb +28 -0
  134. data/lib/farce/engine/ruby/shared/lease.rb +26 -0
  135. data/lib/farce/engine/ruby/shared/lease_pool.rb +24 -0
  136. data/lib/farce/engine/ruby/shared/main_scheduler.rb +9 -0
  137. data/lib/farce/engine/ruby/shared/parallel_scheduler.rb +9 -0
  138. data/lib/farce/engine/ruby/shared/proxy_owner.rb +31 -0
  139. data/lib/farce/engine/ruby/shared/ractor_methods.rb +34 -0
  140. data/lib/farce/engine/ruby/shared/ractor_selector.rb +328 -0
  141. data/lib/farce/engine/ruby/shared/strict_map.rb +13 -0
  142. data/lib/farce/engine/ruby/shared/unshared_vector.rb +14 -0
  143. data/lib/farce/engine/ruby/shared/vault.rb +144 -0
  144. data/lib/farce/engine/ruby/shared/vault_weak_map.rb +336 -0
  145. data/lib/farce/engine/ruby/shared/weak_atom.rb +54 -0
  146. data/lib/farce/engine/ruby/shared/weak_map.rb +413 -0
  147. data/lib/farce/engine/ruby.rb +130 -0
  148. data/lib/farce/engine/shared/atom.rb +10 -0
  149. data/lib/farce/engine/shared/exchanger.rb +130 -0
  150. data/lib/farce/engine/shared/identity_key.rb +22 -0
  151. data/lib/farce/engine/shared/lease.rb +10 -0
  152. data/lib/farce/engine/shared/lease_pool.rb +10 -0
  153. data/lib/farce/engine/shared/main_scheduler.rb +20 -0
  154. data/lib/farce/engine/shared/map_key_coordination.rb +226 -0
  155. data/lib/farce/engine/shared/parallel_scheduler.rb +9 -0
  156. data/lib/farce/engine/shared/port.rb +44 -0
  157. data/lib/farce/engine/shared/portable_bounded_map.rb +619 -0
  158. data/lib/farce/engine/shared/proxy_owner.rb +43 -0
  159. data/lib/farce/engine/shared/queue.rb +233 -0
  160. data/lib/farce/engine/shared/ractor_methods.rb +73 -0
  161. data/lib/farce/engine/shared/rebindable.rb +15 -0
  162. data/lib/farce/engine/shared/strict_atom.rb +73 -0
  163. data/lib/farce/engine/shared/strict_map.rb +117 -0
  164. data/lib/farce/engine/shared/strict_queue_values.rb +27 -0
  165. data/lib/farce/engine/shared/strict_tree_map.rb +56 -0
  166. data/lib/farce/engine/shared/transaction_map_backend.rb +20 -0
  167. data/lib/farce/engine/shared/trie.rb +317 -0
  168. data/lib/farce/engine/shared/trie_builder.rb +199 -0
  169. data/lib/farce/engine/shared/unshareable.rb +14 -0
  170. data/lib/farce/engine/shared/unshared_atom.rb +256 -0
  171. data/lib/farce/engine/shared/unshared_priority_queue.rb +9 -0
  172. data/lib/farce/engine/shared/unshared_queue.rb +18 -0
  173. data/lib/farce/engine/shared/unshared_signal.rb +21 -0
  174. data/lib/farce/engine/shared/unshared_vector.rb +377 -0
  175. data/lib/farce/engine/shared/unshared_weak_atom.rb +30 -0
  176. data/lib/farce/engine/shared/unshared_weak_map.rb +445 -0
  177. data/lib/farce/engine/shared/vault.rb +31 -0
  178. data/lib/farce/engine/shared/vector.rb +83 -0
  179. data/lib/farce/engine/shared/weak_atom/base.rb +189 -0
  180. data/lib/farce/engine/shared/weak_atom.rb +20 -0
  181. data/lib/farce/engine/shared/weak_map/cell.rb +99 -0
  182. data/lib/farce/engine/shared/weak_map/index.rb +249 -0
  183. data/lib/farce/engine/shared/weak_map/lock.rb +80 -0
  184. data/lib/farce/engine/shared/weak_map/reference.rb +33 -0
  185. data/lib/farce/engine/shared.rb +63 -0
  186. data/lib/farce/engine/truffleruby/fiber_scheduler.rb +13 -0
  187. data/lib/farce/engine/truffleruby/lock.rb +121 -0
  188. data/lib/farce/engine/truffleruby/map.rb +656 -0
  189. data/lib/farce/engine/truffleruby/native/counter.rb +105 -0
  190. data/lib/farce/engine/truffleruby/native/flag.rb +77 -0
  191. data/lib/farce/engine/truffleruby/native/ordered_array_support.rb +67 -0
  192. data/lib/farce/engine/truffleruby/native/priority_queue.rb +388 -0
  193. data/lib/farce/engine/truffleruby/native/tree_map.rb +51 -0
  194. data/lib/farce/engine/truffleruby/native/unsafe_tree_map.rb +350 -0
  195. data/lib/farce/engine/truffleruby/signal.rb +77 -0
  196. data/lib/farce/engine/truffleruby.rb +148 -0
  197. data/lib/farce/envelope.rb +335 -0
  198. data/lib/farce/error.rb +35 -0
  199. data/lib/farce/exchanger.rb +41 -0
  200. data/lib/farce/flag.rb +27 -0
  201. data/lib/farce/integrations/active_support/blank.rb +68 -0
  202. data/lib/farce/integrations/active_support/clock.rb +17 -0
  203. data/lib/farce/integrations/active_support/duplicable.rb +78 -0
  204. data/lib/farce/integrations/active_support/map.rb +175 -0
  205. data/lib/farce/integrations/active_support/set.rb +60 -0
  206. data/lib/farce/integrations/active_support/value_serialization.rb +17 -0
  207. data/lib/farce/integrations/active_support/vector.rb +231 -0
  208. data/lib/farce/integrations/active_support.rb +9 -0
  209. data/lib/farce/integrations/activesupport.rb +5 -0
  210. data/lib/farce/integrations/bson.rb +108 -0
  211. data/lib/farce/integrations/cbor.rb +51 -0
  212. data/lib/farce/integrations/concurrent.rb +112 -0
  213. data/lib/farce/integrations/dry-types.rb +5 -0
  214. data/lib/farce/integrations/dry_types.rb +694 -0
  215. data/lib/farce/integrations/json.rb +7 -0
  216. data/lib/farce/integrations/msgpack.rb +112 -0
  217. data/lib/farce/integrations/oj.rb +69 -0
  218. data/lib/farce/integrations/psych.rb +191 -0
  219. data/lib/farce/integrations/ractor-sharing.rb +5 -0
  220. data/lib/farce/integrations/ractor-tmvar.rb +5 -0
  221. data/lib/farce/integrations/ractor_sharing.rb +172 -0
  222. data/lib/farce/integrations/ractor_tmvar.rb +64 -0
  223. data/lib/farce/integrations/shared/to_json.rb +42 -0
  224. data/lib/farce/integrations/sorted_set.rb +11 -0
  225. data/lib/farce/integrations/weakref.rb +38 -0
  226. data/lib/farce/integrations/yajl.rb +10 -0
  227. data/lib/farce/integrations.rb +156 -0
  228. data/lib/farce/internal/_frozen_config.rb +11 -0
  229. data/lib/farce/internal/autoloads.rb +47 -0
  230. data/lib/farce/internal/blocking_priority_queue.rb +122 -0
  231. data/lib/farce/internal/converter.rb +106 -0
  232. data/lib/farce/internal/copyable.rb +31 -0
  233. data/lib/farce/internal/delegation.rb +20 -0
  234. data/lib/farce/internal/external_transaction.rb +59 -0
  235. data/lib/farce/internal/fake_ractor.rb +179 -0
  236. data/lib/farce/internal/freeze.rb +118 -0
  237. data/lib/farce/internal/inspect.rb +157 -0
  238. data/lib/farce/internal/key_lock_map.rb +29 -0
  239. data/lib/farce/internal/key_normalizer.rb +289 -0
  240. data/lib/farce/internal/lease_initialization.rb +115 -0
  241. data/lib/farce/internal/lease_map.rb +441 -0
  242. data/lib/farce/internal/lease_pool_state.rb +228 -0
  243. data/lib/farce/internal/lease_state.rb +342 -0
  244. data/lib/farce/internal/lease_waiting.rb +13 -0
  245. data/lib/farce/internal/managed_queue.rb +26 -0
  246. data/lib/farce/internal/map_value_modes.rb +227 -0
  247. data/lib/farce/internal/marshal_support.rb +227 -0
  248. data/lib/farce/internal/mixin.rb +20 -0
  249. data/lib/farce/internal/mutable_ordered_key_lock_map.rb +63 -0
  250. data/lib/farce/internal/noncopyable.rb +17 -0
  251. data/lib/farce/internal/ordered_key_lock_map.rb +69 -0
  252. data/lib/farce/internal/pool_supervisor.rb +41 -0
  253. data/lib/farce/internal/pool_worker.rb +61 -0
  254. data/lib/farce/internal/portable_transaction/reservation_entry.rb +29 -0
  255. data/lib/farce/internal/portable_transaction/strong_map_entry.rb +137 -0
  256. data/lib/farce/internal/portable_transaction/strong_map_size_entry.rb +44 -0
  257. data/lib/farce/internal/portable_transaction/tree_entry.rb +64 -0
  258. data/lib/farce/internal/portable_transaction.rb +164 -0
  259. data/lib/farce/internal/proxy_owner_notifications.rb +29 -0
  260. data/lib/farce/internal/reservation_waiting.rb +24 -0
  261. data/lib/farce/internal/scheduled_task.rb +48 -0
  262. data/lib/farce/internal/scheduler_io.rb +171 -0
  263. data/lib/farce/internal/scheduler_lifecycle.rb +314 -0
  264. data/lib/farce/internal/select_scheduler.rb +235 -0
  265. data/lib/farce/internal/storage.rb +162 -0
  266. data/lib/farce/internal/strict_lease.rb +30 -0
  267. data/lib/farce/internal/strict_lease_map.rb +20 -0
  268. data/lib/farce/internal/strict_lease_pool.rb +29 -0
  269. data/lib/farce/internal/thread_pool.rb +166 -0
  270. data/lib/farce/internal/transaction_conflict.rb +9 -0
  271. data/lib/farce/internal/transaction_freeze_guard.rb +30 -0
  272. data/lib/farce/internal/transaction_map_snapshot.rb +123 -0
  273. data/lib/farce/internal/undefined.rb +22 -0
  274. data/lib/farce/internal/unshared_lease.rb +29 -0
  275. data/lib/farce/internal/unshared_lease_pool.rb +34 -0
  276. data/lib/farce/internal/unshared_queue_waiting.rb +28 -0
  277. data/lib/farce/internal/value_serialization.rb +13 -0
  278. data/lib/farce/internal/weak_map_value_modes.rb +58 -0
  279. data/lib/farce/internal/weak_mode_manager.rb +26 -0
  280. data/lib/farce/internal.rb +129 -0
  281. data/lib/farce/lazy.rb +100 -0
  282. data/lib/farce/lazy_ref.rb +40 -0
  283. data/lib/farce/lease.rb +28 -0
  284. data/lib/farce/lease_map.rb +34 -0
  285. data/lib/farce/lease_pool.rb +29 -0
  286. data/lib/farce/lfu_map.rb +53 -0
  287. data/lib/farce/local/atom.rb +23 -0
  288. data/lib/farce/local/counter.rb +88 -0
  289. data/lib/farce/local/flag.rb +65 -0
  290. data/lib/farce/local/lazy.rb +38 -0
  291. data/lib/farce/local/lazy_ref.rb +36 -0
  292. data/lib/farce/local/lease.rb +77 -0
  293. data/lib/farce/local/lease_map.rb +80 -0
  294. data/lib/farce/local/lease_pool.rb +38 -0
  295. data/lib/farce/local/lfu_map.rb +55 -0
  296. data/lib/farce/local/lru_map.rb +62 -0
  297. data/lib/farce/local/map.rb +47 -0
  298. data/lib/farce/local/molecule.rb +45 -0
  299. data/lib/farce/local/priority_queue.rb +53 -0
  300. data/lib/farce/local/queue.rb +32 -0
  301. data/lib/farce/local/scoped.rb +166 -0
  302. data/lib/farce/local/set.rb +18 -0
  303. data/lib/farce/local/sorted_set.rb +50 -0
  304. data/lib/farce/local/timer_queue.rb +41 -0
  305. data/lib/farce/local/tree_map.rb +47 -0
  306. data/lib/farce/local/vector.rb +30 -0
  307. data/lib/farce/local/weak_atom.rb +23 -0
  308. data/lib/farce/local/weak_key_map.rb +39 -0
  309. data/lib/farce/local/weak_map.rb +39 -0
  310. data/lib/farce/local/weak_set.rb +18 -0
  311. data/lib/farce/local/weak_value_map.rb +39 -0
  312. data/lib/farce/local.rb +35 -0
  313. data/lib/farce/lock.rb +54 -0
  314. data/lib/farce/lru_map.rb +63 -0
  315. data/lib/farce/map.rb +58 -0
  316. data/lib/farce/mode_manager.rb +142 -0
  317. data/lib/farce/molecule.rb +60 -0
  318. data/lib/farce/mutable.rb +171 -0
  319. data/lib/farce/pool.rb +332 -0
  320. data/lib/farce/port.rb +134 -0
  321. data/lib/farce/priority_queue.rb +70 -0
  322. data/lib/farce/proxy/register.rb +101 -0
  323. data/lib/farce/proxy/supervisor.rb +137 -0
  324. data/lib/farce/proxy/wrapper.rb +49 -0
  325. data/lib/farce/proxy.rb +291 -0
  326. data/lib/farce/queue.rb +60 -0
  327. data/lib/farce/ractor.rb +269 -0
  328. data/lib/farce/read_write_lock.rb +256 -0
  329. data/lib/farce/reference.rb +158 -0
  330. data/lib/farce/resolv/dns.rb +42 -0
  331. data/lib/farce/resolv.rb +85 -0
  332. data/lib/farce/scheduler.rb +536 -0
  333. data/lib/farce/set.rb +39 -0
  334. data/lib/farce/shareable.rb +150 -0
  335. data/lib/farce/signal.rb +97 -0
  336. data/lib/farce/sorted_set.rb +36 -0
  337. data/lib/farce/strict/atom.rb +34 -0
  338. data/lib/farce/strict/counter.rb +10 -0
  339. data/lib/farce/strict/exchanger.rb +25 -0
  340. data/lib/farce/strict/flag.rb +10 -0
  341. data/lib/farce/strict/lazy.rb +26 -0
  342. data/lib/farce/strict/lazy_ref.rb +26 -0
  343. data/lib/farce/strict/lease.rb +27 -0
  344. data/lib/farce/strict/lease_map.rb +33 -0
  345. data/lib/farce/strict/lease_pool.rb +28 -0
  346. data/lib/farce/strict/lfu_map.rb +27 -0
  347. data/lib/farce/strict/lru_map.rb +28 -0
  348. data/lib/farce/strict/map.rb +54 -0
  349. data/lib/farce/strict/molecule.rb +16 -0
  350. data/lib/farce/strict/port.rb +40 -0
  351. data/lib/farce/strict/priority_queue.rb +20 -0
  352. data/lib/farce/strict/queue.rb +17 -0
  353. data/lib/farce/strict/set.rb +14 -0
  354. data/lib/farce/strict/sorted_set.rb +19 -0
  355. data/lib/farce/strict/timer_queue.rb +13 -0
  356. data/lib/farce/strict/tree_map.rb +27 -0
  357. data/lib/farce/strict/vector.rb +24 -0
  358. data/lib/farce/strict/weak_atom.rb +31 -0
  359. data/lib/farce/strict/weak_key_map.rb +49 -0
  360. data/lib/farce/strict/weak_map.rb +49 -0
  361. data/lib/farce/strict/weak_set.rb +14 -0
  362. data/lib/farce/strict/weak_value_map.rb +48 -0
  363. data/lib/farce/strict.rb +28 -0
  364. data/lib/farce/system.rb +40 -0
  365. data/lib/farce/thread_scheduler.rb +70 -0
  366. data/lib/farce/timer_queue.rb +66 -0
  367. data/lib/farce/transaction/atom.rb +73 -0
  368. data/lib/farce/transaction/map.rb +25 -0
  369. data/lib/farce/transaction/map_operations.rb +143 -0
  370. data/lib/farce/transaction/molecule.rb +90 -0
  371. data/lib/farce/transaction/mutable.rb +61 -0
  372. data/lib/farce/transaction/set.rb +17 -0
  373. data/lib/farce/transaction/set_operations.rb +48 -0
  374. data/lib/farce/transaction/sorted_set.rb +18 -0
  375. data/lib/farce/transaction/tree_map.rb +78 -0
  376. data/lib/farce/transaction/vector.rb +139 -0
  377. data/lib/farce/transaction/wrapper.rb +202 -0
  378. data/lib/farce/transaction.rb +343 -0
  379. data/lib/farce/tree_map.rb +56 -0
  380. data/lib/farce/unsafe/lfu_map.rb +36 -0
  381. data/lib/farce/unsafe/lru_map.rb +36 -0
  382. data/lib/farce/unsafe/tree_map.rb +21 -0
  383. data/lib/farce/unsafe.rb +29 -0
  384. data/lib/farce/unshareable.rb +67 -0
  385. data/lib/farce/unshared/atom.rb +24 -0
  386. data/lib/farce/unshared/counter.rb +10 -0
  387. data/lib/farce/unshared/flag.rb +10 -0
  388. data/lib/farce/unshared/lazy.rb +38 -0
  389. data/lib/farce/unshared/lazy_ref.rb +26 -0
  390. data/lib/farce/unshared/lease.rb +27 -0
  391. data/lib/farce/unshared/lease_map.rb +26 -0
  392. data/lib/farce/unshared/lease_pool.rb +25 -0
  393. data/lib/farce/unshared/lfu_map.rb +20 -0
  394. data/lib/farce/unshared/lru_map.rb +20 -0
  395. data/lib/farce/unshared/map.rb +49 -0
  396. data/lib/farce/unshared/molecule.rb +16 -0
  397. data/lib/farce/unshared/priority_queue.rb +16 -0
  398. data/lib/farce/unshared/queue.rb +27 -0
  399. data/lib/farce/unshared/set.rb +14 -0
  400. data/lib/farce/unshared/sorted_set.rb +28 -0
  401. data/lib/farce/unshared/timer_queue.rb +16 -0
  402. data/lib/farce/unshared/tree_map.rb +21 -0
  403. data/lib/farce/unshared/vector.rb +19 -0
  404. data/lib/farce/unshared/weak_atom.rb +31 -0
  405. data/lib/farce/unshared/weak_key_map.rb +44 -0
  406. data/lib/farce/unshared/weak_map.rb +44 -0
  407. data/lib/farce/unshared/weak_set.rb +14 -0
  408. data/lib/farce/unshared/weak_value_map.rb +43 -0
  409. data/lib/farce/unshared.rb +28 -0
  410. data/lib/farce/vector.rb +228 -0
  411. data/lib/farce/version.rb +8 -0
  412. data/lib/farce/walker/definitions.rb +186 -0
  413. data/lib/farce/walker/modification.rb +355 -0
  414. data/lib/farce/walker.rb +319 -0
  415. data/lib/farce/weak_atom.rb +121 -0
  416. data/lib/farce/weak_key_map.rb +31 -0
  417. data/lib/farce/weak_map.rb +33 -0
  418. data/lib/farce/weak_ref.rb +67 -0
  419. data/lib/farce/weak_set.rb +82 -0
  420. data/lib/farce/weak_value.rb +195 -0
  421. data/lib/farce/weak_value_map.rb +32 -0
  422. data/lib/farce.rb +519 -0
  423. 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
+ }