yeptris 0.6.13.1 → 0.6.14.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 +6 -0
- data/lib/yeptris/node.rb +29 -7
- data/lib/yeptris.rb +1 -1
- data/vendor/libyeptris/CMakeLists.txt +1 -1
- data/vendor/libyeptris/src/include/yeptris/dom.h +9 -0
- data/vendor/libyeptris/src/yeptris/cbor/decode.c +2 -0
- data/vendor/libyeptris/src/yeptris/cbor/encode.c +4 -1
- data/vendor/libyeptris/src/yeptris/doc.h +19 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.c +21 -7
- data/vendor/libyeptris/src/yeptris/emit/emit.c +15 -0
- data/vendor/libyeptris/src/yeptris/parse.c +114 -29
- data/vendor/libyeptris/src/yeptris/plan.c +30 -26
- data/vendor/libyeptris/src/yeptris/tape.c +3 -3
- data/vendor/libyeptris/src/yeptris/tape_in.h +19 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 60e8ec13d8221947494e0f163b57a619a3bb6d4f179f53d3f9b324e0ae0a4ffb
|
|
4
|
+
data.tar.gz: 6eda638121e069206be5d900cc044c942b84e74989d6482255b0e9177aab13f4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dbe32b83a5b017191c37a5978325516a5f99a7d85819ead1dcd97c030fec14f586acfb6d5965275cf1416c74f4dbeecb08abea77074ce769f51340ef3c1e9a7a
|
|
7
|
+
data.tar.gz: 9b6db9635acbdc9e83017626594b8417fec8c2953e38d17878bf2e99e5a4e0976f4087862890f78a23155ea3eb34d8ca8afeb87e536991fa80091b626e40875e
|
data/lib/yeptris/ffi.rb
CHANGED
|
@@ -174,6 +174,8 @@ module Yeptris
|
|
|
174
174
|
attach_function :yeptris_node_bool, %i[yeptris_node pointer], :yeptris_status
|
|
175
175
|
attach_function :yeptris_node_seq_count, [:yeptris_node], :size_t
|
|
176
176
|
attach_function :yeptris_node_seq_at, %i[yeptris_node size_t], :yeptris_node
|
|
177
|
+
attach_function :yeptris_node_children,
|
|
178
|
+
%i[yeptris_node pointer size_t], :size_t
|
|
177
179
|
attach_function :yeptris_node_map_count, [:yeptris_node], :size_t
|
|
178
180
|
attach_function :yeptris_node_map_get, %i[yeptris_node pointer size_t], :yeptris_node
|
|
179
181
|
attach_function :yeptris_node_map_at,
|
|
@@ -227,6 +229,10 @@ module Yeptris
|
|
|
227
229
|
attach_function :yeptris_marshal_node,
|
|
228
230
|
%i[yeptris_node pointer pointer], :int
|
|
229
231
|
attach_function :yeptris_marshal_free, [:pointer], :void
|
|
232
|
+
# The bulk child drain (#168's quadratic): O(n) iteration for
|
|
233
|
+
# #each/#each_pair. Engines without it fall back to the per-index
|
|
234
|
+
# walk (correct, quadratic).
|
|
235
|
+
CHILDREN_DRAIN = !(@missing ||= []).include?(:yeptris_node_children)
|
|
230
236
|
MARSHAL = !(@missing ||= []).include?(:yeptris_marshal_node)
|
|
231
237
|
|
|
232
238
|
# bulk build (TODO.impl/15 phase D): one call raises a document
|
data/lib/yeptris/node.rb
CHANGED
|
@@ -150,7 +150,17 @@ class Yeptris::Node
|
|
|
150
150
|
return enum_for(:each) unless block_given?
|
|
151
151
|
raise Yeptris::Error, "#each is for sequences" unless sequence?
|
|
152
152
|
|
|
153
|
-
|
|
153
|
+
if Yeptris::FFI::CHILDREN_DRAIN
|
|
154
|
+
# one bulk walk (#168): the per-index seq_at loop paid i sibling
|
|
155
|
+
# hops per element — n^2/2 over an 80k-row sequence
|
|
156
|
+
n = alive { Yeptris::FFI.yeptris_node_seq_count(@c_ptr) }
|
|
157
|
+
buf = ::FFI::MemoryPointer.new(:pointer, n)
|
|
158
|
+
alive { Yeptris::FFI.yeptris_node_children(@c_ptr, buf, n) }
|
|
159
|
+
step = buf.type_size
|
|
160
|
+
n.times { |i| yield @document.wrap_node(buf.get_pointer(i * step)) }
|
|
161
|
+
else
|
|
162
|
+
(0...seq_count).each { |i| yield seq_at(i) }
|
|
163
|
+
end
|
|
154
164
|
end
|
|
155
165
|
|
|
156
166
|
# ---- mapping access ----
|
|
@@ -177,13 +187,25 @@ class Yeptris::Node
|
|
|
177
187
|
return enum_for(:each_pair) unless block_given?
|
|
178
188
|
raise Yeptris::Error, "#each_pair is for mappings" unless mapping?
|
|
179
189
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
190
|
+
if Yeptris::FFI::CHILDREN_DRAIN
|
|
191
|
+
# key,value interleaved in one walk — the same #168 cure
|
|
192
|
+
pairs = alive { Yeptris::FFI.yeptris_node_map_count(@c_ptr) }
|
|
193
|
+
buf = ::FFI::MemoryPointer.new(:pointer, pairs * 2)
|
|
194
|
+
alive { Yeptris::FFI.yeptris_node_children(@c_ptr, buf, pairs * 2) }
|
|
195
|
+
step = buf.type_size
|
|
196
|
+
pairs.times do |p|
|
|
197
|
+
yield @document.wrap_node(buf.get_pointer((2 * p) * step)),
|
|
198
|
+
@document.wrap_node(buf.get_pointer((2 * p + 1) * step))
|
|
199
|
+
end
|
|
200
|
+
else
|
|
201
|
+
k = ::FFI::MemoryPointer.new(:pointer)
|
|
202
|
+
v = ::FFI::MemoryPointer.new(:pointer)
|
|
203
|
+
(0...map_count).each do |i|
|
|
204
|
+
rc = alive { Yeptris::FFI.yeptris_node_map_at(@c_ptr, i, k, v) }
|
|
205
|
+
next unless rc.zero?
|
|
185
206
|
|
|
186
|
-
|
|
207
|
+
yield @document.wrap_node(k.read_pointer), @document.wrap_node(v.read_pointer)
|
|
208
|
+
end
|
|
187
209
|
end
|
|
188
210
|
end
|
|
189
211
|
|
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.14.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
|
|
@@ -76,6 +76,15 @@ YEPTRIS_API YeptrisNode yeptris_node_map_get(YeptrisNode node, const char* key,
|
|
|
76
76
|
YEPTRIS_API int yeptris_node_map_at(YeptrisNode node, size_t index, YeptrisNode* key,
|
|
77
77
|
YeptrisNode* value);
|
|
78
78
|
|
|
79
|
+
/* Bulk children drain — the O(n) iteration primitive (ruby #168: the
|
|
80
|
+
* per-index seq_at/map_at walks make an 80k-row sequence O(n^2)). One
|
|
81
|
+
* call walks the child list once and fills out[0..cap) with handles:
|
|
82
|
+
* sequence elements in order; mapping KEY,VALUE pairs interleaved.
|
|
83
|
+
* Returns the total child count (mapping pairs count twice); out==NULL
|
|
84
|
+
* or cap==0 returns the count without allocating handles. Handles come
|
|
85
|
+
* from the document's pool — document_free reclaims them. */
|
|
86
|
+
YEPTRIS_API size_t yeptris_node_children(YeptrisNode node, YeptrisNode* out, size_t cap);
|
|
87
|
+
|
|
79
88
|
/* ---- Construction (TODO.impl/11 phase 3) ----
|
|
80
89
|
*
|
|
81
90
|
* Build documents from scratch and mutate them; the emitter and every
|
|
@@ -734,6 +734,7 @@ static YeptrisDocument cbor_wrap(yep_dom* dom, const void* buf, const yep_alloca
|
|
|
734
734
|
d->transcoded_len = 0;
|
|
735
735
|
d->input = (const char*)buf;
|
|
736
736
|
d->finish_pool = NULL;
|
|
737
|
+
d->lazy_tape = NULL; /* field-by-field ctor: no garbage for free */
|
|
737
738
|
return (YeptrisDocument)d;
|
|
738
739
|
}
|
|
739
740
|
|
|
@@ -839,6 +840,7 @@ YEPTRIS_API YeptrisDocument yeptris_cbor_decode(const void* buf, size_t len, uin
|
|
|
839
840
|
doc->transcoded_len = 0;
|
|
840
841
|
doc->input = (const char*)buf;
|
|
841
842
|
doc->finish_pool = NULL;
|
|
843
|
+
doc->lazy_tape = NULL; /* field-by-field ctor: no garbage for free */
|
|
842
844
|
if (status != NULL) {
|
|
843
845
|
*status = YEPTRIS_OK;
|
|
844
846
|
}
|
|
@@ -703,7 +703,10 @@ static void cbor_write_item(cenc* e, uint32_t id, uint8_t* out, size_t* pos) {
|
|
|
703
703
|
static int cbor_run(YeptrisDocument handle, uint32_t opts, uint8_t* out, size_t cap,
|
|
704
704
|
size_t* out_len, int* written) {
|
|
705
705
|
yeptris_document* doc = (yeptris_document*)handle;
|
|
706
|
-
yep_dom* d = doc
|
|
706
|
+
yep_dom* d = yep_doc_dom(doc); /* #342 lazy: materialize on encode */
|
|
707
|
+
if (d == NULL) {
|
|
708
|
+
return YEPTRIS_ERROR_MEMORY;
|
|
709
|
+
}
|
|
707
710
|
if (d->dcount == 0) {
|
|
708
711
|
return YEPTRIS_ERROR_ARG;
|
|
709
712
|
}
|
|
@@ -22,16 +22,35 @@ typedef struct yeptris_document {
|
|
|
22
22
|
void* finish_pool; /* engine finish pool: resolved tags, folded and
|
|
23
23
|
escaped scalars outlive the engine through the
|
|
24
24
|
document */
|
|
25
|
+
void* lazy_tape; /* #342 slice 2: the parsed tape when the tree is
|
|
26
|
+
* deferred (gate-clean strict JSON). dom==NULL until
|
|
27
|
+
* the first tree access materializes via dom_from_tape;
|
|
28
|
+
* freed with the document. void* to avoid a header
|
|
29
|
+
* cycle — parse.c casts to yeptris_json_tape* */
|
|
25
30
|
} yeptris_document;
|
|
26
31
|
|
|
27
32
|
/* Node handle: a (document, node-id) pair so nodes stay usable even if
|
|
28
33
|
* the node pool grows (ids are stable; pointers are not). Defined here
|
|
29
34
|
* (not parse.c) since the query layer and the builder share it. */
|
|
35
|
+
/* #342 slice 2: the lazy-tree choke point. Returns the document's dom,
|
|
36
|
+
* materializing it from lazy_tape on first access (NULL when materialization
|
|
37
|
+
* fails — callers treat it as an empty/unusable tree). Every consumer that
|
|
38
|
+
* reads ->dom on a parse-produced document routes through here; the builder
|
|
39
|
+
* and CBOR decode paths set dom eagerly and are unaffected. parse.c owns it. */
|
|
30
40
|
typedef struct yeptris_node {
|
|
31
41
|
yeptris_document* doc;
|
|
32
42
|
uint32_t id;
|
|
33
43
|
} yeptris_node;
|
|
34
44
|
|
|
45
|
+
#ifdef __cplusplus
|
|
46
|
+
extern "C" {
|
|
47
|
+
#endif
|
|
48
|
+
|
|
49
|
+
yep_dom* yep_doc_dom(yeptris_document* doc);
|
|
35
50
|
yeptris_node* yep_handle_new(yeptris_document* doc, uint32_t id);
|
|
36
51
|
|
|
52
|
+
#ifdef __cplusplus
|
|
53
|
+
}
|
|
54
|
+
#endif
|
|
55
|
+
|
|
37
56
|
#endif /* YEP_DOC_H */
|
|
@@ -866,12 +866,18 @@ int dom_on_flow_commit(void* ctx) {
|
|
|
866
866
|
}
|
|
867
867
|
|
|
868
868
|
int dom_from_tape(yep_dom* d, const yeptris_json_tape* t) {
|
|
869
|
-
if (d == NULL || t == NULL || t->_src == NULL) {
|
|
869
|
+
if (d == NULL || t == NULL || t->_src == NULL) {
|
|
870
|
+
return -1;
|
|
871
|
+
}
|
|
870
872
|
/* the columns are the one representation valid on every route
|
|
871
873
|
* (strict: native; lenient fused: lazily materialized from the
|
|
872
874
|
* records; scalar-root lenient: native column writes) */
|
|
873
|
-
if (yeptris_tape_columns((yeptris_json_tape*)t) != 0) {
|
|
874
|
-
|
|
875
|
+
if (yeptris_tape_columns((yeptris_json_tape*)t) != 0) {
|
|
876
|
+
return -1;
|
|
877
|
+
}
|
|
878
|
+
if (t->kinds == NULL) {
|
|
879
|
+
return -1;
|
|
880
|
+
}
|
|
875
881
|
d->input_base = (const char*)t->_src;
|
|
876
882
|
d->input_len = t->_srclen;
|
|
877
883
|
const char* src = (const char*)t->_src;
|
|
@@ -885,17 +891,23 @@ int dom_from_tape(yep_dom* d, const yeptris_json_tape* t) {
|
|
|
885
891
|
break; /* the stream boundary record */
|
|
886
892
|
case YEP_T_SEQ_OPEN:
|
|
887
893
|
case YEP_T_MAP_OPEN: {
|
|
888
|
-
if (d->depth >= YEP_DOM_MAX_DEPTH) {
|
|
894
|
+
if (d->depth >= YEP_DOM_MAX_DEPTH) {
|
|
895
|
+
return -1;
|
|
896
|
+
}
|
|
889
897
|
uint32_t id =
|
|
890
898
|
dom_open_node(d, kind == YEP_T_SEQ_OPEN ? YEP_DOM_SEQUENCE : YEP_DOM_MAPPING, NULL,
|
|
891
899
|
NULL, 0, 0, 0, 1);
|
|
892
|
-
if (id == UINT32_MAX || dom_place(d, id) != 0) {
|
|
900
|
+
if (id == UINT32_MAX || dom_place(d, id) != 0) {
|
|
901
|
+
return -1;
|
|
902
|
+
}
|
|
893
903
|
d->map_pending_key[d->depth] = 0;
|
|
894
904
|
d->stack[d->depth++] = id;
|
|
895
905
|
break;
|
|
896
906
|
}
|
|
897
907
|
case YEP_T_CLOSE:
|
|
898
|
-
if (d->depth == 0) {
|
|
908
|
+
if (d->depth == 0) {
|
|
909
|
+
return -1;
|
|
910
|
+
}
|
|
899
911
|
d->depth--;
|
|
900
912
|
break;
|
|
901
913
|
case YEP_T_NULL:
|
|
@@ -952,7 +964,9 @@ int dom_from_tape(yep_dom* d, const yeptris_json_tape* t) {
|
|
|
952
964
|
/* escaped: unescape into the arena (the fused
|
|
953
965
|
* builder's arm, byte for byte) */
|
|
954
966
|
char* dst = yep_dom_str_tail(d, len);
|
|
955
|
-
if (dst == NULL) {
|
|
967
|
+
if (dst == NULL) {
|
|
968
|
+
return -1;
|
|
969
|
+
}
|
|
956
970
|
d->nodes[id].value = yep_dom_str_commit(
|
|
957
971
|
d, yep_finish_double_into(src, off, off + len, dst, len));
|
|
958
972
|
} else {
|
|
@@ -45,6 +45,9 @@ YEPTRIS_API size_t yeptris_serialize_into_ex(YeptrisDocument handle,
|
|
|
45
45
|
em.w.grow = 0;
|
|
46
46
|
em.w.oom = 0;
|
|
47
47
|
em.doc = (const yeptris_document*)handle;
|
|
48
|
+
if (yep_doc_dom((yeptris_document*)handle) == NULL) { /* #342 lazy */
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
48
51
|
em.w.p = NULL;
|
|
49
52
|
em.w.last = 0;
|
|
50
53
|
em.w.force_flow = 0;
|
|
@@ -100,6 +103,9 @@ YEPTRIS_API char* yeptris_serialize_ex(YeptrisDocument handle, const yeptris_emi
|
|
|
100
103
|
em.w.grow = 0;
|
|
101
104
|
em.w.oom = 0;
|
|
102
105
|
em.doc = (const yeptris_document*)handle;
|
|
106
|
+
if (yep_doc_dom((yeptris_document*)handle) == NULL) { /* #342 lazy */
|
|
107
|
+
return NULL;
|
|
108
|
+
}
|
|
103
109
|
em.w.p = NULL;
|
|
104
110
|
em.w.last = 0;
|
|
105
111
|
em.w.force_flow = 0;
|
|
@@ -174,6 +180,9 @@ YEPTRIS_API char* yeptris_serialize_json(YeptrisDocument handle, size_t* len) {
|
|
|
174
180
|
em.w.grow = 0;
|
|
175
181
|
em.w.oom = 0;
|
|
176
182
|
em.doc = (const yeptris_document*)handle;
|
|
183
|
+
if (yep_doc_dom((yeptris_document*)handle) == NULL) { /* #342 lazy */
|
|
184
|
+
return NULL;
|
|
185
|
+
}
|
|
177
186
|
em.w.p = NULL;
|
|
178
187
|
em.w.last = 0;
|
|
179
188
|
em.w.force_flow = 0;
|
|
@@ -224,6 +233,9 @@ YEPTRIS_API char* yeptris_serialize_json_ex(YeptrisDocument handle, size_t* len,
|
|
|
224
233
|
em.w.grow = 0;
|
|
225
234
|
em.w.oom = 0;
|
|
226
235
|
em.doc = (const yeptris_document*)handle;
|
|
236
|
+
if (yep_doc_dom((yeptris_document*)handle) == NULL) { /* #342 lazy */
|
|
237
|
+
return NULL;
|
|
238
|
+
}
|
|
227
239
|
em.w.p = NULL;
|
|
228
240
|
em.w.last = 0;
|
|
229
241
|
em.w.force_flow = 0;
|
|
@@ -378,6 +390,9 @@ YEPTRIS_API size_t yeptris_serialize_stream(YeptrisDocument handle,
|
|
|
378
390
|
em.w.grow = 0;
|
|
379
391
|
em.w.oom = 0;
|
|
380
392
|
em.doc = (const yeptris_document*)handle;
|
|
393
|
+
if (yep_doc_dom((yeptris_document*)handle) == NULL) { /* #342 lazy */
|
|
394
|
+
return 0;
|
|
395
|
+
}
|
|
381
396
|
em.w.p = NULL;
|
|
382
397
|
em.w.last = 0;
|
|
383
398
|
em.w.force_flow = 0;
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
#include "parse/numbers.h"
|
|
13
13
|
#include "resolve/resolver.h"
|
|
14
14
|
#include "scan/json.h"
|
|
15
|
+
#include "tape_in.h"
|
|
15
16
|
|
|
16
17
|
#include "parse/events.h"
|
|
17
18
|
#include <errno.h>
|
|
@@ -38,6 +39,59 @@ YEPTRIS_API YeptrisDocument yeptris_parse(const char* buf, size_t len, YeptrisSt
|
|
|
38
39
|
return yeptris_parse_ex(buf, len, NULL, status);
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
/* #342 slice 2: materialize the deferred tree on first access. The
|
|
43
|
+
* gate-clean JSON route carries the parsed TAPE (simdjson's design:
|
|
44
|
+
* its "DOM" is a tape) and pays dom_from_tape only when a consumer
|
|
45
|
+
* touches the tree — parse-only workloads never build nodes. */
|
|
46
|
+
yep_dom* yep_doc_dom(yeptris_document* doc) {
|
|
47
|
+
if (doc == NULL) {
|
|
48
|
+
return NULL;
|
|
49
|
+
}
|
|
50
|
+
if (doc->dom == NULL && doc->lazy_tape != NULL) {
|
|
51
|
+
yeptris_json_tape* t = (yeptris_json_tape*)doc->lazy_tape;
|
|
52
|
+
yep_dom* dom = yep_dom_create(doc->sys);
|
|
53
|
+
if (dom != NULL && dom_from_tape(dom, t) == 0) {
|
|
54
|
+
doc->dom = dom;
|
|
55
|
+
yeptris_tape_free(t);
|
|
56
|
+
yep_free(doc->sys, t);
|
|
57
|
+
doc->lazy_tape = NULL;
|
|
58
|
+
} else {
|
|
59
|
+
yep_dom_destroy(dom);
|
|
60
|
+
dom = NULL; /* the tape stays: a later access retries, or
|
|
61
|
+
free drops it (the materializer only fails on
|
|
62
|
+
malformed NUM spans, which the strict gate
|
|
63
|
+
already rejected at parse) */
|
|
64
|
+
}
|
|
65
|
+
return dom;
|
|
66
|
+
}
|
|
67
|
+
return doc->dom;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* The strict-JSON document wrapper (both routes share it). */
|
|
71
|
+
/* The lazy route's acceptance debt: the fused lenient walk records
|
|
72
|
+
* number runs UNVALIDATED (its simdjson-style deferred contract).
|
|
73
|
+
* parse_json's contract is RFC 8259 at parse time, so the deferred
|
|
74
|
+
* spans settle HERE — full-span shape check per NUM record, decoding
|
|
75
|
+
* the packed records directly (no columns build at parse; the walk
|
|
76
|
+
* guarantees recs are primary on this route). */
|
|
77
|
+
static int lazy_nums_settled(const yeptris_json_tape* t) {
|
|
78
|
+
const char* p = (const char*)t->_src;
|
|
79
|
+
for (uint32_t i = 0; i < t->count; i++) {
|
|
80
|
+
uint64_t r = t->recs[i];
|
|
81
|
+
if ((uint8_t)(r & 0xFFu) != YEP_T_NUM) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
uint32_t len = (uint32_t)((r >> 8) & 0x7FFFFFu);
|
|
85
|
+
uint32_t off = (uint32_t)(r >> 32);
|
|
86
|
+
size_t adv = 0;
|
|
87
|
+
int flt = 0;
|
|
88
|
+
if (yep_json_number_shape(p + off, len, &adv, &flt) == 0 || adv != (size_t)len) {
|
|
89
|
+
return -1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
41
95
|
/* The strict-JSON document wrapper (both routes share it). */
|
|
42
96
|
static YeptrisDocument yep_json_doc_wrap(yep_dom* dom, const char* buf, size_t len,
|
|
43
97
|
const yep_allocator* sys, YeptrisStatus* status) {
|
|
@@ -52,6 +106,7 @@ static YeptrisDocument yep_json_doc_wrap(yep_dom* dom, const char* buf, size_t l
|
|
|
52
106
|
}
|
|
53
107
|
doc->dom = dom;
|
|
54
108
|
doc->sys = sys;
|
|
109
|
+
doc->lazy_tape = NULL;
|
|
55
110
|
doc->schema = YEPTRIS_SCHEMA_12_CORE; /* strict JSON is core by construction */
|
|
56
111
|
doc->transcoded = NULL;
|
|
57
112
|
doc->transcoded_len = 0;
|
|
@@ -75,41 +130,44 @@ YEPTRIS_API YeptrisDocument yeptris_parse_json(const char* buf, size_t len, Yept
|
|
|
75
130
|
* builds. Rejects, non-opener roots, and surprises fall to the
|
|
76
131
|
* original sequence below, whose error precedence is byte-for-byte
|
|
77
132
|
* the pinned behavior (json-suite-strict gates this). */
|
|
78
|
-
|
|
133
|
+
int gated = len > 0 && yep_text_active()->gate_scan(buf, len);
|
|
134
|
+
if (!gated) {
|
|
79
135
|
size_t off = 0;
|
|
80
136
|
while (off < len &&
|
|
81
137
|
(buf[off] == ' ' || buf[off] == '\t' || buf[off] == '\n' || buf[off] == '\r')) {
|
|
82
138
|
off++;
|
|
83
139
|
}
|
|
84
|
-
|
|
140
|
+
/* tabs: the lenient walk's own pinned acceptance (its route
|
|
141
|
+
* takes them); the strict routes reject them (ErrorParity's
|
|
142
|
+
* pinned agreement). A tab-carrying buffer must NOT take the
|
|
143
|
+
* lazy walk — the validating sequence below reports exactly
|
|
144
|
+
* the pinned reject. */
|
|
145
|
+
if (off < len && (buf[off] == '[' || buf[off] == '{') && memchr(buf, '\t', len) == NULL) {
|
|
146
|
+
/* #342 slice 2: the fused LENIENT walk (one pass, records
|
|
147
|
+
* only — no node building) settles the deferred number
|
|
148
|
+
* grammar inline, then the tape rides the document and
|
|
149
|
+
* dom_from_tape builds nodes on the first tree access.
|
|
150
|
+
* A reject or malformed span falls to the original
|
|
151
|
+
* sequence below, whose error precedence is byte-for-byte
|
|
152
|
+
* the pinned behavior. */
|
|
85
153
|
const yep_allocator* sys = yep_system_allocator();
|
|
86
|
-
|
|
87
|
-
if (
|
|
154
|
+
yeptris_json_tape* t = yep_alloc(sys, sizeof(*t));
|
|
155
|
+
if (t == NULL) {
|
|
88
156
|
st = YEPTRIS_ERROR_MEMORY;
|
|
89
157
|
goto jfail;
|
|
90
158
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
dom->flow_strict = 0;
|
|
98
|
-
int tail_ok = 0;
|
|
99
|
-
if (rc == 1) {
|
|
100
|
-
/* trailing garbage after the closer is a reject the
|
|
101
|
-
* fallback reports exactly as before */
|
|
102
|
-
size_t t = close + 1;
|
|
103
|
-
while (t < len &&
|
|
104
|
-
(buf[t] == ' ' || buf[t] == '\t' || buf[t] == '\n' || buf[t] == '\r')) {
|
|
105
|
-
t++;
|
|
159
|
+
if (yep_tape_walk_lenient_fused(buf, len, off, t) == YEPTRIS_OK &&
|
|
160
|
+
lazy_nums_settled(t) == 0) {
|
|
161
|
+
YeptrisDocument h = yep_json_doc_wrap(NULL, buf, len, sys, status);
|
|
162
|
+
if (h != NULL) {
|
|
163
|
+
((yeptris_document*)h)->lazy_tape = t;
|
|
164
|
+
return h;
|
|
106
165
|
}
|
|
107
|
-
|
|
166
|
+
yeptris_tape_free(t); /* wrap failed (memory): below */
|
|
167
|
+
} else {
|
|
168
|
+
yeptris_tape_free(t); /* reject/malformed: below */
|
|
108
169
|
}
|
|
109
|
-
|
|
110
|
-
return yep_json_doc_wrap(dom, buf, len, sys, status);
|
|
111
|
-
}
|
|
112
|
-
yep_dom_destroy(dom); /* reject/rollback: the sequence below */
|
|
170
|
+
yep_free(sys, t);
|
|
113
171
|
}
|
|
114
172
|
}
|
|
115
173
|
size_t verr = 0;
|
|
@@ -120,8 +178,7 @@ YEPTRIS_API YeptrisDocument yeptris_parse_json(const char* buf, size_t len, Yept
|
|
|
120
178
|
goto jfail;
|
|
121
179
|
}
|
|
122
180
|
size_t uerr = 0;
|
|
123
|
-
if (
|
|
124
|
-
!yep_utf8_validate((const unsigned char*)buf, len, &uerr)) {
|
|
181
|
+
if (gated && !yep_utf8_validate((const unsigned char*)buf, len, &uerr)) {
|
|
125
182
|
yep_error_set(yep_error_tls(), YEP_ERR_ENCODING, 0, 0, uerr, "ill-formed UTF-8 at byte %zu",
|
|
126
183
|
uerr);
|
|
127
184
|
st = YEPTRIS_ERROR_ENCODING;
|
|
@@ -343,6 +400,8 @@ engine_enter:
|
|
|
343
400
|
* or an arena copy (dom_ev_str) — nothing references it */
|
|
344
401
|
yep_pool_destroy(finish);
|
|
345
402
|
doc->finish_pool = NULL;
|
|
403
|
+
doc->lazy_tape = NULL; /* field-by-field ctor: leave no garbage
|
|
404
|
+
* (document_free frees a non-NULL tape) */
|
|
346
405
|
return (YeptrisDocument)doc;
|
|
347
406
|
|
|
348
407
|
fail:
|
|
@@ -363,6 +422,10 @@ YEPTRIS_API void yeptris_document_free(YeptrisDocument handle) {
|
|
|
363
422
|
if (doc == NULL) {
|
|
364
423
|
return;
|
|
365
424
|
}
|
|
425
|
+
if (doc->lazy_tape != NULL) { /* never materialized: free stays free */
|
|
426
|
+
yeptris_tape_free((yeptris_json_tape*)doc->lazy_tape);
|
|
427
|
+
yep_free(doc->sys, doc->lazy_tape);
|
|
428
|
+
}
|
|
366
429
|
yep_dom_destroy(doc->dom);
|
|
367
430
|
yep_pool_destroy((yep_pool*)doc->finish_pool);
|
|
368
431
|
yep_free(doc->sys, doc->transcoded);
|
|
@@ -371,7 +434,7 @@ YEPTRIS_API void yeptris_document_free(YeptrisDocument handle) {
|
|
|
371
434
|
|
|
372
435
|
YEPTRIS_API size_t yeptris_document_count(YeptrisDocument handle) {
|
|
373
436
|
yeptris_document* doc = (yeptris_document*)handle;
|
|
374
|
-
return doc ? doc->
|
|
437
|
+
return doc ? yep_doc_dom(doc)->dcount : 0;
|
|
375
438
|
}
|
|
376
439
|
|
|
377
440
|
yeptris_node* yep_handle_new(yeptris_document* doc, uint32_t id) {
|
|
@@ -390,10 +453,14 @@ static YeptrisNode node_new_handle(yeptris_document* doc, uint32_t id) {
|
|
|
390
453
|
|
|
391
454
|
YEPTRIS_API YeptrisNode yeptris_document_root(YeptrisDocument handle, size_t index) {
|
|
392
455
|
yeptris_document* doc = (yeptris_document*)handle;
|
|
393
|
-
if (doc == NULL
|
|
456
|
+
if (doc == NULL) {
|
|
394
457
|
return NULL;
|
|
395
458
|
}
|
|
396
|
-
|
|
459
|
+
yep_dom* dom = yep_doc_dom(doc);
|
|
460
|
+
if (dom == NULL || index >= dom->dcount) {
|
|
461
|
+
return NULL;
|
|
462
|
+
}
|
|
463
|
+
return node_new_handle(doc, dom->docs[index]);
|
|
397
464
|
}
|
|
398
465
|
|
|
399
466
|
static const yep_dnode* node_of(YeptrisNode handle) {
|
|
@@ -588,6 +655,24 @@ YEPTRIS_API YeptrisNode yeptris_node_seq_at(YeptrisNode handle, size_t index) {
|
|
|
588
655
|
return wrap((yeptris_node*)handle, id);
|
|
589
656
|
}
|
|
590
657
|
|
|
658
|
+
YEPTRIS_API size_t yeptris_node_children(YeptrisNode handle, YeptrisNode* out, size_t cap) {
|
|
659
|
+
const yep_dnode* n = node_of(handle);
|
|
660
|
+
if (n == NULL || (n->kind != YEP_DOM_SEQUENCE && n->kind != YEP_DOM_MAPPING)) {
|
|
661
|
+
return 0;
|
|
662
|
+
}
|
|
663
|
+
size_t count = 0;
|
|
664
|
+
uint32_t id = n->first_child;
|
|
665
|
+
while (id != UINT32_MAX) {
|
|
666
|
+
if (out != NULL && count < cap) {
|
|
667
|
+
out[count] = wrap((yeptris_node*)handle, id);
|
|
668
|
+
}
|
|
669
|
+
count++;
|
|
670
|
+
const yep_dnode* cur = yep_dom_node(((yeptris_node*)handle)->doc->dom, id);
|
|
671
|
+
id = cur ? cur->next_sibling : UINT32_MAX;
|
|
672
|
+
}
|
|
673
|
+
return count;
|
|
674
|
+
}
|
|
675
|
+
|
|
591
676
|
YEPTRIS_API size_t yeptris_node_map_count(YeptrisNode handle) {
|
|
592
677
|
const yep_dnode* n = node_of(handle);
|
|
593
678
|
return (n && n->kind == YEP_DOM_MAPPING) ? n->count / 2 : 0;
|
|
@@ -79,12 +79,16 @@ YEPTRIS_API yeptris_plan* yeptris_plan_compile(const char* spec, size_t len, Yep
|
|
|
79
79
|
}
|
|
80
80
|
return NULL;
|
|
81
81
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
yeptris_plan* plan = calloc(1, sizeof(*plan));
|
|
82
|
+
yeptris_document* d = (yeptris_document*)doc;
|
|
83
|
+
yeptris_plan* plan = calloc(1, sizeof(*plan)); /* before any goto: out frees it */
|
|
85
84
|
if (plan == NULL) {
|
|
86
85
|
goto mem;
|
|
87
86
|
}
|
|
87
|
+
yep_dom* dd = yep_doc_dom(d); /* #342 lazy: the spec parse may defer */
|
|
88
|
+
if (dd == NULL) {
|
|
89
|
+
goto mem;
|
|
90
|
+
}
|
|
91
|
+
const yep_dnode* root = yep_dom_node(dd, dd->docs[0]);
|
|
88
92
|
if (root == NULL || root->kind != 2) { /* the spec root is a mapping */
|
|
89
93
|
goto bad;
|
|
90
94
|
}
|
|
@@ -95,10 +99,10 @@ YEPTRIS_API yeptris_plan* yeptris_plan_compile(const char* spec, size_t len, Yep
|
|
|
95
99
|
int saw_kind = 0;
|
|
96
100
|
uint32_t pair = root->first_child;
|
|
97
101
|
while (pair != UINT32_MAX) {
|
|
98
|
-
const yep_dnode* kn = yep_dom_node(
|
|
99
|
-
const yep_dnode* vn = yep_dom_node(
|
|
100
|
-
yep_view kv = sv_view(
|
|
101
|
-
yep_view vv = sv_view(
|
|
102
|
+
const yep_dnode* kn = yep_dom_node(dd, pair);
|
|
103
|
+
const yep_dnode* vn = yep_dom_node(dd, kn->next_sibling);
|
|
104
|
+
yep_view kv = sv_view(dd, kn->value);
|
|
105
|
+
yep_view vv = sv_view(dd, vn->value);
|
|
102
106
|
if (kv.len == 4 && memcmp(kv.p, "kind", 4) == 0) {
|
|
103
107
|
saw_kind = 1;
|
|
104
108
|
if (!(vv.len == 3 && (memcmp(vv.p, "seq", 3) == 0 || memcmp(vv.p, "map", 3) == 0))) {
|
|
@@ -130,7 +134,7 @@ YEPTRIS_API yeptris_plan* yeptris_plan_compile(const char* spec, size_t len, Yep
|
|
|
130
134
|
if (path_node->tag_id != YEPTRIS_TAG_STR) {
|
|
131
135
|
goto bad; /* the path is a string, not a number */
|
|
132
136
|
}
|
|
133
|
-
yep_view vv = sv_view(
|
|
137
|
+
yep_view vv = sv_view(dd, path_node->value);
|
|
134
138
|
if (vv.len != 0) { /* "" rides the seq root like no path */
|
|
135
139
|
plan->seg_off = malloc(sizeof(uint32_t));
|
|
136
140
|
plan->seg_len = malloc(sizeof(uint32_t));
|
|
@@ -159,11 +163,11 @@ YEPTRIS_API yeptris_plan* yeptris_plan_compile(const char* spec, size_t len, Yep
|
|
|
159
163
|
size_t off = 0;
|
|
160
164
|
uint32_t cn = path_node->first_child;
|
|
161
165
|
for (size_t i = 0; i < n; i++) {
|
|
162
|
-
const yep_dnode* seg = yep_dom_node(
|
|
166
|
+
const yep_dnode* seg = yep_dom_node(dd, cn);
|
|
163
167
|
if (seg == NULL || seg->kind != 0 || seg->tag_id != YEPTRIS_TAG_STR) {
|
|
164
168
|
goto bad; /* every segment is a string */
|
|
165
169
|
}
|
|
166
|
-
yep_view sv = sv_view(
|
|
170
|
+
yep_view sv = sv_view(dd, seg->value);
|
|
167
171
|
if (sv.len == 0) {
|
|
168
172
|
goto bad;
|
|
169
173
|
}
|
|
@@ -191,7 +195,7 @@ YEPTRIS_API yeptris_plan* yeptris_plan_compile(const char* spec, size_t len, Yep
|
|
|
191
195
|
}
|
|
192
196
|
uint32_t cn = children->first_child;
|
|
193
197
|
for (size_t i = 0; i < plan->ncols; i++) {
|
|
194
|
-
const yep_dnode* leaf = yep_dom_node(
|
|
198
|
+
const yep_dnode* leaf = yep_dom_node(dd, cn);
|
|
195
199
|
if (leaf == NULL || leaf->kind != 2) {
|
|
196
200
|
goto bad;
|
|
197
201
|
}
|
|
@@ -200,10 +204,10 @@ YEPTRIS_API yeptris_plan* yeptris_plan_compile(const char* spec, size_t len, Yep
|
|
|
200
204
|
int kind = -1;
|
|
201
205
|
uint32_t lp = leaf->first_child;
|
|
202
206
|
while (lp != UINT32_MAX) {
|
|
203
|
-
const yep_dnode* lk = yep_dom_node(
|
|
204
|
-
const yep_dnode* lv = yep_dom_node(
|
|
205
|
-
yep_view lkv = sv_view(
|
|
206
|
-
yep_view lvv = sv_view(
|
|
207
|
+
const yep_dnode* lk = yep_dom_node(dd, lp);
|
|
208
|
+
const yep_dnode* lv = yep_dom_node(dd, lk->next_sibling);
|
|
209
|
+
yep_view lkv = sv_view(dd, lk->value);
|
|
210
|
+
yep_view lvv = sv_view(dd, lv->value);
|
|
207
211
|
if (lkv.len == 4 && memcmp(lkv.p, "name", 4) == 0) {
|
|
208
212
|
if (lv->kind != 0 || lvv.len == 0) {
|
|
209
213
|
goto bad;
|
|
@@ -657,15 +661,15 @@ yeptris_document_plan_walk(YeptrisDocument doc, const yeptris_plan* plan, Yeptri
|
|
|
657
661
|
if (st != NULL) {
|
|
658
662
|
*st = YEPTRIS_OK;
|
|
659
663
|
}
|
|
660
|
-
const
|
|
661
|
-
if (doc == NULL || plan == NULL ||
|
|
664
|
+
const yep_dom* dd = doc == NULL ? NULL : yep_doc_dom((yeptris_document*)doc);
|
|
665
|
+
if (doc == NULL || plan == NULL || dd == NULL || dd->dcount == 0) {
|
|
662
666
|
if (st != NULL) {
|
|
663
667
|
*st = YEPTRIS_ERROR_ARG;
|
|
664
668
|
}
|
|
665
669
|
return NULL;
|
|
666
670
|
}
|
|
667
|
-
const yep_dnode* root = yep_dom_node(
|
|
668
|
-
const yep_dnode* container = dom_find_rows(
|
|
671
|
+
const yep_dnode* root = yep_dom_node(dd, dd->docs[0]);
|
|
672
|
+
const yep_dnode* container = dom_find_rows(dd, root, plan);
|
|
669
673
|
if (container == NULL) {
|
|
670
674
|
if (st != NULL) {
|
|
671
675
|
*st = YEPTRIS_ERROR_PARSE; /* document shape disagreement */
|
|
@@ -676,7 +680,7 @@ yeptris_document_plan_walk(YeptrisDocument doc, const yeptris_plan* plan, Yeptri
|
|
|
676
680
|
size_t rows = 0;
|
|
677
681
|
for (uint32_t cur = container->first_child; cur != UINT32_MAX;) {
|
|
678
682
|
const yep_dnode* row;
|
|
679
|
-
cur = dom_rows_next(
|
|
683
|
+
cur = dom_rows_next(dd, container, cur, &row);
|
|
680
684
|
if (row != NULL) {
|
|
681
685
|
rows++;
|
|
682
686
|
}
|
|
@@ -690,15 +694,15 @@ yeptris_document_plan_walk(YeptrisDocument doc, const yeptris_plan* plan, Yeptri
|
|
|
690
694
|
size_t row_i = 0;
|
|
691
695
|
for (uint32_t cur = container->first_child; cur != UINT32_MAX && row_i < rows;) {
|
|
692
696
|
const yep_dnode* row;
|
|
693
|
-
cur = dom_rows_next(
|
|
697
|
+
cur = dom_rows_next(dd, container, cur, &row);
|
|
694
698
|
if (row == NULL) {
|
|
695
699
|
continue;
|
|
696
700
|
}
|
|
697
701
|
uint32_t cn = row->first_child;
|
|
698
702
|
while (cn != UINT32_MAX) {
|
|
699
|
-
const yep_dnode* k = yep_dom_node(
|
|
700
|
-
const yep_dnode* v = yep_dom_node(
|
|
701
|
-
yep_view kv = sv_view(
|
|
703
|
+
const yep_dnode* k = yep_dom_node(dd, cn);
|
|
704
|
+
const yep_dnode* v = yep_dom_node(dd, k->next_sibling);
|
|
705
|
+
yep_view kv = sv_view(dd, k->value);
|
|
702
706
|
int col_idx = -1;
|
|
703
707
|
if (k->kind == YEP_DOM_SCALAR) {
|
|
704
708
|
for (size_t c = 0; c < plan->ncols; c++) {
|
|
@@ -710,10 +714,10 @@ yeptris_document_plan_walk(YeptrisDocument doc, const yeptris_plan* plan, Yeptri
|
|
|
710
714
|
}
|
|
711
715
|
}
|
|
712
716
|
if (col_idx >= 0) {
|
|
713
|
-
const yep_dnode* val = dom_alias_final(
|
|
717
|
+
const yep_dnode* val = dom_alias_final(dd, v);
|
|
714
718
|
yep_result_col* col = &r->cols[col_idx];
|
|
715
719
|
if (val != NULL && val->kind == YEP_DOM_SCALAR && val->tag_id != YEPTRIS_TAG_NULL) {
|
|
716
|
-
yep_view sv = sv_view(
|
|
720
|
+
yep_view sv = sv_view(dd, val->value);
|
|
717
721
|
int filled = 0;
|
|
718
722
|
switch (col->kind) {
|
|
719
723
|
case YEP_PLAN_STR:
|
|
@@ -601,8 +601,8 @@ reject:
|
|
|
601
601
|
* (the classify scan carries no accept/reject structure) and
|
|
602
602
|
* yeptris_tape_convert owns validation. Everything else matches
|
|
603
603
|
* tape_walk state for state. */
|
|
604
|
-
|
|
605
|
-
|
|
604
|
+
YeptrisStatus yep_tape_walk_lenient_fused(const char* p, size_t len, size_t open,
|
|
605
|
+
yeptris_json_tape* t) {
|
|
606
606
|
if (tape_carve(t, len) != YEPTRIS_OK) {
|
|
607
607
|
return YEPTRIS_ERROR_MEMORY;
|
|
608
608
|
}
|
|
@@ -1090,7 +1090,7 @@ YEPTRIS_API YeptrisStatus yeptris_parse_json_tape_lenient(const char* source, si
|
|
|
1090
1090
|
}
|
|
1091
1091
|
free(idx);
|
|
1092
1092
|
#endif
|
|
1093
|
-
return
|
|
1093
|
+
return yep_tape_walk_lenient_fused(source, len, at, tape);
|
|
1094
1094
|
}
|
|
1095
1095
|
|
|
1096
1096
|
/* scalar root: one record, same deferred split for numbers */
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* tape_in.h — the tape module's internal seams (not public ABI).
|
|
2
|
+
*
|
|
3
|
+
* parse.c's #342 slice-2 lazy route drives the fused lenient walk
|
|
4
|
+
* directly: the gate and opener checks it already ran must not be
|
|
5
|
+
* repeated (the lenient public entry re-gates). The walk's contract
|
|
6
|
+
* is the public entry's: gate-clean buffer, p[open] is '[' or '{',
|
|
7
|
+
* tail must be whitespace-only. */
|
|
8
|
+
#ifndef YEP_TAPE_IN_H
|
|
9
|
+
#define YEP_TAPE_IN_H
|
|
10
|
+
|
|
11
|
+
#include <stddef.h>
|
|
12
|
+
|
|
13
|
+
#include <yeptris/tape.h>
|
|
14
|
+
|
|
15
|
+
/* tape.c (was tape_walk_lnt_fused) */
|
|
16
|
+
YeptrisStatus yep_tape_walk_lenient_fused(const char* p, size_t len, size_t open,
|
|
17
|
+
yeptris_json_tape* t);
|
|
18
|
+
|
|
19
|
+
#endif
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.14.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
@@ -164,6 +164,7 @@ files:
|
|
|
164
164
|
- vendor/libyeptris/src/yeptris/scan/scan.h
|
|
165
165
|
- vendor/libyeptris/src/yeptris/schema.c
|
|
166
166
|
- vendor/libyeptris/src/yeptris/tape.c
|
|
167
|
+
- vendor/libyeptris/src/yeptris/tape_in.h
|
|
167
168
|
- vendor/libyeptris/src/yeptris/version.c
|
|
168
169
|
- vendor/libyeptris/src/yeptris/visit/dom_visit.c
|
|
169
170
|
- vendor/libyeptris/src/yeptris/visit/json_visit.c
|