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.
- checksums.yaml +4 -4
- data/ext/libyeptris/extconf.rb +57 -0
- data/lib/yeptris/ffi.rb +12 -15
- data/lib/yeptris/json.rb +5 -2
- data/lib/yeptris/psych.rb +76 -3
- data/lib/yeptris.rb +36 -14
- data/vendor/libyeptris/CMakeLists.txt +254 -0
- data/vendor/libyeptris/cmake/yeptris-config.cmake.in +5 -0
- data/vendor/libyeptris/cmake/yeptris.pc.in +11 -0
- data/vendor/libyeptris/src/CMakeLists.txt +167 -0
- data/vendor/libyeptris/src/include/yeptris/api.h +32 -0
- data/vendor/libyeptris/src/include/yeptris/cbor.h +101 -0
- data/vendor/libyeptris/src/include/yeptris/dom.h +181 -0
- data/vendor/libyeptris/src/include/yeptris/emit.h +81 -0
- data/vendor/libyeptris/src/include/yeptris/error.h +41 -0
- data/vendor/libyeptris/src/include/yeptris/events.h +157 -0
- data/vendor/libyeptris/src/include/yeptris/json.h +45 -0
- data/vendor/libyeptris/src/include/yeptris/json.hpp +258 -0
- data/vendor/libyeptris/src/include/yeptris/jsonc_compat.h +115 -0
- data/vendor/libyeptris/src/include/yeptris/marshal.h +52 -0
- data/vendor/libyeptris/src/include/yeptris/parse.h +48 -0
- data/vendor/libyeptris/src/include/yeptris/plan.h +100 -0
- data/vendor/libyeptris/src/include/yeptris/resolve.h +71 -0
- data/vendor/libyeptris/src/include/yeptris/schema.h +119 -0
- data/vendor/libyeptris/src/include/yeptris/tape.h +99 -0
- data/vendor/libyeptris/src/include/yeptris/types.h +38 -0
- data/vendor/libyeptris/src/include/yeptris/values.h +102 -0
- data/vendor/libyeptris/src/include/yeptris/version.h.in +29 -0
- data/vendor/libyeptris/src/include/yeptris/visit.h +77 -0
- data/vendor/libyeptris/src/include/yeptris/yajl_compat.h +135 -0
- data/vendor/libyeptris/src/include/yeptris.h +23 -0
- data/vendor/libyeptris/src/yeptris/build.c +371 -0
- data/vendor/libyeptris/src/yeptris/cbor/cbor.h +25 -0
- data/vendor/libyeptris/src/yeptris/cbor/decode.c +846 -0
- data/vendor/libyeptris/src/yeptris/cbor/encode.c +824 -0
- data/vendor/libyeptris/src/yeptris/common/chartype.c +41 -0
- data/vendor/libyeptris/src/yeptris/common/chartype.h +77 -0
- data/vendor/libyeptris/src/yeptris/common/cpu.c +81 -0
- data/vendor/libyeptris/src/yeptris/common/cpu.h +40 -0
- data/vendor/libyeptris/src/yeptris/common/error.c +65 -0
- data/vendor/libyeptris/src/yeptris/common/error.h +79 -0
- data/vendor/libyeptris/src/yeptris/common/mutex.h +45 -0
- data/vendor/libyeptris/src/yeptris/common/nametab.c +275 -0
- data/vendor/libyeptris/src/yeptris/common/nametab.h +68 -0
- data/vendor/libyeptris/src/yeptris/common/port.h +74 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text.c +49 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text.h +295 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_avx2.c +534 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_neon.c +662 -0
- data/vendor/libyeptris/src/yeptris/common/simd_text_scalar.c +298 -0
- data/vendor/libyeptris/src/yeptris/common/string_view.c +34 -0
- data/vendor/libyeptris/src/yeptris/common/string_view.h +93 -0
- data/vendor/libyeptris/src/yeptris/doc.h +37 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.c +973 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.h +290 -0
- data/vendor/libyeptris/src/yeptris/dom/hpool.c +107 -0
- data/vendor/libyeptris/src/yeptris/dom/mapindex.c +173 -0
- data/vendor/libyeptris/src/yeptris/dom/mapindex.h +61 -0
- data/vendor/libyeptris/src/yeptris/dom/mutate.c +429 -0
- data/vendor/libyeptris/src/yeptris/emit/emit.c +349 -0
- data/vendor/libyeptris/src/yeptris/emit/float/api.h +38 -0
- data/vendor/libyeptris/src/yeptris/emit/float/dragon.c +405 -0
- data/vendor/libyeptris/src/yeptris/emit/float/floatint.h +203 -0
- data/vendor/libyeptris/src/yeptris/emit/float/print.c +396 -0
- data/vendor/libyeptris/src/yeptris/emit/style.c +81 -0
- data/vendor/libyeptris/src/yeptris/emit/style.h +24 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.c +746 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.h +61 -0
- data/vendor/libyeptris/src/yeptris/encoding/bom.c +31 -0
- data/vendor/libyeptris/src/yeptris/encoding/encoding.h +63 -0
- data/vendor/libyeptris/src/yeptris/encoding/transcode.c +148 -0
- data/vendor/libyeptris/src/yeptris/encoding/utf8_validate.c +178 -0
- data/vendor/libyeptris/src/yeptris/events/capture.c +150 -0
- data/vendor/libyeptris/src/yeptris/events/capture.h +39 -0
- data/vendor/libyeptris/src/yeptris/events/iterparse.c +208 -0
- data/vendor/libyeptris/src/yeptris/events/pull.c +97 -0
- data/vendor/libyeptris/src/yeptris/events/push.c +104 -0
- data/vendor/libyeptris/src/yeptris/events/recorder.c +107 -0
- data/vendor/libyeptris/src/yeptris/events/values.c +527 -0
- data/vendor/libyeptris/src/yeptris/events/values_priv.h +51 -0
- data/vendor/libyeptris/src/yeptris/events/yaml_compat.c +86 -0
- data/vendor/libyeptris/src/yeptris/events/yaml_compat.h +90 -0
- data/vendor/libyeptris/src/yeptris/jsonapi/jsonc_compat.c +687 -0
- data/vendor/libyeptris/src/yeptris/jsonapi/yajl_compat.c +728 -0
- data/vendor/libyeptris/src/yeptris/marshal.c +744 -0
- data/vendor/libyeptris/src/yeptris/memory/allocator.c +20 -0
- data/vendor/libyeptris/src/yeptris/memory/allocator.h +43 -0
- data/vendor/libyeptris/src/yeptris/memory/arena.c +134 -0
- data/vendor/libyeptris/src/yeptris/memory/arena.h +52 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.c +115 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.h +36 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.c +3689 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.h +65 -0
- data/vendor/libyeptris/src/yeptris/parse/events.h +124 -0
- data/vendor/libyeptris/src/yeptris/parse/numbers.c +349 -0
- data/vendor/libyeptris/src/yeptris/parse/numbers.h +33 -0
- data/vendor/libyeptris/src/yeptris/parse/scalars.c +416 -0
- data/vendor/libyeptris/src/yeptris/parse/scalars.h +75 -0
- data/vendor/libyeptris/src/yeptris/parse.c +650 -0
- data/vendor/libyeptris/src/yeptris/plan.c +817 -0
- data/vendor/libyeptris/src/yeptris/resolve/compat11.c +334 -0
- data/vendor/libyeptris/src/yeptris/resolve/core12.c +143 -0
- data/vendor/libyeptris/src/yeptris/resolve/resolver.h +56 -0
- data/vendor/libyeptris/src/yeptris/resolve/tags.c +58 -0
- data/vendor/libyeptris/src/yeptris/scan/json.c +792 -0
- data/vendor/libyeptris/src/yeptris/scan/json.h +109 -0
- data/vendor/libyeptris/src/yeptris/scan/scan.c +463 -0
- data/vendor/libyeptris/src/yeptris/scan/scan.h +150 -0
- data/vendor/libyeptris/src/yeptris/schema.c +238 -0
- data/vendor/libyeptris/src/yeptris/tape.c +1292 -0
- data/vendor/libyeptris/src/yeptris/version.c +7 -0
- data/vendor/libyeptris/src/yeptris/visit/dom_visit.c +115 -0
- data/vendor/libyeptris/src/yeptris/visit/json_visit.c +293 -0
- data/vendor/libyeptris/src/yeptris/visit/yaml_visit.c +127 -0
- metadata +112 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 802f22f654de443d6a21a15045b6ebea91b025839f7124fb89854e7c90085926
|
|
4
|
+
data.tar.gz: 3abdcf9d94d0f514c331e4ece6add4d792320fc9f1298a0ff9c9d212581a0f8c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
246
|
-
#
|
|
247
|
-
|
|
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.
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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,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
|