yeptris 0.6.12.1 → 0.6.13.1
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/lib/yeptris/ffi.rb +62 -25
- data/lib/yeptris/schema.rb +37 -3
- data/lib/yeptris.rb +1 -1
- data/vendor/libyeptris/CMakeLists.txt +1 -1
- data/vendor/libyeptris/src/yeptris/dom/dom.c +113 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.h +10 -0
- data/vendor/libyeptris/src/yeptris/schema.c +72 -2
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c5dd96eb5583028d0da7d38ee4addaf108b8a2495696aacc6f68c43117773da3
|
|
4
|
+
data.tar.gz: b489c5bfc4b41d9cb3d8b836ac4b88f62aaeab5856788412301cde79ee0dfd2f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4d91f1cdb572d444d4f7a43aec756400375581b3fa7bb1f6bbf62e9ad134ffa392fb1a763f11daffb07ae865e4046b6379a15b7f57eec5fb72c842bb580aee52
|
|
7
|
+
data.tar.gz: 7ac70b81ca8cf423226b48f8e6422686dc6edd9593a94bc82ebc7d0470ee40a9de9f28b4bd0f4ddf1c85ee1e132156fa1d889db8a1af9a788a6f5384f60c08c6
|
data/lib/yeptris/ffi.rb
CHANGED
|
@@ -28,6 +28,32 @@ module Yeptris
|
|
|
28
28
|
MSG
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
class << self
|
|
32
|
+
# #138's class, hit again by the oiml-cs report: a stale
|
|
33
|
+
# libyeptris on the machine (the Windows base-name dedupe makes
|
|
34
|
+
# a system copy win) lacks newer symbols, and a raising attach
|
|
35
|
+
# aborts this file mid-evaluation — every constant after it
|
|
36
|
+
# never defines, so the next autoload dies with
|
|
37
|
+
# 'uninitialized constant Yeptris::FFI::NODE_SCALAR'. Attaches
|
|
38
|
+
# now record the miss and define a raising stub of the same
|
|
39
|
+
# name: the load completes, optional surfaces fail with a clear
|
|
40
|
+
# message at USE, and the ESSENTIAL self-check at the bottom of
|
|
41
|
+
# this file raises the friendly load error for truly old
|
|
42
|
+
# engines.
|
|
43
|
+
def attach_function(name, args, ret, opts = {})
|
|
44
|
+
super
|
|
45
|
+
rescue ::FFI::NotFoundError, ::FFI::TypeError
|
|
46
|
+
define_singleton_method(name) do |*_a, **_kw|
|
|
47
|
+
raise ::FFI::NotFoundError,
|
|
48
|
+
"yeptris: #{name} is unavailable — the loaded " \
|
|
49
|
+
"libyeptris is older than this gem. Update the engine " \
|
|
50
|
+
"library (or clear the stale copy shadowing it)."
|
|
51
|
+
end
|
|
52
|
+
(@missing ||= []) << name
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
31
57
|
|
|
32
58
|
typedef :pointer, :yeptris_document
|
|
33
59
|
typedef :pointer, :yeptris_node
|
|
@@ -194,18 +220,14 @@ module Yeptris
|
|
|
194
220
|
# instead of walking per-value records in pure Ruby. Same feature-
|
|
195
221
|
# detect discipline as the columnar drain (older libraries fall
|
|
196
222
|
# back to the record walk).
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
true
|
|
206
|
-
rescue ::FFI::NotFoundError
|
|
207
|
-
false
|
|
208
|
-
end
|
|
223
|
+
MARSHAL_ALL_DOCS = 0
|
|
224
|
+
MARSHAL_FIRST_DOC = 1
|
|
225
|
+
attach_function :yeptris_marshal,
|
|
226
|
+
%i[pointer size_t int int pointer pointer], :int
|
|
227
|
+
attach_function :yeptris_marshal_node,
|
|
228
|
+
%i[yeptris_node pointer pointer], :int
|
|
229
|
+
attach_function :yeptris_marshal_free, [:pointer], :void
|
|
230
|
+
MARSHAL = !(@missing ||= []).include?(:yeptris_marshal_node)
|
|
209
231
|
|
|
210
232
|
# bulk build (TODO.impl/15 phase D): one call raises a document
|
|
211
233
|
BUILD_SCALAR = 1
|
|
@@ -238,19 +260,15 @@ module Yeptris
|
|
|
238
260
|
|
|
239
261
|
# CBOR (RFC 8949, TODO.cbor): absent on libraries before the
|
|
240
262
|
# codec's release — the CBOR module feature-detects
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
true
|
|
251
|
-
rescue ::FFI::NotFoundError
|
|
252
|
-
false
|
|
253
|
-
end
|
|
263
|
+
attach_function :yeptris_cbor_decode, %i[pointer size_t uint32 yeptris_status_out],
|
|
264
|
+
:yeptris_document
|
|
265
|
+
callback :cbor_item_cb, %i[pointer yeptris_document size_t], :int
|
|
266
|
+
attach_function :yeptris_cbor_decode_sequence,
|
|
267
|
+
%i[pointer size_t uint32 cbor_item_cb pointer yeptris_status_out], :size_t
|
|
268
|
+
attach_function :yeptris_cbor_encode, %i[yeptris_document uint32 pointer], :pointer
|
|
269
|
+
attach_function :yeptris_cbor_encode_sequence,
|
|
270
|
+
%i[pointer size_t uint32 pointer], :pointer
|
|
271
|
+
CBOR_EX = !(@missing ||= []).include?(:yeptris_cbor_decode)
|
|
254
272
|
|
|
255
273
|
# Owned char* results (serialize*): one reader, freed exactly once.
|
|
256
274
|
# The release goes through yeptris_free (libyeptris's own
|
|
@@ -368,5 +386,24 @@ module Yeptris
|
|
|
368
386
|
ERROR_ARG = 6
|
|
369
387
|
ERROR_UNSUPPORTED = 7
|
|
370
388
|
ERROR_INTERNAL = 8
|
|
389
|
+
# The load-time essentials (the read path + the ownership
|
|
390
|
+
# contract): absent on engines far older than the optional
|
|
391
|
+
# surfaces — raise the friendly error once every constant has
|
|
392
|
+
# fully defined, so no partial-module state leaks into autoloads
|
|
393
|
+
# (the #138 class, closed at the root).
|
|
394
|
+
ESSENTIAL = %i[
|
|
395
|
+
yeptris_version yeptris_last_error yeptris_parse yeptris_parse_ex
|
|
396
|
+
yeptris_parse_json yeptris_document_free yeptris_document_count
|
|
397
|
+
yeptris_document_root yeptris_node_kind yeptris_node_value
|
|
398
|
+
yeptris_node_tag_id yeptris_node_style yeptris_document_new
|
|
399
|
+
yeptris_document_set_root yeptris_serialize yeptris_serialize_ex
|
|
400
|
+
].freeze
|
|
401
|
+
missing_core = ESSENTIAL & (@missing ||= [])
|
|
402
|
+
raise ::FFI::NotFoundError,
|
|
403
|
+
"yeptris: the loaded libyeptris is older than this gem " \
|
|
404
|
+
"requires (missing: #{missing_core.join(', ')}). Update the " \
|
|
405
|
+
"engine library, or clear the stale copy shadowing it." \
|
|
406
|
+
unless missing_core.empty?
|
|
407
|
+
|
|
371
408
|
end
|
|
372
409
|
end
|
data/lib/yeptris/schema.rb
CHANGED
|
@@ -9,13 +9,37 @@ module Yeptris
|
|
|
9
9
|
module_function
|
|
10
10
|
|
|
11
11
|
KIND = { scalar: 0, sequence: 1, mapping: 2, callback: 3 }.freeze
|
|
12
|
-
TYPE = { str: 0, int: 1, float: 2, bool: 3, null: 4 }.freeze
|
|
12
|
+
TYPE = { str: 0, int: 1, float: 2, bool: 3, null: 4, any: 5 }.freeze
|
|
13
13
|
REQUIRED = 1 << 0
|
|
14
|
+
FIRST_WINS = 1 << 1 # duplicates within one mapping: first kept
|
|
15
|
+
|
|
16
|
+
# The :any verdict (#238's headroom): the resolver's own typing,
|
|
17
|
+
# one 24-byte record per value — Integer/Float/true/false/nil, a
|
|
18
|
+
# zero-copy String for str/timestamp spans
|
|
19
|
+
ANY_SIZE = 24 # yeptris_value.h's YeptrisValue: kind@0 tag_id@1
|
|
20
|
+
# is_key@2 b@3 off@4 len@8 p@16 — the C layout, ABI-pinned
|
|
21
|
+
ANY_KIND = { 1 => :null, 2 => :bool, 3 => :int, 4 => :float,
|
|
22
|
+
5 => :str, 6 => :timestamp }.freeze
|
|
14
23
|
|
|
15
24
|
class Error < Yeptris::Error; end
|
|
25
|
+
|
|
26
|
+
# #238's executed headroom (ST_ANY/FIRST_WINS) rides the C tag
|
|
27
|
+
# AFTER v0.6.12; probe the engine rather than trusting versions
|
|
28
|
+
class << self
|
|
29
|
+
def headroom_supported?
|
|
30
|
+
return @headroom_supported unless @headroom_supported.nil?
|
|
31
|
+
@headroom_supported = begin
|
|
32
|
+
load("x: 1\n",
|
|
33
|
+
desc: [{ kind: :mapping, child_index: 1, child_count: 1 },
|
|
34
|
+
{ wire_name: "x", kind: :scalar, type: :any }]) && true
|
|
35
|
+
rescue Error, Yeptris::ParseError, Yeptris::Error
|
|
36
|
+
false
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
16
40
|
class RequiredMissing < Error; end
|
|
17
41
|
|
|
18
|
-
ELEMENT_SIZE = { 0 => 16, 1 => 8, 2 => 8, 3 => 1, 4 => 1 }.freeze # by TYPE value
|
|
42
|
+
ELEMENT_SIZE = { 0 => 16, 1 => 8, 2 => 8, 3 => 1, 4 => 1, 5 => ANY_SIZE }.freeze # by TYPE value
|
|
19
43
|
|
|
20
44
|
# A materialized span: zero-copy off/len into the source plus the
|
|
21
45
|
# node's byte offset (the CALLBACK escape hatch).
|
|
@@ -47,7 +71,7 @@ module Yeptris
|
|
|
47
71
|
{ kind: KIND.fetch(n.fetch(:kind)),
|
|
48
72
|
type: TYPE.fetch(n[:type] || :str),
|
|
49
73
|
wire: n[:wire_name],
|
|
50
|
-
flags: n[:required] ? REQUIRED : 0,
|
|
74
|
+
flags: (n[:required] ? REQUIRED : 0) | (n[:first_wins] ? FIRST_WINS : 0),
|
|
51
75
|
child_index: n[:child_index] || 0,
|
|
52
76
|
child_count: n[:child_count] || 0 }
|
|
53
77
|
end
|
|
@@ -120,6 +144,16 @@ module Yeptris
|
|
|
120
144
|
when TYPE[:float] then data.get_double(k * 8)
|
|
121
145
|
when TYPE[:bool] then data.get_uchar(k) == 1
|
|
122
146
|
when TYPE[:null] then nil
|
|
147
|
+
when TYPE[:any]
|
|
148
|
+
rec = k * ANY_SIZE
|
|
149
|
+
case ANY_KIND[data.get_uint8(rec)]
|
|
150
|
+
when :int then data.get_int64(rec + 16)
|
|
151
|
+
when :float then data.get_double(rec + 16)
|
|
152
|
+
when :bool then data.get_uint8(rec + 3) == 1
|
|
153
|
+
when :null then nil
|
|
154
|
+
else # str/timestamp: the zero-copy span
|
|
155
|
+
source.byteslice(data.get_uint32(rec + 4), data.get_uint32(rec + 8))
|
|
156
|
+
end
|
|
123
157
|
else
|
|
124
158
|
off = data.get_uint32(k * 16)
|
|
125
159
|
len = data.get_uint32(k * 16 + 4)
|
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.13.1".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
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/* dom.c — event-built DOM (builder sink + storage). */
|
|
2
2
|
|
|
3
|
+
#include <stdio.h>
|
|
3
4
|
#include <string.h>
|
|
4
5
|
|
|
5
6
|
#include "common/simd_text.h"
|
|
@@ -864,6 +865,118 @@ int dom_on_flow_commit(void* ctx) {
|
|
|
864
865
|
return 1;
|
|
865
866
|
}
|
|
866
867
|
|
|
868
|
+
int dom_from_tape(yep_dom* d, const yeptris_json_tape* t) {
|
|
869
|
+
if (d == NULL || t == NULL || t->_src == NULL) {}
|
|
870
|
+
/* the columns are the one representation valid on every route
|
|
871
|
+
* (strict: native; lenient fused: lazily materialized from the
|
|
872
|
+
* records; scalar-root lenient: native column writes) */
|
|
873
|
+
if (yeptris_tape_columns((yeptris_json_tape*)t) != 0) {}
|
|
874
|
+
if (t->kinds == NULL) {}
|
|
875
|
+
d->input_base = (const char*)t->_src;
|
|
876
|
+
d->input_len = t->_srclen;
|
|
877
|
+
const char* src = (const char*)t->_src;
|
|
878
|
+
d->depth = 0;
|
|
879
|
+
for (uint32_t i = 0; i < t->count; i++) {
|
|
880
|
+
const uint8_t kind = t->kinds[i];
|
|
881
|
+
const uint32_t len = t->lens[i];
|
|
882
|
+
const uint32_t off = t->offs[i];
|
|
883
|
+
switch (kind) {
|
|
884
|
+
case YEP_T_DOC:
|
|
885
|
+
break; /* the stream boundary record */
|
|
886
|
+
case YEP_T_SEQ_OPEN:
|
|
887
|
+
case YEP_T_MAP_OPEN: {
|
|
888
|
+
if (d->depth >= YEP_DOM_MAX_DEPTH) {}
|
|
889
|
+
uint32_t id =
|
|
890
|
+
dom_open_node(d, kind == YEP_T_SEQ_OPEN ? YEP_DOM_SEQUENCE : YEP_DOM_MAPPING, NULL,
|
|
891
|
+
NULL, 0, 0, 0, 1);
|
|
892
|
+
if (id == UINT32_MAX || dom_place(d, id) != 0) {}
|
|
893
|
+
d->map_pending_key[d->depth] = 0;
|
|
894
|
+
d->stack[d->depth++] = id;
|
|
895
|
+
break;
|
|
896
|
+
}
|
|
897
|
+
case YEP_T_CLOSE:
|
|
898
|
+
if (d->depth == 0) {}
|
|
899
|
+
d->depth--;
|
|
900
|
+
break;
|
|
901
|
+
case YEP_T_NULL:
|
|
902
|
+
case YEP_T_TRUE:
|
|
903
|
+
case YEP_T_FALSE:
|
|
904
|
+
case YEP_T_INT:
|
|
905
|
+
case YEP_T_FLOAT:
|
|
906
|
+
case YEP_T_NUM:
|
|
907
|
+
case YEP_T_STR: {
|
|
908
|
+
yep_tag_id tag = YEPTRIS_TAG_STR;
|
|
909
|
+
uint8_t style = YEP_STYLE_PLAIN;
|
|
910
|
+
int implicit = 0;
|
|
911
|
+
switch (kind) {
|
|
912
|
+
case YEP_T_NULL:
|
|
913
|
+
tag = YEPTRIS_TAG_NULL;
|
|
914
|
+
implicit = 1;
|
|
915
|
+
break;
|
|
916
|
+
case YEP_T_TRUE:
|
|
917
|
+
case YEP_T_FALSE:
|
|
918
|
+
tag = YEPTRIS_TAG_BOOL;
|
|
919
|
+
implicit = 1;
|
|
920
|
+
break;
|
|
921
|
+
case YEP_T_INT:
|
|
922
|
+
case YEP_T_FLOAT:
|
|
923
|
+
case YEP_T_NUM:
|
|
924
|
+
tag = YEPTRIS_TAG_INT;
|
|
925
|
+
implicit = 1;
|
|
926
|
+
if (kind == YEP_T_NUM) {
|
|
927
|
+
/* the lenient route's deferred contract: the span
|
|
928
|
+
* carried NO number grammar at parse — classify
|
|
929
|
+
* now (the tape convert's own authority) */
|
|
930
|
+
size_t adv = 0;
|
|
931
|
+
int flt = 0;
|
|
932
|
+
if (yep_json_number_shape(src + off, len, &adv, &flt) == 0 || adv != len) {
|
|
933
|
+
return -1; /* malformed: the strict route rejected at parse */
|
|
934
|
+
}
|
|
935
|
+
tag = flt ? YEPTRIS_TAG_FLOAT : YEPTRIS_TAG_INT;
|
|
936
|
+
} else if (kind == YEP_T_FLOAT) {
|
|
937
|
+
tag = YEPTRIS_TAG_FLOAT;
|
|
938
|
+
}
|
|
939
|
+
break;
|
|
940
|
+
default:
|
|
941
|
+
style = YEP_STYLE_DOUBLE_QUOTED;
|
|
942
|
+
break;
|
|
943
|
+
}
|
|
944
|
+
uint32_t id =
|
|
945
|
+
dom_open_node(d, YEP_DOM_SCALAR, NULL, NULL, 0, style, (uint8_t)implicit, 0);
|
|
946
|
+
if (id == UINT32_MAX) {
|
|
947
|
+
return -1;
|
|
948
|
+
}
|
|
949
|
+
if (kind == YEP_T_STR) {
|
|
950
|
+
/* the record's span is the INNER bytes (quotes off) */
|
|
951
|
+
if (memchr(src + off, '\\', len) != NULL) {
|
|
952
|
+
/* escaped: unescape into the arena (the fused
|
|
953
|
+
* builder's arm, byte for byte) */
|
|
954
|
+
char* dst = yep_dom_str_tail(d, len);
|
|
955
|
+
if (dst == NULL) {}
|
|
956
|
+
d->nodes[id].value = yep_dom_str_commit(
|
|
957
|
+
d, yep_finish_double_into(src, off, off + len, dst, len));
|
|
958
|
+
} else {
|
|
959
|
+
yep_view v = {src + off, len};
|
|
960
|
+
d->nodes[id].value = dom_str_in(d, &v, 1);
|
|
961
|
+
}
|
|
962
|
+
} else {
|
|
963
|
+
yep_view v = {src + off, len};
|
|
964
|
+
d->nodes[id].value = dom_str_in(d, &v, 1);
|
|
965
|
+
}
|
|
966
|
+
d->nodes[id].tag_id = tag;
|
|
967
|
+
if (dom_place(d, id) != 0) {
|
|
968
|
+
return -1;
|
|
969
|
+
}
|
|
970
|
+
break;
|
|
971
|
+
}
|
|
972
|
+
default:
|
|
973
|
+
return -1; /* a CONT run or an unknown kind: neither reaches a
|
|
974
|
+
strict-legal tape (CONT only past 16 MiB tokens) */
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
return d->depth == 0 ? 0 : -1;
|
|
978
|
+
}
|
|
979
|
+
|
|
867
980
|
void dom_on_flow_rollback(void* ctx) {
|
|
868
981
|
yep_dom* d = (yep_dom*)ctx;
|
|
869
982
|
if (d->flow_staged) {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
#include <stdint.h>
|
|
19
19
|
|
|
20
20
|
#include <yeptris/api.h> /* YEPTRIS_CT_ASSERT (the layout gates) */
|
|
21
|
+
#include <yeptris/tape.h> /* yeptris_json_tape (dom_from_tape) */ /* YEPTRIS_CT_ASSERT (the layout gates) */
|
|
21
22
|
|
|
22
23
|
#include "../common/simd_text.h"
|
|
23
24
|
|
|
@@ -246,6 +247,15 @@ void yep_mut_set_depths(yep_dom* d, uint32_t id, uint16_t depth);
|
|
|
246
247
|
* strictly-validated span [open, close] directly through the DOM's own
|
|
247
248
|
* placement/link/anchor laws — node-for-node identical to the event
|
|
248
249
|
* path (the flow-direct-diff ctest pins it). 1 = built, <0 = abort. */
|
|
250
|
+
/* dom_from_tape — the lazy-DOM seam (#342 slice 1): builds the node
|
|
251
|
+
* tree from the tape's records, mirroring dom_on_flow_build's arms
|
|
252
|
+
* (borrowed spans for clean strings, an arena unescape for escaped
|
|
253
|
+
* ones, typed literals by kind). The records are pre-validated, so
|
|
254
|
+
* the walk is placement only. Returns 0, or -1 (allocation/depth/an
|
|
255
|
+
* unexpected record). STRICT tapes only — YEP_T_NUM (the lenient
|
|
256
|
+
* route's deferred numbers) is rejected. */
|
|
257
|
+
int dom_from_tape(yep_dom* d, const yeptris_json_tape* t);
|
|
258
|
+
|
|
249
259
|
int dom_on_flow_build(void* ctx, const char* p, size_t open, size_t len, uint32_t line,
|
|
250
260
|
size_t line_start, yep_view anchor, yep_view tag, uint32_t anchor_id,
|
|
251
261
|
int max_depth, size_t* close);
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
#include <string.h>
|
|
14
14
|
|
|
15
15
|
#include <yeptris/schema.h>
|
|
16
|
+
#include <yeptris/values.h> /* YeptrisValue: ST_ANY records (#238) */
|
|
16
17
|
|
|
17
18
|
#include "dom/dom.h"
|
|
18
19
|
#include "memory/allocator.h"
|
|
@@ -27,7 +28,9 @@ typedef struct {
|
|
|
27
28
|
const yep_dom* dom;
|
|
28
29
|
int oom;
|
|
29
30
|
int overflow;
|
|
30
|
-
int type_mismatch;
|
|
31
|
+
int type_mismatch; /* node index */
|
|
32
|
+
uint32_t* seen_map; /* per-node: the mapping instance that last
|
|
33
|
+
* emitted — YEP_SF_FIRST_WINS skips a repeat */
|
|
31
34
|
} yep_schema_ctx;
|
|
32
35
|
|
|
33
36
|
static int col_put(yep_schema_ctx* c, uint32_t node, const void* rec, size_t size) {
|
|
@@ -84,6 +87,56 @@ static int emit_scalar(yep_schema_ctx* c, uint32_t node, uint32_t at) {
|
|
|
84
87
|
case YEP_ST_NULL:
|
|
85
88
|
col_put(c, node, &(uint8_t){0}, 0);
|
|
86
89
|
return 1;
|
|
90
|
+
case YEP_ST_ANY: {
|
|
91
|
+
/* the resolver's verdict, verbatim: a YeptrisValue record
|
|
92
|
+
* (#238's headroom) — kind/tag_id from the node, the span
|
|
93
|
+
* borrowed, and the numeric payload converted when the
|
|
94
|
+
* verdict says so (the values-drain's conversion, DOM-side) */
|
|
95
|
+
YeptrisValue r = {0, 0, 0, 0, 0, 0, 0};
|
|
96
|
+
r.tag_id = n->tag_id;
|
|
97
|
+
r.off = (uint32_t)((const char*)v.p - c->dom->input_base);
|
|
98
|
+
r.len = v.len;
|
|
99
|
+
switch (n->tag_id) {
|
|
100
|
+
case YEPTRIS_TAG_NULL:
|
|
101
|
+
r.kind = YEP_V_NULL;
|
|
102
|
+
break;
|
|
103
|
+
case YEPTRIS_TAG_BOOL: {
|
|
104
|
+
r.kind = YEP_V_BOOL;
|
|
105
|
+
r.b = (uint8_t)(v.len > 0 && (v.p[0] == 't' || v.p[0] == 'T' || v.p[0] == 'y' ||
|
|
106
|
+
v.p[0] == 'Y' || v.p[0] == 'o' || v.p[0] == '1'));
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
case YEPTRIS_TAG_INT:
|
|
110
|
+
case YEPTRIS_TAG_FLOAT: {
|
|
111
|
+
int64_t iv = 0;
|
|
112
|
+
double dv = 0;
|
|
113
|
+
int shape = 0;
|
|
114
|
+
size_t end = 0;
|
|
115
|
+
if (v.len != 0 && v.p != NULL &&
|
|
116
|
+
yep_json_number_scan(v.p, v.len, &end, &shape, &iv, &dv) != 0 && end == v.len) {
|
|
117
|
+
if (n->tag_id == YEPTRIS_TAG_INT) {
|
|
118
|
+
r.kind = YEP_V_INT;
|
|
119
|
+
r.p = (uint64_t)iv;
|
|
120
|
+
} else {
|
|
121
|
+
r.kind = YEP_V_FLOAT;
|
|
122
|
+
double fd = shape == 0 ? (double)iv : dv;
|
|
123
|
+
memcpy(&r.p, &fd, sizeof(fd));
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
r.kind = YEP_V_STR; /* the resolver said number, the
|
|
127
|
+
bytes disagree: keep the span */
|
|
128
|
+
}
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
case YEPTRIS_TAG_TIMESTAMP:
|
|
132
|
+
r.kind = YEP_V_TIMESTAMP;
|
|
133
|
+
break;
|
|
134
|
+
default:
|
|
135
|
+
r.kind = YEP_V_STR;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
return col_put(c, node, &r, sizeof(r));
|
|
139
|
+
}
|
|
87
140
|
default:
|
|
88
141
|
return -1;
|
|
89
142
|
}
|
|
@@ -140,10 +193,17 @@ static int emit(yep_schema_ctx* c, uint32_t node, uint32_t at) {
|
|
|
140
193
|
size_t wl = strlen(cd->wire_name ? cd->wire_name : "");
|
|
141
194
|
if (cd->wire_name != NULL && key.len == wl &&
|
|
142
195
|
memcmp(key.p, cd->wire_name, wl) == 0) {
|
|
196
|
+
if ((cd->flags & YEP_SF_FIRST_WINS) && c->seen_map != NULL &&
|
|
197
|
+
c->seen_map[ci] == at + 1) {
|
|
198
|
+
break; /* a repeat within THIS mapping: first wins */
|
|
199
|
+
}
|
|
143
200
|
int rc = emit(c, ci, val);
|
|
144
201
|
if (rc != 1) {
|
|
145
202
|
return rc;
|
|
146
203
|
}
|
|
204
|
+
if ((cd->flags & YEP_SF_FIRST_WINS) && c->seen_map != NULL) {
|
|
205
|
+
c->seen_map[ci] = at + 1;
|
|
206
|
+
}
|
|
147
207
|
break;
|
|
148
208
|
}
|
|
149
209
|
}
|
|
@@ -178,6 +238,7 @@ YEPTRIS_API YeptrisStatus yeptris_schema_load(const char* source, size_t len, Ye
|
|
|
178
238
|
const yep_allocator* sys = yep_system_allocator();
|
|
179
239
|
yep_engine* eng = yep_engine_create(sys);
|
|
180
240
|
yep_dom* dom = yep_dom_create(sys);
|
|
241
|
+
uint32_t* seen_map_ptr = NULL;
|
|
181
242
|
YeptrisStatus st = YEPTRIS_OK;
|
|
182
243
|
yep_sink sink = {.on_event = yep_dom_on_event,
|
|
183
244
|
.ctx = dom,
|
|
@@ -194,6 +255,9 @@ YEPTRIS_API YeptrisStatus yeptris_schema_load(const char* source, size_t len, Ye
|
|
|
194
255
|
}
|
|
195
256
|
if (schema == YEPTRIS_SCHEMA_11_COMPAT) {
|
|
196
257
|
yep_engine_set_resolver(eng, yep_resolver_compat11());
|
|
258
|
+
dom->resolver = yep_resolver_compat11(); /* the DOM's direct
|
|
259
|
+
builders resolve their own tag_ids —
|
|
260
|
+
ST_ANY reads the NODE's verdict */
|
|
197
261
|
}
|
|
198
262
|
dom->input_base = source;
|
|
199
263
|
dom->input_len = len;
|
|
@@ -204,7 +268,12 @@ YEPTRIS_API YeptrisStatus yeptris_schema_load(const char* source, size_t len, Ye
|
|
|
204
268
|
}
|
|
205
269
|
|
|
206
270
|
{
|
|
207
|
-
|
|
271
|
+
uint32_t* seen = seen_map_ptr = calloc(desc_len, sizeof(uint32_t));
|
|
272
|
+
if (seen == NULL) {
|
|
273
|
+
st = YEPTRIS_ERROR_MEMORY;
|
|
274
|
+
goto out;
|
|
275
|
+
}
|
|
276
|
+
yep_schema_ctx c = {desc, desc_len, cols, dom, 0, 0, -1, seen};
|
|
208
277
|
int rc = emit(&c, 0, dom->docs[0]);
|
|
209
278
|
if (rc < 0 && c.type_mismatch >= 0) {
|
|
210
279
|
yep_error_set(yep_error_tls(), YEP_ERR_UNEXPECTED, 0, 0, (uint32_t)c.type_mismatch,
|
|
@@ -232,6 +301,7 @@ YEPTRIS_API YeptrisStatus yeptris_schema_load(const char* source, size_t len, Ye
|
|
|
232
301
|
}
|
|
233
302
|
|
|
234
303
|
out:
|
|
304
|
+
free(seen_map_ptr);
|
|
235
305
|
yep_dom_destroy(dom);
|
|
236
306
|
yep_engine_destroy(eng);
|
|
237
307
|
return st;
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: yeptris
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.13.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-09-
|
|
11
|
+
date: 2026-09-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ffi
|