qr_reader-native 0.0.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.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-mruby.txt +22 -0
  3. data/LICENSE-spinel.txt +19 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +61 -0
  6. data/ext/qr_reader_native/SPINEL_VERSION +1 -0
  7. data/ext/qr_reader_native/extconf.rb +8 -0
  8. data/ext/qr_reader_native/mruby/error.h +13 -0
  9. data/ext/qr_reader_native/mruby/internal.h +145 -0
  10. data/ext/qr_reader_native/mruby/string.h +22 -0
  11. data/ext/qr_reader_native/mruby.h +93 -0
  12. data/ext/qr_reader_native/mruby_shim.h +118 -0
  13. data/ext/qr_reader_native/qr_reader_native.c +13513 -0
  14. data/ext/qr_reader_native/qr_reader_native.h +29 -0
  15. data/ext/qr_reader_native/qr_reader_native_ext.c +598 -0
  16. data/ext/qr_reader_native/re_cased.h +71 -0
  17. data/ext/qr_reader_native/re_compile.c +5784 -0
  18. data/ext/qr_reader_native/re_ctype.h +3540 -0
  19. data/ext/qr_reader_native/re_exec.c +2190 -0
  20. data/ext/qr_reader_native/re_internal.h +671 -0
  21. data/ext/qr_reader_native/re_prop.h +4629 -0
  22. data/ext/qr_reader_native/re_spinel.c +363 -0
  23. data/ext/qr_reader_native/re_spinel.h +22 -0
  24. data/ext/qr_reader_native/re_spinel_utf8.inc +97 -0
  25. data/ext/qr_reader_native/re_utf8.c +156 -0
  26. data/ext/qr_reader_native/sp_alloc.c +1447 -0
  27. data/ext/qr_reader_native/sp_alloc.h +1196 -0
  28. data/ext/qr_reader_native/sp_argf.h +33 -0
  29. data/ext/qr_reader_native/sp_array.c +1052 -0
  30. data/ext/qr_reader_native/sp_array.h +456 -0
  31. data/ext/qr_reader_native/sp_bigint.c +5826 -0
  32. data/ext/qr_reader_native/sp_bigint.h +117 -0
  33. data/ext/qr_reader_native/sp_cold.c +5259 -0
  34. data/ext/qr_reader_native/sp_compat.h +402 -0
  35. data/ext/qr_reader_native/sp_core.c +818 -0
  36. data/ext/qr_reader_native/sp_core.h +56 -0
  37. data/ext/qr_reader_native/sp_crypto.c +877 -0
  38. data/ext/qr_reader_native/sp_crypto.h +169 -0
  39. data/ext/qr_reader_native/sp_dtoa.c +1533 -0
  40. data/ext/qr_reader_native/sp_dtoa.h +29 -0
  41. data/ext/qr_reader_native/sp_encoding_names.h +172 -0
  42. data/ext/qr_reader_native/sp_enum.h +86 -0
  43. data/ext/qr_reader_native/sp_exc.c +1185 -0
  44. data/ext/qr_reader_native/sp_exc.h +186 -0
  45. data/ext/qr_reader_native/sp_exc_ctx.h +56 -0
  46. data/ext/qr_reader_native/sp_fiber.c +889 -0
  47. data/ext/qr_reader_native/sp_fiber.h +162 -0
  48. data/ext/qr_reader_native/sp_fiber_ctx.h +84 -0
  49. data/ext/qr_reader_native/sp_format.c +695 -0
  50. data/ext/qr_reader_native/sp_format.h +83 -0
  51. data/ext/qr_reader_native/sp_gc.c +1793 -0
  52. data/ext/qr_reader_native/sp_gc.h +762 -0
  53. data/ext/qr_reader_native/sp_hash.c +131 -0
  54. data/ext/qr_reader_native/sp_hash.h +131 -0
  55. data/ext/qr_reader_native/sp_inspect.c +215 -0
  56. data/ext/qr_reader_native/sp_inspect.h +128 -0
  57. data/ext/qr_reader_native/sp_io.c +1676 -0
  58. data/ext/qr_reader_native/sp_io.h +272 -0
  59. data/ext/qr_reader_native/sp_iobuffer.c +1014 -0
  60. data/ext/qr_reader_native/sp_iobuffer.h +167 -0
  61. data/ext/qr_reader_native/sp_marshal.c +487 -0
  62. data/ext/qr_reader_native/sp_marshal.h +60 -0
  63. data/ext/qr_reader_native/sp_net.c +1029 -0
  64. data/ext/qr_reader_native/sp_net.h +205 -0
  65. data/ext/qr_reader_native/sp_pack.c +1254 -0
  66. data/ext/qr_reader_native/sp_poly_cold.c +1220 -0
  67. data/ext/qr_reader_native/sp_proc.c +410 -0
  68. data/ext/qr_reader_native/sp_proc.h +327 -0
  69. data/ext/qr_reader_native/sp_process.c +547 -0
  70. data/ext/qr_reader_native/sp_process_status.c +211 -0
  71. data/ext/qr_reader_native/sp_process_status.h +66 -0
  72. data/ext/qr_reader_native/sp_random.c +287 -0
  73. data/ext/qr_reader_native/sp_random.h +52 -0
  74. data/ext/qr_reader_native/sp_range.h +144 -0
  75. data/ext/qr_reader_native/sp_re.c +1379 -0
  76. data/ext/qr_reader_native/sp_re.h +186 -0
  77. data/ext/qr_reader_native/sp_re_names.h +59 -0
  78. data/ext/qr_reader_native/sp_sched.c +4044 -0
  79. data/ext/qr_reader_native/sp_sched.h +295 -0
  80. data/ext/qr_reader_native/sp_slab.c +1507 -0
  81. data/ext/qr_reader_native/sp_str.c +1462 -0
  82. data/ext/qr_reader_native/sp_str.h +306 -0
  83. data/ext/qr_reader_native/sp_str_crypt.c +23 -0
  84. data/ext/qr_reader_native/sp_string.c +92 -0
  85. data/ext/qr_reader_native/sp_string.h +253 -0
  86. data/ext/qr_reader_native/sp_system.c +78 -0
  87. data/ext/qr_reader_native/sp_system.h +18 -0
  88. data/ext/qr_reader_native/sp_time.c +860 -0
  89. data/ext/qr_reader_native/sp_time.h +100 -0
  90. data/ext/qr_reader_native/sp_types.h +292 -0
  91. data/ext/qr_reader_native/spinel_rt.h +19017 -0
  92. data/ext/qr_reader_native/unicase.c +399 -0
  93. data/ext/qr_reader_native/unicase.h +901 -0
  94. data/lib/qr_reader/native.rb +6 -0
  95. metadata +153 -0
@@ -0,0 +1,167 @@
1
+ #ifndef SP_IOBUFFER_H
2
+ #define SP_IOBUFFER_H
3
+ /* IO::Buffer -- a fixed-size byte buffer with typed numeric accessors,
4
+ following CRuby 4.x semantics (lowercase type symbols are little-endian,
5
+ uppercase big-endian; U8/S8 are single bytes). The class surface is bound
6
+ through the native-class DSL in packages/io/io/buffer.rb; the compiler
7
+ splices that file whenever a program references `IO::Buffer` (the same
8
+ implicit-require treatment `Set` gets), so the class is available without
9
+ an explicit require, as in CRuby.
10
+
11
+ Memory model: a buffer owns a malloc'd allocation (`data`), freed by the
12
+ GC finalizer. A slice owns nothing: it holds its root buffer in `source`
13
+ (marked by the GC scan hook, so the backing allocation outlives every
14
+ view) and resolves its base pointer through `source->data + off` on every
15
+ access -- a source resize can move the allocation and the views stay
16
+ valid, where CRuby's stored-pointer slices would dangle. */
17
+ #include "sp_types.h"
18
+ #include "sp_gc.h"
19
+ #include "sp_alloc.h"
20
+
21
+ /* Flag bits: the public constants share CRuby's values; SLICE is CRuby's
22
+ internal display-only flag (no public constant). */
23
+ #define SP_IOB_EXTERNAL 1u
24
+ #define SP_IOB_INTERNAL 2u
25
+ #define SP_IOB_MAPPED 4u
26
+ #define SP_IOB_SHARED 8u
27
+ #define SP_IOB_LOCKED 32u
28
+ #define SP_IOB_PRIVATE 64u
29
+ #define SP_IOB_READONLY 128u
30
+ #define SP_IOB_SLICE (1u << 16)
31
+
32
+ typedef struct sp_IOBuffer_s sp_IOBuffer;
33
+ struct sp_IOBuffer_s {
34
+ sp_int cls_id; /* object header: runtime class id, compiler-stamped */
35
+ uint8_t *data; /* owned allocation; NULL for null buffers and slices */
36
+ sp_IOBuffer *source; /* slice: the root (non-slice) buffer, GC-marked */
37
+ int64_t off; /* slice: byte offset into source */
38
+ int64_t size;
39
+ uint32_t flags;
40
+ void *map_base; /* IO::Buffer.map: the mmap'd region `data` points into
41
+ (page-aligned), NULL for a malloc'd allocation */
42
+ size_t map_len; /* ...and its length, for munmap */
43
+ };
44
+
45
+ /* The typed-accessor type enum. Order groups by width; *_BE are the
46
+ uppercase (big-endian) spellings. Mirrored by the SP_IOB_TY_* defines in
47
+ spinel_rt.h that the compiler's literal-symbol fast path emits. */
48
+ enum {
49
+ SP_IOB_TY_U8, SP_IOB_TY_S8,
50
+ SP_IOB_TY_u16, SP_IOB_TY_s16, SP_IOB_TY_U16, SP_IOB_TY_S16,
51
+ SP_IOB_TY_u32, SP_IOB_TY_s32, SP_IOB_TY_U32, SP_IOB_TY_S32,
52
+ SP_IOB_TY_u64, SP_IOB_TY_s64, SP_IOB_TY_U64, SP_IOB_TY_S64,
53
+ SP_IOB_TY_f32, SP_IOB_TY_f64, SP_IOB_TY_F32, SP_IOB_TY_F64,
54
+ SP_IOB_TY__COUNT
55
+ };
56
+
57
+ /* constructors (cls_id first: the compiler stamps the assigned class id) */
58
+ sp_IOBuffer *sp_IOBuffer_new(sp_int cls_id);
59
+ sp_IOBuffer *sp_IOBuffer_new_i(sp_int cls_id, sp_int size);
60
+ sp_IOBuffer *sp_IOBuffer_new_if(sp_int cls_id, sp_int size, sp_int flags);
61
+ void sp_IOBuffer_fin(void *p);
62
+
63
+ /* generic (runtime-dispatched type symbol) accessors */
64
+ sp_RbVal sp_IOBuffer_get_value(sp_IOBuffer *b, sp_RbVal type, sp_int off);
65
+ sp_int sp_IOBuffer_set_value(sp_IOBuffer *b, sp_RbVal type, sp_int off, sp_RbVal val);
66
+
67
+ /* typed fast paths, called by the compiler's literal-symbol lowering */
68
+ sp_int sp_IOBuffer_get_i(sp_IOBuffer *b, sp_int ty, sp_int off);
69
+ sp_RbVal sp_IOBuffer_get_x(sp_IOBuffer *b, sp_int ty, sp_int off);
70
+ double sp_IOBuffer_get_f(sp_IOBuffer *b, sp_int ty, sp_int off);
71
+ sp_int sp_IOBuffer_set_i(sp_IOBuffer *b, sp_int ty, sp_int off, sp_int v);
72
+ sp_int sp_IOBuffer_set_f(sp_IOBuffer *b, sp_int ty, sp_int off, double v);
73
+ sp_int sp_IOBuffer_set_v(sp_IOBuffer *b, sp_int ty, sp_int off, sp_RbVal v);
74
+
75
+ /* strings */
76
+ const char *sp_IOBuffer_get_string0(sp_IOBuffer *b);
77
+ const char *sp_IOBuffer_get_string1(sp_IOBuffer *b, sp_int off);
78
+ const char *sp_IOBuffer_get_string2(sp_IOBuffer *b, sp_int off, sp_int len);
79
+ sp_int sp_IOBuffer_set_string1(sp_IOBuffer *b, const char *s);
80
+ sp_int sp_IOBuffer_set_string2(sp_IOBuffer *b, const char *s, sp_int off);
81
+ sp_int sp_IOBuffer_set_string3(sp_IOBuffer *b, const char *s, sp_int off, sp_int len);
82
+ sp_int sp_IOBuffer_set_string4(sp_IOBuffer *b, const char *s, sp_int off, sp_int len, sp_int soff);
83
+
84
+ /* whole-buffer operations */
85
+ sp_int sp_IOBuffer_size(sp_IOBuffer *b);
86
+ sp_IOBuffer *sp_IOBuffer_resize(sp_IOBuffer *b, sp_int size);
87
+ sp_IOBuffer *sp_IOBuffer_clear0(sp_IOBuffer *b);
88
+ sp_IOBuffer *sp_IOBuffer_clear1(sp_IOBuffer *b, sp_int v);
89
+ sp_IOBuffer *sp_IOBuffer_clear2(sp_IOBuffer *b, sp_int v, sp_int off);
90
+ sp_IOBuffer *sp_IOBuffer_clear3(sp_IOBuffer *b, sp_int v, sp_int off, sp_int len);
91
+ sp_int sp_IOBuffer_copy1(sp_IOBuffer *b, sp_RbVal src);
92
+ sp_int sp_IOBuffer_copy2(sp_IOBuffer *b, sp_RbVal src, sp_int off);
93
+ sp_int sp_IOBuffer_copy3(sp_IOBuffer *b, sp_RbVal src, sp_int off, sp_int len);
94
+ sp_int sp_IOBuffer_copy4(sp_IOBuffer *b, sp_RbVal src, sp_int off, sp_int len, sp_int soff);
95
+ sp_IOBuffer *sp_IOBuffer_slice0(sp_IOBuffer *b);
96
+ sp_IOBuffer *sp_IOBuffer_slice1(sp_IOBuffer *b, sp_int off);
97
+ sp_IOBuffer *sp_IOBuffer_slice2(sp_IOBuffer *b, sp_int off, sp_int len);
98
+ sp_IOBuffer *sp_IOBuffer_transfer(sp_IOBuffer *b);
99
+ sp_IOBuffer *sp_IOBuffer_free_m(sp_IOBuffer *b);
100
+ sp_IOBuffer *sp_IOBuffer_dup_m(sp_IOBuffer *b);
101
+ sp_IOBuffer *sp_IOBuffer_become_for(sp_IOBuffer *b, const char *s);
102
+
103
+ /* comparison */
104
+ sp_int sp_IOBuffer_cmp(sp_IOBuffer *b, sp_RbVal other);
105
+ sp_bool sp_IOBuffer_eq(sp_IOBuffer *b, sp_RbVal other);
106
+
107
+ /* rendering */
108
+ const char *sp_IOBuffer_hexdump0(sp_IOBuffer *b);
109
+ const char *sp_IOBuffer_hexdump1(sp_IOBuffer *b, sp_int off);
110
+ const char *sp_IOBuffer_hexdump2(sp_IOBuffer *b, sp_int off, sp_int len);
111
+ const char *sp_IOBuffer_hexdump3(sp_IOBuffer *b, sp_int off, sp_int len, sp_int width);
112
+ const char *sp_IOBuffer_inspect(sp_IOBuffer *b);
113
+ const char *sp_IOBuffer_to_s(sp_IOBuffer *b);
114
+
115
+ /* predicates */
116
+ sp_bool sp_IOBuffer_null_p(sp_IOBuffer *b);
117
+ sp_bool sp_IOBuffer_empty_p(sp_IOBuffer *b);
118
+ sp_bool sp_IOBuffer_valid_p(sp_IOBuffer *b);
119
+ sp_bool sp_IOBuffer_external_p(sp_IOBuffer *b);
120
+ sp_bool sp_IOBuffer_internal_p(sp_IOBuffer *b);
121
+ sp_bool sp_IOBuffer_mapped_p(sp_IOBuffer *b);
122
+ sp_bool sp_IOBuffer_shared_p(sp_IOBuffer *b);
123
+ sp_bool sp_IOBuffer_locked_p(sp_IOBuffer *b);
124
+ sp_bool sp_IOBuffer_readonly_p(sp_IOBuffer *b);
125
+ sp_bool sp_IOBuffer_private_p(sp_IOBuffer *b);
126
+
127
+ /* bitwise */
128
+ sp_IOBuffer *sp_IOBuffer_and(sp_IOBuffer *b, sp_RbVal other);
129
+ sp_IOBuffer *sp_IOBuffer_or(sp_IOBuffer *b, sp_RbVal other);
130
+ sp_IOBuffer *sp_IOBuffer_xor(sp_IOBuffer *b, sp_RbVal other);
131
+ sp_IOBuffer *sp_IOBuffer_not(sp_IOBuffer *b);
132
+ sp_IOBuffer *sp_IOBuffer_and_ip(sp_IOBuffer *b, sp_RbVal other);
133
+ sp_IOBuffer *sp_IOBuffer_or_ip(sp_IOBuffer *b, sp_RbVal other);
134
+ sp_IOBuffer *sp_IOBuffer_xor_ip(sp_IOBuffer *b, sp_RbVal other);
135
+ sp_IOBuffer *sp_IOBuffer_not_ip(sp_IOBuffer *b);
136
+
137
+ /* locked { } support (the Ruby-side `locked` wraps these) */
138
+ sp_IOBuffer *sp_IOBuffer_lock(sp_IOBuffer *b);
139
+ sp_IOBuffer *sp_IOBuffer_unlock(sp_IOBuffer *b);
140
+
141
+ /* IO integration (#4474): one read(2) / write(2) / pread(2) / pwrite(2)
142
+ against a boxed IO handle, answering the byte count, 0 at EOF, or
143
+ -errno as CRuby does. `length` < 0 stands for nil (the rest of the
144
+ buffer from `offset`). The Ruby side holds the lock around them. */
145
+ sp_int sp_IOBuffer_read_io(sp_IOBuffer *b, sp_RbVal io, sp_int length, sp_int offset);
146
+ sp_int sp_IOBuffer_write_io(sp_IOBuffer *b, sp_RbVal io, sp_int length, sp_int offset);
147
+ sp_int sp_IOBuffer_pread_io(sp_IOBuffer *b, sp_RbVal io, sp_int from, sp_int length, sp_int offset);
148
+ sp_int sp_IOBuffer_pwrite_io(sp_IOBuffer *b, sp_RbVal io, sp_int from, sp_int length, sp_int offset);
149
+ /* IO::Buffer.map(file, size, offset, flags): the receiver (a fresh null
150
+ buffer) becomes a view of the file's bytes through mmap; `size` < 0
151
+ stands for nil (the file's size from `offset`). */
152
+ sp_IOBuffer *sp_IOBuffer_become_map(sp_IOBuffer *b, sp_RbVal io, sp_int size, sp_int offset, sp_int flags);
153
+
154
+ /* FFI: the base address C receives for a buffer in an ffi_func pointer
155
+ argument; `writing` is 0 only for :buffer_in. The _ptr form takes a boxed
156
+ argument and passes anything but an IO::Buffer through as its pointer. */
157
+ void *sp_IOBuffer_ffi_base(sp_IOBuffer *b, sp_int writing);
158
+ void *sp_IOBuffer_ffi_ptr(sp_RbVal v, sp_int cls_id, sp_int writing);
159
+ sp_int sp_IOBuffer_ffi_hold(sp_IOBuffer *b);
160
+ void sp_IOBuffer_ffi_release(sp_IOBuffer *b, sp_int m);
161
+ sp_int sp_IOBuffer_ffi_hold_v(sp_RbVal v, sp_int cls_id);
162
+ void sp_IOBuffer_ffi_release_v(sp_RbVal v, sp_int cls_id, sp_int m);
163
+
164
+ /* IO::Buffer::PAGE_SIZE (the mapped-allocation threshold) */
165
+ sp_int sp_IOBuffer_page_size(void);
166
+
167
+ #endif /* SP_IOBUFFER_H */
@@ -0,0 +1,487 @@
1
+ /* sp_marshal.c -- Marshal.dump / Marshal.load, split out of spinel_rt.h.
2
+
3
+ A standalone translation unit. The read side uses the generic sp_json_* hooks
4
+ (sp_gc.h); the construction side uses the sp_marshal_v vtable the generated TU
5
+ installs at startup (sp_tu_init). Result containers are built through vtable
6
+ wrappers and rooted with SP_GC_ROOT (now shared via sp_gc.h) so a nested
7
+ allocation during the parse can't free a partially-built value. CRuby 4.8 wire
8
+ format; see sp_marshal.h. */
9
+ #include "sp_marshal.h" /* sp_gc.h: sp_RbVal, hooks, SP_GC_ROOT, cls_ids */
10
+ #include "sp_alloc.h" /* sp_str_alloc_raw, sp_str_set_len, sp_str_byte_len, sp_float_to_s */
11
+ #include "sp_dtoa.h" /* sp_format_float / sp_read_float (locale-independent) */
12
+ #include "sp_string.h" /* sp_String: a shared String handle in a box */
13
+ #include <string.h>
14
+ #include <setjmp.h>
15
+ #include <math.h>
16
+
17
+ /* Bignum codec lives in lib/sp_bigint.c. */
18
+ typedef struct sp_Bigint sp_Bigint;
19
+ int sp_bigint_sign(sp_Bigint *b);
20
+ size_t sp_bigint_byte_len(sp_Bigint *b);
21
+ size_t sp_bigint_to_le_bytes(sp_Bigint *b, unsigned char *out, size_t cap);
22
+ sp_Bigint *sp_bigint_from_le_bytes(int negative, const unsigned char *bytes, size_t n);
23
+
24
+ /* Inline sp_RbVal constructors (state-free; avoid pulling spinel_rt.h). Heap
25
+ value types (array/hash/complex/rational) go through the vtable instead. */
26
+ static sp_RbVal mk_nil(void) { sp_RbVal r; r.tag = SP_TAG_NIL; r.cls_id = 0; r.v.i = 0; return r; }
27
+ static sp_RbVal mk_bool(sp_bool b) { sp_RbVal r; r.tag = SP_TAG_BOOL; r.cls_id = 0; r.v.i = (b != 0); return r; } /* full union word: bool equality reads v.i */
28
+ static sp_RbVal mk_int(sp_int n) { sp_RbVal r; r.tag = SP_TAG_INT; r.cls_id = 0; r.v.i = n; return r; }
29
+ static sp_RbVal mk_float(sp_float f){ sp_RbVal r; r.tag = SP_TAG_FLT; r.cls_id = 0; r.v.f = f; return r; }
30
+ static sp_RbVal mk_str(const char *s){ sp_RbVal r; r.tag = SP_TAG_STR; r.cls_id = 0; r.v.s = s; return r; }
31
+ static sp_RbVal mk_sym(sp_sym s) { sp_RbVal r; r.tag = SP_TAG_SYM; r.cls_id = 0; r.v.i = (sp_int)s; return r; }
32
+ static sp_RbVal mk_bigint(void *p) { sp_RbVal r; r.tag = SP_TAG_BIGINT; r.cls_id = 0; r.v.p = p; return r; }
33
+ static sp_float poly_f(sp_RbVal v) { return v.tag == SP_TAG_FLT ? v.v.f : (sp_float)v.v.i; }
34
+ static sp_int poly_i(sp_RbVal v) { return v.tag == SP_TAG_INT ? v.v.i : (sp_int)v.v.f; }
35
+
36
+ static void mar_raise(const char *cls, const char *msg) {
37
+ if (sp_marshal_v.raise) sp_marshal_v.raise(cls, msg);
38
+ }
39
+
40
+ /* ---- dump ---- */
41
+ static void sp_mar_raw(sp_mar_buf *b, const char *s, size_t n) {SP_GC_ROOT_STR(s); for (size_t i = 0; i < n; i++) sp_mar_b(b, (unsigned char)s[i]); }
42
+ void sp_mar_b(sp_mar_buf *b, unsigned char c) {
43
+ if (b->len >= b->cap) { b->cap = b->cap ? b->cap * 2 : 64; b->p = (char *)realloc(b->p, b->cap); if (!b->p) sp_oom_die(); }
44
+ b->p[b->len++] = (char)c;
45
+ }
46
+ /* CRuby's variable-length signed long encoding (Fixnums + every length/count). */
47
+ void sp_mar_long(sp_mar_buf *b, long n) {
48
+ if (n == 0) { sp_mar_b(b, 0); return; }
49
+ if (0 < n && n < 123) { sp_mar_b(b, (unsigned char)(n + 5)); return; }
50
+ if (-124 < n && n < 0) { sp_mar_b(b, (unsigned char)((n - 5) & 0xff)); return; }
51
+ unsigned char buf[1 + sizeof(long)];
52
+ long v = n; int i;
53
+ for (i = 1; i <= (int)sizeof(long); i++) {
54
+ buf[i] = (unsigned char)(v & 0xff);
55
+ v >>= 8;
56
+ if (v == 0) { buf[0] = (unsigned char)i; break; }
57
+ if (v == -1) { buf[0] = (unsigned char)(256 - i); break; }
58
+ }
59
+ sp_mar_raw(b, (char *)buf, (size_t)i + 1);
60
+ }
61
+ static void sp_mar_bytes(sp_mar_buf *b, const char *s, size_t n) {SP_GC_ROOT_STR(s); sp_mar_long(b, (long)n); sp_mar_raw(b, s, n); }
62
+ void sp_mar_sym(sp_mar_buf *b, const char *name) {SP_GC_ROOT_STR(name);
63
+ for (int i = 0; i < b->nws; i++)
64
+ if (!strcmp(b->wsyms[i], name)) { sp_mar_b(b, ';'); sp_mar_long(b, i); return; }
65
+ if (b->nws >= b->cws) { b->cws = b->cws ? b->cws * 2 : 8; b->wsyms = (char **)realloc(b->wsyms, sizeof(char *) * (size_t)b->cws); if (!b->wsyms) sp_oom_die(); }
66
+ b->wsyms[b->nws++] = strdup(name);
67
+ sp_mar_b(b, ':'); sp_mar_bytes(b, name, strlen(name));
68
+ }
69
+ /* CRuby's Marshal float text: the shortest %.g that round-trips, exponent
70
+ normalized ("1e+02" -> "1e2"); 0.0 is "0", 100.0 is "1e2", 0.1 is "0.1". */
71
+ static const char *sp_mar_float_str(double f, char *buf, size_t bufsz) {SP_GC_ROOT_STR(buf);
72
+ for (int prec = 1; prec <= 17; prec++) {
73
+ sp_format_float(f, buf, bufsz, 'g', prec, '\0'); /* locale-independent */
74
+ double rt = 0.0; sp_read_float(buf, NULL, &rt);
75
+ if (rt == f) break;
76
+ }
77
+ char *e = strchr(buf, 'e');
78
+ if (e) { /* drop '+' and leading exponent zeros */
79
+ char *d = e + 1; int neg = 0;
80
+ if (*d == '+') d++;
81
+ else if (*d == '-') { neg = 1; d++; }
82
+ while (*d == '0' && d[1]) d++;
83
+ char tail[32]; snprintf(tail, sizeof tail, "%s%s", neg ? "-" : "", d);
84
+ snprintf(e + 1, bufsz - (size_t)(e + 1 - buf), "%s", tail);
85
+ }
86
+ return buf;
87
+ }
88
+ /* If `ptr` (object identity; NULL for an identity-less linkable like Float) was
89
+ already written, emit `@id` and return 1; else reserve the next id, return 0. */
90
+ static int sp_mar_seen(sp_mar_buf *b, void *ptr) {
91
+ if (ptr) for (int i = 0; i < b->nl; i++) if (b->lptr[i] == ptr) { sp_mar_b(b, '@'); sp_mar_long(b, b->lid[i]); return 1; }
92
+ int id = b->link_next++;
93
+ if (ptr) {
94
+ if (b->nl >= b->cl) { b->cl = b->cl ? b->cl * 2 : 16; b->lptr = (void **)realloc(b->lptr, sizeof(void *) * (size_t)b->cl); b->lid = (int *)realloc(b->lid, sizeof(int) * (size_t)b->cl); }
95
+ b->lptr[b->nl] = ptr; b->lid[b->nl] = id; b->nl++;
96
+ }
97
+ return 0;
98
+ }
99
+ /* A Hash: `{` and its pairs, or, with a default value, `}`, the pairs and
100
+ the default after them, as CRuby writes it; a default proc cannot be
101
+ written (CRuby's TypeError). */
102
+ static void sp_mar_w_hash(sp_mar_buf *b, sp_RbVal v) {
103
+ int has_proc = 0;
104
+ sp_RbVal d = sp_marshal_v.hash_default ? sp_marshal_v.hash_default(v, &has_proc) : mk_nil();
105
+ if (has_proc) mar_raise("TypeError", "can't dump hash with default proc");
106
+ SP_GC_ROOT_RBVAL(d);
107
+ sp_mar_b(b, d.tag == SP_TAG_NIL ? '{' : '}');
108
+ sp_int n = sp_json_len_fn(v);
109
+ sp_mar_long(b, n);
110
+ for (sp_int i = 0; i < n; i++) {
111
+ sp_RbVal k, val; sp_json_hpair_fn(v, i, &k, &val);
112
+ sp_mar_w(b, k); sp_mar_w(b, val);
113
+ }
114
+ if (d.tag != SP_TAG_NIL) sp_mar_w(b, d);
115
+ }
116
+ void sp_mar_w_body(sp_mar_buf *b, sp_RbVal v) {
117
+ if (sp_json_kind_fn(v) == 2) { sp_mar_w_hash(b, v); return; }
118
+ sp_mar_b(b, '['); sp_int n = sp_json_len_fn(v); sp_mar_long(b, n);
119
+ for (sp_int i = 0; i < n; i++) sp_mar_w(b, sp_json_aref_fn(v, i));
120
+ }
121
+ void sp_mar_w(sp_mar_buf *b, sp_RbVal v) {
122
+ /* an Array or Hash subclass instance, boxed as its builtin (#7449): its
123
+ class's own record, `C` with the class, the builtin's body and the ivars
124
+ (sp_marshal_v.obj_dump), so it loads back as an instance of the class */
125
+ if (v.tag == SP_TAG_OBJ && sp_bsub_cls_fn) {
126
+ int k = sp_bsub_cls_fn(v);
127
+ if (k >= 0) {
128
+ if (sp_mar_seen(b, v.v.p)) return;
129
+ if (!sp_marshal_v.obj_dump || !sp_marshal_v.obj_dump(b, k, v.v.p))
130
+ mar_raise("TypeError", "no marshal_dump is defined for this object");
131
+ return;
132
+ }
133
+ }
134
+ switch (v.tag) {
135
+ case SP_TAG_NIL: sp_mar_b(b, '0'); break;
136
+ case SP_TAG_BOOL: sp_mar_b(b, v.v.b ? 'T' : 'F'); break;
137
+ case SP_TAG_INT: sp_mar_b(b, 'i'); sp_mar_long(b, (long)v.v.i); break;
138
+ case SP_TAG_FLT: {
139
+ if (sp_mar_seen(b, NULL)) break;
140
+ sp_mar_b(b, 'f');
141
+ sp_float f = v.v.f; const char *fs; char fbuf[64];
142
+ if (isnan(f)) fs = "nan";
143
+ else if (isinf(f)) fs = f < 0 ? "-inf" : "inf";
144
+ else fs = sp_mar_float_str((double)f, fbuf, sizeof fbuf);
145
+ sp_mar_bytes(b, fs, strlen(fs));
146
+ break;
147
+ }
148
+ case SP_TAG_SYM: sp_mar_sym(b, sp_sym_name_fn ? sp_sym_name_fn((sp_sym)v.v.i) : ""); break;
149
+ case SP_TAG_STR: {
150
+ const char *s = v.v.s ? v.v.s : "";
151
+ if (sp_mar_seen(b, (void *)(uintptr_t)s)) break;
152
+ /* CRuby wraps a String in an encoding ivar: I "<bytes>" 1 :E T. */
153
+ /* A binary String has no encoding ivar. */
154
+ int binary = sp_str_is_binary(v.v.s);
155
+ if (!binary) sp_mar_b(b, 'I');
156
+ sp_mar_b(b, '"'); sp_mar_bytes(b, s, sp_str_byte_len(s));
157
+ if (!binary) { sp_mar_long(b, 1); sp_mar_sym(b, "E"); sp_mar_b(b, 'T'); }
158
+ break;
159
+ }
160
+ case SP_TAG_OBJ:
161
+ if (v.cls_id == SP_BUILTIN_COMPLEX) {
162
+ if (sp_mar_seen(b, v.v.p)) break;
163
+ sp_float *c = (sp_float *)v.v.p; /* sp_Complex = {re, im} */
164
+ sp_mar_b(b, 'U'); sp_mar_sym(b, "Complex");
165
+ sp_mar_seen(b, NULL);
166
+ sp_mar_b(b, '['); sp_mar_long(b, 2);
167
+ sp_mar_w(b, mk_float(c[0])); sp_mar_w(b, mk_float(c[1]));
168
+ }
169
+ else if (v.cls_id == SP_BUILTIN_RATIONAL) {
170
+ if (sp_mar_seen(b, v.v.p)) break;
171
+ sp_int *q = (sp_int *)v.v.p; /* sp_Rational = {num, den} */
172
+ sp_mar_b(b, 'U'); sp_mar_sym(b, "Rational");
173
+ sp_mar_seen(b, NULL);
174
+ sp_mar_b(b, '['); sp_mar_long(b, 2);
175
+ sp_mar_w(b, mk_int(q[0])); sp_mar_w(b, mk_int(q[1]));
176
+ }
177
+ else {
178
+ int kind = sp_json_kind_fn ? sp_json_kind_fn(v) : 0;
179
+ if (kind == 1) { /* array */
180
+ if (sp_mar_seen(b, v.v.p)) break;
181
+ sp_mar_w_body(b, v);
182
+ }
183
+ else if (kind == 2) { /* hash */
184
+ if (sp_mar_seen(b, v.v.p)) break;
185
+ sp_mar_w_hash(b, v);
186
+ }
187
+ /* a shared String handle in a box (--share-strings, #6765): the
188
+ String it holds, with the handle as its identity */
189
+ else if (v.cls_id == SP_BUILTIN_STRBUF) {
190
+ if (sp_mar_seen(b, v.v.p)) break;
191
+ sp_String *h = (sp_String *)v.v.p;
192
+ int binary = h && h->binary;
193
+ if (!binary) sp_mar_b(b, 'I');
194
+ sp_mar_b(b, '"');
195
+ sp_mar_bytes(b, h && h->data ? h->data : "", h && h->data ? (size_t)h->len : 0);
196
+ if (!binary) { sp_mar_long(b, 1); sp_mar_sym(b, "E"); sp_mar_b(b, 'T'); }
197
+ }
198
+ else if (v.cls_id >= 0) { /* user object */
199
+ if (sp_mar_seen(b, v.v.p)) break;
200
+ if (!sp_marshal_v.obj_dump || !sp_marshal_v.obj_dump(b, v.cls_id, v.v.p))
201
+ mar_raise("TypeError", "no marshal_dump is defined for this object");
202
+ }
203
+ else mar_raise("TypeError", "no marshal_dump is defined for this object");
204
+ }
205
+ break;
206
+ case SP_TAG_BIGINT: {
207
+ if (sp_mar_seen(b, v.v.p)) break;
208
+ sp_Bigint *bg = (sp_Bigint *)v.v.p;
209
+ size_t cap = sp_bigint_byte_len(bg) + 1;
210
+ unsigned char *buf = (unsigned char *)calloc(cap + 1, 1);
211
+ size_t nbytes = sp_bigint_to_le_bytes(bg, buf, cap);
212
+ size_t nwords = (nbytes + 1) / 2; if (nwords == 0) nwords = 1;
213
+ sp_mar_b(b, 'l');
214
+ sp_mar_b(b, sp_bigint_sign(bg) < 0 ? '-' : '+');
215
+ sp_mar_long(b, (long)nwords);
216
+ for (size_t i = 0; i < nwords * 2; i++) sp_mar_b(b, i < nbytes ? buf[i] : 0); /* LE, padded */
217
+ free(buf);
218
+ break;
219
+ }
220
+ default:
221
+ mar_raise("TypeError", "unsupported value for Marshal.dump");
222
+ }
223
+ }
224
+ const char *sp_marshal_dump(sp_RbVal v) {
225
+ SP_GC_ROOT_RBVAL(v); /* a Float component allocates via sp_float_to_s -> may GC */
226
+ sp_mar_buf b; memset(&b, 0, sizeof b);
227
+ sp_mar_b(&b, 4); sp_mar_b(&b, 8); /* major.minor = 4.8 */
228
+ sp_mar_w(&b, v);
229
+ char *out = sp_str_alloc_raw(b.len + 1);
230
+ memcpy(out, b.p, b.len); out[b.len] = 0;
231
+ sp_str_set_len(out, b.len);
232
+ /* a marshalled stream is bytes, and CRuby names it ASCII-8BIT: without the
233
+ tag #length counts UTF-8 units over binary data and [] slices by them */
234
+ sp_str_mark_binary(out);
235
+ for (int i = 0; i < b.nws; i++) free(b.wsyms[i]);
236
+ free(b.wsyms);
237
+ free(b.p); free(b.lptr); free(b.lid);
238
+ return out;
239
+ }
240
+
241
+ /* ---- load ---- */
242
+ typedef struct sp_mar_rd_s {
243
+ const char *s; size_t pos, len;
244
+ char **syms; int nsym, csym; /* symbol-link table (`;`) */
245
+ sp_RbVal *objs; int nobj, cobj; /* object-link table (`@`) */
246
+ struct sp_mar_rd_s *prev; /* enclosing load, for a reentrant one */
247
+ } sp_mar_rd;
248
+ /* The object-link table holds every object built so far, so a `@` back-
249
+ reference can return it -- and it is a malloc array, which the collector
250
+ does not walk. Without this the parse is a long run of allocations with its
251
+ own results reachable from nothing: under a minor mark they are freed and
252
+ the next back-reference reads a corpse. Published here and marked through
253
+ the collector's globals hook, which is the same treatment the regex globals
254
+ get. Reentrant loads chain through `prev`. */
255
+ static sp_mar_rd *sp_mar_active = NULL;
256
+ void sp_marshal_mark_active(void) {
257
+ for (sp_mar_rd *r = sp_mar_active; r; r = r->prev) {
258
+ sp_mark_string(r->s);
259
+ for (int i = 0; i < r->nobj; i++) sp_mark_rbval(r->objs[i]);
260
+ }
261
+ }
262
+ static unsigned char sp_mar_rb(sp_mar_rd *r) { return r->pos < r->len ? (unsigned char)r->s[r->pos++] : 0; }
263
+ static long sp_mar_rlong(sp_mar_rd *r) {
264
+ int c = (signed char)sp_mar_rb(r);
265
+ if (c == 0) return 0;
266
+ if (c > 0) {
267
+ if (4 < c && c < 128) return (long)c - 5;
268
+ long x = 0;
269
+ for (int i = 0; i < c && i < (int)sizeof(long); i++) x |= (long)sp_mar_rb(r) << (8 * i);
270
+ return x;
271
+ }
272
+ if (-129 < c && c < -4) return (long)c + 5;
273
+ c = -c;
274
+ long x = -1;
275
+ for (int i = 0; i < c && i < (int)sizeof(long); i++) {
276
+ x &= ~((long)0xff << (8 * i));
277
+ x |= (long)sp_mar_rb(r) << (8 * i);
278
+ }
279
+ return x;
280
+ }
281
+ static char *sp_mar_rstr(sp_mar_rd *r, long n) {
282
+ if (n < 0) n = 0;
283
+ char *out = sp_str_alloc_raw((size_t)n + 1);
284
+ for (long i = 0; i < n; i++) out[i] = (char)sp_mar_rb(r);
285
+ out[n] = 0;
286
+ sp_str_set_len(out, (size_t)n);
287
+ return out;
288
+ }
289
+ /* Reserve the next object-link id (nil placeholder so a self-ref resolves). */
290
+ static int sp_mar_reg(sp_mar_rd *r) {
291
+ if (r->nobj >= r->cobj) { r->cobj = r->cobj ? r->cobj * 2 : 16; r->objs = (sp_RbVal *)realloc(r->objs, sizeof(sp_RbVal) * (size_t)r->cobj); }
292
+ int id = r->nobj; r->objs[r->nobj++] = mk_nil(); return id;
293
+ }
294
+ static sp_sym mar_intern(const char *name) { return sp_marshal_v.sym_intern ? sp_marshal_v.sym_intern(name) : 0; }
295
+ static sp_RbVal sp_mar_r(sp_mar_rd *r);
296
+ /* `C`: an instance of a user subclass of Array or Hash (#7449), its class,
297
+ the builtin's record, and with `ivars` (an `I` around it) the ivars after
298
+ it. The class's generated loader (sp_marshal_v.obj_load, as for an `o`
299
+ object) makes the instance, which takes the record's link id before the
300
+ elements are read, so one that holds itself loads holding itself; the
301
+ elements go into its builtin through the kind's own push or store, and
302
+ the loader then sets the ivars by name. */
303
+ static sp_RbVal sp_mar_r_bsub(sp_mar_rd *r, int ivars) {
304
+ sp_RbVal clssym = sp_mar_r(r);
305
+ const char *cn = (clssym.tag == SP_TAG_SYM && sp_sym_name_fn) ? sp_sym_name_fn((sp_sym)clssym.v.i) : "";
306
+ sp_RbVal iv = sp_marshal_v.arr_new(); SP_GC_ROOT_RBVAL(iv);
307
+ int ok = 0;
308
+ sp_RbVal v = sp_marshal_v.obj_load ? sp_marshal_v.obj_load(cn, mk_nil(), iv, &ok) : mk_nil();
309
+ if (!ok || !sp_bsub_cls_fn || sp_bsub_cls_fn(v) < 0) mar_raise("ArgumentError", "undefined class/module in Marshal.load");
310
+ SP_GC_ROOT_RBVAL(v);
311
+ int kind = sp_json_kind_fn ? sp_json_kind_fn(v) : 0;
312
+ unsigned char t = sp_mar_rb(r);
313
+ if (!((t == '[' && kind == 1) || (t == '{' && kind == 2))) mar_raise("ArgumentError", "dump format error (user class)");
314
+ int id = sp_mar_reg(r);
315
+ r->objs[id] = v;
316
+ long len = sp_mar_rlong(r);
317
+ for (long i = 0; i < len; i++) {
318
+ if (kind == 1) sp_marshal_v.any_push(v, sp_mar_r(r));
319
+ else { sp_RbVal k = sp_mar_r(r); SP_GC_ROOT_RBVAL(k); sp_marshal_v.hash_set(v, k, sp_mar_r(r)); }
320
+ }
321
+ long n = ivars ? sp_mar_rlong(r) : 0;
322
+ for (long i = 0; i < n; i++) {
323
+ sp_marshal_v.arr_push(iv, sp_mar_r(r)); /* ivar symbol */
324
+ sp_marshal_v.arr_push(iv, sp_mar_r(r)); /* ivar value */
325
+ }
326
+ if (n) sp_marshal_v.obj_load(cn, v, iv, &ok);
327
+ return v;
328
+ }
329
+ static sp_RbVal sp_mar_r(sp_mar_rd *r) {
330
+ unsigned char t = sp_mar_rb(r);
331
+ switch (t) {
332
+ case '0': return mk_nil();
333
+ case 'T': return mk_bool(TRUE);
334
+ case 'F': return mk_bool(FALSE);
335
+ case 'i': return mk_int((sp_int)sp_mar_rlong(r));
336
+ case 'f': {
337
+ int id = sp_mar_reg(r);
338
+ long n = sp_mar_rlong(r);
339
+ char *s = sp_mar_rstr(r, n); SP_GC_ROOT(s);
340
+ sp_float f;
341
+ if (!strcmp(s, "inf")) f = (sp_float)INFINITY;
342
+ else if (!strcmp(s, "-inf")) f = -(sp_float)INFINITY;
343
+ else if (!strcmp(s, "nan")) f = (sp_float)NAN;
344
+ else f = strtod(s, NULL);
345
+ sp_RbVal v = mk_float(f); r->objs[id] = v; return v;
346
+ }
347
+ case ':': {
348
+ long n = sp_mar_rlong(r);
349
+ char *name = sp_mar_rstr(r, n); SP_GC_ROOT(name);
350
+ if (r->nsym >= r->csym) { r->csym = r->csym ? r->csym * 2 : 8; r->syms = (char **)realloc(r->syms, sizeof(char *) * (size_t)r->csym); }
351
+ r->syms[r->nsym++] = strdup(name);
352
+ return mk_sym(mar_intern(name));
353
+ }
354
+ case ';': {
355
+ long idx = sp_mar_rlong(r);
356
+ const char *name = (idx >= 0 && idx < r->nsym) ? r->syms[idx] : "";
357
+ return mk_sym(mar_intern(name));
358
+ }
359
+ case '"': {
360
+ int id = sp_mar_reg(r);
361
+ sp_RbVal v = mk_str(sp_mar_rstr(r, sp_mar_rlong(r)));
362
+ r->objs[id] = v; return v;
363
+ }
364
+ case 'I': {
365
+ if (r->pos < r->len && r->s[r->pos] == 'C') { r->pos++; return sp_mar_r_bsub(r, 1); }
366
+ sp_RbVal inner = sp_mar_r(r);
367
+ long nivar = sp_mar_rlong(r);
368
+ for (long i = 0; i < nivar; i++) { sp_mar_r(r); sp_mar_r(r); } /* encoding ivar: ignored */
369
+ return inner;
370
+ }
371
+ case 'C': return sp_mar_r_bsub(r, 0);
372
+ case '@': {
373
+ long id = sp_mar_rlong(r);
374
+ return (id >= 0 && id < r->nobj) ? r->objs[id] : mk_nil();
375
+ }
376
+ case 'U': {
377
+ int id = sp_mar_reg(r);
378
+ sp_RbVal sym = sp_mar_r(r);
379
+ sp_RbVal data = sp_mar_r(r); SP_GC_ROOT_RBVAL(data);
380
+ const char *cn = (sym.tag == SP_TAG_SYM && sp_sym_name_fn) ? sp_sym_name_fn((sp_sym)sym.v.i) : "";
381
+ sp_RbVal a0 = mk_nil(), a1 = mk_nil();
382
+ if (data.tag == SP_TAG_OBJ) { a0 = sp_json_aref_fn(data, 0); a1 = sp_json_aref_fn(data, 1); }
383
+ sp_RbVal v;
384
+ if (!strcmp(cn, "Complex")) v = sp_marshal_v.box_complex(poly_f(a0), poly_f(a1));
385
+ else if (!strcmp(cn, "Rational")) v = sp_marshal_v.box_rational(poly_i(a0), poly_i(a1));
386
+ else { mar_raise("ArgumentError", "unsupported user class in Marshal.load"); v = mk_nil(); }
387
+ r->objs[id] = v; return v;
388
+ }
389
+ case 'o': {
390
+ /* the object is made and registered first, so an ivar that links back
391
+ to it loads as the object itself; then its ivars are set */
392
+ int id = sp_mar_reg(r);
393
+ sp_RbVal clssym = sp_mar_r(r);
394
+ const char *cn = (clssym.tag == SP_TAG_SYM && sp_sym_name_fn) ? sp_sym_name_fn((sp_sym)clssym.v.i) : "";
395
+ sp_RbVal iv = sp_marshal_v.arr_new(); SP_GC_ROOT_RBVAL(iv);
396
+ int ok = 0;
397
+ sp_RbVal v = sp_marshal_v.obj_load(cn, mk_nil(), iv, &ok);
398
+ if (!ok) mar_raise("ArgumentError", "undefined class/module in Marshal.load");
399
+ SP_GC_ROOT_RBVAL(v);
400
+ r->objs[id] = v;
401
+ long n = sp_mar_rlong(r);
402
+ for (long i = 0; i < n; i++) {
403
+ sp_marshal_v.arr_push(iv, sp_mar_r(r)); /* ivar symbol */
404
+ sp_marshal_v.arr_push(iv, sp_mar_r(r)); /* ivar value */
405
+ }
406
+ if (n) sp_marshal_v.obj_load(cn, v, iv, &ok);
407
+ return v;
408
+ }
409
+ case 'l': {
410
+ int id = sp_mar_reg(r);
411
+ char sign = (char)sp_mar_rb(r);
412
+ long nwords = sp_mar_rlong(r);
413
+ size_t nbytes = (size_t)(nwords > 0 ? nwords : 0) * 2;
414
+ unsigned char *buf = (unsigned char *)malloc(nbytes ? nbytes : 1);
415
+ for (size_t i = 0; i < nbytes; i++) buf[i] = sp_mar_rb(r);
416
+ sp_Bigint *bn = sp_bigint_from_le_bytes(sign == '-', buf, nbytes);
417
+ free(buf);
418
+ sp_RbVal v = mk_bigint(bn); r->objs[id] = v; return v;
419
+ }
420
+ case '[': {
421
+ int id = sp_mar_reg(r);
422
+ long n = sp_mar_rlong(r);
423
+ sp_RbVal box = sp_marshal_v.arr_new(); SP_GC_ROOT_RBVAL(box);
424
+ r->objs[id] = box;
425
+ for (long i = 0; i < n; i++) sp_marshal_v.arr_push(box, sp_mar_r(r));
426
+ return box;
427
+ }
428
+ case '{': case '}': { /* `}`: the pairs, then the default value */
429
+ int id = sp_mar_reg(r);
430
+ long n = sp_mar_rlong(r);
431
+ sp_RbVal box = sp_marshal_v.hash_new(); SP_GC_ROOT_RBVAL(box);
432
+ r->objs[id] = box;
433
+ for (long i = 0; i < n; i++) { sp_RbVal k = sp_mar_r(r); sp_RbVal val = sp_mar_r(r); sp_marshal_v.hash_set(box, k, val); }
434
+ if (t == '}') {
435
+ sp_RbVal d = sp_mar_r(r);
436
+ if (sp_marshal_v.hash_set_default) sp_marshal_v.hash_set_default(box, d);
437
+ }
438
+ return box;
439
+ }
440
+ default: mar_raise("ArgumentError", "unsupported type in Marshal.load"); return mk_nil();
441
+ }
442
+ }
443
+ /* The exception hooks the generated unit defines (lib/spinel_rt.h). */
444
+ void sp_exc_arm(jmp_buf b);
445
+ void sp_exc_disarm(void);
446
+ const char *sp_exc_cur_cls(void);
447
+ const char *sp_exc_cur_msg(void);
448
+ void *sp_exc_cur_obj(void);
449
+ void sp_fiber_reraise(const char *cls, const char *msg, void *obj);
450
+ /* A reader leaves the active chain and frees its tables. */
451
+ static void sp_mar_rd_done(sp_mar_rd *r) {
452
+ sp_mar_active = r->prev;
453
+ for (int i = 0; i < r->nsym; i++) free(r->syms[i]);
454
+ free(r->syms); free(r->objs); free(r);
455
+ }
456
+ sp_RbVal sp_marshal_load(const char *s, sp_int len) {
457
+ /* The whole parse reads out of this buffer, and every object it builds
458
+ allocates -- so the source string has to stay rooted for the duration.
459
+ r->s is a plain field, not a root slot; the collector walks registered
460
+ slots, so the parameter is what has to be registered. */
461
+ SP_GC_ROOT_STR(s);
462
+ sp_mar_rd *r = (sp_mar_rd *)calloc(1, sizeof *r);
463
+ r->s = s ? s : ""; r->len = s ? (size_t)len : 0;
464
+ r->prev = sp_mar_active; sp_mar_active = r;
465
+ if (r->len >= 2) r->pos = 2; /* skip the 4.8 version header */
466
+ /* A raise inside the parse (an unknown class, a format error, a value a
467
+ loader cannot convert) longjmps past the return below, and the reader
468
+ stayed on the active chain: the next collection walked its freed
469
+ frame. It is caught here, the reader taken off the chain, and raised
470
+ on to the program's own handler. */
471
+ int nroots = sp_gc_nroots;
472
+ jmp_buf jb;
473
+ if (setjmp(jb) == 0) {
474
+ sp_exc_arm(jb);
475
+ sp_RbVal v = sp_mar_r(r);
476
+ sp_exc_disarm();
477
+ sp_mar_rd_done(r);
478
+ return v;
479
+ }
480
+ const char *cls = sp_exc_cur_cls(), *msg = sp_exc_cur_msg();
481
+ void *obj = sp_exc_cur_obj();
482
+ sp_exc_disarm();
483
+ sp_gc_nroots = nroots; /* the parse's own roots unwound with it */
484
+ sp_mar_rd_done(r);
485
+ sp_fiber_reraise(cls, msg, obj);
486
+ return mk_nil();
487
+ }