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,100 @@
1
+ /* sp_time.h -- Time value type + libc-backed accessors / formatters.
2
+ *
3
+ * The whole of spinel's Time implementation lives in lib/sp_time.c
4
+ * (compiled into libspinel_rt.a), including the formatters, which now
5
+ * return GC-heap strings directly (sp_time.c includes sp_alloc.h) -- so
6
+ * the generated TU calls them straight, with no buffer-copying
7
+ * trampoline in spinel_rt.h.
8
+ *
9
+ * The value ops (constructors, accessors, shifts) use int64_t / double
10
+ * directly, keeping that part decoupled from the runtime's typedefs.
11
+ */
12
+ #ifndef SP_TIME_H
13
+ #define SP_TIME_H
14
+
15
+ #include <stddef.h>
16
+ #include <stdint.h>
17
+ #include <time.h>
18
+
19
+ /* is_utc is a 3-state zone kind: 0 = host-local zone, 1 = UTC, 2 = a fixed
20
+ utc_off-seconds offset (Time.new's 7th argument). utc_off is meaningful
21
+ only for kind 2; the positional (sp_Time){sec, ns, kind} initializers used
22
+ throughout leave it zero. */
23
+ typedef struct { int64_t tv_sec; int32_t tv_nsec; int8_t is_utc; int32_t utc_off; } sp_Time;
24
+ /* Seconds as a Float the way CRuby's Time#to_f and Time#- make them: the
25
+ whole nanosecond count converted, then divided by 1e9. That count is not
26
+ always a double, so the answer can sit just below the decimal value
27
+ (1790544731.561 reads 1790544731.5609999), and an epoch-milliseconds
28
+ `(t.to_f * 1000).to_i` depends on it (#5211). Past Fixnum's reach
29
+ (2**62 ns) CRuby rounds the exact quotient, which the split sum is the
30
+ nearest to here. */
31
+ static inline double sp_time_ns_to_f(int64_t sec, int64_t nsec) {
32
+ if (sec > -4611686018LL && sec < 4611686018LL)
33
+ return (double)(sec * 1000000000LL + nsec) / 1e9;
34
+ return (double)sec + (double)nsec / 1e9;
35
+ }
36
+
37
+ /* Constructors */
38
+ sp_Time sp_time_now(void);
39
+ sp_Time sp_time_at_int(int64_t sec);
40
+ sp_Time sp_time_norm(sp_Time t); /* carry a whole second out of tv_nsec */
41
+ sp_Time sp_time_add_nsec(sp_Time t, int64_t ns);
42
+ int64_t sp_time_zone_arg_off(const char *z, int *is_utc_out);
43
+ sp_Time sp_time_in_zone_i(sp_Time t, int64_t off);
44
+ sp_Time sp_time_in_zone_s(sp_Time t, const char *z);
45
+ sp_Time sp_time_at_float(double epoch);
46
+ sp_Time sp_time_at_div(int64_t num, int64_t den);
47
+ sp_Time sp_time_parse(const char *s);
48
+ sp_Time sp_time_new(int64_t y, int64_t mo, int64_t d, int64_t h, int64_t mi, int64_t s);
49
+ int64_t sp_time_month_arg(const char *s);
50
+ sp_Time sp_time_new_utc(int64_t y, int64_t mo, int64_t d, int64_t h, int64_t mi, int64_t s);
51
+ sp_Time sp_time_new_off(int64_t y, int64_t mo, int64_t d, int64_t h, int64_t mi, int64_t s, int64_t off);
52
+ sp_Time sp_time_with_usec(sp_Time t, int64_t usec);
53
+ sp_Time sp_time_with_usec_f(sp_Time t, double usec);
54
+ sp_Time sp_time_utc(sp_Time t);
55
+ sp_Time sp_time_localtime(sp_Time t);
56
+
57
+ /* Broken-down resolver: is_utc selects gmtime vs localtime, off is the
58
+ UTC offset in seconds, zbuf is the timezone abbreviation (8 bytes). */
59
+ void sp_time_vtm(sp_Time t, struct tm *bd, int32_t *off, char *zbuf);
60
+
61
+ /* Field accessors */
62
+ int64_t sp_time_year(sp_Time t);
63
+ int64_t sp_time_mon(sp_Time t);
64
+ int64_t sp_time_mday(sp_Time t);
65
+ int64_t sp_time_hour(sp_Time t);
66
+ int64_t sp_time_min(sp_Time t);
67
+ int64_t sp_time_sec(sp_Time t);
68
+ int64_t sp_time_wday(sp_Time t);
69
+ int64_t sp_time_yday(sp_Time t);
70
+ int64_t sp_time_isdst(sp_Time t);
71
+ int64_t sp_time_utc_offset(sp_Time t);
72
+ /* Time#floor / #ceil / #round: `ndigits` decimal places of the subsecond part,
73
+ with mode 0 = floor, 1 = ceil, 2 = round-half-up. ndigits 0 is the no-argument
74
+ form (whole seconds) and >= 9 keeps the full nanosecond resolution; a carry
75
+ past a second bumps tv_sec. A negative ndigits is CRuby's ArgumentError. */
76
+ sp_Time sp_time_round_to(sp_Time t, int64_t ndigits, int mode);
77
+
78
+ /* Time + Numeric / Time - Numeric. secs may be fractional. */
79
+ sp_Time sp_time_add(sp_Time t, double secs);
80
+
81
+ /* Comparison + integer/float shifts + Time-Time difference (cold value ops). */
82
+ int sp_time_cmp(sp_Time a, sp_Time b);
83
+ int64_t sp_time_hash(sp_Time t);
84
+ sp_Time sp_time_add_f(sp_Time t, double secs);
85
+ sp_Time sp_time_add_i(sp_Time t, int64_t secs);
86
+ sp_Time sp_time_sub_i(sp_Time t, int64_t secs);
87
+ double sp_time_sub_t(sp_Time a, sp_Time b);
88
+
89
+ /* Formatters: return a GC-heap string (from sp_alloc.h), "" on failure. */
90
+ const char *sp_time_strftime(sp_Time t, const char *fmt);
91
+ const char *sp_time_iso8601(sp_Time t);
92
+ const char *sp_time_httpdate(sp_Time t);
93
+ const char *sp_time_rfc2822(sp_Time t);
94
+ const char *sp_time_iso8601_frac(sp_Time t, int64_t digits);
95
+ sp_Time sp_time_getlocal_off(sp_Time t, int64_t off);
96
+ const char *sp_time_zone(sp_Time t);
97
+ const char *sp_time_inspect_v(sp_Time t); /* renders fractional seconds */
98
+ const char *sp_time_to_s_v(sp_Time t); /* whole seconds only (Time#to_s) */
99
+
100
+ #endif
@@ -0,0 +1,292 @@
1
+ /* sp_types.h -- core value-type definitions split out of spinel_rt.h.
2
+ *
3
+ * Holds the primitive typedefs, the small leaf value structs, the GC
4
+ * header, and the typed array / non-poly hash structs. Both
5
+ * spinel_rt.h (which includes this near the top) and libspinel_rt.a
6
+ * sources can include it to see the layouts without pulling in the
7
+ * header's static/inline function bodies. Pure type/macro definitions
8
+ * only -- no function definitions, no global state.
9
+ *
10
+ * Reorganisation step toward slimming spinel_rt.h; sp_RbVal, the poly
11
+ * containers, and the conditional Proc/Fiber/etc. types still live in
12
+ * spinel_rt.h pending a later pass.
13
+ */
14
+ #ifndef SP_TYPES_H
15
+ #define SP_TYPES_H
16
+
17
+ #ifdef __APPLE__
18
+ /* _DARWIN_C_SOURCE re-enables Darwin extensions (MAP_ANON, used by the fiber
19
+ stack mmap in sp_fiber.c) that a strict POSIX build would otherwise hide.
20
+ Define it here -- the lowest shared base header -- so every TU that pulls
21
+ sp_types.h in sets it before the first system header.
22
+ No _XOPEN_SOURCE / -Wdeprecated-declarations gate is needed: the portable
23
+ asm context switch runs on every Darwin arch (x86_64 / arm64), so the only
24
+ <ucontext.h> include (sp_fiber_ctx.h's non-asm fallback) is never compiled
25
+ there, and PR #1563 removed the stale unconditional include from
26
+ spinel_rt.h. */
27
+ #define _DARWIN_C_SOURCE
28
+ #endif
29
+
30
+ #include "sp_compat.h" /* the GNU extensions behind macros; first, it sets them all */
31
+ #include <stdint.h>
32
+ #include <stdbool.h>
33
+ #include <stddef.h>
34
+ #include <float.h> /* DBL_MAX / DBL_MIN / DBL_EPSILON for Float::* constants */
35
+
36
+ #include <setjmp.h>
37
+ #ifdef __APPLE__
38
+ /* Darwin's setjmp saves the signal mask, a sigprocmask syscall on every
39
+ begin/rescue. Nothing here needs the mask restored (glibc's longjmp doesn't
40
+ restore it either), so use the _ variants. Here so the generated TU and the
41
+ runtime agree on the pair. */
42
+ #define setjmp(env) _setjmp(env)
43
+ #define longjmp(env, val) _longjmp(env, val)
44
+ #endif
45
+
46
+ /* Per-worker storage under true parallelism. In the -DSP_THREADS runtime
47
+ variant (and the generated TU when the program uses threads, compiled with
48
+ the same define) the per-thread execution state -- the GC root stack, the
49
+ current fiber, the match registers -- is thread-local so each OS worker keeps
50
+ its own. The single-threaded build leaves these plain globals, byte-identical
51
+ to before. Both sides must agree on the storage class, so SP_TLS lives in
52
+ this shared base header. */
53
+ #ifdef SP_THREADS
54
+ # ifdef SP_NO_THREAD_LOCAL
55
+ # error "spinel: the threaded runtime (SP_THREADS) needs thread-local storage"
56
+ # endif
57
+ # define SP_TLS SP_THREAD_LOCAL
58
+ #else
59
+ # define SP_TLS
60
+ #endif
61
+
62
+ /* Worker identity, shared by the allocators (per-worker string/object heaps) and
63
+ the scheduler that owns it. In the base header so sp_gc.h's SP_GC_HEAP_PUSH --
64
+ included ahead of sp_alloc.h -- can index the per-worker object heap. */
65
+ #ifdef SP_THREADS
66
+ # ifndef SP_MAX_WORKERS
67
+ # define SP_MAX_WORKERS 256
68
+ # endif
69
+ extern SP_TLS int sp_worker_id; /* this OS worker's slot (0 = main); sp_sched.c */
70
+ extern int sp_active_workers; /* live worker count; sp_sched.c */
71
+ #endif
72
+
73
+ /* Branch-hint / hot-cold attributes (SP_LIKELY, SP_UNLIKELY, SP_COLD,
74
+ SP_NOINLINE, SP_INLINE, SP_NORETURN) live in sp_compat.h. Static
75
+ approximation of PGO: marking rare paths (raise, dispatch fallbacks) cold
76
+ lets the C compiler split them out of the hot path's i-cache footprint. */
77
+
78
+ /* sp_int follows pointer width (decided at compile time via intptr_t):
79
+ int64_t on 64-bit hosts -- PCs, no behavior change -- and int32_t on
80
+ 32-bit embedded targets, where it gives native-word arithmetic, half
81
+ the memory for every integer/array/hash slot, and a pointer-width
82
+ sp_RbVal union. The two paths differ only in that the 32-bit build
83
+ overflows / narrows foreign 64-bit values (e.g. Time#to_i) at
84
+ INT32 limits; see the overflow-mode handling. */
85
+ #if INTPTR_MAX == INT64_MAX
86
+ #elif INTPTR_MAX == INT32_MAX
87
+ #else
88
+ #error "spinel: unsupported intptr_t width (need 32- or 64-bit)"
89
+ #endif
90
+ typedef intptr_t sp_int;
91
+ typedef double sp_float;
92
+ typedef bool sp_bool;
93
+
94
+ /* CRuby's nil.object_id, false.object_id and true.object_id. */
95
+ #define SP_NIL_OBJECT_ID 4
96
+ #define SP_FALSE_OBJECT_ID 0
97
+ #define SP_TRUE_OBJECT_ID 20
98
+
99
+ /* Sentinel value reserved by the int? (scalar-nullable int) type. An
100
+ int? slot is bit-compatible with sp_int; SP_INT_NIL marks the
101
+ "nil" inhabitant. The pattern is INTPTR_MIN -- INT64_MIN on 64-bit
102
+ (unchanged), INT32_MIN on 32-bit.
103
+ `sp_int_is_nil(v)` is the canonical predicate; treat any int? value
104
+ produced by runtime helpers as opaque outside this macro.
105
+
106
+ KNOWN LIMITATION (32-bit builds only). The reservation is a single
107
+ bit pattern, so a *genuine* integer equal to the sentinel is
108
+ indistinguishable from nil. On 64-bit, INT64_MIN is effectively
109
+ unreachable in practice (CRuby would have promoted it to Bignum), so
110
+ this never bites. On 32-bit, INT32_MIN (-2147483648) is an ordinary
111
+ reachable Integer, so a real -2147483648 flowing into an int? slot
112
+ reads back as nil -- e.g. `[-2147483648].pop` yields nil instead of
113
+ the value. This affects ONLY int? (nullable-int) slots; a plain
114
+ (non-nullable) int holding -2147483648 is fine, since it never
115
+ consults sp_int_is_nil. The integer-overflow helpers deliberately do
116
+ NOT reserve this value (checking every add/sub/mul result against it
117
+ would cost the hot path the embedded build is trying to save). Code that
118
+ must store -2147483648 nullably on 32-bit should box it (poly) rather
119
+ than use a flat int? slot. */
120
+ #define SP_INT_NIL ((sp_int)INTPTR_MIN)
121
+ #define sp_int_is_nil(v) ((v) == SP_INT_NIL)
122
+
123
+ /* Nullable float (float?) sentinel: a quiet NaN with a reserved payload.
124
+ NaN != NaN, so nil is detected by bit pattern, not ==. The payload is
125
+ chosen so the canonical NaN (0x7FF8000000000000) and ordinary
126
+ arithmetic NaNs don't collide; a real Float element with this exact
127
+ bit pattern reads back as nil -- the same documented compromise
128
+ SP_INT_NIL makes for INTPTR_MIN. sp_float is double (8 bytes). */
129
+ #define SP_FLOAT_NIL_BITS ((uint64_t)0x7FF8000000000001ULL)
130
+ static inline sp_float sp_float_nil(void) {
131
+ union { uint64_t u; sp_float d; } x; x.u = SP_FLOAT_NIL_BITS; return x.d;
132
+ }
133
+ /* The same value as a CONSTANT expression, for a file-scope initializer
134
+ (a static ivar, a global) where the union read above is not one. The
135
+ payload argument is the low mantissa bits, so "0x1" is exactly
136
+ SP_FLOAT_NIL_BITS on gcc and clang, 32-bit lanes included. */
137
+ #define SP_FLOAT_NIL_CONST (__builtin_nan("0x1"))
138
+ static inline int sp_float_is_nil(sp_float v) {
139
+ union { sp_float d; uint64_t u; } x; x.d = v; return x.u == SP_FLOAT_NIL_BITS;
140
+ }
141
+
142
+ /* sp_sym is defined per-program in emit_sym_runtime, but poly helpers
143
+ below need to reference it by forward declaration. */
144
+ typedef sp_int sp_sym;
145
+
146
+ #ifndef TRUE
147
+ #define TRUE true
148
+ #endif
149
+ #ifndef FALSE
150
+ #define FALSE false
151
+ #endif
152
+
153
+ /* ---- Leaf value structs ---- */
154
+ /* `step` carries the iteration direction/stride for the enumerator forms that a
155
+ plain ascending range cannot express: n.downto(m) is {first:n,last:m,step:-1}.
156
+ A zero step (every range built by the literal `a..b` / sp_range_new path) is
157
+ treated as +1, so existing constructions need no change. */
158
+ /* An Integer begin with a Float end, (1..2.5): CRuby iterates it from the
159
+ Integer, so first / last / excl are the bounds of that walk (1..2), and the
160
+ end as written is kept beside them -- fe 1 for an inclusive Float end, 2
161
+ for an excluded one, fend its value; fe 0 (every other range, and the
162
+ zero-initialized one) has none. Read through sp_range_end_* (sp_range.h).
163
+ All three Range kinds default to frozen; dup sets their unfrozen flag.
164
+ Store it beside a byte-sized flag (fe 0..2, omitted 0..63, excl 0..1)
165
+ in the existing trailing word, keeping the by-value layouts the same size. */
166
+ typedef struct{sp_int first;sp_int last;sp_int excl;sp_int step;sp_float fend;unsigned char fe;unsigned char unfrozen;}sp_Range;
167
+ /* A Float range (1.0..3.0): endpoints kept as sp_float so cover?/include?/begin/
168
+ end are exact (an int-backed sp_Range truncated them). Iteration is a TypeError
169
+ in Ruby (only #step traverses a Float range), so no step/iteration state here.
170
+ beginless/endless ends use -HUGE_VAL / +HUGE_VAL. */
171
+ /* `omitted` records which bound was WRITTEN as absent (`(..5.0)`, `(1.0..)`),
172
+ as opposed to an explicit infinity (`1..Float::INFINITY`): both are +/-HUGE_VAL
173
+ in the value, and only #inspect / #to_s tell them apart -- CRuby prints
174
+ "1.0.." for the first and "1.0..Infinity" for the second (#3670).
175
+ bit 1 = begin omitted, bit 2 = end omitted. */
176
+ typedef struct{sp_float first;sp_float last;sp_int excl;unsigned char omitted;unsigned char unfrozen;}sp_FloatRange;
177
+ #define SP_FRANGE_NO_BEGIN 1
178
+ #define SP_FRANGE_NO_END 2
179
+ /* A mixed literal (1.5..5) is a Float range whose END was written as an
180
+ Integer -- and CRuby renders each endpoint as the user wrote it. The bits
181
+ record that, so #inspect prints "1.5..5" rather than "1.5..5.0" (#3896). */
182
+ #define SP_FRANGE_INT_BEGIN 4
183
+ #define SP_FRANGE_INT_END 8
184
+ /* A String range ("a".."e"): endpoints kept as GC-managed strings so #begin,
185
+ #end, #to_s and #inspect answer as Ruby does. Every traversal (each/to_a/
186
+ include?/...) materializes the element array through
187
+ sp_StrArray_from_string_range, which is how the range behaved before it was
188
+ a value of its own. */
189
+ typedef struct{const char *first;const char *last;unsigned char excl;unsigned char unfrozen;}sp_StrRange;
190
+ /* A class value. `name`, when non-NULL, is a rodata class name carried by a
191
+ class whose cls_id table entry may not exist (an exception's class -- the
192
+ Errno:: family and many builtin error classes have no assigned cls_id). It
193
+ takes precedence over cls_id for to_s / boxing / equality. */
194
+ typedef struct{sp_int cls_id;const char *name;}sp_Class;
195
+ /* The nil-class sentinel: a TY_CLASS value can be nil (BasicObject#superclass),
196
+ carried in-band via a reserved cls_id with no name -- the same nullable-scalar
197
+ convention as SP_INT_NIL for int?. It is distinct from every real class id
198
+ (user >= 0, builtins -100..-146, SP_CLASS_BY_NAME 0x7F000000). It MUST stay
199
+ negative: the class-chain walks route a non-negative cls_id to the user-class
200
+ table (`cur.cls_id>=0 ? sp_class_superclass : sp_builtin_superclass`), so a
201
+ positive sentinel would be looked up as a user class. */
202
+ #define SP_CLASS_NIL_ID ((sp_int)-900)
203
+ #define sp_class_nil_p(c) ((c).name == NULL && (c).cls_id == SP_CLASS_NIL_ID)
204
+ #define SP_CLASS_NIL ((sp_Class){SP_CLASS_NIL_ID, NULL})
205
+ /* A class known only by name (see sp_box_class_name in sp_alloc.h): marks a
206
+ boxed SP_TAG_CLASS value whose v.s carries the name instead of a resolved
207
+ cls_id. */
208
+ #define SP_CLASS_BY_NAME 0x7F000000
209
+ /* fl marks a component as Float-classed (renders "2.0"); clear bits keep the
210
+ Integer-style rendering for whole values. Zero-init (every positional
211
+ compound literal) is the Integer-classed default. */
212
+ typedef struct{sp_float re;sp_float im;unsigned char fl;}sp_Complex;
213
+ #define SP_CPLX_RE_F 1
214
+ #define SP_CPLX_IM_F 2
215
+ typedef struct{sp_int num;sp_int den;}sp_Rational;
216
+ typedef struct{const char *name;}sp_Encoding;
217
+
218
+ /* ---- GC headers ---- */
219
+ /* `marked` is a mark-generation STAMP, not a boolean: an object is live in
220
+ the current collection iff marked == sp_gc_mark_gen. Bumping the generation
221
+ at collect entry unmarks the whole heap in O(1), removing the two
222
+ full-list walks (old unmark + minor re-mark) that made every minor
223
+ collection O(live) even when the live set was untouched. 0 never equals a
224
+ generation (the counter skips it), so freshly-calloc'd headers are unmarked. */
225
+ typedef struct sp_gc_hdr { struct sp_gc_hdr *next; void (*finalize)(void *); void (*scan)(void *); size_t size; unsigned marked : 27; unsigned frozen : 1; unsigned pinned : 1;
226
+ /* `old` says the object has survived a sweep and lives on the old list, so
227
+ a reference stored into it may point at a younger object a minor mark
228
+ would not reach on its own; `dirty` says it is already on the remembered
229
+ set, so the write barrier's push happens once per collection rather than
230
+ once per store. `marked` gives up a bit for them: it is a wrapping
231
+ generation counter whose wrap path re-clears every stamp, and 2^27
232
+ collections between wraps is far past anything real.
233
+
234
+ `pinned` says the object is on sp_gc_pinned, the STICKY half of the
235
+ remembered set. `dirty` records a store the barrier saw and is cleared
236
+ every cycle; `pinned` records a holder whose stores the barrier cannot
237
+ see at all, so it is never cleared while the object lives. The one
238
+ producer is a by-reference String parameter: the callee stores through a
239
+ cell it cannot name the owner of, and the owner is only nameable at the
240
+ lending call site, which is BEFORE the store rather than after (#4391).
241
+
242
+ `aged` says the object has already survived one minor cycle while young.
243
+ A survivor is promoted on its SECOND survival, not its first: an object
244
+ that dies within a cycle or two of its birth then dies young, where the
245
+ next minor frees it, instead of being promoted to sit in the old
246
+ generation until a full cycle -- promoted garbage was a third of an
247
+ interpreter's heap and a third of its wall. */
248
+ unsigned old : 1; unsigned dirty : 1; unsigned aged : 1; void (*recycle)(struct sp_gc_hdr *); } sp_gc_hdr;
249
+ /* size/len packed to uint32 (4 GB per-string cap, far beyond any real
250
+ string) so the cached FNV `hash` fits without growing the 24-byte
251
+ header -- i.e. zero per-string RSS cost vs the pre-cache layout.
252
+ `size` is vestigial (written by sp_str_alloc, never read back; the
253
+ str-heap sweep no longer folds string bytes into sp_gc_bytes).
254
+ `hash` caches sp_str_hash for heap/heap-frozen keys; 0 == not yet
255
+ computed, invalidated to 0 on any in-place mutation (setbyte/set_len). */
256
+ typedef struct sp_str_hdr { struct sp_str_hdr *next; uint32_t size; uint32_t len; uint64_t hash; } sp_str_hdr;
257
+
258
+ /* ---- Typed arrays ---- */
259
+ #define SP_STRARR_INLINE 4
260
+ /* may_nil: the array may hold a nil analyze did not see, carried as the
261
+ slot's sentinel (SP_INT_NIL, the Float NaN payload). Set where the runtime
262
+ puts one -- a gap filled past the end, a converted boxed nil, an element a
263
+ builtin copies -- and carried to copies, so the whole-array reads of an
264
+ array analyze did not mark (sum, min, max, sort, include?(nil), the boxing
265
+ into a mixed array) ask one flag; a marked array scans, as its stores set
266
+ none. Only compact!, delete(nil) and replace drop it. It follows `frozen`,
267
+ which the push / [] / []= paths read, so their code is unchanged; the
268
+ IntArray block stays in its slab class. */
269
+ typedef struct{sp_int*data;sp_int start;sp_int len;sp_int cap;sp_int frozen;int may_nil;}sp_IntArray;
270
+ typedef struct{sp_float*data;sp_int len;sp_int cap;sp_int frozen;int may_nil;}sp_FloatArray;
271
+ #define SP_MAY_NIL(a) ((a)->may_nil)
272
+ /* elem_kind/elem_cls: what the pointers are, stamped when the array is boxed by
273
+ reference (the one cls_id for pointer arrays is type-erased, #4486). 0 until
274
+ then; a stamp never changes since a typed array holds one kind. */
275
+ typedef struct{void**data;sp_int len;sp_int cap;void(*scan_elem)(void*);sp_int frozen;int elem_kind;int elem_cls;}sp_PtrArray;
276
+ #define SP_PTR_ELEM_UNKNOWN 0
277
+ #define SP_PTR_ELEM_OBJ 1 /* user objects, elem_cls = the static class (-1: read each object's own) */
278
+ #define SP_PTR_ELEM_INT_ROWS 2 /* sp_IntArray rows (Array[Array[Integer]]) */
279
+ #define SP_PTR_ELEM_FLT_ROWS 3 /* sp_FloatArray rows (Array[Array[Float]]) */
280
+ typedef struct{const char**data;sp_int len;sp_int cap;sp_int frozen;const char*inline_data[SP_STRARR_INLINE];}sp_StrArray;
281
+
282
+ /* ---- Non-poly typed hashes ---- */
283
+ typedef struct{const char**keys;sp_int*vals;const char**order;sp_int len;sp_int cap;sp_int mask;sp_int default_v;}sp_StrIntHash;
284
+ typedef struct{const char**keys;const char**vals;const char**order;sp_int len;sp_int cap;sp_int mask;const char*default_v;}sp_StrStrHash;
285
+ typedef struct{sp_int*keys;const char**vals;sp_int*order;sp_bool*used;sp_int len;sp_int cap;sp_int mask;const char*default_v;}sp_IntStrHash;
286
+ typedef struct{sp_int*keys;sp_int*vals;sp_int*order;sp_bool*used;sp_int len;sp_int cap;sp_int mask;sp_int default_v;}sp_IntIntHash;
287
+
288
+ /* Signal table bound (0..64): shared by the trap state in the generated TU
289
+ and the trap machinery in lib/sp_cold.c. */
290
+ #define SP_SIG_MAX 65
291
+
292
+ #endif