yeptris 0.6.5.4 → 0.6.7.2

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/ext/libyeptris/extconf.rb +57 -0
  3. data/lib/yeptris/ffi.rb +12 -15
  4. data/lib/yeptris/json.rb +5 -2
  5. data/lib/yeptris/psych.rb +76 -3
  6. data/lib/yeptris.rb +36 -14
  7. data/vendor/libyeptris/CMakeLists.txt +254 -0
  8. data/vendor/libyeptris/cmake/yeptris-config.cmake.in +5 -0
  9. data/vendor/libyeptris/cmake/yeptris.pc.in +11 -0
  10. data/vendor/libyeptris/src/CMakeLists.txt +167 -0
  11. data/vendor/libyeptris/src/include/yeptris/api.h +32 -0
  12. data/vendor/libyeptris/src/include/yeptris/cbor.h +101 -0
  13. data/vendor/libyeptris/src/include/yeptris/dom.h +181 -0
  14. data/vendor/libyeptris/src/include/yeptris/emit.h +81 -0
  15. data/vendor/libyeptris/src/include/yeptris/error.h +41 -0
  16. data/vendor/libyeptris/src/include/yeptris/events.h +157 -0
  17. data/vendor/libyeptris/src/include/yeptris/json.h +45 -0
  18. data/vendor/libyeptris/src/include/yeptris/json.hpp +258 -0
  19. data/vendor/libyeptris/src/include/yeptris/jsonc_compat.h +115 -0
  20. data/vendor/libyeptris/src/include/yeptris/marshal.h +52 -0
  21. data/vendor/libyeptris/src/include/yeptris/parse.h +48 -0
  22. data/vendor/libyeptris/src/include/yeptris/plan.h +100 -0
  23. data/vendor/libyeptris/src/include/yeptris/resolve.h +71 -0
  24. data/vendor/libyeptris/src/include/yeptris/schema.h +119 -0
  25. data/vendor/libyeptris/src/include/yeptris/tape.h +99 -0
  26. data/vendor/libyeptris/src/include/yeptris/types.h +38 -0
  27. data/vendor/libyeptris/src/include/yeptris/values.h +102 -0
  28. data/vendor/libyeptris/src/include/yeptris/version.h.in +29 -0
  29. data/vendor/libyeptris/src/include/yeptris/visit.h +77 -0
  30. data/vendor/libyeptris/src/include/yeptris/yajl_compat.h +135 -0
  31. data/vendor/libyeptris/src/include/yeptris.h +23 -0
  32. data/vendor/libyeptris/src/yeptris/build.c +371 -0
  33. data/vendor/libyeptris/src/yeptris/cbor/cbor.h +25 -0
  34. data/vendor/libyeptris/src/yeptris/cbor/decode.c +846 -0
  35. data/vendor/libyeptris/src/yeptris/cbor/encode.c +824 -0
  36. data/vendor/libyeptris/src/yeptris/common/chartype.c +41 -0
  37. data/vendor/libyeptris/src/yeptris/common/chartype.h +77 -0
  38. data/vendor/libyeptris/src/yeptris/common/cpu.c +81 -0
  39. data/vendor/libyeptris/src/yeptris/common/cpu.h +40 -0
  40. data/vendor/libyeptris/src/yeptris/common/error.c +65 -0
  41. data/vendor/libyeptris/src/yeptris/common/error.h +79 -0
  42. data/vendor/libyeptris/src/yeptris/common/mutex.h +45 -0
  43. data/vendor/libyeptris/src/yeptris/common/nametab.c +275 -0
  44. data/vendor/libyeptris/src/yeptris/common/nametab.h +68 -0
  45. data/vendor/libyeptris/src/yeptris/common/port.h +74 -0
  46. data/vendor/libyeptris/src/yeptris/common/simd_text.c +49 -0
  47. data/vendor/libyeptris/src/yeptris/common/simd_text.h +295 -0
  48. data/vendor/libyeptris/src/yeptris/common/simd_text_avx2.c +534 -0
  49. data/vendor/libyeptris/src/yeptris/common/simd_text_neon.c +662 -0
  50. data/vendor/libyeptris/src/yeptris/common/simd_text_scalar.c +298 -0
  51. data/vendor/libyeptris/src/yeptris/common/string_view.c +34 -0
  52. data/vendor/libyeptris/src/yeptris/common/string_view.h +93 -0
  53. data/vendor/libyeptris/src/yeptris/doc.h +37 -0
  54. data/vendor/libyeptris/src/yeptris/dom/dom.c +973 -0
  55. data/vendor/libyeptris/src/yeptris/dom/dom.h +290 -0
  56. data/vendor/libyeptris/src/yeptris/dom/hpool.c +107 -0
  57. data/vendor/libyeptris/src/yeptris/dom/mapindex.c +173 -0
  58. data/vendor/libyeptris/src/yeptris/dom/mapindex.h +61 -0
  59. data/vendor/libyeptris/src/yeptris/dom/mutate.c +429 -0
  60. data/vendor/libyeptris/src/yeptris/emit/emit.c +349 -0
  61. data/vendor/libyeptris/src/yeptris/emit/float/api.h +38 -0
  62. data/vendor/libyeptris/src/yeptris/emit/float/dragon.c +405 -0
  63. data/vendor/libyeptris/src/yeptris/emit/float/floatint.h +203 -0
  64. data/vendor/libyeptris/src/yeptris/emit/float/print.c +396 -0
  65. data/vendor/libyeptris/src/yeptris/emit/style.c +81 -0
  66. data/vendor/libyeptris/src/yeptris/emit/style.h +24 -0
  67. data/vendor/libyeptris/src/yeptris/emit/writer.c +746 -0
  68. data/vendor/libyeptris/src/yeptris/emit/writer.h +61 -0
  69. data/vendor/libyeptris/src/yeptris/encoding/bom.c +31 -0
  70. data/vendor/libyeptris/src/yeptris/encoding/encoding.h +63 -0
  71. data/vendor/libyeptris/src/yeptris/encoding/transcode.c +148 -0
  72. data/vendor/libyeptris/src/yeptris/encoding/utf8_validate.c +178 -0
  73. data/vendor/libyeptris/src/yeptris/events/capture.c +150 -0
  74. data/vendor/libyeptris/src/yeptris/events/capture.h +39 -0
  75. data/vendor/libyeptris/src/yeptris/events/iterparse.c +208 -0
  76. data/vendor/libyeptris/src/yeptris/events/pull.c +97 -0
  77. data/vendor/libyeptris/src/yeptris/events/push.c +104 -0
  78. data/vendor/libyeptris/src/yeptris/events/recorder.c +107 -0
  79. data/vendor/libyeptris/src/yeptris/events/values.c +527 -0
  80. data/vendor/libyeptris/src/yeptris/events/values_priv.h +51 -0
  81. data/vendor/libyeptris/src/yeptris/events/yaml_compat.c +86 -0
  82. data/vendor/libyeptris/src/yeptris/events/yaml_compat.h +90 -0
  83. data/vendor/libyeptris/src/yeptris/jsonapi/jsonc_compat.c +687 -0
  84. data/vendor/libyeptris/src/yeptris/jsonapi/yajl_compat.c +728 -0
  85. data/vendor/libyeptris/src/yeptris/marshal.c +744 -0
  86. data/vendor/libyeptris/src/yeptris/memory/allocator.c +20 -0
  87. data/vendor/libyeptris/src/yeptris/memory/allocator.h +43 -0
  88. data/vendor/libyeptris/src/yeptris/memory/arena.c +134 -0
  89. data/vendor/libyeptris/src/yeptris/memory/arena.h +52 -0
  90. data/vendor/libyeptris/src/yeptris/memory/pool.c +115 -0
  91. data/vendor/libyeptris/src/yeptris/memory/pool.h +36 -0
  92. data/vendor/libyeptris/src/yeptris/parse/engine.c +3689 -0
  93. data/vendor/libyeptris/src/yeptris/parse/engine.h +65 -0
  94. data/vendor/libyeptris/src/yeptris/parse/events.h +124 -0
  95. data/vendor/libyeptris/src/yeptris/parse/numbers.c +349 -0
  96. data/vendor/libyeptris/src/yeptris/parse/numbers.h +33 -0
  97. data/vendor/libyeptris/src/yeptris/parse/scalars.c +416 -0
  98. data/vendor/libyeptris/src/yeptris/parse/scalars.h +75 -0
  99. data/vendor/libyeptris/src/yeptris/parse.c +650 -0
  100. data/vendor/libyeptris/src/yeptris/plan.c +817 -0
  101. data/vendor/libyeptris/src/yeptris/resolve/compat11.c +334 -0
  102. data/vendor/libyeptris/src/yeptris/resolve/core12.c +143 -0
  103. data/vendor/libyeptris/src/yeptris/resolve/resolver.h +56 -0
  104. data/vendor/libyeptris/src/yeptris/resolve/tags.c +58 -0
  105. data/vendor/libyeptris/src/yeptris/scan/json.c +792 -0
  106. data/vendor/libyeptris/src/yeptris/scan/json.h +109 -0
  107. data/vendor/libyeptris/src/yeptris/scan/scan.c +463 -0
  108. data/vendor/libyeptris/src/yeptris/scan/scan.h +150 -0
  109. data/vendor/libyeptris/src/yeptris/schema.c +238 -0
  110. data/vendor/libyeptris/src/yeptris/tape.c +1292 -0
  111. data/vendor/libyeptris/src/yeptris/version.c +7 -0
  112. data/vendor/libyeptris/src/yeptris/visit/dom_visit.c +115 -0
  113. data/vendor/libyeptris/src/yeptris/visit/json_visit.c +293 -0
  114. data/vendor/libyeptris/src/yeptris/visit/yaml_visit.c +127 -0
  115. metadata +112 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec5a8bfeb0851d4287965e0f7c162cf13b71c90bdbb1f423a2bb0a8f508d22e4
4
- data.tar.gz: d757361d8e14a3f94f34419e71334c4c139dc60aaab7aaf638c9dd88239811c5
3
+ metadata.gz: 802f22f654de443d6a21a15045b6ebea91b025839f7124fb89854e7c90085926
4
+ data.tar.gz: 3abdcf9d94d0f514c331e4ece6add4d792320fc9f1298a0ff9c9d212581a0f8c
5
5
  SHA512:
6
- metadata.gz: 75c1451044793867918c6d9972ba7c452d79684c00e7b92a59a30f392bb741fe6c04b6e6571c3c7e6b956c14e7511bd48ba81ab79e552f54675d6726c2dd24d7
7
- data.tar.gz: debf5f3377d19dfba238bb412145e7335480dbde4f5c3cff8b8d51aea7426f26038a8913caf1902a92e633684377c52994f10a342e39d2a67a68a5739468ba45
6
+ metadata.gz: 3272f726668c0ffe2aff8e9c6f21315daec5ecc57ac4eee14843a763042dd234d1d19b31b24dd13c432c98f1b31e5b9a1408dbecb3fe6d76e8aa90a6115c5968
7
+ data.tar.gz: 628a30a67e89e0263820287266d3c7c06ba25793affe012114301047380d3c4c9344fa3b368d546a6aef80599b40d71c41aef2c871d9525b64d2adfc7136f1e0
@@ -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
data/lib/yeptris/ffi.rb CHANGED
@@ -28,17 +28,6 @@ module Yeptris
28
28
  MSG
29
29
  end
30
30
 
31
- # :c_free (:free, line ~240) must resolve against the C runtime.
32
- # Linux/macOS fall through to the process symbol table, but
33
- # Windows has no such fallback — without LIBC attached the
34
- # ffi.rb load DIED at that attach (leaving every later constant
35
- # undefined: the mingw gem's missing NODE_* / #318).
36
- begin
37
- ffi_lib FFI::Library::LIBC
38
- rescue StandardError
39
- # no LIBC handle: the c_free attach below surfaces the failure
40
- # exactly where it used to, on non-Windows platforms only
41
- end
42
31
 
43
32
  typedef :pointer, :yeptris_document
44
33
  typedef :pointer, :yeptris_node
@@ -242,9 +231,17 @@ module Yeptris
242
231
  end
243
232
 
244
233
  # Owned char* results (serialize*): one reader, freed exactly once.
245
- # The buffers are plain malloc'd C memory (the header contract says
246
- # "caller frees"), so the release is libc free.
247
- attach_function :c_free, :free, [:pointer], :void
234
+ # The release goes through yeptris_free (libyeptris's own
235
+ # allocator-matching free) because ffi's :free attach has no
236
+ # Windows process-symbol fallback — libyeptris.dll doesn't export
237
+ # libc free, so resolving :free against it fails on Windows.
238
+ begin
239
+ attach_function :yeptris_free, [:pointer], :void
240
+ rescue ::FFI::NotFoundError
241
+ # libyeptris < v0.6.6: POSIX resolves :free through the process
242
+ # symbol table, so the direct libc attach still works there.
243
+ attach_function :yeptris_free, :free, [:pointer], :void
244
+ end
248
245
 
249
246
  module Owned
250
247
  module_function
@@ -261,7 +258,7 @@ module Yeptris
261
258
  else
262
259
  ptr.read_string.force_encoding(Encoding::UTF_8)
263
260
  end
264
- ::Yeptris::FFI.c_free(ptr)
261
+ ::Yeptris::FFI.yeptris_free(ptr)
265
262
  s
266
263
  end
267
264
  end
data/lib/yeptris/json.rb CHANGED
@@ -71,7 +71,7 @@ module Yeptris
71
71
  return s unless s.include?("\\")
72
72
 
73
73
  b = s.b
74
- out = +"".b
74
+ out = String.new(encoding: Encoding::BINARY)
75
75
  i = 0
76
76
  n = b.bytesize
77
77
  while i < n
@@ -154,7 +154,10 @@ module Yeptris
154
154
  stack.pop
155
155
  key_sets&.pop
156
156
  when T_STR
157
- text = lens[i] == 0 ? +"".force_encoding(Encoding::UTF_8) : decode_span(src, offs[i], lens[i])
157
+ # empty-string literals are frozen+shared since Ruby 3.4
158
+ # (and `+""` binds after the method chain anyway), so the
159
+ # fresh copy must come from the encoding call itself
160
+ text = lens[i] == 0 ? String.new(encoding: Encoding::UTF_8) : decode_span(src, offs[i], lens[i])
158
161
  if pending_key.nil? && !stack.empty? && stack.last.is_a?(Hash)
159
162
  if key_sets && key_sets.last.key?(text)
160
163
  raise ParseError, %(duplicate key "#{text}" in JSON object)
data/lib/yeptris/psych.rb CHANGED
@@ -4,6 +4,11 @@ require "date"
4
4
  require "time"
5
5
  require "set"
6
6
 
7
+ # Self-sufficient (the #95 yaml.rb fix, same class): the standalone
8
+ # require "yeptris/psych" must register the Document/FFI machinery —
9
+ # consumers load this face directly under the drop-in.
10
+ require "yeptris"
11
+
7
12
  # The Psych drop-in namespace (TODO.impl/15 phase C).
8
13
  #
9
14
  # `require "yeptris/psych"` loads this namespace WITHOUT touching the
@@ -130,7 +135,7 @@ module Yeptris
130
135
  # Psych 5: load is safe — plain data structures only. Tagged
131
136
  # nodes raise DisallowedClass unless their class is permitted
132
137
  # (Date/Time/Symbol are built in; they are plain data here).
133
- def load(yaml, permitted_classes: [], aliases: false, **)
138
+ def load(yaml, permitted_classes: [::Date, ::Time], aliases: false, **)
134
139
  safe_load(yaml, permitted_classes: permitted_classes, aliases: aliases)
135
140
  end
136
141
 
@@ -144,10 +149,78 @@ module Yeptris
144
149
  Visitors::ToRuby.visit(tree.children.first)
145
150
  end
146
151
 
147
- def safe_load(yaml, permitted_classes: [], aliases: false, **)
152
+ def safe_load(yaml, permitted_classes: [::Date, ::Time], aliases: false, **)
153
+ doc = Yeptris::Document.parse(yaml, schema: :compat_11)
154
+ begin
155
+ force_utf8_scalars(walk_safe(doc.root(0), permitted_classes, aliases))
156
+ ensure
157
+ doc.free
158
+ end
159
+ end
160
+
161
+ # A YAML stream is a Unicode character stream: stdlib psych tags
162
+ # non-ASCII scalars UTF-8 even when the input String is BINARY
163
+ # (#135). ASCII-only values keep their (binary) encoding — a
164
+ # re-tag would be observable there and stdlib does not either.
165
+ def force_utf8_scalars(obj)
166
+ case obj
167
+ when ::String
168
+ obj.force_encoding(::Encoding::UTF_8) if obj.encoding == ::Encoding::ASCII_8BIT && !obj.ascii_only?
169
+ obj
170
+ when ::Hash
171
+ obj.each { |k, v| force_utf8_scalars(k); force_utf8_scalars(v) }
172
+ when ::Array
173
+ obj.each { |v| force_utf8_scalars(v) }
174
+ else
175
+ obj
176
+ end
177
+ end
178
+
179
+ # stdlib's file-level faces (#135): BOM-tolerant UTF-8 reads with
180
+ # the fallback contract (load_file path, fallback: false default).
181
+ def load_file(path, fallback: false, **kwargs)
182
+ File.open(path, "r:bom|utf-8") { |f| load(f, **kwargs) }
183
+ rescue Errno::ENOENT
184
+ raise unless fallback
185
+
186
+ false
187
+ end
188
+
189
+ def safe_load_file(path, fallback: false, **kwargs)
190
+ File.open(path, "r:bom|utf-8") { |f| safe_load(f, **kwargs) }
191
+ rescue Errno::ENOENT
192
+ raise unless fallback
193
+
194
+ false
195
+ end
196
+
197
+ def unsafe_load_file(path, fallback: false, **kwargs)
198
+ File.open(path, "r:bom|utf-8") { |f| unsafe_load(f, **kwargs) }
199
+ rescue Errno::ENOENT
200
+ raise unless fallback
201
+
202
+ false
203
+ end
204
+
205
+ def parse_file(path, **kwargs)
206
+ File.open(path, "r:bom|utf-8") { |f| parse(f, **kwargs) }
207
+ end
208
+
209
+
210
+
211
+ # stdlib's deprecated safe/dump split — same output here (every
212
+ # value this loader produces is safe data)
213
+ def safe_dump(obj, io = nil, options = {})
214
+ ::Yeptris::Psych.dump(obj, io, options)
215
+ end
216
+
217
+ def load_stream(yaml, **kwargs)
218
+ # materialize each document's root directly — the stream
219
+ # children share one C document, so their handles would all
220
+ # resolve to the first document's tree
148
221
  doc = Yeptris::Document.parse(yaml, schema: :compat_11)
149
222
  begin
150
- walk_safe(doc.root(0), permitted_classes, aliases)
223
+ (0...doc.document_count).map { |i| doc.root(i).to_ruby }
151
224
  ensure
152
225
  doc.free
153
226
  end
data/lib/yeptris.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Yeptris
4
4
  # The gem's version lives in the parent namespace's file — the last
5
5
  # internal require (yeptris/version) retired with it.
6
- VERSION = "0.6.5.4".freeze
6
+ VERSION = "0.6.7.2".freeze
7
7
  # The error hierarchy lives in THIS file (the parent namespace's
8
8
  # own file): nested constants do not trigger a parent-constant
9
9
  # autoload, and the law forbids internal requires — defining the
@@ -44,14 +44,15 @@ module Yeptris
44
44
  autoload :ValueML, "yeptris/valueml"
45
45
  autoload :Psych, "yeptris/psych"
46
46
  autoload :Schema, "yeptris/schema"
47
-
48
- # The safe_load error names, aliased at the top level so consumers
49
- # writing rescues need no nesting knowledge (issue #73's naming
50
- # note): Yeptris::DisallowedClass is Yeptris::Psych::DisallowedClass.
51
- DisallowedClass = Psych::DisallowedClass
52
- AliasesError = Psych::AliasesError
53
47
  end
54
48
 
49
+ # NOTE (the #318 poisoned-process class): the Psych error-name aliases
50
+ # live BELOW the ffi require — reading Psych here would trigger the
51
+ # psych autoload BEFORE the native library resolves, and a mid-load
52
+ # ffi failure under a drop-in rebind left ::Psych rebound against a
53
+ # half-initialized FFI module (every to_yaml in the process died).
54
+ # With this order an ffi failure is a clean, loud require failure.
55
+
55
56
  # Eager native-library resolution (leptris-ruby lesson): fail at
56
57
  # require time, not at first parse. The ffi require MUST come after
57
58
  # the autoload registrations — ffi.rb opens module Yeptris, and
@@ -67,6 +68,25 @@ rescue LoadError => e
67
68
  MSG
68
69
  end
69
70
 
71
+
72
+ # The safe_load error names, aliased at the top level so consumers
73
+ # writing rescues need no nesting knowledge (issue #73's naming
74
+ # note): Yeptris::DisallowedClass is Yeptris::Psych::DisallowedClass.
75
+ # LAZY (const_missing): a standalone require "yeptris/psych" loads
76
+ # yeptris.rb MID-psych — an eager alias here would re-enter the
77
+ # half-loaded psych.rb through the autoload and NameError. At first
78
+ # touch psych is complete in every load order.
79
+ module Yeptris
80
+ def self.const_missing(name)
81
+ case name
82
+ when :DisallowedClass, :AliasesError
83
+ const_set(name, Psych.const_get(name))
84
+ else
85
+ super
86
+ end
87
+ end
88
+ end
89
+
70
90
  # Optional C-API materializer (TODO.restructure/22): fused visit →
71
91
  # Ruby objects via the Ruby C API. Feature-detected — LoadError leaves
72
92
  # the FFI ladder (Marshal → columns → records) as the sole path.
@@ -75,16 +95,18 @@ end
75
95
  # DLL per supported minor ships and RUBY_VERSION selects. A missing
76
96
  # cell (Ruby 3.3 arm64 has no build) falls back LOUDLY to the ladder.
77
97
  begin
78
- if Gem.win_platform?
79
- require "yeptris/native-#{RUBY_VERSION[/\A\d+\.\d+/]}"
80
- else
98
+ # per-minor FIRST everywhere: a single version-agnostic native.so
99
+ # loads under ANY Ruby (symbols resolve from the host process), and
100
+ # one built for a different minor is an ABI gamble — Ruby 3.0 ran
101
+ # a 3.3-built ext and the JSON parse failed. The plain name stays
102
+ # as the dev-checkout fallback (built for the running Ruby).
103
+ require "yeptris/native-#{RUBY_VERSION[/\A\d+\.\d+/]}"
104
+ rescue LoadError
105
+ begin
81
106
  require "yeptris/native"
82
- end
83
- rescue LoadError => e
84
- if Gem.win_platform?
107
+ rescue LoadError => e
85
108
  warn "yeptris: no precompiled native materializer for Ruby " \
86
109
  "#{RUBY_VERSION[/\A\d+\.\d+/]} on this platform " \
87
110
  "(#{e.message}); the FFI ladder carries the load"
88
111
  end
89
- # FFI ladder only
90
112
  end
@@ -0,0 +1,254 @@
1
+ # yeptris — Ultra-fast YAML 1.2 parser, emitter and streamer in C
2
+ # The YAML counterpart of libleptris. Pure C library with CLI and bindings.
3
+ cmake_minimum_required(VERSION 3.20)
4
+
5
+ project(yeptris
6
+ VERSION 0.6.7
7
+ DESCRIPTION "Ultra-fast YAML 1.2 parser, emitter and streamer in C"
8
+ LANGUAGES C CXX
9
+ )
10
+
11
+ # C11 (the standard question, settled with evidence 2026-09-18): going
12
+ # C99 needs FOUR downgrades, not one — _Static_assert (fixed: the
13
+ # typedef-array YEPTRIS_CT_ASSERT), typedef redefinition (mapindex.c),
14
+ # _Thread_local (the per-thread error channel) and <stdatomics> (the
15
+ # lock-free handle pool). The last two are architecture-critical and
16
+ # absent from MSVC's C99 mode; pthread TLS / lock-based pools would
17
+ # regress the hot paths and weaken the concurrency contract.
18
+ set(CMAKE_C_STANDARD 11)
19
+ set(CMAKE_C_STANDARD_REQUIRED ON)
20
+ set(CMAKE_C_EXTENSIONS OFF)
21
+
22
+ # C++11 standard for tests and benchmarks.
23
+ set(CMAKE_CXX_STANDARD 11)
24
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
25
+ set(CMAKE_CXX_EXTENSIONS OFF)
26
+
27
+ # MSVC (the windows CI leg): the C++ suites use designated initializers
28
+ # (a GCC/Clang extension at C++11; MSVC requires C++20), and the test
29
+ # targets — test_conformance included — need test/compat on the include
30
+ # path for the dirent surface.
31
+ if(MSVC)
32
+ set(CMAKE_CXX_STANDARD 20)
33
+ endif()
34
+
35
+ # MSVC multi-config: one runtime home so ctest -C Release and the CLI
36
+ # smoke find every exe (the per-target default scattered them)
37
+ if(MSVC)
38
+ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
39
+ endif()
40
+
41
+ # MSVC's default stack is 1 MiB; the Unix builds run on 8 MiB — deep
42
+ # document paths (the depth guard's teardown) measured against the
43
+ # Unix default get the same room here
44
+ if(MSVC)
45
+ add_link_options(/STACK:8388608)
46
+ endif()
47
+
48
+ # ---------------------------------------------------------------- options --
49
+ option(BUILD_TESTING "Build tests" ON)
50
+ option(YEPTRIS_BUILD_CLI "Build the yeptris CLI tool" ON)
51
+ option(YEPTRIS_BUILD_STATIC "Build the static library" ON)
52
+ option(YEPTRIS_BUILD_SHARED "Build the shared library" OFF)
53
+ option(YEPTRIS_BUILD_BENCHMARKS "Build performance benchmarks (TODO.impl/18)" OFF)
54
+ option(YEPTRIS_BUILD_JSONC_COMPAT
55
+ "Build libyeptris-jsonc: the json-c drop-in migration layer (TODO.impl/21)" ON)
56
+ option(YEPTRIS_WITH_CBOR "Build the CBOR (RFC 8949) codec surface" ON)
57
+ option(YEPTRIS_ENABLE_ASAN "Enable AddressSanitizer" OFF)
58
+ option(YEPTRIS_ENABLE_TSAN "Enable ThreadSanitizer (requires all-TSAN build)" OFF)
59
+ option(YEPTRIS_ENABLE_UBSAN "Enable UndefinedBehaviorSanitizer" OFF)
60
+ option(YEPTRIS_ENABLE_FUZZING "Build libFuzzer harnesses (TODO.impl/19; requires clang)" OFF)
61
+ option(YEPTRIS_WARNINGS_AS_ERRORS "Treat compiler warnings as errors" OFF)
62
+
63
+ # LTO: ON for Release/RelWithDebInfo by default (libleptris measured
64
+ # 1.3-3.5x on parse paths); override with -DYEPTRIS_ENABLE_LTO=OFF.
65
+ if(NOT DEFINED YEPTRIS_ENABLE_LTO)
66
+ if(CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
67
+ set(YEPTRIS_ENABLE_LTO_DEFAULT ON)
68
+ else()
69
+ set(YEPTRIS_ENABLE_LTO_DEFAULT OFF)
70
+ endif()
71
+ else()
72
+ set(YEPTRIS_ENABLE_LTO_DEFAULT ${YEPTRIS_ENABLE_LTO})
73
+ endif()
74
+ option(YEPTRIS_ENABLE_LTO "Enable link-time optimization (default ON for Release)" ${YEPTRIS_ENABLE_LTO_DEFAULT})
75
+
76
+ # ------------------------------------------------------------- sanitizers --
77
+ if(YEPTRIS_ENABLE_ASAN AND YEPTRIS_ENABLE_TSAN)
78
+ message(FATAL_ERROR "ASAN and TSAN are mutually exclusive")
79
+ endif()
80
+
81
+ if(YEPTRIS_ENABLE_ASAN)
82
+ add_compile_options(-fsanitize=address -fno-omit-frame-pointer -g)
83
+ add_link_options(-fsanitize=address)
84
+ message(STATUS "AddressSanitizer: ENABLED")
85
+ endif()
86
+
87
+ if(YEPTRIS_ENABLE_TSAN)
88
+ add_compile_options(-fsanitize=thread -fno-omit-frame-pointer -g)
89
+ add_link_options(-fsanitize=thread)
90
+ message(STATUS "ThreadSanitizer: ENABLED")
91
+ endif()
92
+
93
+ if(YEPTRIS_ENABLE_UBSAN)
94
+ add_compile_options(
95
+ -fsanitize=undefined -fsanitize=float-cast-overflow
96
+ -fno-sanitize-recover=undefined -fno-omit-frame-pointer -g)
97
+ add_link_options(-fsanitize=undefined -fsanitize=float-cast-overflow)
98
+ message(STATUS "UndefinedBehaviorSanitizer: ENABLED (fatal)")
99
+ endif()
100
+
101
+ # libFuzzer implies ASAN (libleptris TODO 40 pattern).
102
+ if(YEPTRIS_ENABLE_FUZZING)
103
+ add_compile_options(-fsanitize=fuzzer-no-link,address -fno-omit-frame-pointer -g)
104
+ add_link_options(-fsanitize=fuzzer,address)
105
+ message(STATUS "libFuzzer: ENABLED (implies ASAN)")
106
+ endif()
107
+
108
+ # -------------------------------------------------------------------- LTO --
109
+ if(YEPTRIS_ENABLE_LTO)
110
+ include(CheckIPOSupported)
111
+ check_ipo_supported(RESULT yeptris_lto_supported OUTPUT yeptris_lto_error)
112
+ if(yeptris_lto_supported)
113
+ set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
114
+ message(STATUS "Link-time optimization: ENABLED")
115
+ else()
116
+ message(WARNING "YEPTRIS_ENABLE_LTO requested but not supported: ${yeptris_lto_error}")
117
+ endif()
118
+ endif()
119
+
120
+ # -------------------------------------------------------------- visibility --
121
+ # Position-independent code for static libraries (embeddable in DSOs).
122
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
123
+
124
+ # Hide all symbols by default; the public surface is exactly the
125
+ # YEPTRIS_API-marked functions (libleptris TODO 80 pattern).
126
+ set(CMAKE_C_VISIBILITY_PRESET hidden)
127
+ set(CMAKE_CXX_VISIBILITY_PRESET hidden)
128
+ set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
129
+
130
+ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
131
+
132
+ # ---------------------------------------------------------------- warnings --
133
+ # Scoped per-target so fetched dependencies (GTest) keep their own flags.
134
+ function(yeptris_set_warnings target)
135
+ if(MSVC)
136
+ # /wd4100 mirrors -Wno-unused-parameter; /experimental:c11atomics
137
+ # un-breaks the C11 headers' stdatomic gate (the windows sentinel
138
+ # surfaced both — the leg rides in the test matrix now)
139
+ target_compile_options(${target} PRIVATE /W4 /wd4100 /wd4996 /experimental:c11atomics)
140
+ if(YEPTRIS_WARNINGS_AS_ERRORS)
141
+ target_compile_options(${target} PRIVATE /WX)
142
+ endif()
143
+ else()
144
+ target_compile_options(${target} PRIVATE -Wall -Wextra -Wno-unused-parameter)
145
+ if(YEPTRIS_WARNINGS_AS_ERRORS)
146
+ target_compile_options(${target} PRIVATE -Werror)
147
+ endif()
148
+ endif()
149
+ endfunction()
150
+
151
+ # ------------------------------------------------------------- versioning --
152
+ # CMakeLists project(VERSION) is the single source of truth (TODO.impl/01);
153
+ # scripts/bump-version.sh syncs vcpkg.json and CHANGELOG.md from it.
154
+ configure_file(
155
+ ${CMAKE_CURRENT_SOURCE_DIR}/src/include/yeptris/version.h.in
156
+ ${CMAKE_BINARY_DIR}/generated/yeptris/version.h
157
+ @ONLY
158
+ )
159
+
160
+ # --------------------------------------------------------------- subdirs --
161
+ enable_testing()
162
+
163
+ add_subdirectory(src)
164
+
165
+ if(YEPTRIS_BUILD_CLI)
166
+ add_subdirectory(cli)
167
+ endif()
168
+
169
+ if(BUILD_TESTING)
170
+ if(MSVC)
171
+ add_compile_options(-I${CMAKE_SOURCE_DIR}/test/compat)
172
+ endif()
173
+ add_subdirectory(test)
174
+
175
+ # Conformance runner (TODO.impl/16): builds on demand; the corpus
176
+ # arrives via scripts/fetch-corpora.sh.
177
+ add_executable(test_conformance
178
+ test/conformance/main.c
179
+ test/conformance/suite.c
180
+ test/conformance/tree.c)
181
+ # Runs in CI after fetch-corpora.sh; passes-as-skip when absent.
182
+ add_test(NAME conformance
183
+ COMMAND test_conformance ${CMAKE_SOURCE_DIR}/test/conformance/data/yaml-test-suite/src)
184
+ # strict: the suite is at 100% — a regression must fail CI, not hide
185
+ # behind the non-strict exit (a tree-mismatch once slipped through)
186
+ set_tests_properties(conformance PROPERTIES ENVIRONMENT "YEP_STRICT=1")
187
+ # psych-pure's parse-{block,flow,scalar,props,stream} corpora,
188
+ # ported by scripts/port-pure-tml.py (checked-in fixtures)
189
+ add_test(NAME conformance-pure
190
+ COMMAND test_conformance ${CMAKE_SOURCE_DIR}/test/conformance/data/psych-pure)
191
+ set_tests_properties(conformance-pure PROPERTIES ENVIRONMENT "YEP_STRICT=1")
192
+ target_link_libraries(test_conformance PRIVATE yeptris_static)
193
+ target_include_directories(test_conformance PRIVATE
194
+ ${CMAKE_SOURCE_DIR}/src/yeptris
195
+ ${CMAKE_SOURCE_DIR}/src/include
196
+ ${CMAKE_BINARY_DIR}/generated)
197
+ yeptris_set_warnings(test_conformance)
198
+ endif()
199
+
200
+ if(YEPTRIS_BUILD_BENCHMARKS)
201
+ add_subdirectory(benchmarks)
202
+ endif()
203
+
204
+ # --------------------------------------------------------------- install --
205
+ # (TODO.impl/20) targets, headers, pkg-config, and the find_package
206
+ # config; relative paths keep relocatable installs.
207
+ include(GNUInstallDirs)
208
+
209
+ install(TARGETS yeptris_static
210
+ EXPORT yeptris-targets
211
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
212
+ )
213
+ if(TARGET yeptris_jsonc)
214
+ install(TARGETS yeptris_jsonc
215
+ EXPORT yeptris-targets
216
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
217
+ )
218
+ endif()
219
+ if(YEPTRIS_BUILD_CLI AND TARGET yeptris)
220
+ install(TARGETS yeptris DESTINATION ${CMAKE_INSTALL_BINDIR})
221
+ endif()
222
+
223
+ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/include/
224
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
225
+ FILES_MATCHING PATTERN "*.h" PATTERN "*.hpp"
226
+ )
227
+ install(FILES ${CMAKE_BINARY_DIR}/generated/yeptris/version.h
228
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/yeptris
229
+ )
230
+
231
+ # pkg-config
232
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/yeptris.pc.in
233
+ ${CMAKE_BINARY_DIR}/yeptris.pc @ONLY)
234
+ install(FILES ${CMAKE_BINARY_DIR}/yeptris.pc
235
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
236
+
237
+ # find_package(yeptris) config
238
+ include(CMakePackageConfigHelpers)
239
+ write_basic_package_version_file(
240
+ ${CMAKE_BINARY_DIR}/yeptris-config-version.cmake
241
+ VERSION ${PROJECT_VERSION}
242
+ COMPATIBILITY SameMajorVersion
243
+ )
244
+ install(EXPORT yeptris-targets
245
+ NAMESPACE yeptris::
246
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/yeptris)
247
+ configure_package_config_file(
248
+ ${CMAKE_CURRENT_SOURCE_DIR}/cmake/yeptris-config.cmake.in
249
+ ${CMAKE_BINARY_DIR}/yeptris-config.cmake
250
+ INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/yeptris)
251
+ install(FILES ${CMAKE_BINARY_DIR}/yeptris-config.cmake
252
+ ${CMAKE_BINARY_DIR}/yeptris-config-version.cmake
253
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/yeptris)
254
+
@@ -0,0 +1,5 @@
1
+ @PACKAGE_INIT@
2
+
3
+ include("${CMAKE_CURRENT_LIST_DIR}/yeptris-targets.cmake")
4
+
5
+ check_required_components(yeptris)
@@ -0,0 +1,11 @@
1
+ prefix=@CMAKE_INSTALL_PREFIX@
2
+ exec_prefix=${prefix}
3
+ libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
4
+ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
5
+
6
+ Name: yeptris
7
+ Description: @PROJECT_DESCRIPTION@
8
+ Version: @PROJECT_VERSION@
9
+ URL: https://github.com/leptris/yeptris
10
+ Cflags: -I${includedir}
11
+ Libs: -L${libdir} -lyeptris