yeptris 0.6.18.2-arm-linux-eabihf
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 +7 -0
- data/README.adoc +141 -0
- data/ext/build_windows_native.rb +41 -0
- data/ext/libyeptris/extconf.rb +57 -0
- data/ext/yeptris_native/cbor_ruby.c +193 -0
- data/ext/yeptris_native/extconf.rb +47 -0
- data/ext/yeptris_native/json_ruby.c +354 -0
- data/ext/yeptris_native/yeptris_native.c +497 -0
- data/lib/yeptris/cbor.rb +130 -0
- data/lib/yeptris/document.rb +255 -0
- data/lib/yeptris/ffi.rb +426 -0
- data/lib/yeptris/json/descriptor.rb +266 -0
- data/lib/yeptris/json.rb +394 -0
- data/lib/yeptris/materializer.rb +354 -0
- data/lib/yeptris/native-3.4.so +0 -0
- data/lib/yeptris/node.rb +453 -0
- data/lib/yeptris/psych/class_loader.rb +24 -0
- data/lib/yeptris/psych/coder_shim.rb +55 -0
- data/lib/yeptris/psych/drop_in.rb +63 -0
- data/lib/yeptris/psych/encodable.rb +14 -0
- data/lib/yeptris/psych/handler.rb +90 -0
- data/lib/yeptris/psych/parser.rb +105 -0
- data/lib/yeptris/psych/scalar_scanner.rb +149 -0
- data/lib/yeptris/psych/visitors.rb +545 -0
- data/lib/yeptris/psych.rb +622 -0
- data/lib/yeptris/schema.rb +253 -0
- data/lib/yeptris/valueml.rb +455 -0
- data/lib/yeptris/yaml/descriptor.rb +56 -0
- data/lib/yeptris/yaml.rb +422 -0
- data/lib/yeptris.rb +113 -0
- data/libyeptris.so +0 -0
- 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 +190 -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 +121 -0
- data/vendor/libyeptris/src/include/yeptris/tape.h +123 -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 +376 -0
- data/vendor/libyeptris/src/yeptris/cbor/cbor.h +25 -0
- data/vendor/libyeptris/src/yeptris/cbor/decode.c +981 -0
- data/vendor/libyeptris/src/yeptris/cbor/encode.c +835 -0
- data/vendor/libyeptris/src/yeptris/cbor/sink.h +58 -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 +56 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.c +1192 -0
- data/vendor/libyeptris/src/yeptris/dom/dom.h +312 -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 +431 -0
- data/vendor/libyeptris/src/yeptris/emit/emit.c +434 -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 +231 -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 +816 -0
- data/vendor/libyeptris/src/yeptris/emit/writer.h +76 -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 +782 -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 +122 -0
- data/vendor/libyeptris/src/yeptris/memory/pool.h +36 -0
- data/vendor/libyeptris/src/yeptris/parse/engine.c +3698 -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 +737 -0
- data/vendor/libyeptris/src/yeptris/plan.c +824 -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 +308 -0
- data/vendor/libyeptris/src/yeptris/tape.c +1308 -0
- data/vendor/libyeptris/src/yeptris/tape_in.h +19 -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 +199 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
/* yeptris_native.c — Ruby C-API materializer over libyeptris visit
|
|
2
|
+
* (TODO.restructure/22). One Ruby→C call builds the whole object
|
|
3
|
+
* graph via rb_hash_new / rb_ary_push / rb_str_new_len — the same
|
|
4
|
+
* shape as JSON.parse, so the binding can beat it on the fused JSON
|
|
5
|
+
* path. The extension is optional: LoadError falls back to the FFI
|
|
6
|
+
* Marshal ladder. */
|
|
7
|
+
|
|
8
|
+
#include <ruby.h>
|
|
9
|
+
#include <ruby/encoding.h>
|
|
10
|
+
|
|
11
|
+
#include <stdlib.h>
|
|
12
|
+
#include <string.h>
|
|
13
|
+
|
|
14
|
+
#include <yeptris/api.h>
|
|
15
|
+
#include <yeptris/error.h>
|
|
16
|
+
#include <yeptris/resolve.h>
|
|
17
|
+
#include <yeptris/visit.h>
|
|
18
|
+
|
|
19
|
+
#define YEP_RB_MAX_DEPTH 1024
|
|
20
|
+
|
|
21
|
+
static rb_encoding* utf8_enc;
|
|
22
|
+
|
|
23
|
+
typedef struct {
|
|
24
|
+
VALUE stack[YEP_RB_MAX_DEPTH];
|
|
25
|
+
VALUE keys[YEP_RB_MAX_DEPTH]; /* pending map key at this depth */
|
|
26
|
+
int is_map[YEP_RB_MAX_DEPTH];
|
|
27
|
+
int sp;
|
|
28
|
+
VALUE root;
|
|
29
|
+
VALUE anchors; /* Hash name=>object for YAML identity */
|
|
30
|
+
VALUE pending_anchor; /* String name awaiting the next value */
|
|
31
|
+
int failed;
|
|
32
|
+
} rb_ctx;
|
|
33
|
+
|
|
34
|
+
static void rb_fail(rb_ctx* c) {
|
|
35
|
+
c->failed = 1;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static int rb_push_value(rb_ctx* c, VALUE v) {
|
|
39
|
+
if (c->failed) {
|
|
40
|
+
return -1;
|
|
41
|
+
}
|
|
42
|
+
/* bind pending anchor */
|
|
43
|
+
if (!NIL_P(c->pending_anchor) && !NIL_P(c->anchors)) {
|
|
44
|
+
rb_hash_aset(c->anchors, c->pending_anchor, v);
|
|
45
|
+
c->pending_anchor = Qnil;
|
|
46
|
+
}
|
|
47
|
+
if (c->sp == 0) {
|
|
48
|
+
c->root = v;
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
VALUE parent = c->stack[c->sp - 1];
|
|
52
|
+
if (c->is_map[c->sp - 1]) {
|
|
53
|
+
VALUE key = c->keys[c->sp - 1];
|
|
54
|
+
if (NIL_P(key)) {
|
|
55
|
+
rb_fail(c);
|
|
56
|
+
return -1;
|
|
57
|
+
}
|
|
58
|
+
rb_hash_aset(parent, key, v);
|
|
59
|
+
c->keys[c->sp - 1] = Qnil;
|
|
60
|
+
} else {
|
|
61
|
+
rb_ary_push(parent, v);
|
|
62
|
+
}
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static int on_null(void* ctx) {
|
|
67
|
+
return rb_push_value((rb_ctx*)ctx, Qnil);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static int on_bool(void* ctx, int truthy) {
|
|
71
|
+
return rb_push_value((rb_ctx*)ctx, truthy ? Qtrue : Qfalse);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static int on_int(void* ctx, int64_t v) {
|
|
75
|
+
return rb_push_value((rb_ctx*)ctx, LL2NUM(v));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
static int on_float(void* ctx, double v) {
|
|
79
|
+
return rb_push_value((rb_ctx*)ctx, DBL2NUM(v));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static rb_encoding* utf8_enc;
|
|
83
|
+
|
|
84
|
+
static VALUE rb_utf8_str(const char* p, size_t len) {
|
|
85
|
+
return rb_enc_str_new(p, (long)len, utf8_enc);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* One-shot interned string (no intermediate alloc) — keys and the
|
|
89
|
+
* short repeated values ("a"/"b"/…) share one VALUE. */
|
|
90
|
+
static VALUE rb_utf8_interned(const char* p, size_t len) {
|
|
91
|
+
return rb_enc_interned_str(p, (long)len, utf8_enc);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
static int on_string(void* ctx, const char* p, size_t len) {
|
|
95
|
+
rb_ctx* c = (rb_ctx*)ctx;
|
|
96
|
+
/* short strings are almost always repeated tokens in JSON corpora;
|
|
97
|
+
* intern them. Longer payloads stay unique. */
|
|
98
|
+
VALUE s = (len <= 16) ? rb_utf8_interned(p, len) : rb_utf8_str(p, len);
|
|
99
|
+
return rb_push_value(c, s);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
static int on_key(void* ctx, const char* p, size_t len) {
|
|
103
|
+
rb_ctx* c = (rb_ctx*)ctx;
|
|
104
|
+
if (c->sp == 0 || !c->is_map[c->sp - 1]) {
|
|
105
|
+
rb_fail(c);
|
|
106
|
+
return -1;
|
|
107
|
+
}
|
|
108
|
+
c->keys[c->sp - 1] = rb_utf8_interned(p, len);
|
|
109
|
+
return 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
static int on_seq_start(void* ctx) {
|
|
113
|
+
rb_ctx* c = (rb_ctx*)ctx;
|
|
114
|
+
if (c->failed || c->sp >= YEP_RB_MAX_DEPTH) {
|
|
115
|
+
rb_fail(c);
|
|
116
|
+
return -1;
|
|
117
|
+
}
|
|
118
|
+
VALUE a = rb_ary_new();
|
|
119
|
+
/* bind anchor to the container before placing it */
|
|
120
|
+
if (!NIL_P(c->pending_anchor) && !NIL_P(c->anchors)) {
|
|
121
|
+
rb_hash_aset(c->anchors, c->pending_anchor, a);
|
|
122
|
+
c->pending_anchor = Qnil;
|
|
123
|
+
}
|
|
124
|
+
if (c->sp == 0) {
|
|
125
|
+
c->root = a;
|
|
126
|
+
} else {
|
|
127
|
+
VALUE parent = c->stack[c->sp - 1];
|
|
128
|
+
if (c->is_map[c->sp - 1]) {
|
|
129
|
+
VALUE key = c->keys[c->sp - 1];
|
|
130
|
+
if (NIL_P(key)) {
|
|
131
|
+
rb_fail(c);
|
|
132
|
+
return -1;
|
|
133
|
+
}
|
|
134
|
+
rb_hash_aset(parent, key, a);
|
|
135
|
+
c->keys[c->sp - 1] = Qnil;
|
|
136
|
+
} else {
|
|
137
|
+
rb_ary_push(parent, a);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
c->stack[c->sp] = a;
|
|
141
|
+
c->is_map[c->sp] = 0;
|
|
142
|
+
c->keys[c->sp] = Qnil;
|
|
143
|
+
c->sp++;
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static int on_seq_end(void* ctx) {
|
|
148
|
+
rb_ctx* c = (rb_ctx*)ctx;
|
|
149
|
+
if (c->sp <= 0) {
|
|
150
|
+
rb_fail(c);
|
|
151
|
+
return -1;
|
|
152
|
+
}
|
|
153
|
+
c->sp--;
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
static int on_map_start(void* ctx) {
|
|
158
|
+
rb_ctx* c = (rb_ctx*)ctx;
|
|
159
|
+
if (c->failed || c->sp >= YEP_RB_MAX_DEPTH) {
|
|
160
|
+
rb_fail(c);
|
|
161
|
+
return -1;
|
|
162
|
+
}
|
|
163
|
+
VALUE h = rb_hash_new();
|
|
164
|
+
if (!NIL_P(c->pending_anchor) && !NIL_P(c->anchors)) {
|
|
165
|
+
rb_hash_aset(c->anchors, c->pending_anchor, h);
|
|
166
|
+
c->pending_anchor = Qnil;
|
|
167
|
+
}
|
|
168
|
+
if (c->sp == 0) {
|
|
169
|
+
c->root = h;
|
|
170
|
+
} else {
|
|
171
|
+
VALUE parent = c->stack[c->sp - 1];
|
|
172
|
+
if (c->is_map[c->sp - 1]) {
|
|
173
|
+
VALUE key = c->keys[c->sp - 1];
|
|
174
|
+
if (NIL_P(key)) {
|
|
175
|
+
rb_fail(c);
|
|
176
|
+
return -1;
|
|
177
|
+
}
|
|
178
|
+
rb_hash_aset(parent, key, h);
|
|
179
|
+
c->keys[c->sp - 1] = Qnil;
|
|
180
|
+
} else {
|
|
181
|
+
rb_ary_push(parent, h);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
c->stack[c->sp] = h;
|
|
185
|
+
c->is_map[c->sp] = 1;
|
|
186
|
+
c->keys[c->sp] = Qnil;
|
|
187
|
+
c->sp++;
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
static int on_map_end(void* ctx) {
|
|
192
|
+
rb_ctx* c = (rb_ctx*)ctx;
|
|
193
|
+
if (c->sp <= 0) {
|
|
194
|
+
rb_fail(c);
|
|
195
|
+
return -1;
|
|
196
|
+
}
|
|
197
|
+
c->sp--;
|
|
198
|
+
return 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
static int on_anchor(void* ctx, const char* name, size_t len) {
|
|
202
|
+
rb_ctx* c = (rb_ctx*)ctx;
|
|
203
|
+
c->pending_anchor = rb_utf8_str(name, len);
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
static int on_alias(void* ctx, const char* name, size_t len) {
|
|
208
|
+
rb_ctx* c = (rb_ctx*)ctx;
|
|
209
|
+
VALUE key = rb_utf8_str(name, len);
|
|
210
|
+
VALUE v = rb_hash_lookup2(c->anchors, key, Qundef);
|
|
211
|
+
if (v == Qundef) {
|
|
212
|
+
v = Qnil;
|
|
213
|
+
}
|
|
214
|
+
return rb_push_value(c, v);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
static int on_doc(void* ctx) {
|
|
218
|
+
/* multi-doc: for load_all we'd collect; single-load takes first.
|
|
219
|
+
* reset root so subsequent docs replace — load_stream uses a
|
|
220
|
+
* different entry that accumulates. */
|
|
221
|
+
(void)ctx;
|
|
222
|
+
return 0;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
static const YeptrisVisitVTable k_vt = {
|
|
226
|
+
on_null, on_bool, on_int, on_float, on_string,
|
|
227
|
+
on_seq_start, on_seq_end, on_map_start, on_map_end,
|
|
228
|
+
on_key, on_doc, on_anchor, on_alias,
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/* fused JSON→Ruby (json_ruby.c) — no vtable, beats JSON.parse */
|
|
232
|
+
VALUE yep_rb_parse_json(const char* p, size_t len, int strict_dup);
|
|
233
|
+
VALUE yep_rb_cbor_load(const char* p, size_t len, int strict);
|
|
234
|
+
|
|
235
|
+
static VALUE ctx_result(rb_ctx* c, YeptrisStatus st) {
|
|
236
|
+
if (st != YEPTRIS_OK || c->failed) {
|
|
237
|
+
if (st == YEPTRIS_ERROR_PARSE) {
|
|
238
|
+
rb_raise(rb_path2class("Yeptris::ParseError"), "native parse failed");
|
|
239
|
+
}
|
|
240
|
+
if (st == YEPTRIS_ERROR_MEMORY) {
|
|
241
|
+
rb_raise(rb_eNoMemError, "yeptris native");
|
|
242
|
+
}
|
|
243
|
+
rb_raise(rb_path2class("Yeptris::Error"), "native materialize failed (%d)", (int)st);
|
|
244
|
+
}
|
|
245
|
+
return c->root;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
static VALUE native_cbor_load(int argc, VALUE* argv, VALUE self) {
|
|
249
|
+
(void)self;
|
|
250
|
+
VALUE input, strict;
|
|
251
|
+
rb_scan_args(argc, argv, "11", &input, &strict);
|
|
252
|
+
StringValue(input);
|
|
253
|
+
VALUE v = yep_rb_cbor_load(RSTRING_PTR(input), (size_t)RSTRING_LEN(input), RTEST(strict));
|
|
254
|
+
if (v == Qundef) {
|
|
255
|
+
rb_raise(rb_path2class("Yeptris::ParseError"), "native cbor decode failed");
|
|
256
|
+
}
|
|
257
|
+
return v;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
static VALUE native_load_json(int argc, VALUE* argv, VALUE self) {
|
|
261
|
+
(void)self;
|
|
262
|
+
VALUE input, strict;
|
|
263
|
+
rb_scan_args(argc, argv, "11", &input, &strict);
|
|
264
|
+
StringValue(input);
|
|
265
|
+
return yep_rb_parse_json(RSTRING_PTR(input), (size_t)RSTRING_LEN(input), RTEST(strict));
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
static VALUE native_load(VALUE self, VALUE input, VALUE schema) {
|
|
269
|
+
(void)self;
|
|
270
|
+
StringValue(input);
|
|
271
|
+
int sch = YEPTRIS_SCHEMA_11_COMPAT;
|
|
272
|
+
if (!NIL_P(schema)) {
|
|
273
|
+
Check_Type(schema, T_SYMBOL);
|
|
274
|
+
if (rb_sym2id(schema) == rb_intern("core_12")) {
|
|
275
|
+
sch = YEPTRIS_SCHEMA_12_CORE;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
rb_ctx c;
|
|
279
|
+
memset(&c, 0, sizeof(c));
|
|
280
|
+
c.root = Qnil;
|
|
281
|
+
c.pending_anchor = Qnil;
|
|
282
|
+
c.anchors = rb_hash_new();
|
|
283
|
+
VALUE already = rb_gc_disable();
|
|
284
|
+
YeptrisStatus st = yeptris_visit(RSTRING_PTR(input), (size_t)RSTRING_LEN(input),
|
|
285
|
+
(YeptrisSchema)sch, &k_vt, &c);
|
|
286
|
+
if (already == Qfalse) {
|
|
287
|
+
rb_gc_enable();
|
|
288
|
+
}
|
|
289
|
+
return ctx_result(&c, st);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/* load_stream: accumulate documents into an Array. */
|
|
293
|
+
typedef struct {
|
|
294
|
+
rb_ctx inner;
|
|
295
|
+
VALUE docs;
|
|
296
|
+
int in_doc;
|
|
297
|
+
} rb_stream_ctx;
|
|
298
|
+
|
|
299
|
+
static int stream_on_doc(void* ctx) {
|
|
300
|
+
rb_stream_ctx* s = (rb_stream_ctx*)ctx;
|
|
301
|
+
if (s->in_doc && !NIL_P(s->inner.root)) {
|
|
302
|
+
rb_ary_push(s->docs, s->inner.root);
|
|
303
|
+
}
|
|
304
|
+
s->inner.root = Qnil;
|
|
305
|
+
s->inner.sp = 0;
|
|
306
|
+
s->in_doc = 1;
|
|
307
|
+
return 0;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
static VALUE native_load_stream(VALUE self, VALUE input, VALUE schema) {
|
|
311
|
+
(void)self;
|
|
312
|
+
StringValue(input);
|
|
313
|
+
int sch = YEPTRIS_SCHEMA_11_COMPAT;
|
|
314
|
+
if (!NIL_P(schema) && rb_sym2id(schema) == rb_intern("core_12")) {
|
|
315
|
+
sch = YEPTRIS_SCHEMA_12_CORE;
|
|
316
|
+
}
|
|
317
|
+
rb_stream_ctx s;
|
|
318
|
+
memset(&s, 0, sizeof(s));
|
|
319
|
+
s.inner.root = Qnil;
|
|
320
|
+
s.inner.pending_anchor = Qnil;
|
|
321
|
+
s.inner.anchors = rb_hash_new();
|
|
322
|
+
s.docs = rb_ary_new();
|
|
323
|
+
YeptrisVisitVTable vt = k_vt;
|
|
324
|
+
vt.on_doc = stream_on_doc;
|
|
325
|
+
/* trick: the ctx for scalar callbacks is &s.inner, but on_doc needs
|
|
326
|
+
* &s. Use a unified ctx — rebind all callbacks to take stream ctx
|
|
327
|
+
* by making inner the first field (already is). on_doc uses outer;
|
|
328
|
+
* others use inner via same pointer since inner is first field. */
|
|
329
|
+
YeptrisStatus st = yeptris_visit(RSTRING_PTR(input), (size_t)RSTRING_LEN(input),
|
|
330
|
+
(YeptrisSchema)sch, &vt, &s);
|
|
331
|
+
if (st == YEPTRIS_OK && !s.inner.failed) {
|
|
332
|
+
if (!NIL_P(s.inner.root) || s.in_doc) {
|
|
333
|
+
rb_ary_push(s.docs, s.inner.root);
|
|
334
|
+
}
|
|
335
|
+
return s.docs;
|
|
336
|
+
}
|
|
337
|
+
return ctx_result(&s.inner, st == YEPTRIS_OK ? YEPTRIS_ERROR_INTERNAL : st);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/* GC-strategy surface (TODO.restructure/34): ENV at load sets the
|
|
341
|
+
* default; the setter re-picks at runtime so the CI referee can A/B
|
|
342
|
+
* in-process. Symbols: :disable, :none, :start. */
|
|
343
|
+
extern int yep_rb_gc_mode(void);
|
|
344
|
+
extern void yep_rb_set_gc_mode(int mode);
|
|
345
|
+
extern int yep_rb_ins_mode(void);
|
|
346
|
+
extern void yep_rb_set_ins_mode(int mode);
|
|
347
|
+
extern int yep_rb_cache_mode(void);
|
|
348
|
+
extern void yep_rb_set_cache_mode(int mode);
|
|
349
|
+
extern int yep_rb_shape_mode(void);
|
|
350
|
+
extern void yep_rb_set_shape_mode(int mode);
|
|
351
|
+
extern double yep_rb_scan_time(const char* p, size_t len, int n);
|
|
352
|
+
|
|
353
|
+
static VALUE native_gc_mode(VALUE self) {
|
|
354
|
+
(void)self;
|
|
355
|
+
switch (yep_rb_gc_mode()) {
|
|
356
|
+
case 1: return ID2SYM(rb_intern("none"));
|
|
357
|
+
case 2: return ID2SYM(rb_intern("start"));
|
|
358
|
+
default: return ID2SYM(rb_intern("disable"));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
static VALUE native_ins_mode(VALUE self) {
|
|
363
|
+
(void)self;
|
|
364
|
+
return yep_rb_ins_mode() == 1 ? ID2SYM(rb_intern("aset")) : ID2SYM(rb_intern("bulk"));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
static VALUE native_ins_mode_set(VALUE self, VALUE mode) {
|
|
368
|
+
(void)self;
|
|
369
|
+
Check_Type(mode, T_SYMBOL);
|
|
370
|
+
ID id = rb_sym2id(mode);
|
|
371
|
+
if (id == rb_intern("bulk")) yep_rb_set_ins_mode(0);
|
|
372
|
+
else if (id == rb_intern("aset")) yep_rb_set_ins_mode(1);
|
|
373
|
+
else rb_raise(rb_eArgError, "ins_mode must be :bulk or :aset");
|
|
374
|
+
return mode;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
static VALUE native_cache_mode(VALUE self) {
|
|
378
|
+
(void)self;
|
|
379
|
+
return yep_rb_cache_mode() == 1 ? ID2SYM(rb_intern("off")) : ID2SYM(rb_intern("on"));
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
static VALUE native_cache_mode_set(VALUE self, VALUE mode) {
|
|
383
|
+
(void)self;
|
|
384
|
+
Check_Type(mode, T_SYMBOL);
|
|
385
|
+
ID id = rb_sym2id(mode);
|
|
386
|
+
if (id == rb_intern("on")) yep_rb_set_cache_mode(0);
|
|
387
|
+
else if (id == rb_intern("off")) yep_rb_set_cache_mode(1);
|
|
388
|
+
else rb_raise(rb_eArgError, "cache_mode must be :on or :off");
|
|
389
|
+
return mode;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
static VALUE native_scan_time(VALUE self, VALUE input, VALUE count) {
|
|
393
|
+
(void)self;
|
|
394
|
+
StringValue(input);
|
|
395
|
+
int n = NUM2INT(count);
|
|
396
|
+
double secs = yep_rb_scan_time(RSTRING_PTR(input), (size_t)RSTRING_LEN(input), n);
|
|
397
|
+
return DBL2NUM(secs / (double)n);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
static VALUE native_shape_mode(VALUE self) {
|
|
401
|
+
(void)self;
|
|
402
|
+
return yep_rb_shape_mode() == 1 ? ID2SYM(rb_intern("natural")) : ID2SYM(rb_intern("pre"));
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
static VALUE native_shape_mode_set(VALUE self, VALUE mode) {
|
|
406
|
+
(void)self;
|
|
407
|
+
Check_Type(mode, T_SYMBOL);
|
|
408
|
+
ID id = rb_sym2id(mode);
|
|
409
|
+
if (id == rb_intern("pre")) yep_rb_set_shape_mode(0);
|
|
410
|
+
else if (id == rb_intern("natural")) yep_rb_set_shape_mode(1);
|
|
411
|
+
else rb_raise(rb_eArgError, "shape_mode must be :pre or :natural");
|
|
412
|
+
return mode;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
static VALUE native_gc_mode_set(VALUE self, VALUE mode) {
|
|
416
|
+
(void)self;
|
|
417
|
+
Check_Type(mode, T_SYMBOL);
|
|
418
|
+
ID id = rb_sym2id(mode);
|
|
419
|
+
if (id == rb_intern("disable")) yep_rb_set_gc_mode(0);
|
|
420
|
+
else if (id == rb_intern("none")) yep_rb_set_gc_mode(1);
|
|
421
|
+
else if (id == rb_intern("start")) yep_rb_set_gc_mode(2);
|
|
422
|
+
else rb_raise(rb_eArgError, "gc_mode must be :disable, :none, or :start");
|
|
423
|
+
return mode;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/* ABI-drift self-check (the #157-audit silent-nil class): the bundle
|
|
427
|
+
* is compiled against a specific yep_dom layout; engine drift makes
|
|
428
|
+
* the walkers read fields at stale offsets — cbor_load returned NIL
|
|
429
|
+
* on valid CBOR in the dev-checkout audit, with no error anywhere.
|
|
430
|
+
* Both walkers must round-trip a known document at load, or Init
|
|
431
|
+
* raises LoadError: lib/yeptris.rb's existing rescue falls back to
|
|
432
|
+
* the FFI ladder (loudly), and the stale bundle never answers with
|
|
433
|
+
* garbage. Runs inside rb_protect so a drifted walk raises instead of
|
|
434
|
+
* crashing the load. */
|
|
435
|
+
static VALUE native_self_check_body(VALUE unused) {
|
|
436
|
+
/* JSON: {"a"=>[1, 2.5, "x", true, nil]} */
|
|
437
|
+
const char* json = "{\"a\":[1,2.5,\"x\",true,null]}";
|
|
438
|
+
VALUE j = yep_rb_parse_json(json, strlen(json), 0);
|
|
439
|
+
if (j == Qundef || !RB_TYPE_P(j, T_HASH)) return Qfalse;
|
|
440
|
+
VALUE a = rb_hash_lookup(j, rb_str_new_cstr("a"));
|
|
441
|
+
if (!RB_TYPE_P(a, T_ARRAY) || RARRAY_LEN(a) != 5) return Qfalse;
|
|
442
|
+
if (!RB_INTEGER_TYPE_P(rb_ary_entry(a, 0))) return Qfalse;
|
|
443
|
+
if (!RB_FLOAT_TYPE_P(rb_ary_entry(a, 1))) return Qfalse;
|
|
444
|
+
if (!RB_TYPE_P(rb_ary_entry(a, 2), T_STRING)) return Qfalse;
|
|
445
|
+
if (rb_ary_entry(a, 3) != Qtrue || !NIL_P(rb_ary_entry(a, 4))) return Qfalse;
|
|
446
|
+
|
|
447
|
+
/* CBOR: A1 61 6B 01 = {"k" => 1} (canonical, hand-encoded) */
|
|
448
|
+
const char cbor[] = "\xA1\x61\x6B\x01";
|
|
449
|
+
VALUE c = yep_rb_cbor_load(cbor, sizeof(cbor) - 1, 0);
|
|
450
|
+
if (c == Qundef || !RB_TYPE_P(c, T_HASH)) return Qfalse;
|
|
451
|
+
VALUE one = rb_hash_lookup(c, rb_str_new_cstr("k"));
|
|
452
|
+
if (!RB_INTEGER_TYPE_P(one) || !rb_eql(one, INT2FIX(1))) return Qfalse;
|
|
453
|
+
return Qtrue;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
static void native_self_check(void) {
|
|
457
|
+
int state = 0;
|
|
458
|
+
VALUE ok = rb_protect(native_self_check_body, Qnil, &state);
|
|
459
|
+
if (state != 0 || ok != Qtrue) {
|
|
460
|
+
if (state != 0) rb_set_errinfo(Qnil);
|
|
461
|
+
rb_raise(rb_eLoadError,
|
|
462
|
+
"yeptris: native materializer failed its ABI self-check "
|
|
463
|
+
"(stale build against a drifted engine?) — refusing to "
|
|
464
|
+
"register; the FFI ladder will carry the load");
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
RUBY_FUNC_EXPORTED void Init_native(void) {
|
|
469
|
+
utf8_enc = rb_utf8_encoding();
|
|
470
|
+
native_self_check();
|
|
471
|
+
VALUE mYep = rb_define_module("Yeptris");
|
|
472
|
+
VALUE mNat = rb_define_module_under(mYep, "Native");
|
|
473
|
+
rb_define_singleton_method(mNat, "load_json", native_load_json, -1);
|
|
474
|
+
rb_define_singleton_method(mNat, "cbor_load", native_cbor_load, -1);
|
|
475
|
+
rb_define_singleton_method(mNat, "load", native_load, 2);
|
|
476
|
+
rb_define_singleton_method(mNat, "load_stream", native_load_stream, 2);
|
|
477
|
+
rb_define_singleton_method(mNat, "gc_mode", native_gc_mode, 0);
|
|
478
|
+
rb_define_singleton_method(mNat, "gc_mode=", native_gc_mode_set, 1);
|
|
479
|
+
rb_define_singleton_method(mNat, "ins_mode", native_ins_mode, 0);
|
|
480
|
+
rb_define_singleton_method(mNat, "ins_mode=", native_ins_mode_set, 1);
|
|
481
|
+
rb_define_singleton_method(mNat, "cache_mode", native_cache_mode, 0);
|
|
482
|
+
rb_define_singleton_method(mNat, "cache_mode=", native_cache_mode_set, 1);
|
|
483
|
+
rb_define_singleton_method(mNat, "shape_mode", native_shape_mode, 0);
|
|
484
|
+
rb_define_singleton_method(mNat, "shape_mode=", native_shape_mode_set, 1);
|
|
485
|
+
rb_define_singleton_method(mNat, "scan_time", native_scan_time, 2);
|
|
486
|
+
rb_define_const(mNat, "AVAILABLE", Qtrue);
|
|
487
|
+
const char* env = getenv("YEPTRIS_NATIVE_GC");
|
|
488
|
+
if (env != NULL && strcmp(env, "none") == 0) yep_rb_set_gc_mode(1);
|
|
489
|
+
else if (env != NULL && strcmp(env, "start") == 0) yep_rb_set_gc_mode(2);
|
|
490
|
+
else if (env != NULL && strcmp(env, "disable") == 0) yep_rb_set_gc_mode(0);
|
|
491
|
+
const char* ins = getenv("YEPTRIS_NATIVE_INSERT");
|
|
492
|
+
if (ins != NULL && strcmp(ins, "aset") == 0) yep_rb_set_ins_mode(1);
|
|
493
|
+
const char* cache = getenv("YEPTRIS_NATIVE_CACHE");
|
|
494
|
+
if (cache != NULL && strcmp(cache, "off") == 0) yep_rb_set_cache_mode(1);
|
|
495
|
+
const char* shape = getenv("YEPTRIS_NATIVE_SHAPE");
|
|
496
|
+
if (shape != NULL && strcmp(shape, "natural") == 0) yep_rb_set_shape_mode(1);
|
|
497
|
+
}
|
data/lib/yeptris/cbor.rb
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Yeptris
|
|
4
|
+
# CBOR (RFC 8949) over the same FFI document the rest of the
|
|
5
|
+
# binding rides (TODO.cbor/04). Decode materializes through the
|
|
6
|
+
# Psych-compatible path; encode builds the tree with YAMLTree and
|
|
7
|
+
# hands the document to the C encoder — plain data only (aliases
|
|
8
|
+
# and non-decimal tag text are unencodable by the C contract).
|
|
9
|
+
#
|
|
10
|
+
# Representation notes inherited from the decoder: integers beyond
|
|
11
|
+
# int64 and bignums materialize as their shortest-double text, byte
|
|
12
|
+
# strings as tag-chained text — CBOR's JSON-data-model mapping.
|
|
13
|
+
module CBOR
|
|
14
|
+
STRICT = (1 << 0) # decode: reject non-minimal arguments
|
|
15
|
+
CANONICAL = (1 << 1) # encode: s4.2.1 core deterministic profile
|
|
16
|
+
|
|
17
|
+
class Error < Yeptris::Error; end
|
|
18
|
+
class UnencodableError < Error; end
|
|
19
|
+
|
|
20
|
+
module_function
|
|
21
|
+
|
|
22
|
+
def available?
|
|
23
|
+
defined?(::Yeptris::FFI::CBOR_EX) && ::Yeptris::FFI::CBOR_EX
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def load(data, strict: false)
|
|
27
|
+
raise Error, "libyeptris has no CBOR support" unless available?
|
|
28
|
+
|
|
29
|
+
# #157: the native materializer (decode + direct VALUE
|
|
30
|
+
# construction in one C pass) when the extension is loaded;
|
|
31
|
+
# the FFI ladder otherwise
|
|
32
|
+
if defined?(::Yeptris::Native) && ::Yeptris::Native.respond_to?(:cbor_load)
|
|
33
|
+
return ::Yeptris::Native.cbor_load(data, strict)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
doc_ptr = ::Yeptris::FFI.yeptris_cbor_decode(data, data.bytesize, strict ? STRICT : 0, nil)
|
|
37
|
+
raise ParseError, ::Yeptris::FFI.last_error_message if doc_ptr.null?
|
|
38
|
+
|
|
39
|
+
# the wrapper's finalizer also frees — its idempotent #free is
|
|
40
|
+
# the ONLY release path (a raw double free corrupts the heap)
|
|
41
|
+
doc = ::Yeptris::Document.new(doc_ptr)
|
|
42
|
+
doc.root.to_ruby
|
|
43
|
+
ensure
|
|
44
|
+
doc&.free
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def load_sequence(data, strict: false)
|
|
48
|
+
raise Error, "libyeptris has no CBOR support" unless available?
|
|
49
|
+
|
|
50
|
+
items = []
|
|
51
|
+
pending_error = nil
|
|
52
|
+
receiver = ::FFI::Function.new(:int, %i[pointer pointer size_t]) do |_ctx, item, _index|
|
|
53
|
+
# the callback owns the item; materialize then free in place
|
|
54
|
+
# (through the wrapper — the finalizer frees too). A raise
|
|
55
|
+
# inside an FFI callback leaves the return value undefined —
|
|
56
|
+
# abort the C iteration cleanly (nonzero) and re-raise after
|
|
57
|
+
begin
|
|
58
|
+
doc = ::Yeptris::Document.new(item)
|
|
59
|
+
items << doc.root&.to_ruby
|
|
60
|
+
doc.free
|
|
61
|
+
0
|
|
62
|
+
rescue ::Exception => e # rubocop:disable Lint/RescueException
|
|
63
|
+
pending_error = e
|
|
64
|
+
1
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
st = ::Yeptris::FFI.yeptris_cbor_decode_sequence(
|
|
68
|
+
data, data.bytesize, strict ? STRICT : 0, receiver, nil, nil
|
|
69
|
+
)
|
|
70
|
+
raise pending_error if pending_error
|
|
71
|
+
raise ParseError, ::Yeptris::FFI.last_error_message if st.zero? && !data.empty?
|
|
72
|
+
|
|
73
|
+
items
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def dump(obj, canonical: true)
|
|
77
|
+
docs = build_documents([obj])
|
|
78
|
+
begin
|
|
79
|
+
encode_one(docs[0], canonical)
|
|
80
|
+
ensure
|
|
81
|
+
docs.each(&:free)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def dump_sequence(objects, canonical: true)
|
|
86
|
+
docs = build_documents(objects)
|
|
87
|
+
begin
|
|
88
|
+
opts = canonical ? CANONICAL : 0
|
|
89
|
+
arr = ::FFI::MemoryPointer.new(:pointer, docs.size)
|
|
90
|
+
arr.write_array_of_pointer(docs.map(&:c_ptr))
|
|
91
|
+
len_ptr = ::FFI::MemoryPointer.new(:size_t)
|
|
92
|
+
take_buffer(
|
|
93
|
+
::Yeptris::FFI.yeptris_cbor_encode_sequence(arr, docs.size, opts, len_ptr),
|
|
94
|
+
len_ptr
|
|
95
|
+
)
|
|
96
|
+
ensure
|
|
97
|
+
docs.each(&:free)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# -- internals ----------------------------------------------------
|
|
102
|
+
|
|
103
|
+
def build_documents(objects)
|
|
104
|
+
# ONE document per object: a visitor's tree is a single
|
|
105
|
+
# document and push sets its root — reuse would overwrite
|
|
106
|
+
objects.map do |obj|
|
|
107
|
+
visitor = ::Yeptris::Psych::Visitors::YAMLTree.new
|
|
108
|
+
visitor.push(obj)
|
|
109
|
+
visitor.document
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def encode_one(doc, canonical)
|
|
114
|
+
opts = canonical ? CANONICAL : 0
|
|
115
|
+
len_ptr = ::FFI::MemoryPointer.new(:size_t)
|
|
116
|
+
take_buffer(::Yeptris::FFI.yeptris_cbor_encode(doc.c_ptr, opts, len_ptr), len_ptr)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# The encode convenience contract: a malloc'd buffer plus its
|
|
120
|
+
# byte length — binary data, freed through the library's own free.
|
|
121
|
+
def take_buffer(ptr, len_ptr)
|
|
122
|
+
raise UnencodableError, ::Yeptris::FFI.last_error_message if ptr.null?
|
|
123
|
+
|
|
124
|
+
len = Yeptris::FFI.read_c_size_t(len_ptr)
|
|
125
|
+
ptr.read_bytes(len).force_encoding(Encoding::BINARY)
|
|
126
|
+
ensure
|
|
127
|
+
::Yeptris::FFI.yeptris_free(ptr) if ptr && !ptr.null?
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|