yeptris 0.6.18.2-arm-linux-eabihf

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/README.adoc +141 -0
  3. data/ext/build_windows_native.rb +41 -0
  4. data/ext/libyeptris/extconf.rb +57 -0
  5. data/ext/yeptris_native/cbor_ruby.c +193 -0
  6. data/ext/yeptris_native/extconf.rb +47 -0
  7. data/ext/yeptris_native/json_ruby.c +354 -0
  8. data/ext/yeptris_native/yeptris_native.c +497 -0
  9. data/lib/yeptris/cbor.rb +130 -0
  10. data/lib/yeptris/document.rb +255 -0
  11. data/lib/yeptris/ffi.rb +426 -0
  12. data/lib/yeptris/json/descriptor.rb +266 -0
  13. data/lib/yeptris/json.rb +394 -0
  14. data/lib/yeptris/materializer.rb +354 -0
  15. data/lib/yeptris/native-3.4.so +0 -0
  16. data/lib/yeptris/node.rb +453 -0
  17. data/lib/yeptris/psych/class_loader.rb +24 -0
  18. data/lib/yeptris/psych/coder_shim.rb +55 -0
  19. data/lib/yeptris/psych/drop_in.rb +63 -0
  20. data/lib/yeptris/psych/encodable.rb +14 -0
  21. data/lib/yeptris/psych/handler.rb +90 -0
  22. data/lib/yeptris/psych/parser.rb +105 -0
  23. data/lib/yeptris/psych/scalar_scanner.rb +149 -0
  24. data/lib/yeptris/psych/visitors.rb +545 -0
  25. data/lib/yeptris/psych.rb +622 -0
  26. data/lib/yeptris/schema.rb +253 -0
  27. data/lib/yeptris/valueml.rb +455 -0
  28. data/lib/yeptris/yaml/descriptor.rb +56 -0
  29. data/lib/yeptris/yaml.rb +422 -0
  30. data/lib/yeptris.rb +113 -0
  31. data/libyeptris.so +0 -0
  32. data/vendor/libyeptris/CMakeLists.txt +254 -0
  33. data/vendor/libyeptris/cmake/yeptris-config.cmake.in +5 -0
  34. data/vendor/libyeptris/cmake/yeptris.pc.in +11 -0
  35. data/vendor/libyeptris/src/CMakeLists.txt +167 -0
  36. data/vendor/libyeptris/src/include/yeptris/api.h +32 -0
  37. data/vendor/libyeptris/src/include/yeptris/cbor.h +101 -0
  38. data/vendor/libyeptris/src/include/yeptris/dom.h +190 -0
  39. data/vendor/libyeptris/src/include/yeptris/emit.h +81 -0
  40. data/vendor/libyeptris/src/include/yeptris/error.h +41 -0
  41. data/vendor/libyeptris/src/include/yeptris/events.h +157 -0
  42. data/vendor/libyeptris/src/include/yeptris/json.h +45 -0
  43. data/vendor/libyeptris/src/include/yeptris/json.hpp +258 -0
  44. data/vendor/libyeptris/src/include/yeptris/jsonc_compat.h +115 -0
  45. data/vendor/libyeptris/src/include/yeptris/marshal.h +52 -0
  46. data/vendor/libyeptris/src/include/yeptris/parse.h +48 -0
  47. data/vendor/libyeptris/src/include/yeptris/plan.h +100 -0
  48. data/vendor/libyeptris/src/include/yeptris/resolve.h +71 -0
  49. data/vendor/libyeptris/src/include/yeptris/schema.h +121 -0
  50. data/vendor/libyeptris/src/include/yeptris/tape.h +123 -0
  51. data/vendor/libyeptris/src/include/yeptris/types.h +38 -0
  52. data/vendor/libyeptris/src/include/yeptris/values.h +102 -0
  53. data/vendor/libyeptris/src/include/yeptris/version.h.in +29 -0
  54. data/vendor/libyeptris/src/include/yeptris/visit.h +77 -0
  55. data/vendor/libyeptris/src/include/yeptris/yajl_compat.h +135 -0
  56. data/vendor/libyeptris/src/include/yeptris.h +23 -0
  57. data/vendor/libyeptris/src/yeptris/build.c +376 -0
  58. data/vendor/libyeptris/src/yeptris/cbor/cbor.h +25 -0
  59. data/vendor/libyeptris/src/yeptris/cbor/decode.c +981 -0
  60. data/vendor/libyeptris/src/yeptris/cbor/encode.c +835 -0
  61. data/vendor/libyeptris/src/yeptris/cbor/sink.h +58 -0
  62. data/vendor/libyeptris/src/yeptris/common/chartype.c +41 -0
  63. data/vendor/libyeptris/src/yeptris/common/chartype.h +77 -0
  64. data/vendor/libyeptris/src/yeptris/common/cpu.c +81 -0
  65. data/vendor/libyeptris/src/yeptris/common/cpu.h +40 -0
  66. data/vendor/libyeptris/src/yeptris/common/error.c +65 -0
  67. data/vendor/libyeptris/src/yeptris/common/error.h +79 -0
  68. data/vendor/libyeptris/src/yeptris/common/mutex.h +45 -0
  69. data/vendor/libyeptris/src/yeptris/common/nametab.c +275 -0
  70. data/vendor/libyeptris/src/yeptris/common/nametab.h +68 -0
  71. data/vendor/libyeptris/src/yeptris/common/port.h +74 -0
  72. data/vendor/libyeptris/src/yeptris/common/simd_text.c +49 -0
  73. data/vendor/libyeptris/src/yeptris/common/simd_text.h +295 -0
  74. data/vendor/libyeptris/src/yeptris/common/simd_text_avx2.c +534 -0
  75. data/vendor/libyeptris/src/yeptris/common/simd_text_neon.c +662 -0
  76. data/vendor/libyeptris/src/yeptris/common/simd_text_scalar.c +298 -0
  77. data/vendor/libyeptris/src/yeptris/common/string_view.c +34 -0
  78. data/vendor/libyeptris/src/yeptris/common/string_view.h +93 -0
  79. data/vendor/libyeptris/src/yeptris/doc.h +56 -0
  80. data/vendor/libyeptris/src/yeptris/dom/dom.c +1192 -0
  81. data/vendor/libyeptris/src/yeptris/dom/dom.h +312 -0
  82. data/vendor/libyeptris/src/yeptris/dom/hpool.c +107 -0
  83. data/vendor/libyeptris/src/yeptris/dom/mapindex.c +173 -0
  84. data/vendor/libyeptris/src/yeptris/dom/mapindex.h +61 -0
  85. data/vendor/libyeptris/src/yeptris/dom/mutate.c +431 -0
  86. data/vendor/libyeptris/src/yeptris/emit/emit.c +434 -0
  87. data/vendor/libyeptris/src/yeptris/emit/float/api.h +38 -0
  88. data/vendor/libyeptris/src/yeptris/emit/float/dragon.c +405 -0
  89. data/vendor/libyeptris/src/yeptris/emit/float/floatint.h +231 -0
  90. data/vendor/libyeptris/src/yeptris/emit/float/print.c +396 -0
  91. data/vendor/libyeptris/src/yeptris/emit/style.c +81 -0
  92. data/vendor/libyeptris/src/yeptris/emit/style.h +24 -0
  93. data/vendor/libyeptris/src/yeptris/emit/writer.c +816 -0
  94. data/vendor/libyeptris/src/yeptris/emit/writer.h +76 -0
  95. data/vendor/libyeptris/src/yeptris/encoding/bom.c +31 -0
  96. data/vendor/libyeptris/src/yeptris/encoding/encoding.h +63 -0
  97. data/vendor/libyeptris/src/yeptris/encoding/transcode.c +148 -0
  98. data/vendor/libyeptris/src/yeptris/encoding/utf8_validate.c +178 -0
  99. data/vendor/libyeptris/src/yeptris/events/capture.c +150 -0
  100. data/vendor/libyeptris/src/yeptris/events/capture.h +39 -0
  101. data/vendor/libyeptris/src/yeptris/events/iterparse.c +208 -0
  102. data/vendor/libyeptris/src/yeptris/events/pull.c +97 -0
  103. data/vendor/libyeptris/src/yeptris/events/push.c +104 -0
  104. data/vendor/libyeptris/src/yeptris/events/recorder.c +107 -0
  105. data/vendor/libyeptris/src/yeptris/events/values.c +527 -0
  106. data/vendor/libyeptris/src/yeptris/events/values_priv.h +51 -0
  107. data/vendor/libyeptris/src/yeptris/events/yaml_compat.c +86 -0
  108. data/vendor/libyeptris/src/yeptris/events/yaml_compat.h +90 -0
  109. data/vendor/libyeptris/src/yeptris/jsonapi/jsonc_compat.c +687 -0
  110. data/vendor/libyeptris/src/yeptris/jsonapi/yajl_compat.c +728 -0
  111. data/vendor/libyeptris/src/yeptris/marshal.c +782 -0
  112. data/vendor/libyeptris/src/yeptris/memory/allocator.c +20 -0
  113. data/vendor/libyeptris/src/yeptris/memory/allocator.h +43 -0
  114. data/vendor/libyeptris/src/yeptris/memory/arena.c +134 -0
  115. data/vendor/libyeptris/src/yeptris/memory/arena.h +52 -0
  116. data/vendor/libyeptris/src/yeptris/memory/pool.c +122 -0
  117. data/vendor/libyeptris/src/yeptris/memory/pool.h +36 -0
  118. data/vendor/libyeptris/src/yeptris/parse/engine.c +3698 -0
  119. data/vendor/libyeptris/src/yeptris/parse/engine.h +65 -0
  120. data/vendor/libyeptris/src/yeptris/parse/events.h +124 -0
  121. data/vendor/libyeptris/src/yeptris/parse/numbers.c +349 -0
  122. data/vendor/libyeptris/src/yeptris/parse/numbers.h +33 -0
  123. data/vendor/libyeptris/src/yeptris/parse/scalars.c +416 -0
  124. data/vendor/libyeptris/src/yeptris/parse/scalars.h +75 -0
  125. data/vendor/libyeptris/src/yeptris/parse.c +737 -0
  126. data/vendor/libyeptris/src/yeptris/plan.c +824 -0
  127. data/vendor/libyeptris/src/yeptris/resolve/compat11.c +334 -0
  128. data/vendor/libyeptris/src/yeptris/resolve/core12.c +143 -0
  129. data/vendor/libyeptris/src/yeptris/resolve/resolver.h +56 -0
  130. data/vendor/libyeptris/src/yeptris/resolve/tags.c +58 -0
  131. data/vendor/libyeptris/src/yeptris/scan/json.c +792 -0
  132. data/vendor/libyeptris/src/yeptris/scan/json.h +109 -0
  133. data/vendor/libyeptris/src/yeptris/scan/scan.c +463 -0
  134. data/vendor/libyeptris/src/yeptris/scan/scan.h +150 -0
  135. data/vendor/libyeptris/src/yeptris/schema.c +308 -0
  136. data/vendor/libyeptris/src/yeptris/tape.c +1308 -0
  137. data/vendor/libyeptris/src/yeptris/tape_in.h +19 -0
  138. data/vendor/libyeptris/src/yeptris/version.c +7 -0
  139. data/vendor/libyeptris/src/yeptris/visit/dom_visit.c +115 -0
  140. data/vendor/libyeptris/src/yeptris/visit/json_visit.c +293 -0
  141. data/vendor/libyeptris/src/yeptris/visit/yaml_visit.c +127 -0
  142. metadata +199 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: d861d71fda8e06d38693a273a8c3eea7cbe96325af1eb0e4d2fdcc4bf7f08e46
4
+ data.tar.gz: 1120adc922c754da05873a318e2406206606ca966d22b79a686e08192b4a0e4d
5
+ SHA512:
6
+ metadata.gz: a68230754f4f90049a59ea30d8d8abb903c8fdef3f5e26abf5b110fd966d4da973c811d8e4c55750034aaa2207289f9fed60ed50f5065125b7b6854b44c5bce4
7
+ data.tar.gz: eb0d85052b73579c0809a9b1aef3f6be57ee5aff43406b9569f7f77189d23af3471d4bd29b5d9f4815a50bd661e20007a7c93fc6b3a2bac8d842a1a7001da921
data/README.adoc ADDED
@@ -0,0 +1,141 @@
1
+ = yeptris — YAML for Ruby at libleptris speed
2
+
3
+ An FFI-based (no C extension) Ruby YAML library over
4
+ https://github.com/leptris/yeptris[libyeptris] — the YAML counterpart
5
+ of libleptris. Psych-compatible semantics, one shared library, zero
6
+ compilation at install.
7
+
8
+ == Install (development)
9
+
10
+ The gem loads a shared `libyeptris` — from `YEPTRIS_LIB_PATH`, a
11
+ vendored `lib/platform/<tag>/` copy, or the system paths. For
12
+ development against a local build:
13
+
14
+ ....
15
+ # the sibling C checkout: ~/src/leptris/yeptris
16
+ cmake -B build -S . -DCMAKE_BUILD_TYPE=Release -DYEPTRIS_BUILD_SHARED=ON
17
+ cmake --build build
18
+
19
+ cd ~/src/leptris/yeptris-ruby
20
+ YEPTRIS_LIB_PATH=../yeptris/build/src/libyeptris.dylib bundle exec rspec
21
+ ....
22
+
23
+ Without `YEPTRIS_LIB_PATH` the spec helper falls back to a vendored
24
+ `lib/platform/<tag>/` copy, then to the sibling checkout's
25
+ `build-validate` — any `libyeptris.{so,dylib,dll}` path works.
26
+
27
+ == Usage
28
+
29
+ [source,ruby]
30
+ ----
31
+ require "yeptris"
32
+
33
+ Yeptris::YAML.load("name: yeptris\nrating: 10\n")
34
+ # => {"name" => "yeptris", "rating" => 10}
35
+
36
+ # brace mixed hashes on Ruby 3.x: a trailing symbol key otherwise
37
+ # splits the literal into keywords
38
+ Yeptris::YAML.dump({"name" => "yeptris", "tags" => [:yaml, :fast]})
39
+
40
+ doc = Yeptris::Document.parse(config_yaml)
41
+ doc.root["server"]["port"].to_i
42
+ doc.serialize
43
+ ----
44
+
45
+ `Yeptris::YAML.load` defaults to Psych's YAML 1.1 implicit typing
46
+ (`yes` is `true`, `017` is octal); pass `schema: :core_12` for YAML
47
+ 1.2 core semantics. Dump builds through the library's DOM mutation
48
+ API, so the writer's sizing/escape machinery applies to synthesized
49
+ trees unchanged.
50
+
51
+ Handles are document-scoped: `Document#free` releases everything
52
+ (one C call), a GC finalizer backs it up, and any use after free
53
+ raises `Yeptris::FreedError` — never a segfault.
54
+
55
+ == Two load surfaces: YAML and strict JSON
56
+
57
+ `Yeptris::YAML.load` keeps the Psych contract for EVERY input —
58
+ including JSON-shaped ones. `{"a": [1,]}` is legal flow YAML (spec
59
+ production [141] allows the trailing comma); `"1e3"` is a Psych
60
+ String. These semantics never flip because input happens to look
61
+ like JSON.
62
+
63
+ `Yeptris::JSON.load` is the STRICT RFC 8259 surface — exact
64
+ `JSON.parse` semantics by construction (spec-pinned in
65
+ `spec/json_parity_spec.rb`: every value and every error case,
66
+ Bignum integers, exponent-only floats, duplicate keys). Engines,
67
+ fastest first:
68
+
69
+ 1. **Native materializer** (opt-in build; see below): a fused C scan
70
+ → `VALUE` parser. Order-alternating interleaved profile, 152 KB /
71
+ ~29k-value corpus, Ruby 3.4:
72
+ +
73
+ ....
74
+ CI referee (gated at 1.05 on both platforms, order-alternating
75
+ interleave, N=200; prefix-hashed token cache):
76
+ ubuntu (x86_64) mean 0.745x vs JSON.parse, 200/200 head-to-head
77
+ macos (arm64) mean 0.759x vs JSON.parse, 169/200 head-to-head
78
+ ....
79
+
80
+ The parse-window GC strategy is per-arch and runtime-switchable:
81
+ `Yeptris::Native.gc_mode` / `YEPTRIS_NATIVE_GC` = `:none`
82
+ (steady-state: +0 heap pages, the stdlib's own GC cadence — the
83
+ default on arm64) or `:disable` (no GC during the parse — the
84
+ default on x86_64, faster where pages are free). On a LOADED x86_64
85
+ box prefer `:none`: `:disable` grows ~478 heap pages per 50 parses,
86
+ and page growth under contention is exactly the loaded-box
87
+ regression. `:start` (in-window collection) measured 8x — dead.
88
+ 2. **Record-drain fallback** (always available): the strict-JSON
89
+ validator gates, then the value records convert without the Psych
90
+ quirk table — exact parity with engine 1, spec-pinned.
91
+
92
+ The committed profile (`benchmark/json_profile.rb`) is the fair
93
+ benchmark of record — any performance claim runs through it.
94
+
95
+ === Building the native materializer (opt-in)
96
+
97
+ ....
98
+ cd ext/yeptris_native
99
+ YEPTRIS_LIB_PATH=/path/to/libyeptris.dylib ruby extconf.rb && make
100
+ cp native.bundle ../../lib/yeptris/ # or .so on Linux
101
+ ....
102
+
103
+ `require "yeptris"` picks it up automatically (`Yeptris::JSON` then
104
+ uses it; missing builds silently use the fallback — the gem installs
105
+ without compiling).
106
+
107
+ == Shipped beyond the original plan
108
+
109
+ * The columnar value drain (bulk, pre-converted typed columns — the
110
+ load fast path) and the bulk DOM builder (one `document_build`
111
+ call per dump).
112
+ * **Ruby Marshal 4.8 emission** (libyeptris >= 0.1.11): the C side
113
+ converts value records into Marshal bytes; one `Marshal.load`
114
+ materializes the whole graph. ~10× faster than the columnar walk
115
+ on JSON-shaped input, ~5× on YAML, ~50× on the per-node DOM walk
116
+ (`Node#to_ruby` becomes bulk). Alias identity preserved through
117
+ object links; merge keys and timestamps return UNSUPPORTED for the
118
+ record-walk fallback.
119
+ * `Yeptris::Psych` — the Psych-compatible namespace with the ported
120
+ Psych suite (143 specs), `.tml` corpora conformance, and the
121
+ Encodable object protocol (`include Yeptris::Psych::Encodable` +
122
+ `encode_with`/`init_with` — no `respond_to?`, no ivar reflection;
123
+ the library never reaches into an object's internals).
124
+ ** CO-EXISTENCE (issue #69): `require "yeptris/psych"` loads the
125
+ namespace WITHOUT touching the top-level `Psych` constant — it
126
+ coexists with stdlib psych in ANY load order. The process-
127
+ exclusive drop-in rebind (`::Psych = Yeptris::Psych`) is now
128
+ OPT-IN: `require "yeptris/psych/drop_in"`. Once the drop-in runs,
129
+ stdlib psych must NOT be loaded afterwards (its require re-opens
130
+ the rebound module and clobbers constants). Bundles that cannot
131
+ control the load order (activesupport et al.) should use
132
+ `Yeptris::Psych` / `Yeptris::YAML` directly.
133
+ ** `Yeptris::YAML.safe_load(yaml, permitted_classes: [], aliases:
134
+ false)` — Psych's safe_load semantics on the native surface:
135
+ plain data by default; a leaf that would materialize to a
136
+ non-permitted class (a compat_11 date) raises
137
+ `Yeptris::Psych::DisallowedClass`; alias use without `aliases:
138
+ true` raises `Yeptris::Psych::AliasesError`.
139
+ * Lockstep versioning with the C library
140
+ (`{c-semver}.{gem-patch}`); releases published from the C repo's
141
+ workflow through the RubyGems trusted publisher.
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Builds the native materializer DLL for the CURRENT Ruby and
4
+ # installs it under its minor-versioned name
5
+ # (lib/yeptris/native-<major.minor>.so).
6
+ #
7
+ # The leptris-ruby Windows lesson (#207/#227): a PE DLL cannot
8
+ # resolve Ruby imports lazily — it must bind the build Ruby's
9
+ # x64-ucrt-rubyNNN.dll. One DLL per supported minor therefore ships
10
+ # in the Windows platform gems, and the loader in lib/yeptris.rb
11
+ # picks by RUBY_VERSION at require. Ruby 3.3 has no arm64 build:
12
+ # that cell ships no artifact and falls back loudly to the FFI
13
+ # ladder.
14
+ #
15
+ # Standalone by design: the release workflow runs this under each
16
+ # Ruby minor (3.3/3.4/4.0) with no bundler context.
17
+
18
+ require "rbconfig"
19
+ require "fileutils"
20
+
21
+ root = File.expand_path("..", __dir__)
22
+ ext_dir = File.join(root, "ext", "yeptris_native")
23
+ minor = RUBY_VERSION[/\A\d+\.\d+/]
24
+
25
+ Dir.chdir(ext_dir) do
26
+ system(RbConfig.ruby, "extconf.rb") or abort "extconf failed under #{RUBY_VERSION}"
27
+ # mkmf's link binds the CURRENT Ruby's runtime DLL — exactly
28
+ # what the versioned naming is for.
29
+ success = system("make")
30
+ abort "make failed under #{RUBY_VERSION}" unless success
31
+ so = Dir.glob("native.{so,dll}").first
32
+ abort "native bundle not produced under #{RUBY_VERSION}" unless so
33
+ dest = File.join(root, "lib", "yeptris", "native-#{minor}.so")
34
+ # The artifact must reference only this minor's Ruby DLL.
35
+ imported = `strings #{so} 2>/dev/null`[/[a-z0-9-]*ruby\d{3,}\.dll/i]
36
+ if imported && !imported.include?("ruby#{minor.delete('.')}")
37
+ abort "#{so} imports #{imported} but was built under #{RUBY_VERSION} — refuse to mis-name it"
38
+ end
39
+ FileUtils.cp(so, dest)
40
+ puts "Installed native materializer for Ruby #{minor} -> #{dest} (#{imported || 'no ruby dll string found'})"
41
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Builds the vendored libyeptris at gem-install time so EVERY install
4
+ # carries the engine (the pure `ruby`-platform gem has no prebuilt
5
+ # binary). Platform gems vendor the prebuilt lib at the gem root —
6
+ # when one is present this extension no-ops.
7
+
8
+ require "mkmf"
9
+ require "fileutils"
10
+
11
+ gem_root = File.expand_path("../..", __dir__)
12
+ vendored = File.join(gem_root, "vendor", "libyeptris")
13
+
14
+ prebuilt = ["libyeptris.so", "libyeptris.dylib", "libyeptris.dll",
15
+ "yeptris.dll"].any? { |n| File.exist?(File.join(gem_root, n)) }
16
+
17
+ def write_dummy_makefile(reason)
18
+ File.write("Makefile", <<~MAKE)
19
+ all:
20
+ \t@echo "#{reason}"
21
+ install:
22
+ clean:
23
+ MAKE
24
+ end
25
+
26
+ if prebuilt
27
+ write_dummy_makefile("yeptris: prebuilt libyeptris ships in this gem; source build skipped")
28
+ elsif !File.directory?(vendored)
29
+ write_dummy_makefile("yeptris: no vendored libyeptris sources; set YEPTRIS_LIB_PATH at require time")
30
+ elsif find_executable("cmake").nil?
31
+ write_dummy_makefile("yeptris: cmake not found; set YEPTRIS_LIB_PATH at require time")
32
+ else
33
+ # Out-of-source into the ext workdir; the shared lib then moves to
34
+ # the GEM ROOT, where ffi.rb's ladder probes it.
35
+ build = File.join(Dir.pwd, "libyeptris-build")
36
+ # BUILD_TESTING (the CMake standard) gates test/ — the vendored
37
+ # tree ships no test dir; the static target must build because the
38
+ # unconditional install(TARGETS yeptris_static) expects it
39
+ args = [
40
+ "-DCMAKE_BUILD_TYPE=Release",
41
+ "-DBUILD_TESTING=OFF",
42
+ "-DYEPTRIS_BUILD_CLI=OFF",
43
+ "-DYEPTRIS_BUILD_BENCHMARKS=OFF",
44
+ "-DYEPTRIS_BUILD_SHARED=ON",
45
+ ]
46
+ ok = system("cmake", "-S", vendored, "-B", build, *args) &&
47
+ system("cmake", "--build", build, "--config", "Release")
48
+ lib = Dir.glob(File.join(build, "**", "libyeptris.{so,dylib}")).first
49
+ if ok && lib
50
+ FileUtils.cp(lib, File.join(gem_root, File.basename(lib)))
51
+ write_dummy_makefile("yeptris: built vendored libyeptris at gem install")
52
+ else
53
+ # a failed build must not abort the install — require-time raises
54
+ # the informative ladder error instead
55
+ write_dummy_makefile("yeptris: libyeptris source build failed; set YEPTRIS_LIB_PATH at require time")
56
+ end
57
+ end
@@ -0,0 +1,193 @@
1
+ /* cbor_ruby.c — CBOR decode → Ruby VALUE in one C pass (#157).
2
+ *
3
+ * The FFI ladder's CBOR.load pays decode + a Marshal emit/load round
4
+ * trip; this walks the decoded DOM directly, building the same VALUEs
5
+ * the materializer builds (tag-driven scalar semantics, insertion-
6
+ * order mappings), with no intermediate representation.
7
+ */
8
+ #include <ruby.h>
9
+ /* rb_hash_new_capa is Ruby 3.2+; older Rubies grow dynamically (the
10
+ * json_ruby.c guard, verbatim). */
11
+ #if RUBY_API_VERSION_MAJOR > 3 || (RUBY_API_VERSION_MAJOR == 3 && RUBY_API_VERSION_MINOR >= 2)
12
+ #define HASH_NEW_CAPA(n) rb_hash_new_capa(n)
13
+ #else
14
+ #define HASH_NEW_CAPA(n) rb_hash_new()
15
+ #endif
16
+ #include <ruby/encoding.h>
17
+ #include <stdlib.h>
18
+ #include <string.h>
19
+
20
+ static rb_encoding* cr_utf8_enc;
21
+
22
+ /* Map keys repeat across records in CBOR corpora; interning shares one
23
+ * VALUE per distinct key (the json_ruby.c on_key pattern, #157). */
24
+ static VALUE cr_key_str(const char* p, size_t len) {
25
+ return rb_enc_interned_str(p, (long)len, cr_utf8_enc);
26
+ }
27
+
28
+ #include "cbor/sink.h"
29
+ #include "dom/dom.h"
30
+ #include "doc.h" /* the public YeptrisDocument wrapper: ->dom */
31
+ #include <yeptris/cbor.h>
32
+ #include <yeptris/dom.h>
33
+ #include <yeptris/resolve.h>
34
+
35
+ /* ---- the VALUE sink (#157): bytes -> VALUE in ONE pass ----
36
+ *
37
+ * The decode grammar drives; these callbacks build the same VALUEs
38
+ * the old DOM + cr_walk pair produced (verified by the binding's
39
+ * differential suite): nil/true/false by tag, ints via LL2NUM (the
40
+ * INT64_MIN text parity wart kept), floats via DBL2NUM (no
41
+ * text round trip), strings copied utf8 (byte strings today ride the
42
+ * same utf8 String), map keys interned (values never are).
43
+ *
44
+ * GC: the container stack is a Ruby array (every VALUE reachable);
45
+ * the input buffer is read directly, so decode runs with GC disabled
46
+ * (the caller's String may move otherwise — the #160 bus error). */
47
+
48
+ struct rv_ctx {
49
+ VALUE stack; /* [.., container, (pending-key)?] */
50
+ VALUE root; /* Qnil until the top item closes */
51
+ };
52
+
53
+ static VALUE rv_top(const struct rv_ctx* c) {
54
+ long n = RARRAY_LEN(c->stack);
55
+ return n > 0 ? rb_ary_entry(c->stack, n - 1) : Qnil;
56
+ }
57
+
58
+ static VALUE rv_attach(struct rv_ctx* c, VALUE v) {
59
+ VALUE top = rv_top(c);
60
+ if (RB_TYPE_P(top, T_ARRAY)) {
61
+ rb_ary_push(top, v);
62
+ return v;
63
+ }
64
+ if (RB_TYPE_P(top, T_HASH)) {
65
+ rb_ary_push(c->stack, v); /* becomes the pending key */
66
+ return v;
67
+ }
68
+ /* a pending key: pop it, land v in the map */
69
+ VALUE k = rb_ary_pop(c->stack);
70
+ VALUE map = rv_top(c);
71
+ if (RB_TYPE_P(map, T_HASH)) {
72
+ rb_hash_aset(map, k, v);
73
+ return v;
74
+ }
75
+ /* the stack emptied: v is the root */
76
+ c->root = v;
77
+ return v;
78
+ }
79
+
80
+ static int rv_text(void* ctx, const char* s, uint32_t n, uint8_t tag_id, int is_key,
81
+ const char* tag, uint32_t tag_len) {
82
+ (void)tag;
83
+ (void)tag_len;
84
+ struct rv_ctx* c = ctx;
85
+ VALUE v;
86
+ switch (tag_id) {
87
+ case YEPTRIS_TAG_NULL:
88
+ v = Qnil;
89
+ break;
90
+ case YEPTRIS_TAG_BOOL:
91
+ v = (n == 4 && memcmp(s, "true", 4) == 0) ? Qtrue : Qfalse;
92
+ break;
93
+ default: /* rendered text (diagnostics, simple(N), key ints) */
94
+ v = rb_utf8_str_new(s, (long)n);
95
+ break;
96
+ }
97
+ (void)is_key;
98
+ rv_attach(c, v);
99
+ return 1;
100
+ }
101
+
102
+ static int rv_int(void* ctx, int negative, uint64_t mag, int is_key, const char* tag,
103
+ uint32_t tag_len) {
104
+ (void)tag;
105
+ (void)tag_len;
106
+ struct rv_ctx* c = ctx;
107
+ VALUE v;
108
+ if (negative && mag == (uint64_t)1 << 63) {
109
+ /* parity: the DOM path's strtoll guard renders INT64_MIN a
110
+ * String today; the differential suite pins it */
111
+ v = rb_utf8_str_new("-9223372036854775808", 20);
112
+ } else {
113
+ v = LL2NUM(negative ? -(int64_t)mag : (int64_t)mag);
114
+ }
115
+ rv_attach(c, v);
116
+ return 1;
117
+ }
118
+
119
+ static int rv_float(void* ctx, double dv, int is_key, const char* tag, uint32_t tag_len) {
120
+ (void)is_key;
121
+ (void)tag;
122
+ (void)tag_len;
123
+ rv_attach(ctx, DBL2NUM(dv));
124
+ return 1;
125
+ }
126
+
127
+ static int rv_str(void* ctx, const unsigned char* p, uint32_t n, int borrowed, int is_key,
128
+ const char* tag, uint32_t tag_len) {
129
+ (void)borrowed;
130
+ (void)tag;
131
+ (void)tag_len;
132
+ struct rv_ctx* c = ctx;
133
+ VALUE v = is_key ? rb_enc_interned_str((const char*)p, (long)n, cr_utf8_enc)
134
+ : rb_utf8_str_new((const char*)p, (long)n);
135
+ rv_attach(c, v);
136
+ return 1;
137
+ }
138
+
139
+ static int rv_bytes(void* ctx, const unsigned char* p, uint32_t n, int borrowed, int is_key,
140
+ const char* tag, uint32_t tag_len) {
141
+ /* parity: byte strings ride the same utf8 String today */
142
+ return rv_str(ctx, p, n, borrowed, is_key, tag, tag_len);
143
+ }
144
+
145
+ static int rv_open(void* ctx, int is_map, uint64_t cap, const char* tag, uint32_t tag_len) {
146
+ (void)tag;
147
+ (void)tag_len;
148
+ struct rv_ctx* c = ctx;
149
+ VALUE v;
150
+ if (is_map) {
151
+ v = (cap != UINT64_MAX && cap / 2 <= 4096) ? HASH_NEW_CAPA((long)(cap / 2))
152
+ : rb_hash_new();
153
+ } else {
154
+ v = (cap != UINT64_MAX && cap <= 8192) ? rb_ary_new_capa((long)cap) : rb_ary_new();
155
+ }
156
+ rb_ary_push(c->stack, v);
157
+ return 1;
158
+ }
159
+
160
+ static int rv_close(void* ctx, int is_map) {
161
+ (void)is_map;
162
+ struct rv_ctx* c = ctx;
163
+ VALUE v = rb_ary_pop(c->stack);
164
+ rv_attach(c, v);
165
+ return 1;
166
+ }
167
+
168
+ VALUE yep_rb_cbor_load(const char* p, size_t len, int strict) {
169
+ if (cr_utf8_enc == NULL) {
170
+ cr_utf8_enc = rb_utf8_encoding();
171
+ }
172
+ YeptrisStatus st = YEPTRIS_OK;
173
+ /* the decoder reads the caller's buffer directly; GC off across
174
+ * the pass (a compaction would move the String under p — the
175
+ * #160 bus error) */
176
+ VALUE gc_on = rb_gc_disable();
177
+ struct rv_ctx ctx;
178
+ ctx.stack = rb_ary_new();
179
+ ctx.root = Qnil;
180
+ static const yep_cbor_sink RV_SINK = {
181
+ NULL, rv_text, rv_int, rv_float, rv_str, rv_bytes, rv_open, rv_close,
182
+ };
183
+ yep_cbor_sink sink = RV_SINK;
184
+ sink.ctx = &ctx;
185
+ st = yep_cbor_decode_gen((const unsigned char*)p, len, strict, NULL, &sink);
186
+ if (RTEST(gc_on)) {
187
+ rb_gc_enable();
188
+ }
189
+ if (st != YEPTRIS_OK) {
190
+ return Qundef;
191
+ }
192
+ return ctx.root;
193
+ }
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "mkmf"
4
+
5
+ # libyeptris location: YEPTRIS_LIB_PATH (file or dir), then sibling
6
+ # checkouts. CI sets YEPTRIS_LIB_PATH (the built shared library) and
7
+ # YEPTRIS_SRC (the C checkout) explicitly.
8
+ lib_path = ENV["YEPTRIS_LIB_PATH"]
9
+ candidates = []
10
+ if lib_path
11
+ candidates << File.dirname(lib_path)
12
+ candidates << lib_path if File.directory?(lib_path)
13
+ end
14
+ candidates << File.expand_path("../../../../yeptris/build-validate/src", __dir__)
15
+ candidates << File.expand_path("../../../../yeptris/build/src", __dir__)
16
+ candidates << File.expand_path("../../../yeptris/build-validate/src", __dir__)
17
+ candidates << File.expand_path("../../../yeptris/build/src", __dir__)
18
+
19
+ # Source root: YEPTRIS_SRC (CI / explicit), then sibling checkouts.
20
+ src_roots = [ENV["YEPTRIS_SRC"]].compact
21
+ src_roots << File.expand_path("../../../../yeptris/src", __dir__)
22
+ src_roots << File.expand_path("../../../yeptris/src", __dir__)
23
+ src_root = src_roots.find { |d| d && File.directory?(File.join(d, "include")) }
24
+ abort "yeptris sources not found (set YEPTRIS_SRC)" unless src_root
25
+
26
+ $INCFLAGS << " -I#{src_root}/include -I#{src_root}/yeptris"
27
+ %w[build-validate/generated build/generated].each do |g|
28
+ d = File.join(File.dirname(src_root), g)
29
+ $INCFLAGS << " -I#{d}" if File.directory?(d)
30
+ end
31
+
32
+ libdir = candidates.find do |d|
33
+ d && File.directory?(d) && Dir[File.join(d, "libyeptris*.{dylib,so,dll}")].any?
34
+ end
35
+ abort "libyeptris not found (set YEPTRIS_LIB_PATH)" unless libdir
36
+
37
+ $LIBPATH << libdir
38
+ # Platform gems bake a RELATIVE rpath so the bundle finds the
39
+ # vendored libyeptris next to itself; absolute build-time paths
40
+ # would dangle on user machines.
41
+ rpath = ENV["YEPTRIS_RPATH"] || libdir
42
+ $LDFLAGS << " -Wl,-rpath,#{rpath}" if RUBY_PLATFORM =~ /linux|darwin/
43
+ have_library("yeptris", "yep_json_string") or abort "yep_json_string not exported — rebuild libyeptris"
44
+ have_library("yeptris", "yeptris_visit_json") or abort "yeptris_visit_json missing"
45
+
46
+ $CFLAGS << " -O3 -fvisibility=hidden" unless RUBY_PLATFORM =~ /mswin|mingw/
47
+ create_makefile("yeptris/native")