yeptris 0.6.8.1 → 0.6.9.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 +8 -2
- data/lib/yeptris/json.rb +1 -0
- data/lib/yeptris/psych/drop_in.rb +19 -0
- data/lib/yeptris.rb +1 -1
- data/vendor/libyeptris/CMakeLists.txt +1 -1
- data/vendor/libyeptris/src/include/yeptris/tape.h +31 -7
- data/vendor/libyeptris/src/yeptris/plan.c +3 -0
- data/vendor/libyeptris/src/yeptris/tape.c +45 -29
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e582f07e09108f29273584fd4e7cb2387bc9410fda6d11a2f0209c40211d8a1c
|
|
4
|
+
data.tar.gz: 717cca7da0595ecd5ed4d356deabeca49343ce9d81cb40d13d5d2056595d0881
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 552e9e7a9a024ebfb948717acbd15d845844159bd9b66fe4b2db29b5ae611e9cc09584aa1b66fc2a38c813532b4ba779c750b0c5e10cb0ecd81fe960afb5e3e8
|
|
7
|
+
data.tar.gz: f2a9fc0879fae9b0c498203552d48ac7574807eba66187e28e551efaff472f74dc3881fb4a43d552494a59b75f1af2298b6b0b2fa3167d5aabc8d5231a4ef4f8
|
data/lib/yeptris/ffi.rb
CHANGED
|
@@ -66,10 +66,16 @@ module Yeptris
|
|
|
66
66
|
# the caller's string (no arena copy, no second validating parse).
|
|
67
67
|
# v2 records: numbers are spans at parse; yeptris_tape_convert
|
|
68
68
|
# materializes them in one bulk call.
|
|
69
|
+
# v3 (libyeptris 0.6.9): the interleaved records (recs) are the
|
|
70
|
+
# primary storage on the lenient route; the strict route keeps the
|
|
71
|
+
# columns eager. The layout MUST mirror yeptris_json_tape — a stale
|
|
72
|
+
# layout makes C write past the FFI buffer (a silent heap overflow
|
|
73
|
+
# that only some allocators catch; the 0.6.9.1 windows crash).
|
|
69
74
|
class JsonTape < ::FFI::Struct
|
|
70
75
|
layout :count, :size_t, :kinds, :pointer, :offs, :pointer,
|
|
71
|
-
:lens, :pointer, :
|
|
72
|
-
:
|
|
76
|
+
:lens, :pointer, :recs, :pointer, :_cols_ready, :int,
|
|
77
|
+
:_rec_primary, :int, :int_min, :int64, :_src, :pointer,
|
|
78
|
+
:_srclen, :size_t, :_block, :pointer
|
|
73
79
|
end
|
|
74
80
|
|
|
75
81
|
attach_function :yeptris_parse_json_tape, %i[pointer size_t pointer], :int
|
data/lib/yeptris/json.rb
CHANGED
|
@@ -24,6 +24,7 @@ module Yeptris
|
|
|
24
24
|
# behavior — strictness follows it (issue #37, found by canon's
|
|
25
25
|
# CI where json 3.0.0 resolved while the dev box had 2.x).
|
|
26
26
|
require "json"
|
|
27
|
+
require "date" # place_obj's `when Date` — psych loads it elsewhere, JSON.dump must not depend on that
|
|
27
28
|
STRICT_DUPLICATE_KEYS = Gem::Version.new(::JSON::VERSION) >= Gem::Version.new("3")
|
|
28
29
|
|
|
29
30
|
module_function
|
|
@@ -21,3 +21,22 @@ end
|
|
|
21
21
|
# rebind is this file's whole purpose
|
|
22
22
|
Object.class_eval { remove_const(:Psych) } if defined?(::Psych) && !::Psych.equal?(Yeptris::Psych)
|
|
23
23
|
::Psych = Yeptris::Psych
|
|
24
|
+
|
|
25
|
+
# yaml-first boot order: ::YAML still references the ORIGINAL stdlib
|
|
26
|
+
# module object, whose singleton methods route through stdlib's parse
|
|
27
|
+
# stream — the UTF-8 tagging (and every other normalization) never
|
|
28
|
+
# runs (#135's spec/sdo discriminant). Delegate the original's public
|
|
29
|
+
# singleton surface to the Yeptris face so BOTH constants behave
|
|
30
|
+
# identically regardless of which module object a caller holds.
|
|
31
|
+
# ORIGINAL exists only when stdlib psych was already loaded (the
|
|
32
|
+
# drop-in-before-psych order needs no delegation — nothing references
|
|
33
|
+
# the original yet).
|
|
34
|
+
if ::Yeptris::Psych.const_defined?(:ORIGINAL, false)
|
|
35
|
+
::Psych::ORIGINAL.singleton_class.class_eval do
|
|
36
|
+
::Psych::ORIGINAL.singleton_methods(false).each do |m|
|
|
37
|
+
define_method(m) do |*args, **kwargs, &block|
|
|
38
|
+
::Yeptris::Psych.public_send(m, *args, **kwargs, &block)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
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.9.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
|
|
@@ -46,15 +46,32 @@ enum {
|
|
|
46
46
|
spans; simdjson's deferred contract) */
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
+
/* The interleaved record (TODO.max-perf/07): one 8-byte store per
|
|
50
|
+
* token instead of three column stores. Layout: off:u32 | len:u23 |
|
|
51
|
+
* kind:u8 — a span longer than 0xFEFFFF bytes carries len == 0xFFFFFF
|
|
52
|
+
* with the true length stored as a CONT-terminated extension record
|
|
53
|
+
* (kind YEP_T_CONT; unreachable for <16 MiB tokens). The columns
|
|
54
|
+
* stay the compatibility ABI: yeptris_tape_columns materializes them
|
|
55
|
+
* lazily from the records on first touch. */
|
|
56
|
+
#define YEP_T_CONT 8 /* record-length extension (not a token kind) */
|
|
57
|
+
typedef uint64_t yeptris_tape_rec;
|
|
58
|
+
|
|
49
59
|
typedef struct yeptris_json_tape {
|
|
50
60
|
size_t count;
|
|
51
|
-
uint8_t* kinds;
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
uint8_t* kinds; /* compat columns — materialized lazily (see
|
|
62
|
+
yeptris_tape_columns) when the interleaved
|
|
63
|
+
records are the primary storage */
|
|
64
|
+
uint32_t* offs; /* span starts (STR/INT/FLOAT); container links */
|
|
65
|
+
uint32_t* lens; /* span lengths (STR/INT/FLOAT) */
|
|
66
|
+
yeptris_tape_rec* recs; /* primary storage (may be NULL on legacy
|
|
67
|
+
column-built tapes: the strict route) */
|
|
68
|
+
int _cols_ready; /* columns materialized from recs already */
|
|
69
|
+
int _rec_primary; /* the lenient route: recs carry the data and
|
|
70
|
+
the columns are lazy */
|
|
71
|
+
int64_t int_min; /* set by yeptris_tape_convert when an INT span
|
|
72
|
+
exceeds int64 (materialize-time discovery) */
|
|
73
|
+
const void* _src; /* the parsed buffer (spans borrow it; it must
|
|
74
|
+
outlive the tape — same contract as the spans) */
|
|
58
75
|
size_t _srclen;
|
|
59
76
|
void* _block; /* the carved allocation base */
|
|
60
77
|
} yeptris_json_tape;
|
|
@@ -89,6 +106,13 @@ YEPTRIS_API void yeptris_tape_free(yeptris_json_tape* tape);
|
|
|
89
106
|
* number records converted, or SIZE_MAX if a span does not re-scan
|
|
90
107
|
* as a number (impossible for a tape this library produced).
|
|
91
108
|
* Sets t->int_min when an INT span exceeds int64. */
|
|
109
|
+
/* Materializes the kinds/offs/lens columns from the interleaved
|
|
110
|
+
* records (no-op when the tape was built column-primary or the
|
|
111
|
+
* columns are already materialized). Consumers reading the column
|
|
112
|
+
* pointers directly (the FFI binding) must call this once first.
|
|
113
|
+
* Returns 0 on success, nonzero on allocation failure. */
|
|
114
|
+
YEPTRIS_API int yeptris_tape_columns(yeptris_json_tape* t);
|
|
115
|
+
|
|
92
116
|
YEPTRIS_API size_t yeptris_tape_convert(yeptris_json_tape* t, size_t from, size_t to,
|
|
93
117
|
int64_t* ivals, double* dvals);
|
|
94
118
|
|
|
@@ -415,6 +415,9 @@ static yeptris_plan_result* plan_result_carve(const yeptris_plan* plan, size_t r
|
|
|
415
415
|
|
|
416
416
|
YEPTRIS_API yeptris_plan_result*
|
|
417
417
|
yeptris_tape_plan_walk(const yeptris_json_tape* tape, const yeptris_plan* plan, YeptrisStatus* st) {
|
|
418
|
+
/* item 07: the lenient tape's columns are lazy — materialize (a
|
|
419
|
+
* cache write through a const view; idempotent) */
|
|
420
|
+
yeptris_tape_columns((yeptris_json_tape*)tape);
|
|
418
421
|
if (st != NULL) {
|
|
419
422
|
*st = YEPTRIS_OK;
|
|
420
423
|
}
|
|
@@ -59,7 +59,8 @@ static int rec_put(tape_ctx* c, uint8_t kind, uint32_t off, uint32_t len) {
|
|
|
59
59
|
static YeptrisStatus tape_carve(yeptris_json_tape* t, size_t len) {
|
|
60
60
|
size_t cap = len + 2;
|
|
61
61
|
size_t off_o = (cap + 15) & ~(size_t)15;
|
|
62
|
-
char* block = yep_alloc(yep_system_allocator(),
|
|
62
|
+
char* block = yep_alloc(yep_system_allocator(),
|
|
63
|
+
off_o + 2 * cap * sizeof(uint32_t) + cap * sizeof(yeptris_tape_rec));
|
|
63
64
|
if (block == NULL) {
|
|
64
65
|
return YEPTRIS_ERROR_MEMORY;
|
|
65
66
|
}
|
|
@@ -67,11 +68,31 @@ static YeptrisStatus tape_carve(yeptris_json_tape* t, size_t len) {
|
|
|
67
68
|
t->kinds = (uint8_t*)block;
|
|
68
69
|
t->offs = (uint32_t*)(void*)(block + off_o);
|
|
69
70
|
t->lens = t->offs + cap;
|
|
71
|
+
t->recs = (yeptris_tape_rec*)(void*)(t->lens + cap);
|
|
70
72
|
t->count = 0;
|
|
71
73
|
t->int_min = 0;
|
|
74
|
+
t->_rec_primary = 0;
|
|
75
|
+
t->_cols_ready = 0;
|
|
72
76
|
return YEPTRIS_OK;
|
|
73
77
|
}
|
|
74
78
|
|
|
79
|
+
/* The lazy column materialization (TODO.max-perf/07): records are the
|
|
80
|
+
* primary storage on the lenient route; the column ABI materializes
|
|
81
|
+
* from them on first touch. */
|
|
82
|
+
YEPTRIS_API int yeptris_tape_columns(yeptris_json_tape* t) {
|
|
83
|
+
if (t == NULL || !t->_rec_primary || t->recs == NULL || t->_cols_ready) {
|
|
84
|
+
return 0; /* column-primary (the strict route) or ready */
|
|
85
|
+
}
|
|
86
|
+
for (size_t i = 0; i < t->count; i++) {
|
|
87
|
+
yeptris_tape_rec r = t->recs[i];
|
|
88
|
+
t->lens[i] = (uint32_t)((r >> 8) & 0xFFFFFFu);
|
|
89
|
+
t->offs[i] = (uint32_t)(r >> 32);
|
|
90
|
+
t->kinds[i] = (uint8_t)(r & 0xFFu);
|
|
91
|
+
}
|
|
92
|
+
t->_cols_ready = 1;
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
75
96
|
/* Scalar roots: the walk needs an opener, so a bare root value
|
|
76
97
|
* converts through the same kernels. */
|
|
77
98
|
static int tape_put_root_scalar(tape_ctx* c, const char* p, size_t len, size_t at) {
|
|
@@ -585,20 +606,18 @@ static YeptrisStatus tape_walk_lnt_fused(const char* p, size_t len, size_t open,
|
|
|
585
606
|
if (tape_carve(t, len) != YEPTRIS_OK) {
|
|
586
607
|
return YEPTRIS_ERROR_MEMORY;
|
|
587
608
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
609
|
+
/* the interleaved records are the primary storage (item 07); the
|
|
610
|
+
* columns materialize lazily via yeptris_tape_columns */
|
|
611
|
+
t->_rec_primary = 1;
|
|
612
|
+
yeptris_tape_rec* recs = t->recs;
|
|
591
613
|
uint32_t open_at[YEP_JSON_WALK_DEPTH];
|
|
592
614
|
uint8_t kind[YEP_JSON_WALK_DEPTH];
|
|
593
615
|
|
|
594
|
-
|
|
595
|
-
offs[0] = 0;
|
|
596
|
-
lens[0] = 0;
|
|
616
|
+
recs[0] = ((uint64_t)0 << 32) | ((uint64_t)0 << 8) | YEP_T_DOC;
|
|
597
617
|
|
|
598
618
|
uint8_t top_kind = p[open] == '[' ? 0 : 1;
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
lens[1] = 0;
|
|
619
|
+
recs[1] =
|
|
620
|
+
((uint64_t)0 << 32) | ((uint64_t)0 << 8) | (top_kind ? YEP_T_MAP_OPEN : YEP_T_SEQ_OPEN);
|
|
602
621
|
uint32_t top_open = 1;
|
|
603
622
|
size_t count = 2;
|
|
604
623
|
uint8_t top_expect = top_kind ? JW_KEY_OR_CLOSE : JW_VALUE_OR_CLOSE;
|
|
@@ -656,9 +675,8 @@ static YeptrisStatus tape_walk_lnt_fused(const char* p, size_t len, size_t open,
|
|
|
656
675
|
uint64_t c0 = (w - 0x2020202020202020ull) & ~w & 0x8080808080808080ull;
|
|
657
676
|
if (qm != 0 && ((bm | c0) & (qm - 1)) == 0) {
|
|
658
677
|
close = j + (size_t)yep_ctz64(qm) / 8;
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
lens[count] = (uint32_t)(close - j);
|
|
678
|
+
recs[count] = ((uint64_t)((uint32_t)j) << 32) |
|
|
679
|
+
((uint64_t)((uint32_t)(close - j)) << 8) | (uint64_t)(YEP_T_STR);
|
|
662
680
|
count++;
|
|
663
681
|
i = close + 1;
|
|
664
682
|
goto lstr_done;
|
|
@@ -668,9 +686,8 @@ static YeptrisStatus tape_walk_lnt_fused(const char* p, size_t len, size_t open,
|
|
|
668
686
|
if (!yep_json_string(p, len, &i, &close, &esc)) {
|
|
669
687
|
goto lreject;
|
|
670
688
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
lens[count] = (uint32_t)(close - at - 1);
|
|
689
|
+
recs[count] = ((uint64_t)((uint32_t)(at + 1)) << 32) |
|
|
690
|
+
((uint64_t)((uint32_t)(close - at - 1)) << 8) | (uint64_t)(YEP_T_STR);
|
|
674
691
|
count++;
|
|
675
692
|
lstr_done:
|
|
676
693
|
if (key_slot) {
|
|
@@ -698,9 +715,8 @@ static YeptrisStatus tape_walk_lnt_fused(const char* p, size_t len, size_t open,
|
|
|
698
715
|
}
|
|
699
716
|
break;
|
|
700
717
|
}
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
lens[count] = (uint32_t)(i - at);
|
|
718
|
+
recs[count] = ((uint64_t)((uint32_t)at) << 32) | ((uint64_t)((uint32_t)(i - at)) << 8) |
|
|
719
|
+
(uint64_t)(YEP_T_NUM);
|
|
704
720
|
count++;
|
|
705
721
|
lcomma:
|
|
706
722
|
if (i < len && p[i] == ',') {
|
|
@@ -719,10 +735,11 @@ static YeptrisStatus tape_walk_lnt_fused(const char* p, size_t len, size_t open,
|
|
|
719
735
|
top_expect != JW_COMMA_OR_CLOSE)) {
|
|
720
736
|
goto lreject;
|
|
721
737
|
}
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
738
|
+
recs[count] = ((uint64_t)top_open << 32) | ((uint64_t)0 << 8) | YEP_T_CLOSE;
|
|
739
|
+
/* back-patch the opener's count link: keep the original
|
|
740
|
+
* off/len, set the link into the record's off word */
|
|
741
|
+
recs[top_open] = (recs[top_open] & ~(uint64_t)0xFFFFFFFF00000000u) |
|
|
742
|
+
((uint64_t)(uint32_t)count << 32);
|
|
726
743
|
count++;
|
|
727
744
|
i = at + 1;
|
|
728
745
|
depth--;
|
|
@@ -745,9 +762,8 @@ static YeptrisStatus tape_walk_lnt_fused(const char* p, size_t len, size_t open,
|
|
|
745
762
|
kind[depth - 1] = top_kind;
|
|
746
763
|
open_at[depth - 1] = top_open;
|
|
747
764
|
top_kind = c == '[' ? 0 : 1;
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
lens[count] = 0;
|
|
765
|
+
recs[count] = ((uint64_t)0 << 32) | ((uint64_t)0 << 8) |
|
|
766
|
+
(uint64_t)(c == '[' ? YEP_T_SEQ_OPEN : YEP_T_MAP_OPEN);
|
|
751
767
|
top_open = (uint32_t)count;
|
|
752
768
|
count++;
|
|
753
769
|
top_expect = top_kind ? JW_KEY_OR_CLOSE : JW_VALUE_OR_CLOSE;
|
|
@@ -778,9 +794,8 @@ static YeptrisStatus tape_walk_lnt_fused(const char* p, size_t len, size_t open,
|
|
|
778
794
|
goto lreject;
|
|
779
795
|
}
|
|
780
796
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
lens[count] = (uint32_t)wl;
|
|
797
|
+
recs[count] = ((uint64_t)((uint32_t)at) << 32) | ((uint64_t)((uint32_t)wl) << 8) |
|
|
798
|
+
(uint64_t)(c == 'n' ? YEP_T_NULL : (c == 't' ? YEP_T_TRUE : YEP_T_FALSE));
|
|
784
799
|
count++;
|
|
785
800
|
i = at + wl;
|
|
786
801
|
goto lcomma;
|
|
@@ -1244,6 +1259,7 @@ YEPTRIS_API YeptrisStatus yeptris_parse_json_tape(const char* source, size_t len
|
|
|
1244
1259
|
|
|
1245
1260
|
YEPTRIS_API size_t yeptris_tape_convert(yeptris_json_tape* t, size_t from, size_t to,
|
|
1246
1261
|
int64_t* ivals, double* dvals) {
|
|
1262
|
+
yeptris_tape_columns(t);
|
|
1247
1263
|
if (t == NULL || t->_src == NULL || from > to || to > t->count) {
|
|
1248
1264
|
return SIZE_MAX;
|
|
1249
1265
|
}
|