pigeonhole-db 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Cargo.lock +899 -0
- data/Cargo.toml +45 -0
- data/LICENSE +21 -0
- data/README.md +22 -0
- data/extconf.rb +12 -0
- data/lib/pigeonhole/api.rb +615 -0
- data/lib/pigeonhole/index.rb +208 -0
- data/lib/pigeonhole/memory.rb +507 -0
- data/lib/pigeonhole/version.rb +6 -0
- data/lib/pigeonhole-db.rb +6 -0
- data/lib/pigeonhole.rb +47 -0
- data/sig/pigeonhole.rbs +1484 -0
- data/src/budget.rs +205 -0
- data/src/classes.rs +279 -0
- data/src/convert.rs +192 -0
- data/src/db.rs +685 -0
- data/src/error.rs +151 -0
- data/src/feed.rs +316 -0
- data/src/fork.rs +64 -0
- data/src/gvl.rs +112 -0
- data/src/index.rs +1157 -0
- data/src/lib.rs +534 -0
- data/src/memory.rs +2178 -0
- data/src/read.rs +312 -0
- data/src/table.rs +1161 -0
- data/src/vector.rs +262 -0
- data/src/walk.rs +406 -0
- data/vendor/bundle/ruby/3.4.0/bin/minitest +29 -0
- data/vendor/bundle/ruby/3.4.0/bin/rake +29 -0
- data/vendor/bundle/ruby/3.4.0/bin/rake-compiler +29 -0
- data/vendor/bundle/ruby/3.4.0/bin/rake-compiler-dock +29 -0
- data/vendor/bundle/ruby/3.4.0/bin/rb-sys-dock +29 -0
- data/vendor/bundle/ruby/3.4.0/bin/rbs +29 -0
- data/vendor/bundle/ruby/3.4.0/cache/drb-2.2.3.gem +0 -0
- data/vendor/bundle/ruby/3.4.0/cache/logger-1.7.0.gem +0 -0
- data/vendor/bundle/ruby/3.4.0/cache/minitest-6.0.6.gem +0 -0
- data/vendor/bundle/ruby/3.4.0/cache/prism-1.9.0.gem +0 -0
- data/vendor/bundle/ruby/3.4.0/cache/rake-13.4.2.gem +0 -0
- data/vendor/bundle/ruby/3.4.0/cache/rake-compiler-1.3.2.gem +0 -0
- data/vendor/bundle/ruby/3.4.0/cache/rake-compiler-dock-1.12.0.gem +0 -0
- data/vendor/bundle/ruby/3.4.0/cache/rb_sys-0.9.130.gem +0 -0
- data/vendor/bundle/ruby/3.4.0/cache/rbs-4.2.0.gem +0 -0
- data/vendor/bundle/ruby/3.4.0/cache/toml-rb-6.0.0.gem +0 -0
- data/vendor/bundle/ruby/3.4.0/cache/tsort-0.2.0.gem +0 -0
- data/vendor/bundle/ruby/3.4.0/extensions/x86_64-linux/3.4.0/prism-1.9.0/gem.build_complete +0 -0
- data/vendor/bundle/ruby/3.4.0/extensions/x86_64-linux/3.4.0/prism-1.9.0/gem_make.out +44 -0
- data/vendor/bundle/ruby/3.4.0/extensions/x86_64-linux/3.4.0/prism-1.9.0/mkmf.log +74 -0
- data/vendor/bundle/ruby/3.4.0/extensions/x86_64-linux/3.4.0/prism-1.9.0/prism/prism.so +0 -0
- data/vendor/bundle/ruby/3.4.0/extensions/x86_64-linux/3.4.0/rbs-4.2.0/gem.build_complete +0 -0
- data/vendor/bundle/ruby/3.4.0/extensions/x86_64-linux/3.4.0/rbs-4.2.0/gem_make.out +41 -0
- data/vendor/bundle/ruby/3.4.0/extensions/x86_64-linux/3.4.0/rbs-4.2.0/mkmf.log +102 -0
- data/vendor/bundle/ruby/3.4.0/extensions/x86_64-linux/3.4.0/rbs-4.2.0/rbs_extension.so +0 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/LICENSE.txt +22 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/drb.gemspec +42 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/lib/drb/acl.rb +239 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/lib/drb/drb.rb +1993 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/lib/drb/eq.rb +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/lib/drb/extserv.rb +44 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/lib/drb/extservm.rb +94 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/lib/drb/gw.rb +161 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/lib/drb/observer.rb +26 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/lib/drb/ssl.rb +354 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/lib/drb/timeridconv.rb +97 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/lib/drb/unix.rb +118 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/lib/drb/version.rb +3 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/lib/drb/weakidconv.rb +31 -0
- data/vendor/bundle/ruby/3.4.0/gems/drb-2.2.3/lib/drb.rb +3 -0
- data/vendor/bundle/ruby/3.4.0/gems/logger-1.7.0/BSDL +22 -0
- data/vendor/bundle/ruby/3.4.0/gems/logger-1.7.0/COPYING +56 -0
- data/vendor/bundle/ruby/3.4.0/gems/logger-1.7.0/README.md +104 -0
- data/vendor/bundle/ruby/3.4.0/gems/logger-1.7.0/lib/logger/errors.rb +9 -0
- data/vendor/bundle/ruby/3.4.0/gems/logger-1.7.0/lib/logger/formatter.rb +36 -0
- data/vendor/bundle/ruby/3.4.0/gems/logger-1.7.0/lib/logger/log_device.rb +265 -0
- data/vendor/bundle/ruby/3.4.0/gems/logger-1.7.0/lib/logger/period.rb +47 -0
- data/vendor/bundle/ruby/3.4.0/gems/logger-1.7.0/lib/logger/severity.rb +38 -0
- data/vendor/bundle/ruby/3.4.0/gems/logger-1.7.0/lib/logger/version.rb +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/logger-1.7.0/lib/logger.rb +789 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/History.rdoc +1860 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/Manifest.txt +41 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/README.rdoc +763 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/Rakefile +89 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/bin/minitest +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/design_rationale.rb +54 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/hoe/minitest.rb +30 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/assertions.rb +821 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/autorun.rb +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/benchmark.rb +452 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/bisect.rb +304 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/complete.rb +56 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/compress.rb +94 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/error_on_warning.rb +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/expectations.rb +321 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/find_minimal_combination.rb +127 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/hell.rb +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/manual_plugins.rb +4 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/parallel.rb +72 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/path_expander.rb +432 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/pride.rb +4 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/pride_plugin.rb +135 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/server.rb +49 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/server_plugin.rb +88 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/spec.rb +324 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/sprint.rb +105 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/sprint_plugin.rb +39 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/test.rb +232 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest/test_task.rb +331 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/lib/minitest.rb +1232 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/test/minitest/metametameta.rb +150 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/test/minitest/test_bisect.rb +249 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/test/minitest/test_find_minimal_combination.rb +138 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/test/minitest/test_minitest_assertions.rb +1729 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/test/minitest/test_minitest_benchmark.rb +151 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/test/minitest/test_minitest_reporter.rb +437 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/test/minitest/test_minitest_spec.rb +1095 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/test/minitest/test_minitest_test.rb +1295 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/test/minitest/test_minitest_test_task.rb +57 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/test/minitest/test_path_expander.rb +229 -0
- data/vendor/bundle/ruby/3.4.0/gems/minitest-6.0.6/test/minitest/test_server.rb +146 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/BSDmakefile +58 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/CHANGELOG.md +786 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/CODE_OF_CONDUCT.md +76 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/CONTRIBUTING.md +58 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/LICENSE.md +7 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/Makefile +116 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/README.md +143 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/config.yml +4739 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/build_system.md +119 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/configuration.md +68 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/cruby_compilation.md +27 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/design.md +53 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/encoding.md +121 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/fuzzing.md +88 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/heredocs.md +36 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/javascript.md +118 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/local_variable_depth.md +229 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/mapping.md +117 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/parser_translation.md +24 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/parsing_rules.md +22 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/releasing.md +75 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/relocation.md +34 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/ripper_translation.md +63 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/ruby_api.md +45 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/ruby_parser_translation.md +19 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/serialization.md +233 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/docs/testing.md +55 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/ext/prism/Makefile +273 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/ext/prism/api_node.c +6945 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/ext/prism/api_pack.c +276 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/ext/prism/extconf.rb +127 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/ext/prism/extension.c +1427 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/ext/prism/extension.h +19 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/ast.h +8254 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/defines.h +260 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/diagnostic.h +458 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/encoding.h +283 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/node.h +129 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/options.h +488 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/pack.h +163 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/parser.h +936 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/prettyprint.h +34 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/regexp.h +43 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/static_literals.h +121 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/util/pm_buffer.h +236 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/util/pm_char.h +204 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/util/pm_constant_pool.h +218 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/util/pm_integer.h +130 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/util/pm_list.h +103 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/util/pm_memchr.h +29 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/util/pm_newline_list.h +113 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/util/pm_string.h +200 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/util/pm_strncasecmp.h +32 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/util/pm_strpbrk.h +46 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism/version.h +29 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/include/prism.h +408 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/compiler.rb +801 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/desugar_compiler.rb +392 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/dispatcher.rb +2210 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/dot_visitor.rb +4767 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/dsl.rb +1003 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/ffi.rb +578 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/inspect_visitor.rb +2393 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/lex_compat.rb +911 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/mutation_compiler.rb +772 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/node.rb +19966 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/node_ext.rb +511 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/pack.rb +230 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/parse_result/comments.rb +188 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/parse_result/errors.rb +66 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/parse_result/newlines.rb +155 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/parse_result.rb +907 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/pattern.rb +269 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/polyfill/append_as_bytes.rb +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/polyfill/byteindex.rb +13 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/polyfill/scan_byte.rb +14 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/polyfill/unpack1.rb +14 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/polyfill/warn.rb +36 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/prism.so +0 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/reflection.rb +416 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/relocation.rb +505 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/serialize.rb +2400 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/string_query.rb +31 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/translation/parser/builder.rb +62 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/translation/parser/compiler.rb +2234 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/translation/parser/lexer.rb +820 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/translation/parser.rb +376 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/translation/parser_current.rb +26 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/translation/parser_versions.rb +36 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/translation/ripper/filter.rb +53 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/translation/ripper/lexer.rb +135 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/translation/ripper/sexp.rb +126 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/translation/ripper/shim.rb +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/translation/ripper.rb +3520 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/translation/ruby_parser.rb +1964 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/translation.rb +18 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism/visitor.rb +813 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/lib/prism.rb +98 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/prism.gemspec +172 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/rbi/prism/compiler.rbi +12 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/rbi/prism/dsl.rbi +524 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/rbi/prism/inspect_visitor.rbi +12 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/rbi/prism/node.rbi +8750 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/rbi/prism/node_ext.rbi +107 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/rbi/prism/parse_result.rbi +404 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/rbi/prism/reflection.rbi +58 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/rbi/prism/string_query.rbi +12 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/rbi/prism/translation/parser.rbi +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/rbi/prism/translation/parser_versions.rbi +23 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/rbi/prism/translation/ripper.rbi +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/rbi/prism/visitor.rbi +473 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/rbi/prism.rbi +63 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/compiler.rbs +9 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/dispatcher.rbs +19 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/dot_visitor.rbs +6 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/dsl.rbs +351 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/inspect_visitor.rbs +22 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/lex_compat.rbs +10 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/mutation_compiler.rbs +159 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/node.rbs +4042 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/node_ext.rbs +149 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/pack.rbs +43 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/parse_result/comments.rbs +38 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/parse_result.rbs +197 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/pattern.rbs +13 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/reflection.rbs +50 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/relocation.rbs +185 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/serialize.rbs +8 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/string_query.rbs +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism/visitor.rbs +169 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/sig/prism.rbs +272 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/diagnostic.c +854 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/encoding.c +5340 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/node.c +8685 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/options.c +338 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/pack.c +509 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/prettyprint.c +8957 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/prism.c +22679 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/regexp.c +790 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/serialize.c +2274 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/static_literals.c +617 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/token_type.c +703 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/util/pm_buffer.c +357 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/util/pm_char.c +318 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/util/pm_constant_pool.c +342 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/util/pm_integer.c +670 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/util/pm_list.c +49 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/util/pm_memchr.c +35 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/util/pm_newline_list.c +125 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/util/pm_string.c +381 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/util/pm_strncasecmp.c +36 -0
- data/vendor/bundle/ruby/3.4.0/gems/prism-1.9.0/src/util/pm_strpbrk.c +206 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/History.rdoc +2454 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/MIT-LICENSE +21 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/README.rdoc +155 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/doc/command_line_usage.rdoc +171 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/doc/example/Rakefile1 +38 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/doc/example/Rakefile2 +35 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/doc/example/a.c +6 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/doc/example/b.c +6 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/doc/example/main.c +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/doc/glossary.rdoc +42 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/doc/jamis.rb +592 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/doc/proto_rake.rdoc +127 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/doc/rake.1 +156 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/doc/rakefile.rdoc +635 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/doc/rational.rdoc +151 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/exe/rake +27 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/application.rb +847 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/backtrace.rb +25 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/clean.rb +78 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/cloneable.rb +17 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/cpu_counter.rb +122 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/default_loader.rb +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/dsl_definition.rb +196 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/early_time.rb +22 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/ext/core.rb +26 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/ext/string.rb +176 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/file_creation_task.rb +25 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/file_list.rb +435 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/file_task.rb +58 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/file_utils.rb +137 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/file_utils_ext.rb +135 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/invocation_chain.rb +57 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/invocation_exception_mixin.rb +17 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/late_time.rb +18 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/linked_list.rb +112 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/loaders/makefile.rb +54 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/multi_task.rb +14 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/name_space.rb +38 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/options.rb +31 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/packagetask.rb +222 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/phony.rb +16 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/private_reader.rb +21 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/promise.rb +100 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/pseudo_status.rb +30 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/rake_module.rb +67 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/rake_test_loader.rb +27 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/rule_recursion_overflow_error.rb +20 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/scope.rb +43 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/task.rb +434 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/task_argument_error.rb +8 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/task_arguments.rb +113 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/task_manager.rb +333 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/tasklib.rb +12 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/testtask.rb +192 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/thread_history_display.rb +49 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/thread_pool.rb +157 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/trace_output.rb +23 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/version.rb +10 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake/win32.rb +17 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/lib/rake.rb +69 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-13.4.2/rake.gemspec +102 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/Gemfile +8 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/History.md +708 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/LICENSE.txt +20 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/README.md +505 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/Rakefile +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/appveyor.yml +22 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/bin/rake-compiler +24 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/cucumber.yml +4 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/compile.feature +79 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/cross-compile.feature +23 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/cross-package-multi.feature +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/cross-package.feature +14 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/java-compile.feature +22 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/java-no-native-compile.feature +33 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/java-package.feature +24 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/package.feature +40 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/step_definitions/compilation.rb +70 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/step_definitions/cross_compilation.rb +27 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/step_definitions/execution.rb +52 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/step_definitions/folders.rb +32 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/step_definitions/gem.rb +46 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/step_definitions/java_compilation.rb +7 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/support/env.rb +10 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/support/file_template_helpers.rb +137 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/support/generator_helpers.rb +123 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/features/support/platform_extension_helpers.rb +27 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/lib/rake/baseextensiontask.rb +90 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/lib/rake/compiler_config.rb +38 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/lib/rake/extensioncompiler.rb +51 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/lib/rake/extensiontask.rb +621 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/lib/rake/javaextensiontask.rb +321 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/tasks/bin/cross-ruby.rake +189 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/tasks/bootstrap.rake +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/tasks/common.rake +10 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/tasks/cucumber.rake +23 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/tasks/gem.rake +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-1.3.2/tasks/rspec.rake +9 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/CHANGELOG.md +446 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/CONTRIBUTING.md +109 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/Dockerfile.jruby +79 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/Dockerfile.mri.erb +282 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/Gemfile +8 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/LICENSE.txt +22 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/README.md +447 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/Rakefile +246 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/bin/rake-compiler-dock +18 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/buildkitd.toml +2 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/gem_helper.rb +54 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/mk_i686.rb +18 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/mk_musl_cross.sh +37 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/mk_osxcross.sh +45 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/mk_pkg_config.sh +24 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/parallel_docker_build.rb +169 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/patches/rake-compiler-1.3.1/0004-Enable-build-of-static-libruby.patch +38 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/patches/rake-compiler-1.3.1/0005-build-miniruby-first.patch +16 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/patches/rake-compiler-1.3.1/0006-ruby-4-rubyspec-capiext.patch +16 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/rcd-env.sh +6 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/runas +7 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/sigfw.c +45 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/strip_wrapper_codesign +17 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/strip_wrapper_vbox +30 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/build/sudoers +1 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/colors.rb +43 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/docker_check.rb +356 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/predefined_user_group.rb +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/starter.rb +206 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock/version.rb +4 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/lib/rake_compiler_dock.rb +151 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/Dockerfile +66 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/README.md +14 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/binutils-mingw-w64-ignore-check-errors.patch +13 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/gcc-mingw-w64-only-c-c++.patch +13 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/mingw64-ucrt/mingw-w64-enable-ucrt.patch +22 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/rake-compiler-dock.gemspec +34 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/env/Dockerfile.alpine +17 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/env/Dockerfile.debian +24 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/fixtures/mig_test_rpc.defs +8 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/Gemfile +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/Rakefile +97 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/java/RcdTestExtService.java +19 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/java/RubyRcdTest.java +16 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/mri/extconf.rb +111 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/mri/rcd_test_ext.c +65 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/ext/mri/rcd_test_ext.h +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/lib/rcd_test.rb +6 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/rcd_test.gemspec +28 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/rcd_test/test/test_basic.rb +49 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/test_environment_variables.rb +108 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/test_mig.rb +18 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/test_parallel_docker_build.rb +95 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/test_rubygems_plugins.rb +12 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/test_starter.rb +158 -0
- data/vendor/bundle/ruby/3.4.0/gems/rake-compiler-dock-1.12.0/test/test_versions.rb +82 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/LICENSE-APACHE +190 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/LICENSE-MIT +21 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/README.md +42 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/certs/ianks.pem +21 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/exe/rb-sys-dock +495 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys/cargo/config.rb +72 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys/cargo/metadata.rb +158 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys/cargo.rb +9 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys/cargo_builder/link_flag_converter.rb +37 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys/cargo_builder.rb +370 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys/error.rb +43 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys/extensiontask.rb +188 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys/mkmf/config.rb +75 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys/mkmf.rb +413 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys/toolchain_info/data.rb +10 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys/toolchain_info.rb +82 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys/util/logger.rb +73 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys/version.rb +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/rb_sys-0.9.130/lib/rb_sys.rb +8 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/BSDL +22 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/CHANGELOG.md +2463 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/COPYING +56 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/Dockerfile.jruby +53 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/README.md +240 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/Rakefile +1213 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/Steepfile +55 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/config.yml +920 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/array.rbs +4205 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/basic_object.rbs +376 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/binding.rbs +148 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/builtin.rbs +278 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/class.rbs +223 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/comparable.rbs +192 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/complex.rbs +812 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/constants.rbs +96 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/data.rbs +415 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/dir.rbs +993 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/encoding.rbs +1368 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/enumerable.rbs +2506 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/enumerator/arithmetic_sequence.rbs +70 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/enumerator/product.rbs +92 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/enumerator.rbs +705 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/env.rbs +6 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/errno.rbs +682 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/errors.rbs +789 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/exception.rbs +485 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/false_class.rbs +82 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/fiber.rbs +570 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/fiber_error.rbs +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/file.rbs +2045 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/file_constants.rbs +463 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/file_stat.rbs +534 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/file_test.rbs +331 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/float.rbs +1292 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/gc.rbs +788 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/global_variables.rbs +184 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/hash.rbs +2183 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/integer.rbs +1374 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/io/buffer.rbs +995 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/io/wait.rbs +48 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/io.rbs +3473 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/kernel.rbs +3172 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/marshal.rbs +207 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/match_data.rbs +637 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/math.rbs +770 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/method.rbs +422 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/module.rbs +1856 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/nil_class.rbs +210 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/numeric.rbs +832 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/object.rbs +108 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/object_space/weak_key_map.rbs +166 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/object_space.rbs +197 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/pathname.rbs +1312 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/proc.rbs +905 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/process.rbs +2316 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/ractor.rbs +914 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/random.rbs +255 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/range.rbs +1209 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rational.rbs +502 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rb_config.rbs +88 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rbs/ops.rbs +154 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rbs/unnamed/argf.rbs +1236 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rbs/unnamed/env_class.rbs +1214 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rbs/unnamed/main_class.rbs +123 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rbs/unnamed/random.rbs +186 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/refinement.rbs +59 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/regexp.rbs +1974 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/ruby.rbs +53 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/ruby_vm.rbs +809 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/basic_specification.rbs +6 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/config_file.rbs +38 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/dependency_installer.rbs +6 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/errors.rbs +112 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/installer.rbs +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/path_support.rbs +6 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/platform.rbs +7 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/request_set.rbs +49 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/requirement.rbs +138 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/rubygems.rbs +1108 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/source_list.rbs +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/specification.rbs +31 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/stream_ui.rbs +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/uninstaller.rbs +10 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/rubygems/version.rbs +133 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/set.rbs +751 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/signal.rbs +110 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/string.rbs +5532 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/struct.rbs +668 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/symbol.rbs +482 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/thread.rbs +1821 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/thread_group.rbs +79 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/time.rbs +1793 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/trace_point.rbs +483 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/true_class.rbs +98 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/unbound_method.rbs +337 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/core/warning.rbs +87 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/CONTRIBUTING.md +107 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/aliases.md +79 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/architecture.md +110 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/collection.md +192 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/config.md +171 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/data_and_struct.md +86 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/encoding.md +56 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/gem.md +56 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/inline.md +634 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/rbs_by_example.md +309 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/release.md +303 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/repo.md +125 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/rust.md +96 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/sigs.md +167 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/stdlib.md +155 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/syntax.md +952 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/tools.md +17 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/type_fingerprint.md +21 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/docs/wasm_serialization.md +80 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/exe/rbs +7 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/ext/rbs_extension/Makefile +273 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/ext/rbs_extension/ast_translation.c +1870 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/ext/rbs_extension/ast_translation.h +41 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/ext/rbs_extension/class_constants.c +189 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/ext/rbs_extension/class_constants.h +105 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/ext/rbs_extension/compat.h +10 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/ext/rbs_extension/extconf.rb +40 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/ext/rbs_extension/legacy_location.c +299 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/ext/rbs_extension/legacy_location.h +82 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/ext/rbs_extension/main.c +649 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/ext/rbs_extension/rbs_extension.h +16 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/ext/rbs_extension/rbs_string_bridging.c +9 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/ext/rbs_extension/rbs_string_bridging.h +24 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/goodcheck.yml +91 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs/ast.h +1055 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs/defines.h +104 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs/lexer.h +208 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs/location.h +40 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs/parser.h +187 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs/serialize.h +39 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs/string.h +47 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs/util/rbs_allocator.h +59 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs/util/rbs_assert.h +20 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs/util/rbs_buffer.h +83 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs/util/rbs_constant_pool.h +155 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs/util/rbs_encoding.h +282 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs/util/rbs_unescape.h +24 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/include/rbs.h +14 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ancestor_graph.rb +92 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/annotate/annotations.rb +199 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/annotate/formatter.rb +82 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/annotate/rdoc_annotator.rb +398 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/annotate/rdoc_source.rb +131 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/annotate.rb +8 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ast/annotation.rb +29 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ast/comment.rb +29 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ast/declarations.rb +472 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ast/directives.rb +49 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ast/members.rb +451 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ast/ruby/annotations.rb +451 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ast/ruby/comment_block.rb +247 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ast/ruby/declarations.rb +291 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ast/ruby/helpers/constant_helper.rb +28 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ast/ruby/helpers/location_helper.rb +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ast/ruby/members.rb +762 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ast/type_param.rb +235 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/ast/visitor.rb +137 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/buffer.rb +189 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/builtin_names.rb +58 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/cli/colored_io.rb +48 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/cli/diff.rb +84 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/cli/validate.rb +294 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/cli.rb +1253 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/collection/cleaner.rb +38 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/collection/config/lockfile.rb +92 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/collection/config/lockfile_generator.rb +269 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/collection/config.rb +81 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/collection/installer.rb +32 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/collection/sources/base.rb +14 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/collection/sources/git.rb +265 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/collection/sources/local.rb +81 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/collection/sources/rubygems.rb +48 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/collection/sources/stdlib.rb +50 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/collection/sources.rb +38 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/collection.rb +16 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/constant.rb +28 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/definition.rb +415 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/definition_builder/ancestor_builder.rb +681 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/definition_builder/method_builder.rb +295 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/definition_builder.rb +1057 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/diff.rb +131 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/environment/class_entry.rb +81 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/environment/module_entry.rb +91 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/environment/use_map.rb +77 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/environment.rb +1028 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/environment_loader.rb +167 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/environment_walker.rb +155 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/errors.rb +634 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/factory.rb +18 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/file_finder.rb +28 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/inline_parser/comment_association.rb +117 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/inline_parser.rb +568 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/location_aux.rb +170 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/locator.rb +247 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/method_type.rb +145 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/namespace.rb +154 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/parser/lex_result.rb +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/parser/token.rb +23 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/parser_aux.rb +144 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/prototype/helpers.rb +164 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/prototype/node_usage.rb +99 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/prototype/rb.rb +816 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/prototype/rbi.rb +793 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/prototype/runtime/helpers.rb +59 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/prototype/runtime/reflection.rb +19 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/prototype/runtime/value_object_generator.rb +278 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/prototype/runtime.rb +680 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/repository.rb +127 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/resolver/constant_resolver.rb +219 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/resolver/type_name_resolver.rb +167 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/rewriter.rb +70 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/sorter.rb +198 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/source.rb +99 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/substitution.rb +83 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/subtractor.rb +204 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/test/errors.rb +80 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/test/guaranteed.rb +30 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/test/hook.rb +212 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/test/observer.rb +19 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/test/setup.rb +84 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/test/setup_helper.rb +50 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/test/tester.rb +167 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/test/type_check.rb +457 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/test.rb +112 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/type_alias_dependency.rb +100 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/type_alias_regularity.rb +126 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/type_name.rb +122 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/types.rb +1646 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/unit_test/convertibles.rb +177 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/unit_test/spy.rb +138 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/unit_test/type_assertions.rb +383 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/unit_test/with_aliases.rb +145 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/unit_test.rb +6 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/validator.rb +186 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/variance_calculator.rb +189 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/vendorer.rb +71 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/version.rb +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/wasm/deserializer.rb +213 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/wasm/location.rb +61 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/wasm/parser.rb +174 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/wasm/runtime.rb +211 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/wasm/serialization_schema.rb +111 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs/writer.rb +424 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs.rb +117 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs_extension.so +0 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rbs_jars.rb +39 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rdoc/discover.rb +20 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/lib/rdoc_plugin/parser.rb +168 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/rbs.gemspec +77 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/schema/annotation.json +14 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/schema/comment.json +26 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/schema/decls.json +326 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/schema/function.json +98 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/schema/location.json +56 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/schema/members.json +266 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/schema/methodType.json +50 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/schema/typeParam.json +52 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/schema/types.json +317 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/ancestor_builder.rbs +163 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/ancestor_graph.rbs +60 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/annotate/annotations.rbs +102 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/annotate/formatter.rbs +24 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/annotate/rdoc_annotater.rbs +85 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/annotate/rdoc_source.rbs +32 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/annotation.rbs +27 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/ast/ruby/annotations.rbs +470 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/ast/ruby/comment_block.rbs +127 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/ast/ruby/declarations.rbs +158 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/ast/ruby/helpers/constant_helper.rbs +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/ast/ruby/helpers/location_helper.rbs +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/ast/ruby/members.rbs +198 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/buffer.rbs +108 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/builtin_names.rbs +44 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/cli/colored_io.rbs +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/cli/diff.rbs +15 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/cli/validate.rbs +47 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/cli.rbs +89 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/collection/cleaner.rbs +13 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/collection/config/lockfile.rbs +74 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/collection/config/lockfile_generator.rbs +68 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/collection/config.rbs +46 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/collection/installer.rbs +17 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/collection/sources.rbs +214 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/collection.rbs +4 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/comment.rbs +26 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/constant.rbs +21 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/declarations.rbs +274 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/definition.rbs +232 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/definition_builder.rbs +181 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/diff.rbs +28 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/directives.rbs +77 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/environment/class_entry.rbs +56 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/environment/module_entry.rbs +65 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/environment.rbs +286 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/environment_loader.rbs +111 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/environment_walker.rbs +65 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/errors.rbs +408 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/factory.rbs +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/file_finder.rbs +28 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/inline_parser/comment_association.rbs +71 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/inline_parser.rbs +126 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/location.rbs +135 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/locator.rbs +56 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/manifest.yaml +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/members.rbs +258 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/method_builder.rbs +89 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/method_types.rbs +58 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/namespace.rbs +161 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/parser.rbs +164 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/prototype/helpers.rbs +29 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/prototype/node_usage.rbs +20 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/prototype/rb.rbs +96 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/prototype/rbi.rbs +104 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/prototype/runtime.rbs +182 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/rbs.rbs +21 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/rdoc/rbs.rbs +67 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/repository.rbs +85 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/resolver/constant_resolver.rbs +92 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/resolver/context.rbs +34 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/resolver/type_name_resolver.rbs +61 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/rewriter.rbs +45 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/shims/bundler.rbs +38 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/shims/enumerable.rbs +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/shims/rubygems.rbs +19 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/sorter.rbs +41 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/source.rbs +48 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/substitution.rbs +48 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/subtractor.rbs +37 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/test/errors.rbs +52 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/test/guranteed.rbs +9 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/test/type_check.rbs +19 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/test.rbs +82 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/type_alias_dependency.rbs +53 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/type_alias_regularity.rbs +98 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/type_param.rbs +115 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/typename.rbs +89 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/types.rbs +588 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/unit_test/convertibles.rbs +154 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/unit_test/spy.rbs +22 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/unit_test/type_assertions.rbs +211 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/unit_test/with_aliases.rbs +136 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/use_map.rbs +35 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/util.rbs +9 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/validator.rbs +63 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/variance_calculator.rbs +87 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/vendorer.rbs +51 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/version.rbs +3 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/visitor.rbs +47 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/wasm/deserializer.rbs +66 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/wasm/serialization_schema.rbs +13 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/sig/writer.rbs +127 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/ast.c +1644 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/lexer.c +3223 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/lexer.re +187 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/lexstate.c +222 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/location.c +31 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/parser.c +4318 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/serialize.c +965 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/string.c +41 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/util/rbs_allocator.c +171 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/util/rbs_assert.c +19 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/util/rbs_buffer.c +54 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/util/rbs_constant_pool.c +268 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/util/rbs_encoding.c +21454 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/src/util/rbs_unescape.c +167 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/abbrev/0/abbrev.rbs +66 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/abbrev/0/array.rbs +26 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/base64/0/base64.rbs +355 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/benchmark/0/benchmark.rbs +452 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/bigdecimal/0/big_decimal.rbs +1647 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/bigdecimal-math/0/big_math.rbs +280 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/bigdecimal-math/0/manifest.yaml +2 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/cgi/0/core.rbs +911 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/cgi/0/manifest.yaml +4 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/cgi-escape/0/escape.rbs +171 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/coverage/0/coverage.rbs +266 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/csv/0/csv.rbs +3776 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/csv/0/manifest.yaml +3 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/date/0/date.rbs +1598 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/date/0/date_time.rbs +617 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/date/0/time.rbs +26 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/dbm/0/dbm.rbs +421 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/delegate/0/delegator.rbs +188 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/delegate/0/kernel.rbs +47 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/delegate/0/simple_delegator.rbs +96 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/did_you_mean/0/did_you_mean.rbs +344 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/digest/0/digest.rbs +693 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/erb/0/erb.rbs +933 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/etc/0/etc.rbs +884 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/fileutils/0/fileutils.rbs +1753 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/find/0/find.rbs +49 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/forwardable/0/forwardable.rbs +271 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/io-console/0/io-console.rbs +414 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/ipaddr/0/ipaddr.rbs +431 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/json/0/json.rbs +1963 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/kconv/0/kconv.rbs +166 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/logger/0/formatter.rbs +45 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/logger/0/log_device.rbs +100 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/logger/0/logger.rbs +796 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/logger/0/manifest.yaml +2 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/logger/0/period.rbs +17 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/logger/0/severity.rbs +34 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/kernel.rbs +42 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/abstract_reporter.rbs +52 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/assertion.rbs +17 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/assertions.rbs +590 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/backtrace_filter.rbs +23 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/bench_spec.rbs +102 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/benchmark.rbs +259 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/composite_reporter.rbs +25 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/compress.rbs +13 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/error_on_warning.rbs +3 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/expectation.rbs +2 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/expectations.rbs +21 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/guard.rbs +64 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/mock.rbs +64 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/parallel/executor.rbs +46 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/parallel/test/class_methods.rbs +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/parallel/test.rbs +3 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/parallel.rbs +2 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/pride_io.rbs +62 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/pride_lol.rbs +19 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/progress_reporter.rbs +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/reportable.rbs +53 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/reporter.rbs +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/result.rbs +28 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/runnable.rbs +163 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/skip.rbs +6 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/spec/dsl/instance_methods.rbs +48 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/spec/dsl.rbs +129 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/spec.rbs +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/statistics_reporter.rbs +81 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/summary_reporter.rbs +18 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/test/lifecycle_hooks.rbs +92 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/test.rbs +69 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/unexpected_error.rbs +12 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/unexpected_warning.rbs +6 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/unit/test_case.rbs +3 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest/unit.rbs +4 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/minitest/0/minitest.rbs +115 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/monitor/0/monitor.rbs +363 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/mutex_m/0/mutex_m.rbs +104 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/net-http/0/manifest.yaml +3 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/net-http/0/net-http.rbs +5580 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/net-protocol/0/manifest.yaml +2 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/net-protocol/0/net-protocol.rbs +56 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/net-smtp/0/manifest.yaml +2 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/net-smtp/0/net-smtp.rbs +55 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/nkf/0/nkf.rbs +402 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/objspace/0/objspace.rbs +470 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/observable/0/observable.rbs +217 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/open-uri/0/manifest.yaml +4 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/open-uri/0/open-uri.rbs +433 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/open3/0/open3.rbs +606 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/openssl/0/manifest.yaml +3 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/openssl/0/openssl.rbs +12237 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/optparse/0/optparse.rbs +1734 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/pathname/0/pathname.rbs +36 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/pp/0/manifest.yaml +2 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/pp/0/pp.rbs +301 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/prettyprint/0/prettyprint.rbs +383 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/pstore/0/pstore.rbs +608 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/psych/0/core_ext.rbs +12 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/psych/0/dbm.rbs +237 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/psych/0/manifest.yaml +3 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/psych/0/psych.rbs +455 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/psych/0/store.rbs +57 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/pty/0/pty.rbs +240 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/random-formatter/0/random-formatter.rbs +277 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/rdoc/0/code_object.rbs +52 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/rdoc/0/comment.rbs +61 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/rdoc/0/context.rbs +153 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/rdoc/0/markup.rbs +117 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/rdoc/0/options.rbs +76 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/rdoc/0/parser.rbs +56 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/rdoc/0/rdoc.rbs +393 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/rdoc/0/ri.rbs +17 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/rdoc/0/store.rbs +48 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/rdoc/0/top_level.rbs +97 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/resolv/0/manifest.yaml +3 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/resolv/0/resolv.rbs +1787 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/ripper/0/ripper.rbs +1654 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/securerandom/0/manifest.yaml +2 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/securerandom/0/securerandom.rbs +49 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/shellwords/0/shellwords.rbs +229 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/singleton/0/singleton.rbs +137 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/socket/0/addrinfo.rbs +666 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/socket/0/basic_socket.rbs +590 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/socket/0/constants.rbs +2295 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/socket/0/ip_socket.rbs +94 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/socket/0/socket.rbs +4170 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/socket/0/socket_error.rbs +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/socket/0/tcp_server.rbs +192 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/socket/0/tcp_socket.rbs +87 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/socket/0/udp_socket.rbs +133 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/socket/0/unix_server.rbs +169 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/socket/0/unix_socket.rbs +172 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/stringio/0/stringio.rbs +1682 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/strscan/0/string_scanner.rbs +1648 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/tempfile/0/manifest.yaml +3 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/tempfile/0/tempfile.rbs +483 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/time/0/time.rbs +434 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/timeout/0/timeout.rbs +132 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/tmpdir/0/tmpdir.rbs +69 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/tsort/0/cyclic.rbs +8 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/tsort/0/interfaces.rbs +20 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/tsort/0/tsort.rbs +410 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/uri/0/common.rbs +621 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/uri/0/file.rbs +118 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/uri/0/ftp.rbs +13 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/uri/0/generic.rbs +1111 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/uri/0/http.rbs +104 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/uri/0/https.rbs +14 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/uri/0/ldap.rbs +230 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/uri/0/ldaps.rbs +14 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/uri/0/mailto.rbs +92 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/uri/0/rfc2396_parser.rbs +189 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/uri/0/rfc3986_parser.rbs +2 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/uri/0/ws.rbs +13 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/uri/0/wss.rbs +9 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/yaml/0/manifest.yaml +2 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/yaml/0/yaml.rbs +1 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/buf_error.rbs +10 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/data_error.rbs +10 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/deflate.rbs +211 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/error.rbs +20 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/gzip_file/crc_error.rbs +12 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/gzip_file/error.rbs +23 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/gzip_file/length_error.rbs +12 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/gzip_file/no_footer.rbs +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/gzip_file.rbs +156 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/gzip_reader.rbs +293 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/gzip_writer.rbs +169 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/inflate.rbs +180 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/mem_error.rbs +10 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/need_dict.rbs +13 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/stream_end.rbs +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/stream_error.rbs +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/version_error.rbs +11 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/zlib.rbs +449 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/stdlib/zlib/0/zstream.rbs +200 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/wasm/README.md +109 -0
- data/vendor/bundle/ruby/3.4.0/gems/rbs-4.2.0/wasm/rbs_wasm.c +479 -0
- data/vendor/bundle/ruby/3.4.0/gems/toml-rb-6.0.0/LICENSE +19 -0
- data/vendor/bundle/ruby/3.4.0/gems/toml-rb-6.0.0/README.md +135 -0
- data/vendor/bundle/ruby/3.4.0/gems/toml-rb-6.0.0/lib/toml-rb/datetime.rb +54 -0
- data/vendor/bundle/ruby/3.4.0/gems/toml-rb-6.0.0/lib/toml-rb/dumper.rb +182 -0
- data/vendor/bundle/ruby/3.4.0/gems/toml-rb-6.0.0/lib/toml-rb/errors.rb +17 -0
- data/vendor/bundle/ruby/3.4.0/gems/toml-rb-6.0.0/lib/toml-rb/inline_table.rb +18 -0
- data/vendor/bundle/ruby/3.4.0/gems/toml-rb-6.0.0/lib/toml-rb/keyvalue.rb +45 -0
- data/vendor/bundle/ruby/3.4.0/gems/toml-rb-6.0.0/lib/toml-rb/parser.rb +296 -0
- data/vendor/bundle/ruby/3.4.0/gems/toml-rb-6.0.0/lib/toml-rb/string.rb +41 -0
- data/vendor/bundle/ruby/3.4.0/gems/toml-rb-6.0.0/lib/toml-rb/table.rb +34 -0
- data/vendor/bundle/ruby/3.4.0/gems/toml-rb-6.0.0/lib/toml-rb/table_array.rb +43 -0
- data/vendor/bundle/ruby/3.4.0/gems/toml-rb-6.0.0/lib/toml-rb/version.rb +5 -0
- data/vendor/bundle/ruby/3.4.0/gems/toml-rb-6.0.0/lib/toml-rb.rb +90 -0
- data/vendor/bundle/ruby/3.4.0/gems/tsort-0.2.0/Gemfile +4 -0
- data/vendor/bundle/ruby/3.4.0/gems/tsort-0.2.0/LICENSE.txt +22 -0
- data/vendor/bundle/ruby/3.4.0/gems/tsort-0.2.0/README.md +72 -0
- data/vendor/bundle/ruby/3.4.0/gems/tsort-0.2.0/Rakefile +10 -0
- data/vendor/bundle/ruby/3.4.0/gems/tsort-0.2.0/bin/console +14 -0
- data/vendor/bundle/ruby/3.4.0/gems/tsort-0.2.0/bin/setup +8 -0
- data/vendor/bundle/ruby/3.4.0/gems/tsort-0.2.0/lib/tsort.rb +455 -0
- data/vendor/bundle/ruby/3.4.0/gems/tsort-0.2.0/tsort.gemspec +29 -0
- data/vendor/bundle/ruby/3.4.0/specifications/drb-2.2.3.gemspec +22 -0
- data/vendor/bundle/ruby/3.4.0/specifications/logger-1.7.0.gemspec +22 -0
- data/vendor/bundle/ruby/3.4.0/specifications/minitest-6.0.6.gemspec +35 -0
- data/vendor/bundle/ruby/3.4.0/specifications/prism-1.9.0.gemspec +24 -0
- data/vendor/bundle/ruby/3.4.0/specifications/rake-13.4.2.gemspec +26 -0
- data/vendor/bundle/ruby/3.4.0/specifications/rake-compiler-1.3.2.gemspec +33 -0
- data/vendor/bundle/ruby/3.4.0/specifications/rake-compiler-dock-1.12.0.gemspec +28 -0
- data/vendor/bundle/ruby/3.4.0/specifications/rb_sys-0.9.130.gemspec +29 -0
- data/vendor/bundle/ruby/3.4.0/specifications/rbs-4.2.0.gemspec +33 -0
- data/vendor/bundle/ruby/3.4.0/specifications/toml-rb-6.0.0.gemspec +27 -0
- data/vendor/bundle/ruby/3.4.0/specifications/tsort-0.2.0.gemspec +23 -0
- data/vendor/cache/Cargo.toml +21 -0
- data/vendor/cache/LICENSE +21 -0
- data/vendor/cache/README.md +5 -0
- data/vendor/cache/src/hash.rs +85 -0
- data/vendor/cache/src/lib.rs +751 -0
- data/vendor/cache/src/loom_tests.rs +340 -0
- data/vendor/cache/src/s3fifo.rs +648 -0
- data/vendor/cache/src/sync.rs +25 -0
- data/vendor/cache/src/tests.rs +260 -0
- data/vendor/compaction/Cargo.toml +32 -0
- data/vendor/compaction/LICENSE +21 -0
- data/vendor/compaction/README.md +5 -0
- data/vendor/compaction/src/blob.rs +478 -0
- data/vendor/compaction/src/cursor.rs +700 -0
- data/vendor/compaction/src/gc.rs +802 -0
- data/vendor/compaction/src/job.rs +861 -0
- data/vendor/compaction/src/lib.rs +102 -0
- data/vendor/compaction/src/merge.rs +995 -0
- data/vendor/compaction/src/picker.rs +681 -0
- data/vendor/compaction/src/resolver.rs +1346 -0
- data/vendor/engine/Cargo.toml +56 -0
- data/vendor/engine/LICENSE +21 -0
- data/vendor/engine/README.md +5 -0
- data/vendor/engine/src/admission.rs +421 -0
- data/vendor/engine/src/catalog.rs +1233 -0
- data/vendor/engine/src/check.rs +1386 -0
- data/vendor/engine/src/clock.rs +244 -0
- data/vendor/engine/src/clock_loom.rs +482 -0
- data/vendor/engine/src/close_gate.rs +325 -0
- data/vendor/engine/src/compact.rs +1509 -0
- data/vendor/engine/src/engine/hooks.rs +1654 -0
- data/vendor/engine/src/engine/info.rs +229 -0
- data/vendor/engine/src/engine.rs +6114 -0
- data/vendor/engine/src/error.rs +490 -0
- data/vendor/engine/src/feed.rs +960 -0
- data/vendor/engine/src/feed_reader.rs +1600 -0
- data/vendor/engine/src/flush.rs +839 -0
- data/vendor/engine/src/flushed_roots.rs +158 -0
- data/vendor/engine/src/flushed_through_loom.rs +77 -0
- data/vendor/engine/src/health.rs +201 -0
- data/vendor/engine/src/large.rs +372 -0
- data/vendor/engine/src/large_settle.rs +201 -0
- data/vendor/engine/src/lib.rs +112 -0
- data/vendor/engine/src/load_slot.rs +324 -0
- data/vendor/engine/src/maintenance.rs +935 -0
- data/vendor/engine/src/manifest/flight.rs +563 -0
- data/vendor/engine/src/manifest.rs +1516 -0
- data/vendor/engine/src/nonblocking.rs +603 -0
- data/vendor/engine/src/options.rs +334 -0
- data/vendor/engine/src/position.rs +125 -0
- data/vendor/engine/src/purge_guard.rs +220 -0
- data/vendor/engine/src/read/keys.rs +1049 -0
- data/vendor/engine/src/read/knn.rs +423 -0
- data/vendor/engine/src/read/reduce.rs +2057 -0
- data/vendor/engine/src/read/traverse.rs +1013 -0
- data/vendor/engine/src/read.rs +2429 -0
- data/vendor/engine/src/row_cache.rs +1101 -0
- data/vendor/engine/src/shard/tablets.rs +3582 -0
- data/vendor/engine/src/shard.rs +12215 -0
- data/vendor/engine/src/snapshot.rs +1429 -0
- data/vendor/engine/src/source.rs +1379 -0
- data/vendor/engine/src/timer_state.rs +226 -0
- data/vendor/engine/src/waker.rs +124 -0
- data/vendor/engine/src/watermark_waiters.rs +674 -0
- data/vendor/engine/src/write.rs +1321 -0
- data/vendor/format/Cargo.toml +40 -0
- data/vendor/format/LICENSE +21 -0
- data/vendor/format/README.md +5 -0
- data/vendor/format/src/blob.rs +101 -0
- data/vendor/format/src/block.rs +1120 -0
- data/vendor/format/src/bloom.rs +456 -0
- data/vendor/format/src/buckets.rs +552 -0
- data/vendor/format/src/bytes.rs +101 -0
- data/vendor/format/src/checksum.rs +132 -0
- data/vendor/format/src/compress.rs +196 -0
- data/vendor/format/src/cursor.rs +110 -0
- data/vendor/format/src/distance.rs +598 -0
- data/vendor/format/src/error.rs +93 -0
- data/vendor/format/src/exact.rs +602 -0
- data/vendor/format/src/feed.rs +285 -0
- data/vendor/format/src/filter.rs +157 -0
- data/vendor/format/src/half.rs +182 -0
- data/vendor/format/src/hash.rs +127 -0
- data/vendor/format/src/hll.rs +500 -0
- data/vendor/format/src/ids.rs +95 -0
- data/vendor/format/src/key.rs +620 -0
- data/vendor/format/src/kind_params.rs +101 -0
- data/vendor/format/src/lib.rs +54 -0
- data/vendor/format/src/manifest.rs +1287 -0
- data/vendor/format/src/roaring.rs +865 -0
- data/vendor/format/src/scan.rs +297 -0
- data/vendor/format/src/shm.rs +805 -0
- data/vendor/format/src/sst.rs +233 -0
- data/vendor/format/src/superblock.rs +345 -0
- data/vendor/format/src/value.rs +163 -0
- data/vendor/format/src/varint.rs +87 -0
- data/vendor/format/src/vector.rs +936 -0
- data/vendor/format/src/version.rs +92 -0
- data/vendor/format/src/wal.rs +999 -0
- data/vendor/io/Cargo.toml +29 -0
- data/vendor/io/LICENSE +21 -0
- data/vendor/io/README.md +5 -0
- data/vendor/io/src/async_poll.rs +44 -0
- data/vendor/io/src/buf.rs +694 -0
- data/vendor/io/src/completion.rs +444 -0
- data/vendor/io/src/error.rs +110 -0
- data/vendor/io/src/file.rs +206 -0
- data/vendor/io/src/lib.rs +53 -0
- data/vendor/io/src/nowait.rs +256 -0
- data/vendor/io/src/os/mod.rs +149 -0
- data/vendor/io/src/os/unix.rs +897 -0
- data/vendor/io/src/os/windows.rs +552 -0
- data/vendor/io/src/own.rs +257 -0
- data/vendor/io/src/pread.rs +854 -0
- data/vendor/io/src/ring_wake.rs +113 -0
- data/vendor/io/src/shared.rs +410 -0
- data/vendor/io/src/sim.rs +1545 -0
- data/vendor/io/src/sys.rs +214 -0
- data/vendor/io/src/uring.rs +1839 -0
- data/vendor/io/src/vfs.rs +154 -0
- data/vendor/memory/Cargo.toml +34 -0
- data/vendor/memory/LICENSE +21 -0
- data/vendor/memory/README.md +11 -0
- data/vendor/memory/src/consolidate.rs +640 -0
- data/vendor/memory/src/dedup.rs +231 -0
- data/vendor/memory/src/error.rs +131 -0
- data/vendor/memory/src/feed_loop.rs +572 -0
- data/vendor/memory/src/forget.rs +724 -0
- data/vendor/memory/src/hooks.rs +109 -0
- data/vendor/memory/src/ivf.rs +204 -0
- data/vendor/memory/src/layout.rs +340 -0
- data/vendor/memory/src/lib.rs +61 -0
- data/vendor/memory/src/recall.rs +1182 -0
- data/vendor/memory/src/store.rs +1785 -0
- data/vendor/memory/src/watch.rs +339 -0
- data/vendor/memtable/Cargo.toml +24 -0
- data/vendor/memtable/LICENSE +21 -0
- data/vendor/memtable/README.md +5 -0
- data/vendor/memtable/src/lib.rs +1658 -0
- data/vendor/memtable/src/loom_tests.rs +274 -0
- data/vendor/memtable/src/mem.rs +344 -0
- data/vendor/memtable/src/tests.rs +1061 -0
- data/vendor/pager/Cargo.toml +23 -0
- data/vendor/pager/LICENSE +21 -0
- data/vendor/pager/README.md +5 -0
- data/vendor/pager/src/alloc.rs +1008 -0
- data/vendor/pager/src/lib.rs +1710 -0
- data/vendor/pigeonhole/Cargo.toml +42 -0
- data/vendor/pigeonhole/LICENSE +21 -0
- data/vendor/pigeonhole/README.md +99 -0
- data/vendor/pigeonhole/src/cell.rs +829 -0
- data/vendor/pigeonhole/src/check.rs +368 -0
- data/vendor/pigeonhole/src/db.rs +2324 -0
- data/vendor/pigeonhole/src/doc_support.rs +89 -0
- data/vendor/pigeonhole/src/error.rs +257 -0
- data/vendor/pigeonhole/src/feed.rs +696 -0
- data/vendor/pigeonhole/src/info.rs +425 -0
- data/vendor/pigeonhole/src/lib.rs +202 -0
- data/vendor/pigeonhole/src/merge.rs +135 -0
- data/vendor/pigeonhole/src/metrics.rs +476 -0
- data/vendor/pigeonhole/src/nonblocking.rs +666 -0
- data/vendor/pigeonhole/src/options.rs +2075 -0
- data/vendor/pigeonhole/src/read/budget.rs +584 -0
- data/vendor/pigeonhole/src/read.rs +2463 -0
- data/vendor/pigeonhole/src/recovery.rs +876 -0
- data/vendor/pigeonhole/src/retry.rs +358 -0
- data/vendor/pigeonhole/src/set.rs +103 -0
- data/vendor/pigeonhole/src/table.rs +861 -0
- data/vendor/pigeonhole/src/vector.rs +351 -0
- data/vendor/pigeonhole/src/write.rs +2815 -0
- data/vendor/runtime/Cargo.toml +21 -0
- data/vendor/runtime/LICENSE +21 -0
- data/vendor/runtime/README.md +5 -0
- data/vendor/runtime/src/completion.rs +629 -0
- data/vendor/runtime/src/drivers.rs +1175 -0
- data/vendor/runtime/src/lib.rs +1546 -0
- data/vendor/runtime/src/pool.rs +992 -0
- data/vendor/runtime/src/sched.rs +809 -0
- data/vendor/runtime/src/signal.rs +532 -0
- data/vendor/runtime/src/submit_gate.rs +144 -0
- data/vendor/shm/Cargo.toml +24 -0
- data/vendor/shm/LICENSE +21 -0
- data/vendor/shm/README.md +5 -0
- data/vendor/shm/src/lib.rs +427 -0
- data/vendor/shm/src/lock.rs +153 -0
- data/vendor/shm/src/region.rs +1609 -0
- data/vendor/sst/Cargo.toml +26 -0
- data/vendor/sst/LICENSE +21 -0
- data/vendor/sst/README.md +5 -0
- data/vendor/sst/src/blob.rs +380 -0
- data/vendor/sst/src/iter.rs +803 -0
- data/vendor/sst/src/lib.rs +695 -0
- data/vendor/sst/src/reader.rs +814 -0
- data/vendor/sst/src/writer.rs +596 -0
- data/vendor/text/Cargo.toml +26 -0
- data/vendor/text/LICENSE +21 -0
- data/vendor/text/README.md +9 -0
- data/vendor/text/src/error.rs +108 -0
- data/vendor/text/src/fuzzy.rs +265 -0
- data/vendor/text/src/highlight.rs +236 -0
- data/vendor/text/src/index.rs +1152 -0
- data/vendor/text/src/layout.rs +424 -0
- data/vendor/text/src/lib.rs +75 -0
- data/vendor/text/src/maxscore.rs +658 -0
- data/vendor/text/src/query.rs +450 -0
- data/vendor/text/src/search.rs +569 -0
- data/vendor/text/src/sloppy.rs +460 -0
- data/vendor/text/src/tokenizer.rs +163 -0
- data/vendor/vector/Cargo.toml +28 -0
- data/vendor/vector/LICENSE +21 -0
- data/vendor/vector/README.md +9 -0
- data/vendor/vector/src/error.rs +63 -0
- data/vendor/vector/src/half.rs +167 -0
- data/vendor/vector/src/hooks.rs +135 -0
- data/vendor/vector/src/index.rs +2690 -0
- data/vendor/vector/src/layout.rs +102 -0
- data/vendor/vector/src/lib.rs +52 -0
- data/vendor/vector/src/point.rs +500 -0
- data/vendor/vector/src/rebalancer.rs +200 -0
- data/vendor/vector/src/registry.rs +81 -0
- data/vendor/wal/Cargo.toml +25 -0
- data/vendor/wal/LICENSE +21 -0
- data/vendor/wal/README.md +5 -0
- data/vendor/wal/src/feed.rs +610 -0
- data/vendor/wal/src/foreground.rs +122 -0
- data/vendor/wal/src/lib.rs +458 -0
- data/vendor/wal/src/mem.rs +166 -0
- data/vendor/wal/src/recovery.rs +426 -0
- data/vendor/wal/src/stream.rs +2382 -0
- metadata +1310 -0
|
@@ -0,0 +1,2690 @@
|
|
|
1
|
+
//! The index (D306): writes, inline split and merge, search, and verification.
|
|
2
|
+
|
|
3
|
+
use std::collections::{HashMap, HashSet, VecDeque};
|
|
4
|
+
use std::sync::atomic::{AtomicBool, Ordering};
|
|
5
|
+
use std::sync::{Arc, Mutex, PoisonError, RwLock};
|
|
6
|
+
use std::time::Duration;
|
|
7
|
+
|
|
8
|
+
use pigeonhole::{
|
|
9
|
+
Element, ErrorCode, Family, KeySet, KeysRead, Pigeonhole, PigeonholeReader, ReadTable,
|
|
10
|
+
RetryPolicy, Scan, Snapshot, Table, Transaction, Vector, VectorSpec,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
use crate::layout::{
|
|
14
|
+
CEN, CNT, GLOBAL, LAYOUT, LIST, MAP, MARK, META, NEXT, OPTS, VEC, VERSION, centroid_row,
|
|
15
|
+
count_row, list_of, list_row, map_row, queue_row, scramble,
|
|
16
|
+
};
|
|
17
|
+
use crate::point::{Input, Plan, Point, Stored, distance, two_means, unit};
|
|
18
|
+
use crate::rebalancer::{Rebalancer, Signal};
|
|
19
|
+
use crate::{Error, Result};
|
|
20
|
+
|
|
21
|
+
/// A list row stays at most this large (D306 §1): the target list length is scaled down
|
|
22
|
+
/// for large vectors so `2 × target` vectors fit.
|
|
23
|
+
const MAX_LIST_BYTES: u64 = 4 << 20;
|
|
24
|
+
/// Attempts of one split or merge before it leaves the list for the next trigger (rev 3 S1).
|
|
25
|
+
const ATTEMPTS: usize = 4;
|
|
26
|
+
/// Centroid reloads an insert or delete makes before it gives up with `Conflict`.
|
|
27
|
+
const RELOADS: usize = 32;
|
|
28
|
+
/// Splits and merges one insert or delete runs after its commit, at most; the rest stay
|
|
29
|
+
/// queued for the next call.
|
|
30
|
+
const MAINTENANCE_PER_CALL: usize = 16;
|
|
31
|
+
|
|
32
|
+
/// An index's options, fixed when it is created ([`Index::create`]) and stored with it.
|
|
33
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
34
|
+
pub struct IndexOptions {
|
|
35
|
+
target: u32,
|
|
36
|
+
nprobe: u32,
|
|
37
|
+
reassign: u32,
|
|
38
|
+
rebalance: Rebalance,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/// Who splits and merges an index's lists (D306 §3).
|
|
42
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
|
43
|
+
#[non_exhaustive]
|
|
44
|
+
pub enum Rebalance {
|
|
45
|
+
/// Inside the insert or delete that took the list out of its bounds, before the call
|
|
46
|
+
/// returns: the writer pays for the split.
|
|
47
|
+
#[default]
|
|
48
|
+
Inline,
|
|
49
|
+
/// In a [`Rebalancer`]: a write marks the list and returns, and the rebalancer splits or
|
|
50
|
+
/// merges it soon after. [`Index::open`] runs one on a thread of the crate's own;
|
|
51
|
+
/// [`Index::open_with_rebalancer`] hands it to the application instead. Until it runs,
|
|
52
|
+
/// a list may hold more than [`Index::max_list_len`] vectors, which costs searches that
|
|
53
|
+
/// probe it, never their correctness.
|
|
54
|
+
Background,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
impl Default for IndexOptions {
|
|
58
|
+
fn default() -> Self {
|
|
59
|
+
Self {
|
|
60
|
+
target: 1000,
|
|
61
|
+
nprobe: 24,
|
|
62
|
+
reassign: 2,
|
|
63
|
+
rebalance: Rebalance::Inline,
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
impl IndexOptions {
|
|
69
|
+
/// The defaults: lists of about 1,000 vectors, 24 lists probed per search (95.8%
|
|
70
|
+
/// recall@10 on SIFT1M, D306 PR 3), and 2
|
|
71
|
+
/// neighbouring lists reassigned after a split.
|
|
72
|
+
pub fn new() -> Self {
|
|
73
|
+
Self::default()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// The length a list is kept near (at least 4): a list splits above twice this, and
|
|
77
|
+
/// merges into its nearest neighbour below a quarter of it. Scaled down for large
|
|
78
|
+
/// vectors, so a list row stays within 4 MiB.
|
|
79
|
+
pub fn target_list_len(mut self, n: u32) -> Self {
|
|
80
|
+
self.target = n;
|
|
81
|
+
self
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/// Lists a search probes unless it says otherwise ([`Search::nprobe`]; at least 1).
|
|
85
|
+
/// Fixed when the index is created, like every option here. The default, 24, gave
|
|
86
|
+
/// 95.8% recall@10 on SIFT1M; recall depends on the data, so measure your own.
|
|
87
|
+
pub fn nprobe(mut self, n: u32) -> Self {
|
|
88
|
+
self.nprobe = n;
|
|
89
|
+
self
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/// Neighbouring lists a split re-examines for vectors now nearer one of its two new
|
|
93
|
+
/// centroids (SPFresh's LIRE; 0 turns it off). It improves clustering, not correctness.
|
|
94
|
+
pub fn reassign_neighbours(mut self, r: u32) -> Self {
|
|
95
|
+
self.reassign = r;
|
|
96
|
+
self
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// Who splits and merges the lists ([`Rebalance::Inline`] by default).
|
|
100
|
+
pub fn rebalance(mut self, r: Rebalance) -> Self {
|
|
101
|
+
self.rebalance = r;
|
|
102
|
+
self
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/// Who splits and merges the lists.
|
|
106
|
+
pub fn rebalance_mode(&self) -> Rebalance {
|
|
107
|
+
self.rebalance
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fn checked(mut self, spec: &VectorSpec) -> Result<Self> {
|
|
111
|
+
if self.target < 4 || self.nprobe == 0 {
|
|
112
|
+
return Err(Error::InvalidArgument(format!(
|
|
113
|
+
"index options: the target list length must be at least 4 and nprobe at least \
|
|
114
|
+
1, not {} and {}",
|
|
115
|
+
self.target, self.nprobe
|
|
116
|
+
)));
|
|
117
|
+
}
|
|
118
|
+
let bytes = match spec.element() {
|
|
119
|
+
Element::F32 => 4 * u64::from(spec.dim()),
|
|
120
|
+
Element::Bit => u64::from(spec.dim()) / 8,
|
|
121
|
+
_ => u64::from(spec.dim()),
|
|
122
|
+
};
|
|
123
|
+
let cap = (MAX_LIST_BYTES / (2 * bytes)).max(4);
|
|
124
|
+
self.target = self.target.min(u32::try_from(cap).unwrap_or(u32::MAX));
|
|
125
|
+
Ok(self)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/// Version 2: version 1's three `u32`s, then the rebalance mode (0 inline, 1
|
|
129
|
+
/// background). Version 1 (PR 1) decodes as inline.
|
|
130
|
+
fn encode(&self) -> Vec<u8> {
|
|
131
|
+
let mut out = vec![2];
|
|
132
|
+
for v in [self.target, self.nprobe, self.reassign] {
|
|
133
|
+
out.extend_from_slice(&v.to_le_bytes());
|
|
134
|
+
}
|
|
135
|
+
out.push(match self.rebalance {
|
|
136
|
+
Rebalance::Inline => 0,
|
|
137
|
+
Rebalance::Background => 1,
|
|
138
|
+
});
|
|
139
|
+
out
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
fn decode(b: &[u8]) -> Option<Self> {
|
|
143
|
+
let (rest, rebalance) = match b {
|
|
144
|
+
[1, rest @ ..] => (rest, Rebalance::Inline),
|
|
145
|
+
[2, rest @ .., 0] => (rest, Rebalance::Inline),
|
|
146
|
+
[2, rest @ .., 1] => (rest, Rebalance::Background),
|
|
147
|
+
_ => return None,
|
|
148
|
+
};
|
|
149
|
+
let (words, []) = rest.as_chunks::<4>() else {
|
|
150
|
+
return None;
|
|
151
|
+
};
|
|
152
|
+
let [target, nprobe, reassign] = words else {
|
|
153
|
+
return None;
|
|
154
|
+
};
|
|
155
|
+
Some(Self {
|
|
156
|
+
target: u32::from_le_bytes(*target),
|
|
157
|
+
nprobe: u32::from_le_bytes(*nprobe),
|
|
158
|
+
reassign: u32::from_le_bytes(*reassign),
|
|
159
|
+
rebalance,
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/// One result of a [`Search`]: the id given at insert, and its distance to the query under
|
|
165
|
+
/// the index's metric (smaller is nearer), as [`VectorSpec`]'s `distance_*` give it.
|
|
166
|
+
///
|
|
167
|
+
/// Hits at an equal distance come back in the order of the lists that hold them, then by
|
|
168
|
+
/// id, as `nearest_*` orders a list's row then its qualifiers. That order is fixed at a
|
|
169
|
+
/// snapshot, but a split can move a tied id to another list, so which of several tied ids
|
|
170
|
+
/// makes the cut at `k` can change from one layout to the next.
|
|
171
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
172
|
+
#[non_exhaustive]
|
|
173
|
+
pub struct Hit {
|
|
174
|
+
/// The vector's id.
|
|
175
|
+
pub id: Vec<u8>,
|
|
176
|
+
/// Its distance to the query.
|
|
177
|
+
pub distance: f64,
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/// One transaction's writes to an index ([`Index::insert_f32_in`] and its forms,
|
|
181
|
+
/// [`Index::delete_in`]): make one per transaction with [`Placed::new`], pass it to each
|
|
182
|
+
/// write, and to [`Index::after_commit`] once the transaction commits.
|
|
183
|
+
///
|
|
184
|
+
/// A transaction's reads don't see its own writes, so the index remembers here where it
|
|
185
|
+
/// put each id: a second write of an id in the same transaction (an upsert, a delete after
|
|
186
|
+
/// an insert, an insert after a delete) acts on that, not on the snapshot (cli692's B1 on
|
|
187
|
+
/// #1412). Writes of one transaction through two `Placed` lose that, so use one.
|
|
188
|
+
///
|
|
189
|
+
/// **Make it inside the transaction's body** (the closure
|
|
190
|
+
/// [`Pigeonhole::transact`](pigeonhole::Pigeonhole::transact) runs), so a retried attempt
|
|
191
|
+
/// starts with a fresh one: one made outside would carry an aborted attempt's record into
|
|
192
|
+
/// the retry. A `Placed` belongs to the index its first write used; using it with another
|
|
193
|
+
/// index is [`Error::InvalidArgument`] (cli692's C1 and C2).
|
|
194
|
+
#[derive(Debug, Clone, Default)]
|
|
195
|
+
#[must_use = "pass it to Index::after_commit once the transaction commits; dropping it only delays a split or merge until the next write to its lists"]
|
|
196
|
+
pub struct Placed {
|
|
197
|
+
lists: Vec<u32>,
|
|
198
|
+
/// Each id this transaction wrote: the list it's in now, or `None` once deleted.
|
|
199
|
+
ids: HashMap<Vec<u8>, Option<u32>>,
|
|
200
|
+
/// This transaction created list 0 (the index was empty at its snapshot).
|
|
201
|
+
bootstrapped: bool,
|
|
202
|
+
/// The index it belongs to, set by its first write. A `Weak` keeps the shared state's
|
|
203
|
+
/// allocation reserved, so another index can't reuse its address while this lives
|
|
204
|
+
/// (cli692 on #1412).
|
|
205
|
+
index: Option<std::sync::Weak<Inner>>,
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
impl PartialEq for Placed {
|
|
209
|
+
fn eq(&self, other: &Self) -> bool {
|
|
210
|
+
self.lists == other.lists
|
|
211
|
+
&& self.ids == other.ids
|
|
212
|
+
&& self.bootstrapped == other.bootstrapped
|
|
213
|
+
&& match (&self.index, &other.index) {
|
|
214
|
+
(None, None) => true,
|
|
215
|
+
(Some(a), Some(b)) => a.ptr_eq(b),
|
|
216
|
+
_ => false,
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
impl Eq for Placed {}
|
|
222
|
+
|
|
223
|
+
// A `Placed` crosses threads with the transaction it records (cli692 on #1412).
|
|
224
|
+
const _: fn() = || {
|
|
225
|
+
fn send<T: Send + Sync>() {}
|
|
226
|
+
send::<Placed>();
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
impl Placed {
|
|
230
|
+
/// No writes yet.
|
|
231
|
+
pub fn new() -> Self {
|
|
232
|
+
Self::default()
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/// Binds this record to `index` on first use; another index is `InvalidArgument`.
|
|
236
|
+
fn bind(&mut self, index: &Index) -> Result<()> {
|
|
237
|
+
match &self.index {
|
|
238
|
+
None => {
|
|
239
|
+
self.index = Some(Arc::downgrade(&index.inner));
|
|
240
|
+
Ok(())
|
|
241
|
+
}
|
|
242
|
+
Some(i) if std::ptr::eq(i.as_ptr(), Arc::as_ptr(&index.inner)) => Ok(()),
|
|
243
|
+
Some(_) => Err(Error::InvalidArgument(
|
|
244
|
+
"a Placed records one index's writes: make one per index and transaction".into(),
|
|
245
|
+
)),
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
fn wrote(&mut self, id: &[u8], now: Option<u32>, lists: impl IntoIterator<Item = u32>) {
|
|
250
|
+
for l in lists {
|
|
251
|
+
if !self.lists.contains(&l) {
|
|
252
|
+
self.lists.push(l);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
self.ids.insert(id.to_vec(), now);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/// What a bulk load did ([`Index::bulk_load_f32`] and its forms).
|
|
260
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
261
|
+
#[non_exhaustive]
|
|
262
|
+
pub struct BulkStats {
|
|
263
|
+
/// Vectors written (a repeated id counts once per occurrence).
|
|
264
|
+
pub vectors: u64,
|
|
265
|
+
/// Lists k-means made before the load (splits and merges may change the count after).
|
|
266
|
+
pub lists: u32,
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/// Vectors a bulk load clusters to choose its lists: the first this many it reads.
|
|
270
|
+
const BULK_SAMPLE: usize = 50_000;
|
|
271
|
+
/// Vectors per transaction when a bulk load writes a list.
|
|
272
|
+
const BULK_BATCH: usize = 256;
|
|
273
|
+
/// Lloyd iterations of a bulk load's k-means.
|
|
274
|
+
const KMEANS_ITERATIONS: usize = 10;
|
|
275
|
+
|
|
276
|
+
/// Counts from [`Index::verify`].
|
|
277
|
+
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
278
|
+
#[non_exhaustive]
|
|
279
|
+
pub struct IndexStats {
|
|
280
|
+
/// Each list's length, in list order.
|
|
281
|
+
pub list_lens: Vec<u64>,
|
|
282
|
+
/// Vectors in the index.
|
|
283
|
+
pub vectors: u64,
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/// An IVF index over one table (D306). Cheap to clone; clones share the centroid cache.
|
|
287
|
+
///
|
|
288
|
+
/// ```
|
|
289
|
+
/// use pigeonhole::{Element, Metric, Options, Pigeonhole, VectorSpec};
|
|
290
|
+
/// use pigeonhole_vector::{Index, IndexOptions};
|
|
291
|
+
///
|
|
292
|
+
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
293
|
+
/// # let dir = pigeonhole::doc_support::temp_dir();
|
|
294
|
+
/// let db = Pigeonhole::open(dir.join("app.phdb"), Options::default())?;
|
|
295
|
+
/// let spec = VectorSpec::new(2, Element::F32, Metric::L2)?;
|
|
296
|
+
/// let index = Index::create(&db, "points", spec, IndexOptions::new().target_list_len(4))?;
|
|
297
|
+
/// for i in 0..40u32 {
|
|
298
|
+
/// let x = i as f32;
|
|
299
|
+
/// index.insert_f32(&i.to_be_bytes(), &[x, -x])?;
|
|
300
|
+
/// }
|
|
301
|
+
/// let hits = index.search_f32(&[10.2, -10.2], 3).nprobe(2).run()?;
|
|
302
|
+
/// assert_eq!(hits[0].id, 10u32.to_be_bytes());
|
|
303
|
+
///
|
|
304
|
+
/// assert!(index.delete(&10u32.to_be_bytes())?);
|
|
305
|
+
/// let stats = index.verify()?;
|
|
306
|
+
/// assert_eq!(stats.vectors, 39);
|
|
307
|
+
/// # db.close()?;
|
|
308
|
+
/// # Ok(())
|
|
309
|
+
/// # }
|
|
310
|
+
/// ```
|
|
311
|
+
#[derive(Clone)]
|
|
312
|
+
pub struct Index {
|
|
313
|
+
inner: Arc<Inner>,
|
|
314
|
+
/// Counts the application's handles: a rebalancer's own temporary handle has a token of
|
|
315
|
+
/// its own, so it can tell when every application handle is gone (simp724's R1 on
|
|
316
|
+
/// #1271).
|
|
317
|
+
user: Arc<()>,
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
impl std::fmt::Debug for Index {
|
|
321
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
322
|
+
f.debug_struct("Index")
|
|
323
|
+
.field("table", &self.inner.table.name())
|
|
324
|
+
.field("spec", &self.inner.spec)
|
|
325
|
+
.field("options", &self.inner.opts)
|
|
326
|
+
.finish_non_exhaustive()
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
pub(crate) struct Inner {
|
|
331
|
+
db: Pigeonhole,
|
|
332
|
+
table: Table,
|
|
333
|
+
spec: VectorSpec,
|
|
334
|
+
/// The centroid family's spec: `f32` of the index's dimension and metric, or the
|
|
335
|
+
/// index's own for bits (rev 2 S1).
|
|
336
|
+
cen: VectorSpec,
|
|
337
|
+
opts: IndexOptions,
|
|
338
|
+
/// The centroids as last loaded at the current layout (rev 3 S4).
|
|
339
|
+
layout: RwLock<Arc<Layout>>,
|
|
340
|
+
/// Lists with a split or merge running in this process (rev 4).
|
|
341
|
+
in_flight: Mutex<HashSet<u32>>,
|
|
342
|
+
/// Lists to check against their bounds: after writes, and found by `open` (rev 3 M3).
|
|
343
|
+
pending: Mutex<VecDeque<u32>>,
|
|
344
|
+
/// Wakes the background rebalancer, and tells it when the last handle is gone.
|
|
345
|
+
signal: Arc<Signal>,
|
|
346
|
+
/// The crate's rebalancing thread was started for this index ([`Index::open`] in
|
|
347
|
+
/// background mode): one per index, however many opens share it (#1268 item 8).
|
|
348
|
+
thread: AtomicBool,
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
impl Inner {
|
|
352
|
+
pub(crate) fn db(&self) -> &Pigeonhole {
|
|
353
|
+
&self.db
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/// Whether this is still the index its table name names: its options row reads through
|
|
357
|
+
/// its own table handle. A dropped table (perhaps created again since) or a closed
|
|
358
|
+
/// database refuses the read (the registry's stale-entry check, #1268 item 8).
|
|
359
|
+
pub(crate) fn is_live(&self) -> bool {
|
|
360
|
+
matches!(self.table.get(GLOBAL, META, OPTS), Ok(Some(_)))
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
impl Drop for Inner {
|
|
365
|
+
fn drop(&mut self) {
|
|
366
|
+
self.signal.close();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/// The centroids at one snapshot.
|
|
371
|
+
#[derive(Debug)]
|
|
372
|
+
struct Layout {
|
|
373
|
+
seqno: u64,
|
|
374
|
+
/// `G:layout`: centroids change only with it.
|
|
375
|
+
version: u64,
|
|
376
|
+
/// By list id.
|
|
377
|
+
lists: Vec<List>,
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
#[derive(Debug)]
|
|
381
|
+
struct List {
|
|
382
|
+
id: u32,
|
|
383
|
+
version: u64,
|
|
384
|
+
centroid: Vector,
|
|
385
|
+
point: Point,
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
impl Layout {
|
|
389
|
+
fn list(&self, id: u32) -> Option<&List> {
|
|
390
|
+
self.lists
|
|
391
|
+
.binary_search_by_key(&id, |l| l.id)
|
|
392
|
+
.ok()
|
|
393
|
+
.map(|i| &self.lists[i])
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/// The `n` lists nearest `p`, nearest first, ties by list id, leaving out `skip`.
|
|
397
|
+
fn nearest(&self, cen: &VectorSpec, p: &Point, n: usize, skip: &[u32]) -> Result<Vec<u32>> {
|
|
398
|
+
let mut all = Vec::with_capacity(self.lists.len());
|
|
399
|
+
for l in self.lists.iter().filter(|l| !skip.contains(&l.id)) {
|
|
400
|
+
all.push((distance(cen, p, &l.centroid)?, l.id));
|
|
401
|
+
}
|
|
402
|
+
all.sort_by(|a, b| a.0.total_cmp(&b.0).then(a.1.cmp(&b.1)));
|
|
403
|
+
Ok(all.into_iter().take(n).map(|(_, id)| id).collect())
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/// How a transaction body ends besides committing.
|
|
408
|
+
enum Tx {
|
|
409
|
+
Db(pigeonhole::Error),
|
|
410
|
+
/// The layout it acted on is gone: reload the centroids and try again, or stop.
|
|
411
|
+
Stale,
|
|
412
|
+
Fail(Error),
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
impl From<pigeonhole::Error> for Tx {
|
|
416
|
+
fn from(e: pigeonhole::Error) -> Self {
|
|
417
|
+
Tx::Db(e)
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
impl From<Error> for Tx {
|
|
422
|
+
fn from(e: Error) -> Self {
|
|
423
|
+
Tx::Fail(e)
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
impl From<Tx> for Error {
|
|
428
|
+
fn from(t: Tx) -> Self {
|
|
429
|
+
match t {
|
|
430
|
+
Tx::Db(e) => Error::Db(e),
|
|
431
|
+
Tx::Stale => Error::Conflict("the index's layout changed".into()),
|
|
432
|
+
Tx::Fail(e) => e,
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/// A retry's pause: 5 µs doubling, at most 1 ms.
|
|
438
|
+
fn backoff(attempt: usize) -> Duration {
|
|
439
|
+
Duration::from_micros(5u64 << attempt.min(8)).min(Duration::from_millis(1))
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
fn is_conflict(t: &Tx) -> bool {
|
|
443
|
+
matches!(t, Tx::Db(e) if e.code() == ErrorCode::Conflict)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/// A transaction's snapshot shows `M{id}` naming a list with no centroid. A merge moves a
|
|
447
|
+
/// list's vectors and mappings and deletes its centroid in one transaction that reads the
|
|
448
|
+
/// list's whole row, so no consistent snapshot shows this: it's what `verify` reports as
|
|
449
|
+
/// corruption (cli692's B2 on #1412).
|
|
450
|
+
fn unmapped() -> Error {
|
|
451
|
+
corrupt("a mapping names a list with no centroid")
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
fn corrupt(what: &str) -> Error {
|
|
455
|
+
Error::Corruption(what.to_owned())
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/// The centroid family's spec for an index of `spec` (rev 2 S1).
|
|
459
|
+
fn centroid_spec(spec: &VectorSpec) -> Result<VectorSpec> {
|
|
460
|
+
match spec.element() {
|
|
461
|
+
Element::Bit => Ok(*spec),
|
|
462
|
+
// Half indexes keep `f32` centroids too (#1268 item 7): a mean of halves isn't a half.
|
|
463
|
+
Element::F32 | Element::I8 | Element::F16 | Element::BF16 => {
|
|
464
|
+
Ok(VectorSpec::new(spec.dim(), Element::F32, spec.metric())?)
|
|
465
|
+
}
|
|
466
|
+
other => Err(Error::InvalidArgument(format!(
|
|
467
|
+
"an index of {other:?} vectors is not supported"
|
|
468
|
+
))),
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
fn read_u64(
|
|
473
|
+
tx: &mut Transaction,
|
|
474
|
+
t: &Table,
|
|
475
|
+
row: &[u8],
|
|
476
|
+
family: &str,
|
|
477
|
+
q: &[u8],
|
|
478
|
+
) -> std::result::Result<Option<u64>, Tx> {
|
|
479
|
+
match tx.get(t, row, family, q)? {
|
|
480
|
+
None => Ok(None),
|
|
481
|
+
Some(c) => c
|
|
482
|
+
.as_u64()
|
|
483
|
+
.map(Some)
|
|
484
|
+
.ok_or_else(|| Tx::Fail(corrupt("an index counter or version is not an integer"))),
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/// `C{list}:meta:v`, or `None` when the list doesn't exist (merged away, rev 3 M1).
|
|
489
|
+
fn read_version(
|
|
490
|
+
tx: &mut Transaction,
|
|
491
|
+
t: &Table,
|
|
492
|
+
list: u32,
|
|
493
|
+
) -> std::result::Result<Option<u64>, Tx> {
|
|
494
|
+
read_u64(tx, t, ¢roid_row(list), META, VERSION)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/// `M{id}`: the list holding `id`.
|
|
498
|
+
fn read_list_of(
|
|
499
|
+
tx: &mut Transaction,
|
|
500
|
+
t: &Table,
|
|
501
|
+
id: &[u8],
|
|
502
|
+
) -> std::result::Result<Option<u32>, Tx> {
|
|
503
|
+
read_u64(tx, t, &map_row(id), MAP, LIST)?
|
|
504
|
+
.map(|l| u32::try_from(l).map_err(|_| Tx::Fail(corrupt("a mapping names no list"))))
|
|
505
|
+
.transpose()
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/// The vector `id` holds in the list row `row`, read in the transaction (rev 4 N1).
|
|
509
|
+
fn read_stored(
|
|
510
|
+
tx: &mut Transaction,
|
|
511
|
+
t: &Table,
|
|
512
|
+
row: &[u8],
|
|
513
|
+
id: &[u8],
|
|
514
|
+
) -> std::result::Result<Option<Stored>, Tx> {
|
|
515
|
+
match tx.get(t, row, VEC, id)? {
|
|
516
|
+
None => Ok(None),
|
|
517
|
+
Some(c) => {
|
|
518
|
+
let v = c
|
|
519
|
+
.as_vector()
|
|
520
|
+
.ok_or_else(|| Tx::Fail(corrupt("a list column is not a vector")))?;
|
|
521
|
+
Ok(Some(Stored::of(v)?))
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/// Writes `id`'s vector into list `l`, moving it out of `old` (the list `id` is in) when
|
|
527
|
+
/// that's another list. The caller has read `l`'s version in `tx`. With `check_old` (`old`
|
|
528
|
+
/// came from `M{id}` at `tx`'s snapshot) this reads `old`'s version, and a mapping to a
|
|
529
|
+
/// list with no centroid is `Stale`; without it, `old` is the list this transaction itself
|
|
530
|
+
/// wrote `id` to.
|
|
531
|
+
fn write_into(
|
|
532
|
+
tx: &mut Transaction,
|
|
533
|
+
t: &Table,
|
|
534
|
+
l: u32,
|
|
535
|
+
old: Option<u32>,
|
|
536
|
+
check_old: bool,
|
|
537
|
+
id: &[u8],
|
|
538
|
+
v: Input<'_>,
|
|
539
|
+
) -> std::result::Result<(), Tx> {
|
|
540
|
+
match old {
|
|
541
|
+
// An upsert within its list: the vector only (M2).
|
|
542
|
+
Some(o) if o == l => v.put(tx, t, &list_row(l), id),
|
|
543
|
+
Some(o) => {
|
|
544
|
+
if check_old && read_version(tx, t, o)?.is_none() {
|
|
545
|
+
return Err(Tx::Stale);
|
|
546
|
+
}
|
|
547
|
+
tx.delete_column(t, &list_row(o), VEC, id);
|
|
548
|
+
v.put(tx, t, &list_row(l), id);
|
|
549
|
+
tx.put_u64(t, &map_row(id), MAP, LIST, u64::from(l))
|
|
550
|
+
.incr(t, &count_row(o), CNT, b"", -1)
|
|
551
|
+
.incr(t, &count_row(l), CNT, b"", 1);
|
|
552
|
+
}
|
|
553
|
+
None => {
|
|
554
|
+
v.put(tx, t, &list_row(l), id);
|
|
555
|
+
tx.put_u64(t, &map_row(id), MAP, LIST, u64::from(l)).incr(
|
|
556
|
+
t,
|
|
557
|
+
&count_row(l),
|
|
558
|
+
CNT,
|
|
559
|
+
b"",
|
|
560
|
+
1,
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
Ok(())
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/// The bootstrap's writes: list 0, centred on `centroid`, holding `id`. The caller has read
|
|
568
|
+
/// `G:meta:next` as 0 in `tx`.
|
|
569
|
+
fn write_bootstrap(tx: &mut Transaction, t: &Table, centroid: &Point, id: &[u8], v: Input<'_>) {
|
|
570
|
+
put_centroid(tx, t, 0, centroid);
|
|
571
|
+
tx.put_u64(t, ¢roid_row(0), META, VERSION, 1)
|
|
572
|
+
.put_u64(t, GLOBAL, META, NEXT, 1)
|
|
573
|
+
.put_u64(t, GLOBAL, META, LAYOUT, 1);
|
|
574
|
+
v.put(tx, t, &list_row(0), id);
|
|
575
|
+
tx.put_u64(t, &map_row(id), MAP, LIST, 0)
|
|
576
|
+
.incr(t, &count_row(0), CNT, b"", 1);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
fn put_centroid(tx: &mut Transaction, t: &Table, list: u32, c: &Point) {
|
|
580
|
+
let row = centroid_row(list);
|
|
581
|
+
match c {
|
|
582
|
+
Point::F32(x) => tx.put_vector_f32(t, &row, CEN, b"", x),
|
|
583
|
+
Point::Bits(b) => tx.put_vector_bits(t, &row, CEN, b"", b),
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/// The centroids at `s`.
|
|
588
|
+
/// The reads a search and a centroid load make, from the writer's table or a reader
|
|
589
|
+
/// process's (#1268 item 5, [`ReadIndex`]).
|
|
590
|
+
pub(crate) trait Rows: Sync {
|
|
591
|
+
/// `G:layout` at `s`.
|
|
592
|
+
fn layout_version(&self, s: &Snapshot) -> Result<u64>;
|
|
593
|
+
/// [`layout_version`](Self::layout_version), without blocking on the device.
|
|
594
|
+
#[cfg(feature = "async")]
|
|
595
|
+
fn layout_version_async(&self, s: &Snapshot) -> pigeonhole::nonblocking::GetFuture;
|
|
596
|
+
/// Every `C{list}` row's centroid and metadata.
|
|
597
|
+
fn centroids(&self) -> Scan<'_>;
|
|
598
|
+
/// The rows of `keys`.
|
|
599
|
+
fn rows_of<'t>(&'t self, keys: &KeySet) -> KeysRead<'t>;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
impl Rows for Table {
|
|
603
|
+
fn layout_version(&self, s: &Snapshot) -> Result<u64> {
|
|
604
|
+
Ok(self
|
|
605
|
+
.get_at(s, GLOBAL, META, LAYOUT)?
|
|
606
|
+
.and_then(|c| c.as_u64())
|
|
607
|
+
.unwrap_or(0))
|
|
608
|
+
}
|
|
609
|
+
#[cfg(feature = "async")]
|
|
610
|
+
fn layout_version_async(&self, s: &Snapshot) -> pigeonhole::nonblocking::GetFuture {
|
|
611
|
+
self.get_at_async(s, GLOBAL, META, LAYOUT)
|
|
612
|
+
}
|
|
613
|
+
fn centroids(&self) -> Scan<'_> {
|
|
614
|
+
self.scan_prefix(b"C").families([CEN, META])
|
|
615
|
+
}
|
|
616
|
+
fn rows_of<'t>(&'t self, keys: &KeySet) -> KeysRead<'t> {
|
|
617
|
+
self.keys(keys)
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
impl Rows for ReadTable {
|
|
622
|
+
fn layout_version(&self, s: &Snapshot) -> Result<u64> {
|
|
623
|
+
Ok(self
|
|
624
|
+
.get_at(s, GLOBAL, META, LAYOUT)?
|
|
625
|
+
.and_then(|c| c.as_u64())
|
|
626
|
+
.unwrap_or(0))
|
|
627
|
+
}
|
|
628
|
+
#[cfg(feature = "async")]
|
|
629
|
+
fn layout_version_async(&self, s: &Snapshot) -> pigeonhole::nonblocking::GetFuture {
|
|
630
|
+
self.get_at_async(s, GLOBAL, META, LAYOUT)
|
|
631
|
+
}
|
|
632
|
+
fn centroids(&self) -> Scan<'_> {
|
|
633
|
+
self.scan_prefix(b"C").families([CEN, META])
|
|
634
|
+
}
|
|
635
|
+
fn rows_of<'t>(&'t self, keys: &KeySet) -> KeysRead<'t> {
|
|
636
|
+
self.keys(keys)
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
fn load(t: &impl Rows, cen: &VectorSpec, s: &Snapshot) -> Result<Layout> {
|
|
641
|
+
let version = t.layout_version(s)?;
|
|
642
|
+
let mut lists = Vec::new();
|
|
643
|
+
for row in t.centroids().snapshot(s).iter()? {
|
|
644
|
+
lists.push(list_of_row(&row?, cen)?);
|
|
645
|
+
}
|
|
646
|
+
Ok(Layout {
|
|
647
|
+
seqno: s.seqno(),
|
|
648
|
+
version,
|
|
649
|
+
lists,
|
|
650
|
+
})
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/// The centroids at `s`, as [`load`] reads them, without blocking on the device.
|
|
654
|
+
#[cfg(feature = "async")]
|
|
655
|
+
async fn load_async(t: &impl Rows, cen: &VectorSpec, s: &Snapshot) -> Result<Layout> {
|
|
656
|
+
use futures_core::Stream;
|
|
657
|
+
let version = t
|
|
658
|
+
.layout_version_async(s)
|
|
659
|
+
.await?
|
|
660
|
+
.and_then(|c| c.as_u64())
|
|
661
|
+
.unwrap_or(0);
|
|
662
|
+
let mut rows = std::pin::pin!(t.centroids().snapshot(s).stream());
|
|
663
|
+
let mut lists = Vec::new();
|
|
664
|
+
while let Some(row) = std::future::poll_fn(|cx| rows.as_mut().poll_next(cx)).await {
|
|
665
|
+
lists.push(list_of_row(&row?, cen)?);
|
|
666
|
+
}
|
|
667
|
+
Ok(Layout {
|
|
668
|
+
seqno: s.seqno(),
|
|
669
|
+
version,
|
|
670
|
+
lists,
|
|
671
|
+
})
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/// The layout at `s` for a search: the shared cache when `G:layout` still matches it, else
|
|
675
|
+
/// a fresh load, which replaces the cache when the search is at the current snapshot and the
|
|
676
|
+
/// cache is older (rev 3 S4).
|
|
677
|
+
fn layout_at(
|
|
678
|
+
t: &impl Rows,
|
|
679
|
+
cen: &VectorSpec,
|
|
680
|
+
cache: &RwLock<Arc<Layout>>,
|
|
681
|
+
s: &Snapshot,
|
|
682
|
+
current: Option<&dyn Fn() -> Option<u64>>,
|
|
683
|
+
) -> Result<Arc<Layout>> {
|
|
684
|
+
let shared = cached(cache);
|
|
685
|
+
if t.layout_version(s)? == shared.version {
|
|
686
|
+
return Ok(shared);
|
|
687
|
+
}
|
|
688
|
+
Ok(adopt(cache, load(t, cen, s)?, current))
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/// [`layout_at`], without blocking on the device.
|
|
692
|
+
#[cfg(feature = "async")]
|
|
693
|
+
async fn layout_at_async(
|
|
694
|
+
t: &impl Rows,
|
|
695
|
+
cen: &VectorSpec,
|
|
696
|
+
cache: &RwLock<Arc<Layout>>,
|
|
697
|
+
s: &Snapshot,
|
|
698
|
+
current: Option<&(dyn Fn() -> Option<u64> + Sync)>,
|
|
699
|
+
) -> Result<Arc<Layout>> {
|
|
700
|
+
let shared = cached(cache);
|
|
701
|
+
let version = t
|
|
702
|
+
.layout_version_async(s)
|
|
703
|
+
.await?
|
|
704
|
+
.and_then(|c| c.as_u64())
|
|
705
|
+
.unwrap_or(0);
|
|
706
|
+
if version == shared.version {
|
|
707
|
+
return Ok(shared);
|
|
708
|
+
}
|
|
709
|
+
// Converted after the await: the future holds only the `Sync` reference across it.
|
|
710
|
+
let fresh = load_async(t, cen, s).await?;
|
|
711
|
+
Ok(adopt(
|
|
712
|
+
cache,
|
|
713
|
+
fresh,
|
|
714
|
+
current.map(|f| f as &dyn Fn() -> Option<u64>),
|
|
715
|
+
))
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
fn cached(cache: &RwLock<Arc<Layout>>) -> Arc<Layout> {
|
|
719
|
+
Arc::clone(&cache.read().unwrap_or_else(PoisonError::into_inner))
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
/// A layout a search loaded replaces the shared cache when the search is at the current
|
|
723
|
+
/// snapshot (`latest` given: the latest visible seqno now) and the cache is older (rev 3
|
|
724
|
+
/// S4), so a slower search at an earlier snapshot never overwrites a newer layout. Or when
|
|
725
|
+
/// the cache is from **beyond** the latest visible seqno: commits the writer lost (a
|
|
726
|
+
/// restart after a power loss took a visible tail, perf287 on #1385). The cache then can't
|
|
727
|
+
/// be current, and without this every search would reload the centroids until the writer
|
|
728
|
+
/// passed the lost seqno again.
|
|
729
|
+
fn adopt(
|
|
730
|
+
cache: &RwLock<Arc<Layout>>,
|
|
731
|
+
fresh: Layout,
|
|
732
|
+
latest: Option<&dyn Fn() -> Option<u64>>,
|
|
733
|
+
) -> Arc<Layout> {
|
|
734
|
+
let fresh = Arc::new(fresh);
|
|
735
|
+
if let Some(latest) = latest {
|
|
736
|
+
let mut w = cache.write().unwrap_or_else(PoisonError::into_inner);
|
|
737
|
+
if fresh.seqno >= w.seqno || latest().is_some_and(|now| w.seqno > now) {
|
|
738
|
+
*w = Arc::clone(&fresh);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
fresh
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/// One `C{list}` row's list.
|
|
745
|
+
fn list_of_row(row: &pigeonhole::Row, cen: &VectorSpec) -> Result<List> {
|
|
746
|
+
let id = list_of(row.key()).ok_or_else(|| corrupt("a centroid row key"))?;
|
|
747
|
+
let v = row
|
|
748
|
+
.get(CEN, b"")
|
|
749
|
+
.and_then(|c| c.as_vector())
|
|
750
|
+
.ok_or_else(|| corrupt("a list has no centroid"))?;
|
|
751
|
+
let point = Point::of(v);
|
|
752
|
+
let version = row
|
|
753
|
+
.get(META, VERSION)
|
|
754
|
+
.and_then(|c| c.as_u64())
|
|
755
|
+
.ok_or_else(|| corrupt("a list has no version"))?;
|
|
756
|
+
Ok(List {
|
|
757
|
+
id,
|
|
758
|
+
version,
|
|
759
|
+
centroid: point.to_vector(cen.dim())?,
|
|
760
|
+
point,
|
|
761
|
+
})
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/// `k` centroids of `points` by Lloyd's k-means (D306 §5), seeded deterministically with
|
|
765
|
+
/// evenly spaced points; a cluster that empties keeps its centroid.
|
|
766
|
+
fn kmeans(cen: &VectorSpec, points: &[Point], k: usize) -> Result<Vec<Point>> {
|
|
767
|
+
let metric = cen.metric();
|
|
768
|
+
let dim = cen.dim();
|
|
769
|
+
let mut centroids: Vec<Point> = (0..k)
|
|
770
|
+
.map(|i| crate::point::unit(metric, points[i * points.len() / k].clone()))
|
|
771
|
+
.collect();
|
|
772
|
+
let mut owner = vec![0usize; points.len()];
|
|
773
|
+
for _ in 0..KMEANS_ITERATIONS {
|
|
774
|
+
let vectors = centroids
|
|
775
|
+
.iter()
|
|
776
|
+
.map(|c| c.to_vector(dim))
|
|
777
|
+
.collect::<Result<Vec<_>>>()?;
|
|
778
|
+
let mut changed = false;
|
|
779
|
+
for (p, o) in points.iter().zip(owner.iter_mut()) {
|
|
780
|
+
let mut best = (f64::INFINITY, 0);
|
|
781
|
+
for (j, c) in vectors.iter().enumerate() {
|
|
782
|
+
let d = distance(cen, p, c)?;
|
|
783
|
+
if d < best.0 {
|
|
784
|
+
best = (d, j);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
changed |= *o != best.1;
|
|
788
|
+
*o = best.1;
|
|
789
|
+
}
|
|
790
|
+
for (j, c) in centroids.iter_mut().enumerate() {
|
|
791
|
+
let members = points
|
|
792
|
+
.iter()
|
|
793
|
+
.zip(&owner)
|
|
794
|
+
.filter(|(_, o)| **o == j)
|
|
795
|
+
.map(|(p, _)| p);
|
|
796
|
+
if let Some(m) = crate::point::mean(metric, members) {
|
|
797
|
+
*c = m;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
if !changed {
|
|
801
|
+
break;
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
Ok(centroids)
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
/// Removes its list from the in-flight set when the split or merge ends, however it ends.
|
|
808
|
+
struct Claim<'a> {
|
|
809
|
+
set: &'a Mutex<HashSet<u32>>,
|
|
810
|
+
list: u32,
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
impl Drop for Claim<'_> {
|
|
814
|
+
fn drop(&mut self) {
|
|
815
|
+
self.set
|
|
816
|
+
.lock()
|
|
817
|
+
.unwrap_or_else(PoisonError::into_inner)
|
|
818
|
+
.remove(&self.list);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
impl Index {
|
|
823
|
+
/// Creates the index `name`, a new table, for vectors of `spec`, and opens it as
|
|
824
|
+
/// [`open`](Self::open) does.
|
|
825
|
+
///
|
|
826
|
+
/// The table holds the index's rows only; don't write to it directly. Fails with
|
|
827
|
+
/// `TableExists` if a table of that name exists.
|
|
828
|
+
pub fn create(
|
|
829
|
+
db: &Pigeonhole,
|
|
830
|
+
name: &str,
|
|
831
|
+
spec: VectorSpec,
|
|
832
|
+
options: IndexOptions,
|
|
833
|
+
) -> Result<Index> {
|
|
834
|
+
Self::create_table(db, name, spec, options)?;
|
|
835
|
+
Self::open(db, name)
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/// [`create`](Self::create), opened as
|
|
839
|
+
/// [`open_with_rebalancer`](Self::open_with_rebalancer) is.
|
|
840
|
+
pub fn create_with_rebalancer(
|
|
841
|
+
db: &Pigeonhole,
|
|
842
|
+
name: &str,
|
|
843
|
+
spec: VectorSpec,
|
|
844
|
+
options: IndexOptions,
|
|
845
|
+
) -> Result<(Index, Rebalancer)> {
|
|
846
|
+
Self::create_table(db, name, spec, options)?;
|
|
847
|
+
Self::open_with_rebalancer(db, name)
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
fn create_table(
|
|
851
|
+
db: &Pigeonhole,
|
|
852
|
+
name: &str,
|
|
853
|
+
spec: VectorSpec,
|
|
854
|
+
options: IndexOptions,
|
|
855
|
+
) -> Result<()> {
|
|
856
|
+
let opts = options.checked(&spec)?;
|
|
857
|
+
let cen = centroid_spec(&spec)?;
|
|
858
|
+
let table = db
|
|
859
|
+
.table(name)?
|
|
860
|
+
.family(VEC, Family::vector(spec).max_versions(1))
|
|
861
|
+
.family(CEN, Family::vector(cen).max_versions(1))
|
|
862
|
+
.family(META, Family::default().max_versions(1))
|
|
863
|
+
.family(MAP, Family::default().max_versions(1))
|
|
864
|
+
.family(CNT, Family::counter())
|
|
865
|
+
.create()?;
|
|
866
|
+
table
|
|
867
|
+
.mutate(GLOBAL)
|
|
868
|
+
.put(META, OPTS, &opts.encode())
|
|
869
|
+
.put_u64(META, LAYOUT, 0)
|
|
870
|
+
.put_u64(META, NEXT, 0)
|
|
871
|
+
.commit()?;
|
|
872
|
+
Ok(())
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/// Opens the index `name`. Lists a crash left outside their bounds (a write committed,
|
|
876
|
+
/// and its split or merge never ran) are queued, and the next insert or delete
|
|
877
|
+
/// rebalances them, or with [`Rebalance::Background`] the crate's rebalancing thread
|
|
878
|
+
/// does: it starts here, and stops when the last handle to this index drops. An
|
|
879
|
+
/// application that runs its own threads opens with
|
|
880
|
+
/// [`open_with_rebalancer`](Self::open_with_rebalancer) instead: in an
|
|
881
|
+
/// application-owned database (D284) the crate shouldn't start threads of its own.
|
|
882
|
+
///
|
|
883
|
+
/// An index already open in this process (an application handle to it still alive) is
|
|
884
|
+
/// not opened again: `open` returns a clone of that handle, sharing its centroid cache,
|
|
885
|
+
/// its queue of lists to rebalance and its one rebalancing thread (#1268 item 8). "The
|
|
886
|
+
/// same index" is the same table name in the same open database
|
|
887
|
+
/// ([`Pigeonhole::same_database`]), and a table that was dropped since (and perhaps
|
|
888
|
+
/// created again) is opened afresh.
|
|
889
|
+
///
|
|
890
|
+
/// The rebalancing thread holds the index, and with it a database handle, until it sees
|
|
891
|
+
/// the last [`Index`] handle drop, at most one split later. To reopen the database in
|
|
892
|
+
/// the same process, [`close`](Pigeonhole::close) it first: a closed database refuses
|
|
893
|
+
/// the thread's next write, and the thread ends.
|
|
894
|
+
pub fn open(db: &Pigeonhole, name: &str) -> Result<Index> {
|
|
895
|
+
let index = Self::registered(db, name)?;
|
|
896
|
+
if index.inner.opts.rebalance == Rebalance::Background
|
|
897
|
+
&& !index.inner.thread.swap(true, Ordering::AcqRel)
|
|
898
|
+
{
|
|
899
|
+
let started =
|
|
900
|
+
crate::rebalancer::spawn(Rebalancer::new(&index, Arc::clone(&index.inner.signal)));
|
|
901
|
+
if let Err(e) = started {
|
|
902
|
+
// A later open tries again.
|
|
903
|
+
index.inner.thread.store(false, Ordering::Release);
|
|
904
|
+
return Err(Error::InvalidArgument(format!(
|
|
905
|
+
"starting the rebalancer: {e}"
|
|
906
|
+
)));
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
Ok(index)
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
/// Opens the index `name` with its [`Rebalancer`] handed to the caller, and no thread of
|
|
913
|
+
/// the crate's own: for an application that runs its threads itself (an
|
|
914
|
+
/// application-owned database, D284). With [`Rebalance::Background`] the application
|
|
915
|
+
/// runs the rebalancer; with [`Rebalance::Inline`] the writes rebalance, and the
|
|
916
|
+
/// rebalancer only finds what `open` queued.
|
|
917
|
+
///
|
|
918
|
+
/// As with [`open`](Self::open), an index already open in this process shares its state:
|
|
919
|
+
/// the handle is a clone of the open one, and the rebalancer takes from the same queue as
|
|
920
|
+
/// any other (each list is split or merged by one of them at a time). They share one
|
|
921
|
+
/// [`set_wakeup`](Rebalancer::set_wakeup): the last one set is called. A later
|
|
922
|
+
/// [`open`](Self::open) of the index in background mode starts the crate's thread too,
|
|
923
|
+
/// which takes from the same queue: in an application-owned database, open with this
|
|
924
|
+
/// function only.
|
|
925
|
+
pub fn open_with_rebalancer(db: &Pigeonhole, name: &str) -> Result<(Index, Rebalancer)> {
|
|
926
|
+
let index = Self::registered(db, name)?;
|
|
927
|
+
let rebalancer = Rebalancer::new(&index, Arc::clone(&index.inner.signal));
|
|
928
|
+
Ok((index, rebalancer))
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
/// Opens the index `name` from a reader process ([`PigeonholeReader`], #1268 item 5): a
|
|
932
|
+
/// [`ReadIndex`] that searches, at the reader's snapshots, and never writes. The
|
|
933
|
+
/// centroids load with reads only; the writer process inserts, deletes and rebalances.
|
|
934
|
+
/// The same checks as [`open`](Self::open) refuse a table that isn't a vector index.
|
|
935
|
+
pub fn open_reader(reader: &PigeonholeReader, name: &str) -> Result<ReadIndex> {
|
|
936
|
+
let table = reader.table(name)?;
|
|
937
|
+
let not = |why: &str| {
|
|
938
|
+
Error::InvalidArgument(format!("table {name:?} is not a vector index: {why}"))
|
|
939
|
+
};
|
|
940
|
+
let spec = table
|
|
941
|
+
.family_info(VEC)
|
|
942
|
+
.and_then(|f| f.vector)
|
|
943
|
+
.ok_or_else(|| not("it has no vector family `vec`"))?;
|
|
944
|
+
let cen = centroid_spec(&spec)?;
|
|
945
|
+
if table.family_info(CEN).and_then(|f| f.vector) != Some(cen) {
|
|
946
|
+
return Err(not("its centroid family doesn't match"));
|
|
947
|
+
}
|
|
948
|
+
let opts = table
|
|
949
|
+
.get(GLOBAL, META, OPTS)?
|
|
950
|
+
.and_then(|c| IndexOptions::decode(c.value()))
|
|
951
|
+
.ok_or_else(|| not("it has no options"))?;
|
|
952
|
+
let s = reader.snapshot()?;
|
|
953
|
+
let layout = load(&table, &cen, &s)?;
|
|
954
|
+
Ok(ReadIndex {
|
|
955
|
+
inner: Arc::new(ReadInner {
|
|
956
|
+
db: reader.clone(),
|
|
957
|
+
table,
|
|
958
|
+
spec,
|
|
959
|
+
cen,
|
|
960
|
+
opts,
|
|
961
|
+
layout: RwLock::new(Arc::new(layout)),
|
|
962
|
+
}),
|
|
963
|
+
})
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
/// A rebalancer's temporary handle: not one of the application's.
|
|
967
|
+
pub(crate) fn from_inner(inner: Arc<Inner>) -> Self {
|
|
968
|
+
Index {
|
|
969
|
+
inner,
|
|
970
|
+
user: Arc::new(()),
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
pub(crate) fn inner(&self) -> &Arc<Inner> {
|
|
975
|
+
&self.inner
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
pub(crate) fn user(&self) -> &Arc<()> {
|
|
979
|
+
&self.user
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
/// The index from this process's registry, or opened and registered (#1268 item 8).
|
|
983
|
+
fn registered(db: &Pigeonhole, name: &str) -> Result<Index> {
|
|
984
|
+
let (inner, user) = crate::registry::open(db, name, || {
|
|
985
|
+
let index = Self::open_inner(db, name)?;
|
|
986
|
+
Ok((index.inner, index.user))
|
|
987
|
+
})?;
|
|
988
|
+
Ok(Index { inner, user })
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
fn open_inner(db: &Pigeonhole, name: &str) -> Result<Index> {
|
|
992
|
+
let table = db.table(name)?.open()?;
|
|
993
|
+
let not = |why: &str| {
|
|
994
|
+
Error::InvalidArgument(format!("table {name:?} is not a vector index: {why}"))
|
|
995
|
+
};
|
|
996
|
+
let spec = table
|
|
997
|
+
.family_info(VEC)
|
|
998
|
+
.and_then(|f| f.vector)
|
|
999
|
+
.ok_or_else(|| not("it has no vector family `vec`"))?;
|
|
1000
|
+
let cen = centroid_spec(&spec)?;
|
|
1001
|
+
if table.family_info(CEN).and_then(|f| f.vector) != Some(cen) {
|
|
1002
|
+
return Err(not("its centroid family doesn't match"));
|
|
1003
|
+
}
|
|
1004
|
+
let opts = table
|
|
1005
|
+
.get(GLOBAL, META, OPTS)?
|
|
1006
|
+
.and_then(|c| IndexOptions::decode(c.value()))
|
|
1007
|
+
.ok_or_else(|| not("it has no options"))?;
|
|
1008
|
+
let s = db.snapshot()?;
|
|
1009
|
+
let layout = load(&table, &cen, &s)?;
|
|
1010
|
+
let index = Index {
|
|
1011
|
+
user: Arc::new(()),
|
|
1012
|
+
inner: Arc::new(Inner {
|
|
1013
|
+
db: db.clone(),
|
|
1014
|
+
table,
|
|
1015
|
+
spec,
|
|
1016
|
+
cen,
|
|
1017
|
+
opts,
|
|
1018
|
+
layout: RwLock::new(Arc::new(layout)),
|
|
1019
|
+
in_flight: Mutex::new(HashSet::new()),
|
|
1020
|
+
pending: Mutex::new(VecDeque::new()),
|
|
1021
|
+
signal: Arc::new(Signal::default()),
|
|
1022
|
+
thread: AtomicBool::new(false),
|
|
1023
|
+
}),
|
|
1024
|
+
};
|
|
1025
|
+
index.queue_out_of_bounds(&s)?;
|
|
1026
|
+
Ok(index)
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/// The vectors' spec.
|
|
1030
|
+
pub fn spec(&self) -> VectorSpec {
|
|
1031
|
+
self.inner.spec
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
/// The options it was created with (the target list length as scaled).
|
|
1035
|
+
pub fn options(&self) -> IndexOptions {
|
|
1036
|
+
self.inner.opts
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/// A list splits when it holds more than this many vectors.
|
|
1040
|
+
pub fn max_list_len(&self) -> u64 {
|
|
1041
|
+
2 * u64::from(self.inner.opts.target)
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/// A list merges into its nearest neighbour when it holds fewer than this many
|
|
1045
|
+
/// vectors, unless it is the only list.
|
|
1046
|
+
pub fn min_list_len(&self) -> u64 {
|
|
1047
|
+
u64::from(self.inner.opts.target) / 4
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
/// Inserts the `f32` vector `vector` as `id`, or replaces `id`'s vector (an upsert).
|
|
1051
|
+
/// One transaction, retried on conflicts; a list that grows past its bound is split
|
|
1052
|
+
/// before the call returns. An error after the commit (from that split) leaves the
|
|
1053
|
+
/// insert committed, and retrying it is safe.
|
|
1054
|
+
pub fn insert_f32(&self, id: &[u8], vector: &[f32]) -> Result<()> {
|
|
1055
|
+
self.insert(id, Input::F32(vector))
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
/// [`insert_f32`](Self::insert_f32) for an `i8` index.
|
|
1059
|
+
pub fn insert_i8(&self, id: &[u8], vector: &[i8]) -> Result<()> {
|
|
1060
|
+
self.insert(id, Input::I8(vector))
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
/// [`insert_f32`](Self::insert_f32) for a bit index (bits packed eight to a byte, least
|
|
1064
|
+
/// significant first).
|
|
1065
|
+
pub fn insert_bits(&self, id: &[u8], vector: &[u8]) -> Result<()> {
|
|
1066
|
+
self.insert(id, Input::Bits(vector))
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/// [`insert_f32`](Self::insert_f32) for an `f16` index: `vector` is IEEE binary16 bit
|
|
1070
|
+
/// patterns, stored bit for bit (#1268 item 7). An `f16` or `bf16` index also takes
|
|
1071
|
+
/// [`insert_f32`](Self::insert_f32), rounding each component to the element.
|
|
1072
|
+
pub fn insert_f16(&self, id: &[u8], vector: &[u16]) -> Result<()> {
|
|
1073
|
+
self.insert(id, Input::F16(vector))
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
/// [`insert_f16`](Self::insert_f16) for a `bf16` index (bfloat16 bit patterns).
|
|
1077
|
+
pub fn insert_bf16(&self, id: &[u8], vector: &[u16]) -> Result<()> {
|
|
1078
|
+
self.insert(id, Input::BF16(vector))
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/// Deletes `id`'s vector; `false` if there was none. A list that shrinks below its
|
|
1082
|
+
/// bound is merged into its nearest neighbour before the call returns.
|
|
1083
|
+
pub fn delete(&self, id: &[u8]) -> Result<bool> {
|
|
1084
|
+
let inn = &*self.inner;
|
|
1085
|
+
let t = &inn.table;
|
|
1086
|
+
for attempt in 0..RELOADS {
|
|
1087
|
+
let r = inn.db.transact(&RetryPolicy::default(), |tx| {
|
|
1088
|
+
let Some(l) = read_list_of(tx, t, id)? else {
|
|
1089
|
+
return Ok(None);
|
|
1090
|
+
};
|
|
1091
|
+
// A mapping to a list with no centroid can only be seen mid-merge: retry.
|
|
1092
|
+
if read_version(tx, t, l)?.is_none() {
|
|
1093
|
+
return Err(Tx::Stale);
|
|
1094
|
+
}
|
|
1095
|
+
#[cfg(feature = "__unstable-hooks")]
|
|
1096
|
+
crate::hooks::hold(crate::hooks::Hold::DeleteBeforeCommit);
|
|
1097
|
+
tx.delete_column(t, &list_row(l), VEC, id)
|
|
1098
|
+
.delete_column(t, &map_row(id), MAP, LIST)
|
|
1099
|
+
.incr(t, &count_row(l), CNT, b"", -1);
|
|
1100
|
+
Ok(Some(l))
|
|
1101
|
+
});
|
|
1102
|
+
match r {
|
|
1103
|
+
Ok((None, _)) => return Ok(false),
|
|
1104
|
+
Ok((Some(l), _)) => {
|
|
1105
|
+
self.after_write([l])?;
|
|
1106
|
+
return Ok(true);
|
|
1107
|
+
}
|
|
1108
|
+
// Only a merge in flight shows a mapping to a list with no centroid: back off
|
|
1109
|
+
// (capped, as `RetryPolicy`'s pauses) rather than spin against it (#1268).
|
|
1110
|
+
Err(Tx::Stale) => std::thread::sleep(backoff(attempt)),
|
|
1111
|
+
Err(e) => return Err(e.into()),
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
Err(Error::Conflict(
|
|
1115
|
+
"a delete kept finding its list mid-merge".into(),
|
|
1116
|
+
))
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
/// [`insert_f32`](Self::insert_f32) inside the caller's transaction `tx`, so the vector
|
|
1120
|
+
/// commits or rolls back with the caller's other writes (D311 T8). `placed` records the
|
|
1121
|
+
/// transaction's writes to this index: one [`Placed`] per transaction, passed to every
|
|
1122
|
+
/// `*_in` write of it, so writing an id twice in one transaction acts on the first write
|
|
1123
|
+
/// (a transaction's reads don't see its own writes). It neither retries nor splits:
|
|
1124
|
+
/// pass `placed` to [`after_commit`](Self::after_commit) once `tx` commits, so a list
|
|
1125
|
+
/// that grew past its bound is split (or, with [`Rebalance::Background`], marked for
|
|
1126
|
+
/// the rebalancer).
|
|
1127
|
+
///
|
|
1128
|
+
/// ```
|
|
1129
|
+
/// use pigeonhole::{Element, Family, Metric, Options, Pigeonhole, RetryPolicy, VectorSpec};
|
|
1130
|
+
/// use pigeonhole_vector::{Index, IndexOptions, Placed};
|
|
1131
|
+
///
|
|
1132
|
+
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|
1133
|
+
/// # let dir = pigeonhole::doc_support::temp_dir();
|
|
1134
|
+
/// let db = Pigeonhole::open(dir.join("app.phdb"), Options::default())?;
|
|
1135
|
+
/// let spec = VectorSpec::new(2, Element::F32, Metric::L2)?;
|
|
1136
|
+
/// let index = Index::create(&db, "points", spec, IndexOptions::new())?;
|
|
1137
|
+
/// let docs = db.table("docs")?.family("f", Family::default()).create_if_missing()?;
|
|
1138
|
+
/// let (placed, _) = db.transact(&RetryPolicy::default(), |tx| {
|
|
1139
|
+
/// let mut placed = Placed::new();
|
|
1140
|
+
/// tx.put(&docs, b"d1", "f", b"", b"text");
|
|
1141
|
+
/// index.insert_f32_in(tx, &mut placed, b"d1", &[1.0, 2.0])?;
|
|
1142
|
+
/// index.insert_f32_in(tx, &mut placed, b"d1", &[3.0, 4.0])?; // an upsert of it
|
|
1143
|
+
/// Ok::<_, pigeonhole_vector::Error>(placed)
|
|
1144
|
+
/// })?;
|
|
1145
|
+
/// index.after_commit(placed)?;
|
|
1146
|
+
/// assert_eq!(index.verify()?.vectors, 1);
|
|
1147
|
+
/// # db.close()?;
|
|
1148
|
+
/// # Ok(())
|
|
1149
|
+
/// # }
|
|
1150
|
+
/// ```
|
|
1151
|
+
///
|
|
1152
|
+
/// It places against the centroids the index has cached, reloaded once if the list's
|
|
1153
|
+
/// version read in `tx` differs. If it still differs, `tx`'s snapshot predates a split
|
|
1154
|
+
/// or a merge: the vector is written anyway, and the commit fails with `Conflict`
|
|
1155
|
+
/// (retried by [`Pigeonhole::transact`](pigeonhole::Pigeonhole::transact)), because the
|
|
1156
|
+
/// version `tx` read has been rewritten since. A snapshot showing `id` mapped to a list
|
|
1157
|
+
/// with no centroid is [`Error::Corruption`], as [`verify`](Self::verify) reports it: a
|
|
1158
|
+
/// merge is one transaction, so no consistent snapshot shows that.
|
|
1159
|
+
pub fn insert_f32_in(
|
|
1160
|
+
&self,
|
|
1161
|
+
tx: &mut Transaction,
|
|
1162
|
+
placed: &mut Placed,
|
|
1163
|
+
id: &[u8],
|
|
1164
|
+
vector: &[f32],
|
|
1165
|
+
) -> Result<()> {
|
|
1166
|
+
self.insert_in(tx, placed, id, Input::F32(vector))
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
/// [`insert_f32_in`](Self::insert_f32_in) for an `i8` index.
|
|
1170
|
+
pub fn insert_i8_in(
|
|
1171
|
+
&self,
|
|
1172
|
+
tx: &mut Transaction,
|
|
1173
|
+
placed: &mut Placed,
|
|
1174
|
+
id: &[u8],
|
|
1175
|
+
vector: &[i8],
|
|
1176
|
+
) -> Result<()> {
|
|
1177
|
+
self.insert_in(tx, placed, id, Input::I8(vector))
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
/// [`insert_f32_in`](Self::insert_f32_in) for a bit index.
|
|
1181
|
+
pub fn insert_bits_in(
|
|
1182
|
+
&self,
|
|
1183
|
+
tx: &mut Transaction,
|
|
1184
|
+
placed: &mut Placed,
|
|
1185
|
+
id: &[u8],
|
|
1186
|
+
vector: &[u8],
|
|
1187
|
+
) -> Result<()> {
|
|
1188
|
+
self.insert_in(tx, placed, id, Input::Bits(vector))
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
/// [`insert_f32_in`](Self::insert_f32_in) for an `f16` index (bit patterns).
|
|
1192
|
+
pub fn insert_f16_in(
|
|
1193
|
+
&self,
|
|
1194
|
+
tx: &mut Transaction,
|
|
1195
|
+
placed: &mut Placed,
|
|
1196
|
+
id: &[u8],
|
|
1197
|
+
vector: &[u16],
|
|
1198
|
+
) -> Result<()> {
|
|
1199
|
+
self.insert_in(tx, placed, id, Input::F16(vector))
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
/// [`insert_f32_in`](Self::insert_f32_in) for a `bf16` index (bit patterns).
|
|
1203
|
+
pub fn insert_bf16_in(
|
|
1204
|
+
&self,
|
|
1205
|
+
tx: &mut Transaction,
|
|
1206
|
+
placed: &mut Placed,
|
|
1207
|
+
id: &[u8],
|
|
1208
|
+
vector: &[u16],
|
|
1209
|
+
) -> Result<()> {
|
|
1210
|
+
self.insert_in(tx, placed, id, Input::BF16(vector))
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
/// [`delete`](Self::delete) inside the caller's transaction `tx`, recorded in `placed`
|
|
1214
|
+
/// as [`insert_f32_in`](Self::insert_f32_in) records: `false` (and nothing written)
|
|
1215
|
+
/// when there's no vector for `id`, as of this transaction's own earlier writes, else
|
|
1216
|
+
/// as of its snapshot. Pass `placed` to [`after_commit`](Self::after_commit) once `tx`
|
|
1217
|
+
/// commits, so a list that shrank below its bound is merged.
|
|
1218
|
+
pub fn delete_in(&self, tx: &mut Transaction, placed: &mut Placed, id: &[u8]) -> Result<bool> {
|
|
1219
|
+
placed.bind(self)?;
|
|
1220
|
+
let t = &self.inner.table;
|
|
1221
|
+
let l = match placed.ids.get(id).copied() {
|
|
1222
|
+
Some(Some(l)) => l,
|
|
1223
|
+
Some(None) => return Ok(false),
|
|
1224
|
+
None => {
|
|
1225
|
+
let Some(l) = read_list_of(tx, t, id)? else {
|
|
1226
|
+
return Ok(false);
|
|
1227
|
+
};
|
|
1228
|
+
if read_version(tx, t, l)?.is_none() {
|
|
1229
|
+
return Err(unmapped());
|
|
1230
|
+
}
|
|
1231
|
+
l
|
|
1232
|
+
}
|
|
1233
|
+
};
|
|
1234
|
+
tx.delete_column(t, &list_row(l), VEC, id)
|
|
1235
|
+
.delete_column(t, &map_row(id), MAP, LIST)
|
|
1236
|
+
.incr(t, &count_row(l), CNT, b"", -1);
|
|
1237
|
+
placed.wrote(id, None, [l]);
|
|
1238
|
+
Ok(true)
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
/// After the transaction `placed` recorded has committed: splits or merges the lists
|
|
1242
|
+
/// its writes left out of bounds, as [`insert_f32`](Self::insert_f32) and
|
|
1243
|
+
/// [`delete`](Self::delete) do after theirs. It reads the lists' current lengths, so
|
|
1244
|
+
/// calling it after a rollback, twice, or late is harmless, and never calling it only
|
|
1245
|
+
/// delays a rebalance until the next write to those lists. A `Placed` of another index is
|
|
1246
|
+
/// [`Error::InvalidArgument`].
|
|
1247
|
+
pub fn after_commit(&self, mut placed: Placed) -> Result<()> {
|
|
1248
|
+
placed.bind(self)?;
|
|
1249
|
+
self.after_write(placed.lists)
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
/// The `k` vectors nearest the `f32` vector `query`, as a [`Search`] to run.
|
|
1253
|
+
pub fn search_f32<'a>(&'a self, query: &'a [f32], k: usize) -> Search<'a> {
|
|
1254
|
+
Search::new(Of::W(self), Input::F32(query), k)
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
/// [`search_f32`](Self::search_f32) for an `i8` index.
|
|
1258
|
+
pub fn search_i8<'a>(&'a self, query: &'a [i8], k: usize) -> Search<'a> {
|
|
1259
|
+
Search::new(Of::W(self), Input::I8(query), k)
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
/// [`search_f32`](Self::search_f32) for a bit index.
|
|
1263
|
+
pub fn search_bits<'a>(&'a self, query: &'a [u8], k: usize) -> Search<'a> {
|
|
1264
|
+
Search::new(Of::W(self), Input::Bits(query), k)
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
/// [`search_f32`](Self::search_f32) with an `f16` query (bit patterns), for an `f16`
|
|
1268
|
+
/// index. The query widens to `f32` exactly, and the search compares in `f32`, as
|
|
1269
|
+
/// `nearest_f32` does over a half family; [`search_f32`](Self::search_f32) works too.
|
|
1270
|
+
pub fn search_f16<'a>(&'a self, query: &'a [u16], k: usize) -> Search<'a> {
|
|
1271
|
+
Search::new(Of::W(self), Input::F16(query), k)
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
/// [`search_f16`](Self::search_f16) for a `bf16` index (a bfloat16 query).
|
|
1275
|
+
pub fn search_bf16<'a>(&'a self, query: &'a [u16], k: usize) -> Search<'a> {
|
|
1276
|
+
Search::new(Of::W(self), Input::BF16(query), k)
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
/// Loads many vectors into an **empty** index faster than inserting them one by one: it
|
|
1280
|
+
/// clusters the first 50,000 vectors (or more if `expected` needs more lists) with
|
|
1281
|
+
/// k-means into about `expected / target` lists, writes those centroids, then streams
|
|
1282
|
+
/// every vector to its nearest list, a list's vectors written a batch per transaction.
|
|
1283
|
+
/// Lists left out of bounds are then split or merged as the index rebalances
|
|
1284
|
+
/// ([`Rebalance`]).
|
|
1285
|
+
///
|
|
1286
|
+
/// `expected` is about how many vectors you're about to load. It only chooses how many
|
|
1287
|
+
/// lists k-means starts with (`expected / target`, at least one); a wrong estimate costs
|
|
1288
|
+
/// time, not correctness, since the lists then split or merge to their bounds after the
|
|
1289
|
+
/// load. The sample is the input's first vectors, so shuffle an input that is sorted by
|
|
1290
|
+
/// cluster. A repeated id keeps its last vector, and [`BulkStats::vectors`] counts every
|
|
1291
|
+
/// occurrence.
|
|
1292
|
+
///
|
|
1293
|
+
/// Fails with `InvalidArgument` if the index already holds a list, or on the first
|
|
1294
|
+
/// vector of another shape; the vectors before that are loaded.
|
|
1295
|
+
pub fn bulk_load_f32<K, V>(
|
|
1296
|
+
&self,
|
|
1297
|
+
expected: u64,
|
|
1298
|
+
vectors: impl IntoIterator<Item = (K, V)>,
|
|
1299
|
+
) -> Result<BulkStats>
|
|
1300
|
+
where
|
|
1301
|
+
K: AsRef<[u8]>,
|
|
1302
|
+
V: AsRef<[f32]>,
|
|
1303
|
+
{
|
|
1304
|
+
self.bulk(expected, vectors, |v: &V| Input::F32(v.as_ref()))
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
/// [`bulk_load_f32`](Self::bulk_load_f32) for an `i8` index.
|
|
1308
|
+
pub fn bulk_load_i8<K, V>(
|
|
1309
|
+
&self,
|
|
1310
|
+
expected: u64,
|
|
1311
|
+
vectors: impl IntoIterator<Item = (K, V)>,
|
|
1312
|
+
) -> Result<BulkStats>
|
|
1313
|
+
where
|
|
1314
|
+
K: AsRef<[u8]>,
|
|
1315
|
+
V: AsRef<[i8]>,
|
|
1316
|
+
{
|
|
1317
|
+
self.bulk(expected, vectors, |v: &V| Input::I8(v.as_ref()))
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
/// [`bulk_load_f32`](Self::bulk_load_f32) for a bit index.
|
|
1321
|
+
pub fn bulk_load_bits<K, V>(
|
|
1322
|
+
&self,
|
|
1323
|
+
expected: u64,
|
|
1324
|
+
vectors: impl IntoIterator<Item = (K, V)>,
|
|
1325
|
+
) -> Result<BulkStats>
|
|
1326
|
+
where
|
|
1327
|
+
K: AsRef<[u8]>,
|
|
1328
|
+
V: AsRef<[u8]>,
|
|
1329
|
+
{
|
|
1330
|
+
self.bulk(expected, vectors, |v: &V| Input::Bits(v.as_ref()))
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
/// [`bulk_load_f32`](Self::bulk_load_f32) for an `f16` index: each vector is IEEE
|
|
1334
|
+
/// binary16 bit patterns, stored bit for bit, as [`insert_f16`](Self::insert_f16)
|
|
1335
|
+
/// stores them. ([`bulk_load_f32`](Self::bulk_load_f32) also loads a half index,
|
|
1336
|
+
/// rounding each component to the element.)
|
|
1337
|
+
pub fn bulk_load_f16<K, V>(
|
|
1338
|
+
&self,
|
|
1339
|
+
expected: u64,
|
|
1340
|
+
vectors: impl IntoIterator<Item = (K, V)>,
|
|
1341
|
+
) -> Result<BulkStats>
|
|
1342
|
+
where
|
|
1343
|
+
K: AsRef<[u8]>,
|
|
1344
|
+
V: AsRef<[u16]>,
|
|
1345
|
+
{
|
|
1346
|
+
self.bulk(expected, vectors, |v: &V| Input::F16(v.as_ref()))
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
/// [`bulk_load_f16`](Self::bulk_load_f16) for a `bf16` index (bfloat16 bit patterns).
|
|
1350
|
+
pub fn bulk_load_bf16<K, V>(
|
|
1351
|
+
&self,
|
|
1352
|
+
expected: u64,
|
|
1353
|
+
vectors: impl IntoIterator<Item = (K, V)>,
|
|
1354
|
+
) -> Result<BulkStats>
|
|
1355
|
+
where
|
|
1356
|
+
K: AsRef<[u8]>,
|
|
1357
|
+
V: AsRef<[u16]>,
|
|
1358
|
+
{
|
|
1359
|
+
self.bulk(expected, vectors, |v: &V| Input::BF16(v.as_ref()))
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
fn bulk<K: AsRef<[u8]>, V>(
|
|
1363
|
+
&self,
|
|
1364
|
+
expected: u64,
|
|
1365
|
+
vectors: impl IntoIterator<Item = (K, V)>,
|
|
1366
|
+
input: for<'v> fn(&'v V) -> Input<'v>,
|
|
1367
|
+
) -> Result<BulkStats> {
|
|
1368
|
+
let inn = &*self.inner;
|
|
1369
|
+
let t = &inn.table;
|
|
1370
|
+
let empty = || Error::InvalidArgument("bulk_load needs an empty index".into());
|
|
1371
|
+
if t.get(GLOBAL, META, NEXT)?.and_then(|c| c.as_u64()) != Some(0) {
|
|
1372
|
+
return Err(empty());
|
|
1373
|
+
}
|
|
1374
|
+
let target = u64::from(inn.opts.target);
|
|
1375
|
+
let want = usize::try_from(expected.div_ceil(target).max(1)).unwrap_or(usize::MAX);
|
|
1376
|
+
let mut it = vectors.into_iter();
|
|
1377
|
+
let sample: Vec<(K, V)> = it.by_ref().take(BULK_SAMPLE.max(want)).collect();
|
|
1378
|
+
if sample.is_empty() {
|
|
1379
|
+
return Ok(BulkStats {
|
|
1380
|
+
vectors: 0,
|
|
1381
|
+
lists: 0,
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
let mut points = Vec::with_capacity(sample.len());
|
|
1385
|
+
for (_, v) in &sample {
|
|
1386
|
+
let v = input(v);
|
|
1387
|
+
v.check(&inn.spec)?;
|
|
1388
|
+
points.push(v.placed(&inn.spec));
|
|
1389
|
+
}
|
|
1390
|
+
let centroids = kmeans(&inn.cen, &points, want.min(sample.len()))?;
|
|
1391
|
+
drop(points);
|
|
1392
|
+
let lists = u32::try_from(centroids.len()).map_err(|_| corrupt("too many lists"))?;
|
|
1393
|
+
inn.db.transact(&RetryPolicy::default(), |tx| {
|
|
1394
|
+
if read_u64(tx, t, GLOBAL, META, NEXT)? != Some(0) {
|
|
1395
|
+
return Err(Tx::Fail(empty()));
|
|
1396
|
+
}
|
|
1397
|
+
for (i, c) in (0u32..).zip(¢roids) {
|
|
1398
|
+
put_centroid(tx, t, i, c);
|
|
1399
|
+
tx.put_u64(t, ¢roid_row(i), META, VERSION, 1);
|
|
1400
|
+
}
|
|
1401
|
+
tx.put_u64(t, GLOBAL, META, NEXT, u64::from(lists))
|
|
1402
|
+
.put_u64(t, GLOBAL, META, LAYOUT, 1);
|
|
1403
|
+
Ok(())
|
|
1404
|
+
})?;
|
|
1405
|
+
let layout = self.reload()?;
|
|
1406
|
+
// Each list's unwritten batch, and which batch holds an id (a repeat replaces it).
|
|
1407
|
+
let mut batches: HashMap<u32, Vec<(K, V)>> = HashMap::new();
|
|
1408
|
+
let mut waiting: HashMap<Vec<u8>, u32> = HashMap::new();
|
|
1409
|
+
let mut loaded = 0u64;
|
|
1410
|
+
// One point, refilled per vector: a half or `i8` vector widens into its buffer.
|
|
1411
|
+
let mut point = Point::F32(Vec::new());
|
|
1412
|
+
for (id, v) in sample.into_iter().chain(it) {
|
|
1413
|
+
let vi = input(&v);
|
|
1414
|
+
vi.check(&inn.spec)?;
|
|
1415
|
+
vi.placed_into(&inn.spec, &mut point);
|
|
1416
|
+
let l = layout.nearest(&inn.cen, &point, 1, &[])?[0];
|
|
1417
|
+
if let Some(prev) = waiting.insert(id.as_ref().to_vec(), l) {
|
|
1418
|
+
let b = batches.get_mut(&prev).expect("a waiting id's batch");
|
|
1419
|
+
b.retain(|(k, _)| k.as_ref() != id.as_ref());
|
|
1420
|
+
}
|
|
1421
|
+
let batch = batches.entry(l).or_default();
|
|
1422
|
+
batch.push((id, v));
|
|
1423
|
+
loaded += 1;
|
|
1424
|
+
if batch.len() >= BULK_BATCH {
|
|
1425
|
+
let full = std::mem::take(batch);
|
|
1426
|
+
for (k, _) in &full {
|
|
1427
|
+
waiting.remove(k.as_ref());
|
|
1428
|
+
}
|
|
1429
|
+
self.write_batch(l, &full, input)?;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
for (l, batch) in batches {
|
|
1433
|
+
if !batch.is_empty() {
|
|
1434
|
+
self.write_batch(l, &batch, input)?;
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
let all: Vec<u32> = layout.lists.iter().map(|l| l.id).collect();
|
|
1438
|
+
match inn.opts.rebalance {
|
|
1439
|
+
Rebalance::Inline => {
|
|
1440
|
+
self.queue(all);
|
|
1441
|
+
while self.maintain_one()?.is_some() {}
|
|
1442
|
+
}
|
|
1443
|
+
_ => self.after_write(all)?,
|
|
1444
|
+
}
|
|
1445
|
+
Ok(BulkStats {
|
|
1446
|
+
vectors: loaded,
|
|
1447
|
+
lists,
|
|
1448
|
+
})
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
/// One transaction writing `batch` into list `l`, each vector as an upsert would.
|
|
1452
|
+
fn write_batch<K: AsRef<[u8]>, V>(
|
|
1453
|
+
&self,
|
|
1454
|
+
l: u32,
|
|
1455
|
+
batch: &[(K, V)],
|
|
1456
|
+
input: for<'v> fn(&'v V) -> Input<'v>,
|
|
1457
|
+
) -> Result<()> {
|
|
1458
|
+
let inn = &*self.inner;
|
|
1459
|
+
let t = &inn.table;
|
|
1460
|
+
let row = list_row(l);
|
|
1461
|
+
inn.db.transact(&RetryPolicy::default(), |tx| {
|
|
1462
|
+
if read_version(tx, t, l)?.is_none() {
|
|
1463
|
+
return Err(Tx::Fail(Error::Conflict(
|
|
1464
|
+
"a list merged away during a bulk load".into(),
|
|
1465
|
+
)));
|
|
1466
|
+
}
|
|
1467
|
+
let mut added = 0i64;
|
|
1468
|
+
for (id, v) in batch {
|
|
1469
|
+
let id = id.as_ref();
|
|
1470
|
+
let old = read_list_of(tx, t, id)?;
|
|
1471
|
+
match old {
|
|
1472
|
+
Some(o) if o == l => input(v).put(tx, t, &row, id),
|
|
1473
|
+
Some(o) => {
|
|
1474
|
+
tx.delete_column(t, &list_row(o), VEC, id)
|
|
1475
|
+
.incr(t, &count_row(o), CNT, b"", -1)
|
|
1476
|
+
.put_u64(t, &map_row(id), MAP, LIST, u64::from(l));
|
|
1477
|
+
input(v).put(tx, t, &row, id);
|
|
1478
|
+
added += 1;
|
|
1479
|
+
}
|
|
1480
|
+
None => {
|
|
1481
|
+
tx.put_u64(t, &map_row(id), MAP, LIST, u64::from(l));
|
|
1482
|
+
input(v).put(tx, t, &row, id);
|
|
1483
|
+
added += 1;
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
tx.incr(t, &count_row(l), CNT, b"", added);
|
|
1488
|
+
Ok(())
|
|
1489
|
+
})?;
|
|
1490
|
+
Ok(())
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
/// Checks the index's rows against each other at one snapshot: every id in exactly one
|
|
1494
|
+
/// list, its mapping naming that list, every list with a centroid, and every list's
|
|
1495
|
+
/// length counter equal to its vectors. Reads the whole index; for tests and repair
|
|
1496
|
+
/// tooling. A contradiction is `Corruption`.
|
|
1497
|
+
pub fn verify(&self) -> Result<IndexStats> {
|
|
1498
|
+
let inn = &*self.inner;
|
|
1499
|
+
let t = &inn.table;
|
|
1500
|
+
let s = inn.db.snapshot()?;
|
|
1501
|
+
let layout = load(t, &inn.cen, &s)?;
|
|
1502
|
+
let by_row: HashMap<u32, u32> = layout
|
|
1503
|
+
.lists
|
|
1504
|
+
.iter()
|
|
1505
|
+
.map(|l| (scramble(l.id), l.id))
|
|
1506
|
+
.collect();
|
|
1507
|
+
let mut lens: HashMap<u32, u64> = layout.lists.iter().map(|l| (l.id, 0)).collect();
|
|
1508
|
+
let mut home: HashMap<Vec<u8>, u32> = HashMap::new();
|
|
1509
|
+
let mut cells = t.scan_prefix(b"L").family(VEC).snapshot(&s).cells()?;
|
|
1510
|
+
while let Some((row, e)) = cells.next()? {
|
|
1511
|
+
let list = list_of(row)
|
|
1512
|
+
.and_then(|r| by_row.get(&r))
|
|
1513
|
+
.copied()
|
|
1514
|
+
.ok_or_else(|| corrupt("vectors in a list with no centroid"))?;
|
|
1515
|
+
if home.insert(e.qualifier.to_vec(), list).is_some() {
|
|
1516
|
+
return Err(Error::Corruption(format!(
|
|
1517
|
+
"id {:?} is in two lists",
|
|
1518
|
+
e.qualifier
|
|
1519
|
+
)));
|
|
1520
|
+
}
|
|
1521
|
+
*lens.get_mut(&list).expect("listed") += 1;
|
|
1522
|
+
}
|
|
1523
|
+
let mut mapped = 0usize;
|
|
1524
|
+
for row in t.scan_prefix(b"M").family(MAP).snapshot(&s).iter()? {
|
|
1525
|
+
let row = row?;
|
|
1526
|
+
let id = &row.key()[1..];
|
|
1527
|
+
let list = row.get(MAP, LIST).and_then(|c| c.as_u64());
|
|
1528
|
+
if list != home.get(id).map(|l| u64::from(*l)) {
|
|
1529
|
+
return Err(Error::Corruption(format!(
|
|
1530
|
+
"id {id:?} maps to {list:?} but is in {:?}",
|
|
1531
|
+
home.get(id)
|
|
1532
|
+
)));
|
|
1533
|
+
}
|
|
1534
|
+
mapped += 1;
|
|
1535
|
+
}
|
|
1536
|
+
if mapped != home.len() {
|
|
1537
|
+
return Err(corrupt("a vector has no mapping"));
|
|
1538
|
+
}
|
|
1539
|
+
for row in t.scan_prefix(b"N").family(CNT).snapshot(&s).iter()? {
|
|
1540
|
+
let row = row?;
|
|
1541
|
+
let list = list_of(row.key()).ok_or_else(|| corrupt("a count row key"))?;
|
|
1542
|
+
let n = row.get(CNT, b"").and_then(|c| c.as_i64()).unwrap_or(0);
|
|
1543
|
+
let want = lens.get(&list).copied();
|
|
1544
|
+
if want != Some(n.max(0) as u64) || n < 0 {
|
|
1545
|
+
return Err(Error::Corruption(format!(
|
|
1546
|
+
"list {list} counts {n} but holds {want:?}"
|
|
1547
|
+
)));
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
for l in &layout.lists {
|
|
1551
|
+
if lens[&l.id] > 0 && t.get_at(&s, &count_row(l.id), CNT, b"")?.is_none() {
|
|
1552
|
+
return Err(Error::Corruption(format!("list {} has no count", l.id)));
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
Ok(IndexStats {
|
|
1556
|
+
list_lens: layout.lists.iter().map(|l| lens[&l.id]).collect(),
|
|
1557
|
+
vectors: home.len() as u64,
|
|
1558
|
+
})
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
fn insert(&self, id: &[u8], v: Input<'_>) -> Result<()> {
|
|
1562
|
+
v.check(&self.inner.spec)?;
|
|
1563
|
+
let point = v.placed(&self.inner.spec);
|
|
1564
|
+
let mut layout = self.layout();
|
|
1565
|
+
for _ in 0..RELOADS {
|
|
1566
|
+
let r = if layout.lists.is_empty() {
|
|
1567
|
+
self.bootstrap(id, v, &point)
|
|
1568
|
+
} else {
|
|
1569
|
+
self.place(&layout, id, v, &point)
|
|
1570
|
+
};
|
|
1571
|
+
match r {
|
|
1572
|
+
Ok(touched) => return self.after_write(touched),
|
|
1573
|
+
Err(Tx::Stale) => layout = self.reload()?,
|
|
1574
|
+
Err(e) => return Err(e.into()),
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
Err(Error::Conflict(
|
|
1578
|
+
"the index's layout changed under every attempt of an insert".into(),
|
|
1579
|
+
))
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
/// `insert_*_in`'s body: see [`insert_f32_in`](Self::insert_f32_in).
|
|
1583
|
+
fn insert_in(
|
|
1584
|
+
&self,
|
|
1585
|
+
tx: &mut Transaction,
|
|
1586
|
+
placed: &mut Placed,
|
|
1587
|
+
id: &[u8],
|
|
1588
|
+
v: Input<'_>,
|
|
1589
|
+
) -> Result<()> {
|
|
1590
|
+
let inn = &*self.inner;
|
|
1591
|
+
v.check(&inn.spec)?;
|
|
1592
|
+
placed.bind(self)?;
|
|
1593
|
+
let point = v.placed(&inn.spec);
|
|
1594
|
+
let t = &inn.table;
|
|
1595
|
+
// Where `id` is: as this transaction left it, else as its snapshot shows it.
|
|
1596
|
+
let tracked = placed.ids.get(id).copied();
|
|
1597
|
+
let l = if placed.bootstrapped {
|
|
1598
|
+
// This transaction created list 0, the only one its snapshot can have.
|
|
1599
|
+
0
|
|
1600
|
+
} else {
|
|
1601
|
+
let mut layout = self.layout();
|
|
1602
|
+
let mut reloaded = false;
|
|
1603
|
+
loop {
|
|
1604
|
+
if layout.lists.is_empty() {
|
|
1605
|
+
if read_u64(tx, t, GLOBAL, META, NEXT)? == Some(0) {
|
|
1606
|
+
write_bootstrap(tx, t, &unit(inn.cen.metric(), point.clone()), id, v);
|
|
1607
|
+
placed.bootstrapped = true;
|
|
1608
|
+
placed.wrote(id, Some(0), [0]);
|
|
1609
|
+
return Ok(());
|
|
1610
|
+
}
|
|
1611
|
+
if reloaded {
|
|
1612
|
+
// `G:meta:next` isn't 0 in `tx` though the fresh layout has no
|
|
1613
|
+
// lists: the layout and `tx` disagree (a lost tail, say). A `tx`
|
|
1614
|
+
// older than a racing first insert reads 0 and bootstraps, and its
|
|
1615
|
+
// commit conflicts.
|
|
1616
|
+
return Err(Error::Conflict(
|
|
1617
|
+
"the index's layout is newer in the transaction than in the database"
|
|
1618
|
+
.into(),
|
|
1619
|
+
));
|
|
1620
|
+
}
|
|
1621
|
+
layout = self.reload()?;
|
|
1622
|
+
reloaded = true;
|
|
1623
|
+
continue;
|
|
1624
|
+
}
|
|
1625
|
+
let l = layout.nearest(&inn.cen, &point, 1, &[])?[0];
|
|
1626
|
+
let want = layout.list(l).expect("a listed list").version;
|
|
1627
|
+
if read_version(tx, t, l)? != Some(want) && !reloaded {
|
|
1628
|
+
layout = self.reload()?;
|
|
1629
|
+
reloaded = true;
|
|
1630
|
+
continue;
|
|
1631
|
+
}
|
|
1632
|
+
// The version `tx` read is `want`, or was rewritten after `tx`'s snapshot (the
|
|
1633
|
+
// reload's snapshot is newer than it): then the commit fails with `Conflict`.
|
|
1634
|
+
break l;
|
|
1635
|
+
}
|
|
1636
|
+
};
|
|
1637
|
+
let old = match tracked {
|
|
1638
|
+
Some(now) => now,
|
|
1639
|
+
None => read_list_of(tx, t, id)?,
|
|
1640
|
+
};
|
|
1641
|
+
match write_into(tx, t, l, old, tracked.is_none(), id, v) {
|
|
1642
|
+
Ok(()) => {
|
|
1643
|
+
placed.wrote(id, Some(l), std::iter::once(l).chain(old));
|
|
1644
|
+
Ok(())
|
|
1645
|
+
}
|
|
1646
|
+
Err(Tx::Stale) => Err(unmapped()),
|
|
1647
|
+
Err(e) => Err(e.into()),
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
/// The first insert into an empty index: list 0, centred on the vector.
|
|
1652
|
+
fn bootstrap(
|
|
1653
|
+
&self,
|
|
1654
|
+
id: &[u8],
|
|
1655
|
+
v: Input<'_>,
|
|
1656
|
+
point: &Point,
|
|
1657
|
+
) -> std::result::Result<Vec<u32>, Tx> {
|
|
1658
|
+
let inn = &*self.inner;
|
|
1659
|
+
let t = &inn.table;
|
|
1660
|
+
let centroid = unit(inn.cen.metric(), point.clone());
|
|
1661
|
+
inn.db.transact(&RetryPolicy::default(), |tx| {
|
|
1662
|
+
// Any list ever made means this cache is stale.
|
|
1663
|
+
if read_u64(tx, t, GLOBAL, META, NEXT)? != Some(0) {
|
|
1664
|
+
return Err(Tx::Stale);
|
|
1665
|
+
}
|
|
1666
|
+
write_bootstrap(tx, t, ¢roid, id, v);
|
|
1667
|
+
Ok(())
|
|
1668
|
+
})?;
|
|
1669
|
+
Ok(vec![0])
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
/// An insert or upsert into the list nearest `point` (D306 §2, rev 3's read sets): it
|
|
1673
|
+
/// reads `M{id}` and the versions of the lists it writes, never `N` or `G`.
|
|
1674
|
+
fn place(
|
|
1675
|
+
&self,
|
|
1676
|
+
layout: &Layout,
|
|
1677
|
+
id: &[u8],
|
|
1678
|
+
v: Input<'_>,
|
|
1679
|
+
point: &Point,
|
|
1680
|
+
) -> std::result::Result<Vec<u32>, Tx> {
|
|
1681
|
+
let inn = &*self.inner;
|
|
1682
|
+
let t = &inn.table;
|
|
1683
|
+
let l = layout.nearest(&inn.cen, point, 1, &[])?[0];
|
|
1684
|
+
let want = layout.list(l).expect("a listed list").version;
|
|
1685
|
+
let (old, _) = inn.db.transact(&RetryPolicy::default(), |tx| {
|
|
1686
|
+
let old = read_list_of(tx, t, id)?;
|
|
1687
|
+
// Absent: merged away (M1). Another version: the cache predates a split or a
|
|
1688
|
+
// reassignment, so place against the current centroids (rev 2 nit 3).
|
|
1689
|
+
if read_version(tx, t, l)? != Some(want) {
|
|
1690
|
+
return Err(Tx::Stale);
|
|
1691
|
+
}
|
|
1692
|
+
write_into(tx, t, l, old, true, id, v)?;
|
|
1693
|
+
#[cfg(feature = "__unstable-hooks")]
|
|
1694
|
+
crate::hooks::hold(crate::hooks::Hold::PlaceBeforeCommit);
|
|
1695
|
+
Ok(old)
|
|
1696
|
+
})?;
|
|
1697
|
+
Ok(match old {
|
|
1698
|
+
Some(o) if o != l => vec![l, o],
|
|
1699
|
+
_ => vec![l],
|
|
1700
|
+
})
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
fn layout(&self) -> Arc<Layout> {
|
|
1704
|
+
Arc::clone(
|
|
1705
|
+
&self
|
|
1706
|
+
.inner
|
|
1707
|
+
.layout
|
|
1708
|
+
.read()
|
|
1709
|
+
.unwrap_or_else(PoisonError::into_inner),
|
|
1710
|
+
)
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
/// Loads the centroids at a fresh snapshot into the shared cache.
|
|
1714
|
+
fn reload(&self) -> Result<Arc<Layout>> {
|
|
1715
|
+
let inn = &*self.inner;
|
|
1716
|
+
let s = inn.db.snapshot()?;
|
|
1717
|
+
let fresh = Arc::new(load(&inn.table, &inn.cen, &s)?);
|
|
1718
|
+
let mut shared = inn.layout.write().unwrap_or_else(PoisonError::into_inner);
|
|
1719
|
+
if fresh.seqno >= shared.seqno {
|
|
1720
|
+
*shared = Arc::clone(&fresh);
|
|
1721
|
+
}
|
|
1722
|
+
Ok(fresh)
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
/// The centroids at `s`: the shared cache when it holds `s`'s layout, else loaded. Only
|
|
1726
|
+
/// a search at the current snapshot replaces the shared cache (rev 3 S4).
|
|
1727
|
+
fn layout_at(&self, s: &Snapshot, current: bool) -> Result<Arc<Layout>> {
|
|
1728
|
+
let inn = &*self.inner;
|
|
1729
|
+
let latest = || inn.db.snapshot().ok().map(|s| s.seqno());
|
|
1730
|
+
let latest: &dyn Fn() -> Option<u64> = &latest;
|
|
1731
|
+
layout_at(
|
|
1732
|
+
&inn.table,
|
|
1733
|
+
&inn.cen,
|
|
1734
|
+
&inn.layout,
|
|
1735
|
+
s,
|
|
1736
|
+
current.then_some(latest),
|
|
1737
|
+
)
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
fn count(&self, list: u32) -> Result<u64> {
|
|
1741
|
+
let n = self
|
|
1742
|
+
.inner
|
|
1743
|
+
.table
|
|
1744
|
+
.get(&count_row(list), CNT, b"")?
|
|
1745
|
+
.and_then(|c| c.as_i64())
|
|
1746
|
+
.unwrap_or(0);
|
|
1747
|
+
Ok(n.max(0) as u64)
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
fn queue(&self, lists: impl IntoIterator<Item = u32>) {
|
|
1751
|
+
let mut q = self
|
|
1752
|
+
.inner
|
|
1753
|
+
.pending
|
|
1754
|
+
.lock()
|
|
1755
|
+
.unwrap_or_else(PoisonError::into_inner);
|
|
1756
|
+
for l in lists {
|
|
1757
|
+
if !q.contains(&l) {
|
|
1758
|
+
q.push_back(l);
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
/// Queues every list outside its bounds at `s` (`open`'s scan, rev 3 M3).
|
|
1764
|
+
fn queue_out_of_bounds(&self, s: &Snapshot) -> Result<()> {
|
|
1765
|
+
let t = &self.inner.table;
|
|
1766
|
+
let lists = self.layout().lists.len();
|
|
1767
|
+
let mut out = Vec::new();
|
|
1768
|
+
for row in t.scan_prefix(b"N").family(CNT).snapshot(s).iter()? {
|
|
1769
|
+
let row = row?;
|
|
1770
|
+
let list = list_of(row.key()).ok_or_else(|| corrupt("a count row key"))?;
|
|
1771
|
+
let n = row
|
|
1772
|
+
.get(CNT, b"")
|
|
1773
|
+
.and_then(|c| c.as_i64())
|
|
1774
|
+
.unwrap_or(0)
|
|
1775
|
+
.max(0) as u64;
|
|
1776
|
+
if n > self.max_list_len() || (n < self.min_list_len() && lists > 1) {
|
|
1777
|
+
out.push(list);
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
// Background markers: a hint, re-checked before any work (rev 3 M3).
|
|
1781
|
+
for row in t.scan_prefix(b"Q").family(META).snapshot(s).iter()? {
|
|
1782
|
+
let row = row?;
|
|
1783
|
+
out.push(list_of(row.key()).ok_or_else(|| corrupt("a marker row key"))?);
|
|
1784
|
+
}
|
|
1785
|
+
self.queue(out);
|
|
1786
|
+
Ok(())
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
/// Splits or merges queued lists that are outside their bounds, a bounded number per
|
|
1790
|
+
/// call (a bound check, never an edge: rev 3 M3).
|
|
1791
|
+
fn maintain(&self) -> Result<()> {
|
|
1792
|
+
for _ in 0..MAINTENANCE_PER_CALL {
|
|
1793
|
+
if self.maintain_one()?.is_none() {
|
|
1794
|
+
break;
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
Ok(())
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
/// Whether lists are queued for a bound check.
|
|
1801
|
+
pub(crate) fn has_pending(&self) -> bool {
|
|
1802
|
+
!self
|
|
1803
|
+
.inner
|
|
1804
|
+
.pending
|
|
1805
|
+
.lock()
|
|
1806
|
+
.unwrap_or_else(PoisonError::into_inner)
|
|
1807
|
+
.is_empty()
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/// Checks the next queued list against its bounds, and splits or merges it: `None` when
|
|
1811
|
+
/// nothing is queued, else whether it was out of bounds. In background mode a list found
|
|
1812
|
+
/// within its bounds has its stale marker removed. An error puts the list back.
|
|
1813
|
+
pub(crate) fn maintain_one(&self) -> Result<Option<bool>> {
|
|
1814
|
+
let next = self
|
|
1815
|
+
.inner
|
|
1816
|
+
.pending
|
|
1817
|
+
.lock()
|
|
1818
|
+
.unwrap_or_else(PoisonError::into_inner)
|
|
1819
|
+
.pop_front();
|
|
1820
|
+
let Some(l) = next else {
|
|
1821
|
+
return Ok(None);
|
|
1822
|
+
};
|
|
1823
|
+
let r = (|| {
|
|
1824
|
+
let n = self.count(l)?;
|
|
1825
|
+
if n > self.max_list_len() {
|
|
1826
|
+
self.split(l)?;
|
|
1827
|
+
Ok(true)
|
|
1828
|
+
} else if n < self.min_list_len() && self.layout().lists.len() > 1 {
|
|
1829
|
+
self.merge(l)?;
|
|
1830
|
+
Ok(true)
|
|
1831
|
+
} else {
|
|
1832
|
+
if self.inner.opts.rebalance == Rebalance::Background {
|
|
1833
|
+
self.inner
|
|
1834
|
+
.table
|
|
1835
|
+
.mutate(&queue_row(l))
|
|
1836
|
+
.delete_row()
|
|
1837
|
+
.commit()?;
|
|
1838
|
+
}
|
|
1839
|
+
Ok(false)
|
|
1840
|
+
}
|
|
1841
|
+
})();
|
|
1842
|
+
if r.is_err() {
|
|
1843
|
+
self.queue([l]);
|
|
1844
|
+
}
|
|
1845
|
+
r.map(Some)
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
/// After a write committed into `lists`: rebalance them now ([`Rebalance::Inline`]), or
|
|
1849
|
+
/// mark those out of bounds for the rebalancer ([`Rebalance::Background`]): a `Q` row in
|
|
1850
|
+
/// its own transaction (rev 4 N2) and the in-process queue. A list already queued here
|
|
1851
|
+
/// isn't marked again.
|
|
1852
|
+
fn after_write(&self, lists: impl IntoIterator<Item = u32>) -> Result<()> {
|
|
1853
|
+
#[cfg(feature = "__unstable-hooks")]
|
|
1854
|
+
if crate::hooks::skipping_rebalance() {
|
|
1855
|
+
return Ok(());
|
|
1856
|
+
}
|
|
1857
|
+
if self.inner.opts.rebalance == Rebalance::Inline {
|
|
1858
|
+
self.queue(lists);
|
|
1859
|
+
return self.maintain();
|
|
1860
|
+
}
|
|
1861
|
+
let mut marked = false;
|
|
1862
|
+
for l in lists {
|
|
1863
|
+
let queued = self
|
|
1864
|
+
.inner
|
|
1865
|
+
.pending
|
|
1866
|
+
.lock()
|
|
1867
|
+
.unwrap_or_else(PoisonError::into_inner)
|
|
1868
|
+
.contains(&l);
|
|
1869
|
+
if queued {
|
|
1870
|
+
continue;
|
|
1871
|
+
}
|
|
1872
|
+
let n = self.count(l)?;
|
|
1873
|
+
let several = self.layout().lists.len() > 1;
|
|
1874
|
+
if n > self.max_list_len() || (n < self.min_list_len() && several) {
|
|
1875
|
+
self.inner
|
|
1876
|
+
.table
|
|
1877
|
+
.mutate(&queue_row(l))
|
|
1878
|
+
.put_u64(META, MARK, 1)
|
|
1879
|
+
.commit()?;
|
|
1880
|
+
self.queue([l]);
|
|
1881
|
+
marked = true;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
if marked {
|
|
1885
|
+
self.inner.signal.notify();
|
|
1886
|
+
}
|
|
1887
|
+
Ok(())
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
/// Marks `list` in flight in this process, or `None` if it already is (rev 4).
|
|
1891
|
+
fn claim(&self, list: u32) -> Option<Claim<'_>> {
|
|
1892
|
+
let set = &self.inner.in_flight;
|
|
1893
|
+
// The lock is released before a `Claim` exists: its drop takes the lock again.
|
|
1894
|
+
let fresh = set
|
|
1895
|
+
.lock()
|
|
1896
|
+
.unwrap_or_else(PoisonError::into_inner)
|
|
1897
|
+
.insert(list);
|
|
1898
|
+
fresh.then(|| Claim { set, list })
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
/// The ids and points of `list` at `s`, in id order.
|
|
1902
|
+
fn members(&self, s: &Snapshot, list: u32) -> Result<(Vec<Vec<u8>>, Vec<Point>)> {
|
|
1903
|
+
let mut cells = self
|
|
1904
|
+
.inner
|
|
1905
|
+
.table
|
|
1906
|
+
.row(&list_row(list))
|
|
1907
|
+
.family(VEC)
|
|
1908
|
+
.snapshot(s)
|
|
1909
|
+
.cells()?;
|
|
1910
|
+
let (mut ids, mut points) = (Vec::new(), Vec::new());
|
|
1911
|
+
while let Some(e) = cells.next()? {
|
|
1912
|
+
let v = e
|
|
1913
|
+
.cell
|
|
1914
|
+
.as_vector()
|
|
1915
|
+
.ok_or_else(|| corrupt("a list column is not a vector"))?;
|
|
1916
|
+
ids.push(e.qualifier.to_vec());
|
|
1917
|
+
points.push(Point::of(v));
|
|
1918
|
+
}
|
|
1919
|
+
Ok((ids, points))
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1922
|
+
/// Splits `list` (D306 §3): 2-means on a snapshot read, then one transaction moving
|
|
1923
|
+
/// what it reads itself (rev 3 S1, rev 4 N1); then phase 2 over its neighbours.
|
|
1924
|
+
fn split(&self, list: u32) -> Result<()> {
|
|
1925
|
+
let Some(_claim) = self.claim(list) else {
|
|
1926
|
+
return Ok(());
|
|
1927
|
+
};
|
|
1928
|
+
for _ in 0..ATTEMPTS {
|
|
1929
|
+
// The loser of a duplicate split re-checks the length before splitting the
|
|
1930
|
+
// fresh half (rev 3 S2).
|
|
1931
|
+
if self.count(list)? <= self.max_list_len() {
|
|
1932
|
+
return Ok(());
|
|
1933
|
+
}
|
|
1934
|
+
let s0 = self.inner.db.snapshot()?;
|
|
1935
|
+
let mut layout = self.layout();
|
|
1936
|
+
if layout.list(list).is_none() {
|
|
1937
|
+
layout = self.reload()?;
|
|
1938
|
+
}
|
|
1939
|
+
let Some(current) = layout.list(list) else {
|
|
1940
|
+
return Ok(());
|
|
1941
|
+
};
|
|
1942
|
+
let (ids, points) = self.members(&s0, list)?;
|
|
1943
|
+
if ids.len() as u64 <= self.max_list_len() {
|
|
1944
|
+
return Ok(());
|
|
1945
|
+
}
|
|
1946
|
+
#[cfg(feature = "__unstable-hooks")]
|
|
1947
|
+
crate::hooks::hold(crate::hooks::Hold::SplitBeforeCommit);
|
|
1948
|
+
let min = usize::try_from(self.min_list_len()).unwrap_or(usize::MAX);
|
|
1949
|
+
let plan = two_means(&self.inner.cen, &points, ¤t.point, min)?;
|
|
1950
|
+
let moving: Vec<&[u8]> = plan.moving.iter().map(|i| ids[*i].as_slice()).collect();
|
|
1951
|
+
match self.split_commit(list, &plan, &moving) {
|
|
1952
|
+
Ok(new) => {
|
|
1953
|
+
let layout = self.reload()?;
|
|
1954
|
+
self.queue([list, new]);
|
|
1955
|
+
#[cfg(feature = "__unstable-hooks")]
|
|
1956
|
+
if crate::hooks::failing_after_split() {
|
|
1957
|
+
// A crash between the split's commit and its phase 2.
|
|
1958
|
+
return Ok(());
|
|
1959
|
+
}
|
|
1960
|
+
return self.reassign(&layout, list, new, &plan);
|
|
1961
|
+
}
|
|
1962
|
+
Err(Tx::Stale) => return Ok(()),
|
|
1963
|
+
Err(e) if is_conflict(&e) => {}
|
|
1964
|
+
Err(e) => return Err(e.into()),
|
|
1965
|
+
}
|
|
1966
|
+
}
|
|
1967
|
+
// Lost every attempt: the next write into the list triggers it again.
|
|
1968
|
+
Ok(())
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
fn split_commit(
|
|
1972
|
+
&self,
|
|
1973
|
+
list: u32,
|
|
1974
|
+
plan: &Plan,
|
|
1975
|
+
moving: &[&[u8]],
|
|
1976
|
+
) -> std::result::Result<u32, Tx> {
|
|
1977
|
+
let inn = &*self.inner;
|
|
1978
|
+
let t = &inn.table;
|
|
1979
|
+
let (new, _) = inn
|
|
1980
|
+
.db
|
|
1981
|
+
.transact(&RetryPolicy::default().max_attempts(1), |tx| {
|
|
1982
|
+
let Some(version) = read_version(tx, t, list)? else {
|
|
1983
|
+
return Err(Tx::Stale);
|
|
1984
|
+
};
|
|
1985
|
+
let next = read_u64(tx, t, GLOBAL, META, NEXT)?
|
|
1986
|
+
.ok_or_else(|| corrupt("no next list id"))?;
|
|
1987
|
+
let layout = read_u64(tx, t, GLOBAL, META, LAYOUT)?.unwrap_or(0);
|
|
1988
|
+
let new = u32::try_from(next).map_err(|_| corrupt("list ids exhausted"))?;
|
|
1989
|
+
let (from, to) = (list_row(list), list_row(new));
|
|
1990
|
+
let mut moved = 0i64;
|
|
1991
|
+
for id in moving {
|
|
1992
|
+
// Deleted or moved away since the 2-means snapshot: skip it (rev 4 N1).
|
|
1993
|
+
let Some(stored) = read_stored(tx, t, &from, id)? else {
|
|
1994
|
+
continue;
|
|
1995
|
+
};
|
|
1996
|
+
stored.input().put(tx, t, &to, id);
|
|
1997
|
+
tx.delete_column(t, &from, VEC, id).put_u64(
|
|
1998
|
+
t,
|
|
1999
|
+
&map_row(id),
|
|
2000
|
+
MAP,
|
|
2001
|
+
LIST,
|
|
2002
|
+
u64::from(new),
|
|
2003
|
+
);
|
|
2004
|
+
moved += 1;
|
|
2005
|
+
}
|
|
2006
|
+
if moved == 0 {
|
|
2007
|
+
return Err(Tx::Stale);
|
|
2008
|
+
}
|
|
2009
|
+
put_centroid(tx, t, list, &plan.stay);
|
|
2010
|
+
put_centroid(tx, t, new, &plan.go);
|
|
2011
|
+
tx.put_u64(t, ¢roid_row(list), META, VERSION, version + 1)
|
|
2012
|
+
.put_u64(t, ¢roid_row(new), META, VERSION, 1)
|
|
2013
|
+
.incr(t, &count_row(list), CNT, b"", -moved)
|
|
2014
|
+
.incr(t, &count_row(new), CNT, b"", moved)
|
|
2015
|
+
.put_u64(t, GLOBAL, META, NEXT, next + 1)
|
|
2016
|
+
.put_u64(t, GLOBAL, META, LAYOUT, layout + 1);
|
|
2017
|
+
if inn.opts.rebalance == Rebalance::Background {
|
|
2018
|
+
tx.delete_row(t, &queue_row(list));
|
|
2019
|
+
}
|
|
2020
|
+
Ok(new)
|
|
2021
|
+
})?;
|
|
2022
|
+
Ok(new)
|
|
2023
|
+
}
|
|
2024
|
+
|
|
2025
|
+
/// Phase 2 (SPFresh's LIRE): each of the split's nearest neighbour lists gives up the
|
|
2026
|
+
/// vectors now nearer one of the two new centroids, one transaction per neighbour that
|
|
2027
|
+
/// bumps the neighbour's version (rev 3 S3). A conflict skips that neighbour: this is
|
|
2028
|
+
/// clustering, not correctness.
|
|
2029
|
+
fn reassign(&self, layout: &Layout, list: u32, new: u32, plan: &Plan) -> Result<()> {
|
|
2030
|
+
let inn = &*self.inner;
|
|
2031
|
+
let t = &inn.table;
|
|
2032
|
+
let r = inn.opts.reassign as usize;
|
|
2033
|
+
if r == 0 {
|
|
2034
|
+
return Ok(());
|
|
2035
|
+
}
|
|
2036
|
+
let dim = inn.cen.dim();
|
|
2037
|
+
let (stay, go) = (plan.stay.to_vector(dim)?, plan.go.to_vector(dim)?);
|
|
2038
|
+
for nbr in layout.nearest(&inn.cen, &plan.stay, r, &[list, new])? {
|
|
2039
|
+
let Some(n) = layout.list(nbr) else {
|
|
2040
|
+
continue;
|
|
2041
|
+
};
|
|
2042
|
+
let s = inn.db.snapshot()?;
|
|
2043
|
+
let (ids, points) = self.members(&s, nbr)?;
|
|
2044
|
+
let mut moves: Vec<(&[u8], u32)> = Vec::new();
|
|
2045
|
+
for (id, p) in ids.iter().zip(&points) {
|
|
2046
|
+
let here = distance(&inn.cen, p, &n.centroid)?;
|
|
2047
|
+
let (ds, dg) = (distance(&inn.cen, p, &stay)?, distance(&inn.cen, p, &go)?);
|
|
2048
|
+
let (d, dest) = if dg < ds { (dg, new) } else { (ds, list) };
|
|
2049
|
+
if d < here {
|
|
2050
|
+
moves.push((id, dest));
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
if moves.is_empty() {
|
|
2054
|
+
continue;
|
|
2055
|
+
}
|
|
2056
|
+
let done = inn
|
|
2057
|
+
.db
|
|
2058
|
+
.transact(&RetryPolicy::default().max_attempts(1), |tx| {
|
|
2059
|
+
let Some(version) = read_version(tx, t, nbr)? else {
|
|
2060
|
+
return Err(Tx::Stale);
|
|
2061
|
+
};
|
|
2062
|
+
if read_version(tx, t, list)?.is_none() || read_version(tx, t, new)?.is_none() {
|
|
2063
|
+
return Err(Tx::Stale);
|
|
2064
|
+
}
|
|
2065
|
+
let from = list_row(nbr);
|
|
2066
|
+
let mut gained: HashMap<u32, i64> = HashMap::new();
|
|
2067
|
+
for (id, dest) in &moves {
|
|
2068
|
+
let Some(stored) = read_stored(tx, t, &from, id)? else {
|
|
2069
|
+
continue;
|
|
2070
|
+
};
|
|
2071
|
+
stored.input().put(tx, t, &list_row(*dest), id);
|
|
2072
|
+
tx.delete_column(t, &from, VEC, id).put_u64(
|
|
2073
|
+
t,
|
|
2074
|
+
&map_row(id),
|
|
2075
|
+
MAP,
|
|
2076
|
+
LIST,
|
|
2077
|
+
u64::from(*dest),
|
|
2078
|
+
);
|
|
2079
|
+
*gained.entry(*dest).or_default() += 1;
|
|
2080
|
+
}
|
|
2081
|
+
let lost: i64 = gained.values().sum();
|
|
2082
|
+
if lost == 0 {
|
|
2083
|
+
return Err(Tx::Stale);
|
|
2084
|
+
}
|
|
2085
|
+
tx.incr(t, &count_row(nbr), CNT, b"", -lost).put_u64(
|
|
2086
|
+
t,
|
|
2087
|
+
¢roid_row(nbr),
|
|
2088
|
+
META,
|
|
2089
|
+
VERSION,
|
|
2090
|
+
version + 1,
|
|
2091
|
+
);
|
|
2092
|
+
for (dest, n) in &gained {
|
|
2093
|
+
tx.incr(t, &count_row(*dest), CNT, b"", *n);
|
|
2094
|
+
}
|
|
2095
|
+
#[cfg(feature = "__unstable-hooks")]
|
|
2096
|
+
crate::hooks::hold(crate::hooks::Hold::ReassignBeforeCommit);
|
|
2097
|
+
Ok(())
|
|
2098
|
+
});
|
|
2099
|
+
match done {
|
|
2100
|
+
Ok(_) => self.queue([nbr, list, new]),
|
|
2101
|
+
Err(Tx::Stale) => {}
|
|
2102
|
+
Err(e) if is_conflict(&e) => {}
|
|
2103
|
+
Err(e) => return Err(e.into()),
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
Ok(())
|
|
2107
|
+
}
|
|
2108
|
+
|
|
2109
|
+
/// Merges `list` into its nearest neighbour (D306 §3): one transaction that reads the
|
|
2110
|
+
/// list row whole and moves every vector (rev 3's table).
|
|
2111
|
+
fn merge(&self, list: u32) -> Result<()> {
|
|
2112
|
+
let Some(_claim) = self.claim(list) else {
|
|
2113
|
+
return Ok(());
|
|
2114
|
+
};
|
|
2115
|
+
let inn = &*self.inner;
|
|
2116
|
+
let t = &inn.table;
|
|
2117
|
+
for _ in 0..ATTEMPTS {
|
|
2118
|
+
let mut layout = self.layout();
|
|
2119
|
+
if layout.list(list).is_none() {
|
|
2120
|
+
layout = self.reload()?;
|
|
2121
|
+
}
|
|
2122
|
+
let Some(src) = layout.list(list) else {
|
|
2123
|
+
return Ok(());
|
|
2124
|
+
};
|
|
2125
|
+
if layout.lists.len() <= 1 || self.count(list)? >= self.min_list_len() {
|
|
2126
|
+
return Ok(());
|
|
2127
|
+
}
|
|
2128
|
+
let Some(&dst) = layout.nearest(&inn.cen, &src.point, 1, &[list])?.first() else {
|
|
2129
|
+
return Ok(());
|
|
2130
|
+
};
|
|
2131
|
+
let r = inn
|
|
2132
|
+
.db
|
|
2133
|
+
.transact(&RetryPolicy::default().max_attempts(1), |tx| {
|
|
2134
|
+
if read_version(tx, t, list)?.is_none() {
|
|
2135
|
+
return Ok(false);
|
|
2136
|
+
}
|
|
2137
|
+
// A destination merged away meanwhile: reload and choose again (M1).
|
|
2138
|
+
let Some(version) = read_version(tx, t, dst)? else {
|
|
2139
|
+
return Err(Tx::Stale);
|
|
2140
|
+
};
|
|
2141
|
+
let from = list_row(list);
|
|
2142
|
+
let moving: Vec<(Vec<u8>, Stored)> = match tx.get_row(t, &from, &[VEC])? {
|
|
2143
|
+
None => Vec::new(),
|
|
2144
|
+
Some(row) => row
|
|
2145
|
+
.iter()
|
|
2146
|
+
.map(|e| {
|
|
2147
|
+
let v = e
|
|
2148
|
+
.cell
|
|
2149
|
+
.as_vector()
|
|
2150
|
+
.ok_or_else(|| corrupt("a list column is not a vector"))?;
|
|
2151
|
+
Ok((e.qualifier.to_vec(), Stored::of(v)?))
|
|
2152
|
+
})
|
|
2153
|
+
.collect::<Result<_>>()?,
|
|
2154
|
+
};
|
|
2155
|
+
let to = list_row(dst);
|
|
2156
|
+
for (id, stored) in &moving {
|
|
2157
|
+
stored.input().put(tx, t, &to, id);
|
|
2158
|
+
tx.put_u64(t, &map_row(id), MAP, LIST, u64::from(dst));
|
|
2159
|
+
}
|
|
2160
|
+
let layout = read_u64(tx, t, GLOBAL, META, LAYOUT)?.unwrap_or(0);
|
|
2161
|
+
tx.delete_family(t, &from, VEC)
|
|
2162
|
+
.delete_row(t, ¢roid_row(list))
|
|
2163
|
+
.delete_row(t, &count_row(list))
|
|
2164
|
+
.incr(t, &count_row(dst), CNT, b"", moving.len() as i64)
|
|
2165
|
+
.put_u64(t, ¢roid_row(dst), META, VERSION, version + 1)
|
|
2166
|
+
.put_u64(t, GLOBAL, META, LAYOUT, layout + 1);
|
|
2167
|
+
if inn.opts.rebalance == Rebalance::Background {
|
|
2168
|
+
tx.delete_row(t, &queue_row(list));
|
|
2169
|
+
}
|
|
2170
|
+
Ok(true)
|
|
2171
|
+
});
|
|
2172
|
+
match r {
|
|
2173
|
+
Ok((merged, _)) => {
|
|
2174
|
+
if merged {
|
|
2175
|
+
#[cfg(feature = "__unstable-hooks")]
|
|
2176
|
+
crate::hooks::hold(crate::hooks::Hold::MergeBeforeReload);
|
|
2177
|
+
self.reload()?;
|
|
2178
|
+
self.queue([dst]);
|
|
2179
|
+
}
|
|
2180
|
+
return Ok(());
|
|
2181
|
+
}
|
|
2182
|
+
Err(Tx::Stale) => {
|
|
2183
|
+
self.reload()?;
|
|
2184
|
+
}
|
|
2185
|
+
Err(e) if is_conflict(&e) => {}
|
|
2186
|
+
Err(e) => return Err(e.into()),
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
Ok(())
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
/// The rows of `lists`, for one multi-get.
|
|
2194
|
+
fn rows(lists: &[u32]) -> KeySet {
|
|
2195
|
+
KeySet::from_unsorted(lists.iter().map(|l| list_row(*l)))
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
fn hits(near: Vec<pigeonhole::Neighbour>) -> Vec<Hit> {
|
|
2199
|
+
near.into_iter()
|
|
2200
|
+
.map(|n| Hit {
|
|
2201
|
+
id: n.qualifier,
|
|
2202
|
+
distance: n.distance,
|
|
2203
|
+
})
|
|
2204
|
+
.collect()
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
/// An index opened from a reader process ([`Index::open_reader`], #1268 item 5): searches
|
|
2208
|
+
/// only, at the reader's snapshots, with the same results as the writer's
|
|
2209
|
+
/// [`Index`] at the same snapshot. It holds its own centroid cache, reloaded when a search
|
|
2210
|
+
/// sees the layout changed. If the writer restarts, a search fails with `NoWriter` or
|
|
2211
|
+
/// `SnapshotExpired` (`Error::Db`) until the reader attaches to the new writer; searching
|
|
2212
|
+
/// again is enough. A reader process has its own block cache: size it as the writer's.
|
|
2213
|
+
#[derive(Clone)]
|
|
2214
|
+
pub struct ReadIndex {
|
|
2215
|
+
inner: Arc<ReadInner>,
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
struct ReadInner {
|
|
2219
|
+
db: PigeonholeReader,
|
|
2220
|
+
table: ReadTable,
|
|
2221
|
+
spec: VectorSpec,
|
|
2222
|
+
cen: VectorSpec,
|
|
2223
|
+
opts: IndexOptions,
|
|
2224
|
+
layout: RwLock<Arc<Layout>>,
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
impl std::fmt::Debug for ReadIndex {
|
|
2228
|
+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
2229
|
+
f.debug_struct("ReadIndex")
|
|
2230
|
+
.field("table", &self.inner.table.name())
|
|
2231
|
+
.field("spec", &self.inner.spec)
|
|
2232
|
+
.field("options", &self.inner.opts)
|
|
2233
|
+
.finish_non_exhaustive()
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2236
|
+
|
|
2237
|
+
impl ReadIndex {
|
|
2238
|
+
/// The vectors' spec.
|
|
2239
|
+
pub fn spec(&self) -> VectorSpec {
|
|
2240
|
+
self.inner.spec
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
/// The options it was created with (the target list length as scaled).
|
|
2244
|
+
pub fn options(&self) -> IndexOptions {
|
|
2245
|
+
self.inner.opts
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2248
|
+
/// The `k` vectors nearest the `f32` vector `query`, as a [`Search`] to run.
|
|
2249
|
+
pub fn search_f32<'a>(&'a self, query: &'a [f32], k: usize) -> Search<'a> {
|
|
2250
|
+
Search::new(Of::R(self), Input::F32(query), k)
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
/// [`search_f32`](Self::search_f32) for an `i8` index.
|
|
2254
|
+
pub fn search_i8<'a>(&'a self, query: &'a [i8], k: usize) -> Search<'a> {
|
|
2255
|
+
Search::new(Of::R(self), Input::I8(query), k)
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
/// [`search_f32`](Self::search_f32) for a bit index.
|
|
2259
|
+
pub fn search_bits<'a>(&'a self, query: &'a [u8], k: usize) -> Search<'a> {
|
|
2260
|
+
Search::new(Of::R(self), Input::Bits(query), k)
|
|
2261
|
+
}
|
|
2262
|
+
|
|
2263
|
+
/// [`Index::search_f16`] from a reader process.
|
|
2264
|
+
pub fn search_f16<'a>(&'a self, query: &'a [u16], k: usize) -> Search<'a> {
|
|
2265
|
+
Search::new(Of::R(self), Input::F16(query), k)
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
/// [`Index::search_bf16`] from a reader process.
|
|
2269
|
+
pub fn search_bf16<'a>(&'a self, query: &'a [u16], k: usize) -> Search<'a> {
|
|
2270
|
+
Search::new(Of::R(self), Input::BF16(query), k)
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
|
|
2274
|
+
/// A nearest-neighbour search under construction ([`Index::search_f32`],
|
|
2275
|
+
/// [`ReadIndex::search_f32`] and their `i8` and bit forms). Finish with
|
|
2276
|
+
/// [`run`](Search::run).
|
|
2277
|
+
#[derive(Debug)]
|
|
2278
|
+
#[must_use = "a search does nothing until .run()"]
|
|
2279
|
+
pub struct Search<'a> {
|
|
2280
|
+
index: Of<'a>,
|
|
2281
|
+
query: Input<'a>,
|
|
2282
|
+
k: usize,
|
|
2283
|
+
nprobe: Option<u32>,
|
|
2284
|
+
snapshot: Option<&'a Snapshot>,
|
|
2285
|
+
parallel: usize,
|
|
2286
|
+
id_prefix: Option<&'a [u8]>,
|
|
2287
|
+
max_probe: Option<u32>,
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
/// What a [`Search`] read ([`Search::run_with_stats`]).
|
|
2291
|
+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
2292
|
+
#[non_exhaustive]
|
|
2293
|
+
pub struct SearchStats {
|
|
2294
|
+
/// Lists probed: `nprobe` (at most every list), or more for a search with an
|
|
2295
|
+
/// [`id_prefix`](Search::id_prefix) that widened to find `k` hits.
|
|
2296
|
+
pub lists_probed: u32,
|
|
2297
|
+
/// Lists in the index at the search's snapshot.
|
|
2298
|
+
pub lists: u32,
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
/// The index a [`Search`] reads: the writer's, or a reader process's.
|
|
2302
|
+
#[derive(Debug, Clone, Copy)]
|
|
2303
|
+
enum Of<'a> {
|
|
2304
|
+
W(&'a Index),
|
|
2305
|
+
R(&'a ReadIndex),
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
impl<'a> Of<'a> {
|
|
2309
|
+
fn spec(&self) -> &'a VectorSpec {
|
|
2310
|
+
match *self {
|
|
2311
|
+
Of::W(i) => &i.inner.spec,
|
|
2312
|
+
Of::R(i) => &i.inner.spec,
|
|
2313
|
+
}
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
fn cen(&self) -> &'a VectorSpec {
|
|
2317
|
+
match *self {
|
|
2318
|
+
Of::W(i) => &i.inner.cen,
|
|
2319
|
+
Of::R(i) => &i.inner.cen,
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
fn nprobe(&self) -> u32 {
|
|
2324
|
+
match *self {
|
|
2325
|
+
Of::W(i) => i.inner.opts.nprobe,
|
|
2326
|
+
Of::R(i) => i.inner.opts.nprobe,
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
fn snapshot(&self) -> pigeonhole::Result<Snapshot> {
|
|
2331
|
+
match *self {
|
|
2332
|
+
Of::W(i) => i.inner.db.snapshot(),
|
|
2333
|
+
Of::R(i) => i.inner.db.snapshot(),
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2337
|
+
fn layout_at(&self, s: &Snapshot, current: bool) -> Result<Arc<Layout>> {
|
|
2338
|
+
match *self {
|
|
2339
|
+
Of::W(i) => i.layout_at(s, current),
|
|
2340
|
+
Of::R(i) => {
|
|
2341
|
+
let inn = &*i.inner;
|
|
2342
|
+
let latest = || inn.db.snapshot().ok().map(|s| s.seqno());
|
|
2343
|
+
let latest: &dyn Fn() -> Option<u64> = &latest;
|
|
2344
|
+
layout_at(
|
|
2345
|
+
&inn.table,
|
|
2346
|
+
&inn.cen,
|
|
2347
|
+
&inn.layout,
|
|
2348
|
+
s,
|
|
2349
|
+
current.then_some(latest),
|
|
2350
|
+
)
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
}
|
|
2354
|
+
|
|
2355
|
+
#[cfg(feature = "async")]
|
|
2356
|
+
async fn layout_at_async(&self, s: &Snapshot, current: bool) -> Result<Arc<Layout>> {
|
|
2357
|
+
match *self {
|
|
2358
|
+
Of::W(i) => {
|
|
2359
|
+
let inn = &*i.inner;
|
|
2360
|
+
let latest = || inn.db.snapshot().ok().map(|s| s.seqno());
|
|
2361
|
+
let latest: &(dyn Fn() -> Option<u64> + Sync) = &latest;
|
|
2362
|
+
layout_at_async(
|
|
2363
|
+
&inn.table,
|
|
2364
|
+
&inn.cen,
|
|
2365
|
+
&inn.layout,
|
|
2366
|
+
s,
|
|
2367
|
+
current.then_some(latest),
|
|
2368
|
+
)
|
|
2369
|
+
.await
|
|
2370
|
+
}
|
|
2371
|
+
Of::R(i) => {
|
|
2372
|
+
let inn = &*i.inner;
|
|
2373
|
+
let latest = || inn.db.snapshot().ok().map(|s| s.seqno());
|
|
2374
|
+
let latest: &(dyn Fn() -> Option<u64> + Sync) = &latest;
|
|
2375
|
+
layout_at_async(
|
|
2376
|
+
&inn.table,
|
|
2377
|
+
&inn.cen,
|
|
2378
|
+
&inn.layout,
|
|
2379
|
+
s,
|
|
2380
|
+
current.then_some(latest),
|
|
2381
|
+
)
|
|
2382
|
+
.await
|
|
2383
|
+
}
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
|
|
2387
|
+
fn rows_of(&self, keys: &KeySet) -> KeysRead<'a> {
|
|
2388
|
+
match *self {
|
|
2389
|
+
Of::W(i) => i.inner.table.rows_of(keys),
|
|
2390
|
+
Of::R(i) => i.inner.table.rows_of(keys),
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
impl<'a> Search<'a> {
|
|
2396
|
+
fn new(index: Of<'a>, query: Input<'a>, k: usize) -> Self {
|
|
2397
|
+
Self {
|
|
2398
|
+
index,
|
|
2399
|
+
query,
|
|
2400
|
+
k,
|
|
2401
|
+
nprobe: None,
|
|
2402
|
+
snapshot: None,
|
|
2403
|
+
parallel: 1,
|
|
2404
|
+
id_prefix: None,
|
|
2405
|
+
max_probe: None,
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
/// Only ids starting with `prefix`. The probed lists hold every id near the query, so a
|
|
2410
|
+
/// prefix that's rare among them can leave fewer than `k` hits at `nprobe`: the search
|
|
2411
|
+
/// then doubles the lists it probes (nearest first) until `k` hits pass, every list is
|
|
2412
|
+
/// probed, or [`max_probe`](Self::max_probe) lists are. Each round reads only its new
|
|
2413
|
+
/// lists, each list once: a round widens only when the earlier ones found fewer than
|
|
2414
|
+
/// `k`, so it already holds every prefixed vector of the lists they read. A prefixed
|
|
2415
|
+
/// search that probes every list is exact for the prefix.
|
|
2416
|
+
///
|
|
2417
|
+
/// Ties among the hits come back by probe round, then by list, then by id.
|
|
2418
|
+
///
|
|
2419
|
+
/// **Cost:** a seek into each probed list, then the prefix's own cells there: the list's
|
|
2420
|
+
/// other cells aren't read (the prefix becomes the read's qualifier seek). On one 100,000
|
|
2421
|
+
/// cell row, 100 cells under a prefix took 0.01 ms against 5.14 ms for the whole row
|
|
2422
|
+
/// (#1342, R1).
|
|
2423
|
+
pub fn id_prefix(mut self, prefix: &'a [u8]) -> Self {
|
|
2424
|
+
self.id_prefix = Some(prefix);
|
|
2425
|
+
self
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
/// With an [`id_prefix`](Self::id_prefix), widen to at most `n` lists (default: every
|
|
2429
|
+
/// list). Never fewer than `nprobe`. Without a prefix it has no effect.
|
|
2430
|
+
pub fn max_probe(mut self, n: u32) -> Self {
|
|
2431
|
+
self.max_probe = Some(n);
|
|
2432
|
+
self
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2435
|
+
/// Probe the `n` lists whose centroids are nearest the query (at least 1; the index's
|
|
2436
|
+
/// [`IndexOptions::nprobe`] by default). More lists, better recall, more work; with
|
|
2437
|
+
/// every list probed the search is exact.
|
|
2438
|
+
pub fn nprobe(mut self, n: u32) -> Self {
|
|
2439
|
+
self.nprobe = Some(n);
|
|
2440
|
+
self
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
/// Search at `snapshot`: the centroids, the lists and their vectors as of it.
|
|
2444
|
+
pub fn snapshot(mut self, snapshot: &'a Snapshot) -> Self {
|
|
2445
|
+
self.snapshot = Some(snapshot);
|
|
2446
|
+
self
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
/// Read the probed lists on up to `n` threads owned by the call (default 1). The
|
|
2450
|
+
/// result is the same at any `n`.
|
|
2451
|
+
pub fn parallel(mut self, n: usize) -> Self {
|
|
2452
|
+
self.parallel = n;
|
|
2453
|
+
self
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
/// Runs the search: at most `k` hits, nearest first. One snapshot covers the centroids
|
|
2457
|
+
/// and the lists, so a concurrent split never shows a vector twice or hides it.
|
|
2458
|
+
///
|
|
2459
|
+
/// Hits at an equal distance come back by the list that holds them, then by id (for a
|
|
2460
|
+
/// widened [`id_prefix`](Self::id_prefix) search, by probe round first). A split
|
|
2461
|
+
/// can move a tied id to another list, so which of several ids tied at the `k`-th
|
|
2462
|
+
/// distance makes the cut can differ between layouts; at one snapshot it's fixed.
|
|
2463
|
+
pub fn run(self) -> Result<Vec<Hit>> {
|
|
2464
|
+
Ok(self.run_with_stats()?.0)
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
/// [`run`](Self::run), with what it read. A prefixed search that stopped at
|
|
2468
|
+
/// [`max_probe`](Self::max_probe) shows fewer hits than `k` with `lists_probed` below
|
|
2469
|
+
/// `lists`. [`run_async_with_stats`](Self::run_async_with_stats) is its async form.
|
|
2470
|
+
pub fn run_with_stats(self) -> Result<(Vec<Hit>, SearchStats)> {
|
|
2471
|
+
let index = self.index;
|
|
2472
|
+
self.query.check(index.spec())?;
|
|
2473
|
+
let taken;
|
|
2474
|
+
let s = match self.snapshot {
|
|
2475
|
+
Some(s) => s,
|
|
2476
|
+
None => {
|
|
2477
|
+
taken = index.snapshot()?;
|
|
2478
|
+
&taken
|
|
2479
|
+
}
|
|
2480
|
+
};
|
|
2481
|
+
let layout = index.layout_at(s, self.snapshot.is_none())?;
|
|
2482
|
+
let Some(order) = self.order(&layout)? else {
|
|
2483
|
+
return Ok((Vec::new(), self.stats(&layout, 0)));
|
|
2484
|
+
};
|
|
2485
|
+
let (mut done, mut probe) = (0, self.first_probe(&layout));
|
|
2486
|
+
let mut found = Vec::new();
|
|
2487
|
+
loop {
|
|
2488
|
+
let keys = rows(&order[done..probe]);
|
|
2489
|
+
let near = self.nearest(&keys, s).parallel(self.parallel).run()?;
|
|
2490
|
+
self.union(&mut found, near);
|
|
2491
|
+
match self.widen(&layout, probe, found.len()) {
|
|
2492
|
+
Some(next) => (done, probe) = (probe, next),
|
|
2493
|
+
None => return Ok((hits(found), self.stats(&layout, probe))),
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
/// The lists this search may probe, nearest the query first: `nprobe` of them, or, with
|
|
2499
|
+
/// an [`id_prefix`](Self::id_prefix), as many as it may widen to. `None` when there's
|
|
2500
|
+
/// nothing to read.
|
|
2501
|
+
fn order(&self, layout: &Layout) -> Result<Option<Vec<u32>>> {
|
|
2502
|
+
if layout.lists.is_empty() || self.k == 0 {
|
|
2503
|
+
return Ok(None);
|
|
2504
|
+
}
|
|
2505
|
+
let first = self.first_probe(layout);
|
|
2506
|
+
let n = if self.id_prefix.is_some() {
|
|
2507
|
+
self.cap(layout, first)
|
|
2508
|
+
} else {
|
|
2509
|
+
first
|
|
2510
|
+
};
|
|
2511
|
+
Ok(Some(layout.nearest(
|
|
2512
|
+
self.index.cen(),
|
|
2513
|
+
&self.query.point(),
|
|
2514
|
+
n,
|
|
2515
|
+
&[],
|
|
2516
|
+
)?))
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
/// Adds a round's hits to the earlier rounds'. A round widens only when the earlier
|
|
2520
|
+
/// ones found fewer than `k`, so `found` then holds every prefixed vector of the lists
|
|
2521
|
+
/// they read, and the nearest `k` of the union are the nearest of every list probed.
|
|
2522
|
+
/// The sort is stable: ties keep the earlier round's hits first, then each round's own
|
|
2523
|
+
/// order (list, then id).
|
|
2524
|
+
fn union(&self, found: &mut Vec<pigeonhole::Neighbour>, near: Vec<pigeonhole::Neighbour>) {
|
|
2525
|
+
if found.is_empty() {
|
|
2526
|
+
*found = near;
|
|
2527
|
+
return;
|
|
2528
|
+
}
|
|
2529
|
+
found.extend(near);
|
|
2530
|
+
found.sort_by(|a, b| a.distance.total_cmp(&b.distance));
|
|
2531
|
+
found.truncate(self.k);
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
/// Lists to probe first: `nprobe`, at least 1, at most every list.
|
|
2535
|
+
fn first_probe(&self, layout: &Layout) -> usize {
|
|
2536
|
+
let probe = self.nprobe.unwrap_or(self.index.nprobe()).max(1) as usize;
|
|
2537
|
+
probe.min(layout.lists.len().max(1))
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
/// After probing `probe` lists for `found` hits: how many to probe next, or `None` to
|
|
2541
|
+
/// stop. Only a prefixed search short of `k` widens, doubling up to `max_probe`.
|
|
2542
|
+
fn widen(&self, layout: &Layout, probe: usize, found: usize) -> Option<usize> {
|
|
2543
|
+
self.id_prefix?;
|
|
2544
|
+
let cap = self.cap(layout, probe);
|
|
2545
|
+
(found < self.k && probe < cap).then(|| probe.saturating_mul(2).min(cap))
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2548
|
+
/// The most lists a prefixed search widens to: `max_probe` (default every list), at
|
|
2549
|
+
/// least `first`.
|
|
2550
|
+
fn cap(&self, layout: &Layout, first: usize) -> usize {
|
|
2551
|
+
let all = layout.lists.len();
|
|
2552
|
+
self.max_probe
|
|
2553
|
+
.map_or(all, |m| (m as usize).min(all))
|
|
2554
|
+
.max(first)
|
|
2555
|
+
}
|
|
2556
|
+
|
|
2557
|
+
fn stats(&self, layout: &Layout, probed: usize) -> SearchStats {
|
|
2558
|
+
let n = |x: usize| u32::try_from(x).unwrap_or(u32::MAX);
|
|
2559
|
+
SearchStats {
|
|
2560
|
+
lists_probed: n(probed),
|
|
2561
|
+
lists: n(layout.lists.len()),
|
|
2562
|
+
}
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
/// [`run`](Self::run) as a future that never blocks on the device (D296 PR 5): the
|
|
2566
|
+
/// layout version is read with `get_at_async`, a layout that changed is reloaded through
|
|
2567
|
+
/// an async scan, and the probed lists are read with `Nearest::run_async`, which yields
|
|
2568
|
+
/// within a wide list. Its result equals `run`'s. It never fans out:
|
|
2569
|
+
/// [`parallel`](Self::parallel) is ignored. The future is `Send`.
|
|
2570
|
+
#[cfg(feature = "async")]
|
|
2571
|
+
pub async fn run_async(self) -> Result<Vec<Hit>> {
|
|
2572
|
+
Ok(self.run_async_with_stats().await?.0)
|
|
2573
|
+
}
|
|
2574
|
+
|
|
2575
|
+
/// [`run_async`](Self::run_async), with what it read, as
|
|
2576
|
+
/// [`run_with_stats`](Self::run_with_stats) reports it. The future is `Send`.
|
|
2577
|
+
#[cfg(feature = "async")]
|
|
2578
|
+
pub async fn run_async_with_stats(self) -> Result<(Vec<Hit>, SearchStats)> {
|
|
2579
|
+
let index = self.index;
|
|
2580
|
+
self.query.check(index.spec())?;
|
|
2581
|
+
let taken;
|
|
2582
|
+
let s = match self.snapshot {
|
|
2583
|
+
Some(s) => s,
|
|
2584
|
+
None => {
|
|
2585
|
+
taken = index.snapshot()?;
|
|
2586
|
+
&taken
|
|
2587
|
+
}
|
|
2588
|
+
};
|
|
2589
|
+
let layout = index.layout_at_async(s, self.snapshot.is_none()).await?;
|
|
2590
|
+
let Some(order) = self.order(&layout)? else {
|
|
2591
|
+
return Ok((Vec::new(), self.stats(&layout, 0)));
|
|
2592
|
+
};
|
|
2593
|
+
let (mut done, mut probe) = (0, self.first_probe(&layout));
|
|
2594
|
+
let mut found = Vec::new();
|
|
2595
|
+
loop {
|
|
2596
|
+
let keys = rows(&order[done..probe]);
|
|
2597
|
+
let near = self.nearest(&keys, s).run_async().await?;
|
|
2598
|
+
self.union(&mut found, near);
|
|
2599
|
+
match self.widen(&layout, probe, found.len()) {
|
|
2600
|
+
Some(next) => (done, probe) = (probe, next),
|
|
2601
|
+
None => return Ok((hits(found), self.stats(&layout, probe))),
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2606
|
+
fn nearest(&self, keys: &KeySet, s: &Snapshot) -> pigeonhole::Nearest<'a> {
|
|
2607
|
+
let mut read = self.index.rows_of(keys).family(VEC).snapshot(s);
|
|
2608
|
+
if let Some(p) = self.id_prefix {
|
|
2609
|
+
read = read.qualifier_prefix(p);
|
|
2610
|
+
}
|
|
2611
|
+
match self.query {
|
|
2612
|
+
Input::F32(q) => read.nearest_f32(q, self.k),
|
|
2613
|
+
Input::I8(q) => read.nearest_i8(q, self.k),
|
|
2614
|
+
Input::Bits(q) => read.nearest_bits(q, self.k),
|
|
2615
|
+
// A half query widens exactly and searches as `f32` (`nearest_f32` takes a half
|
|
2616
|
+
// family; there's no `nearest_f16`). The search keeps its own copy.
|
|
2617
|
+
q @ (Input::F16(_) | Input::BF16(_)) => {
|
|
2618
|
+
let mut widened = Vec::new();
|
|
2619
|
+
q.widen_into(&mut widened);
|
|
2620
|
+
read.nearest_f32(&widened, self.k)
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
|
|
2626
|
+
#[cfg(test)]
|
|
2627
|
+
mod tests {
|
|
2628
|
+
use super::*;
|
|
2629
|
+
|
|
2630
|
+
fn layout(seqno: u64, version: u64) -> Layout {
|
|
2631
|
+
Layout {
|
|
2632
|
+
seqno,
|
|
2633
|
+
version,
|
|
2634
|
+
lists: Vec::new(),
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
|
|
2638
|
+
/// The shared layout cache (rev 3 S4, perf287 on #1385): a search at the current snapshot
|
|
2639
|
+
/// adopts a newer layout, never an older one a slower search loaded, and does adopt one
|
|
2640
|
+
/// when the cache is from beyond the latest visible seqno (commits a restarted writer
|
|
2641
|
+
/// lost). A search at an older snapshot never adopts.
|
|
2642
|
+
#[test]
|
|
2643
|
+
fn the_layout_cache_adopts_newer_layouts_and_drops_a_lost_tails() {
|
|
2644
|
+
let cache = RwLock::new(Arc::new(layout(100, 5)));
|
|
2645
|
+
let at = |c: &RwLock<Arc<Layout>>| (c.read().unwrap().seqno, c.read().unwrap().version);
|
|
2646
|
+
let now_120 = || Some(120);
|
|
2647
|
+
let now: &dyn Fn() -> Option<u64> = &now_120;
|
|
2648
|
+
// A slower search's older layout, the cache still visible: kept.
|
|
2649
|
+
adopt(&cache, layout(90, 4), Some(now));
|
|
2650
|
+
assert_eq!(at(&cache), (100, 5));
|
|
2651
|
+
// A newer one: adopted.
|
|
2652
|
+
adopt(&cache, layout(110, 6), Some(now));
|
|
2653
|
+
assert_eq!(at(&cache), (110, 6));
|
|
2654
|
+
// Not at the current snapshot: never adopted.
|
|
2655
|
+
adopt(&cache, layout(115, 7), None);
|
|
2656
|
+
assert_eq!(at(&cache), (110, 6));
|
|
2657
|
+
// The writer lost its tail: the latest visible seqno is 60, below the cache's 110.
|
|
2658
|
+
// The fresh layout (seqno 55) replaces it, and later loads adopt as usual.
|
|
2659
|
+
let now_60 = || Some(60);
|
|
2660
|
+
let lost: &dyn Fn() -> Option<u64> = &now_60;
|
|
2661
|
+
adopt(&cache, layout(55, 3), Some(lost));
|
|
2662
|
+
assert_eq!(at(&cache), (55, 3));
|
|
2663
|
+
adopt(&cache, layout(58, 4), Some(lost));
|
|
2664
|
+
assert_eq!(at(&cache), (58, 4));
|
|
2665
|
+
// The latest seqno unknown (the snapshot failed): an older layout is kept.
|
|
2666
|
+
let unknown = || None;
|
|
2667
|
+
let unknown: &dyn Fn() -> Option<u64> = &unknown;
|
|
2668
|
+
adopt(&cache, layout(50, 2), Some(unknown));
|
|
2669
|
+
assert_eq!(at(&cache), (58, 4));
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
#[test]
|
|
2673
|
+
fn options_round_trip_and_scale_for_large_vectors() {
|
|
2674
|
+
let o = IndexOptions::new()
|
|
2675
|
+
.target_list_len(7)
|
|
2676
|
+
.nprobe(3)
|
|
2677
|
+
.reassign_neighbours(0);
|
|
2678
|
+
assert_eq!(IndexOptions::decode(&o.encode()), Some(o));
|
|
2679
|
+
assert_eq!(IndexOptions::decode(&[2]), None);
|
|
2680
|
+
let wide = VectorSpec::new(4096, Element::F32, pigeonhole::Metric::L2).unwrap();
|
|
2681
|
+
// 2 × target × 16 KiB ≤ 4 MiB.
|
|
2682
|
+
assert_eq!(IndexOptions::new().checked(&wide).unwrap().target, 128);
|
|
2683
|
+
assert!(
|
|
2684
|
+
IndexOptions::new()
|
|
2685
|
+
.target_list_len(3)
|
|
2686
|
+
.checked(&wide)
|
|
2687
|
+
.is_err()
|
|
2688
|
+
);
|
|
2689
|
+
}
|
|
2690
|
+
}
|