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,306 @@
1
+ #ifndef SP_STR_H
2
+ #define SP_STR_H
3
+ /* sp_str.h -- cold String transforms compiled once in lib/sp_str.c.
4
+ *
5
+ * These are leaf `const char*` operations (case, strip, split-family,
6
+ * partition, dump/undump, concat, repeat, ...) that depend only on the
7
+ * shared string heap (sp_alloc.h) and the typed arrays (sp_array.h). The
8
+ * The UTF-8 decode/advance/encode + length-cache lookup are inline here
9
+ * (relocated from spinel_rt.h, codegen-neutral). The FNV hash cascade
10
+ * (#282) and sp_str_eq stay inline in spinel_rt.h (optcarrot-sensitive).
11
+ *
12
+ * sp_sprintf / sp_raise_cls are provided by the generated TU and resolved
13
+ * at the final link (same as lib/sp_core.c). */
14
+ #include "sp_array.h" /* sp_StrArray, sp_IntArray + sp_alloc.h / sp_gc.h / sp_types.h */
15
+
16
+ const char *sp_sprintf(const char *fmt, ...) SP_PRINTF_FORMAT(1, 2); /* defined in the generated TU */
17
+
18
+ /* ---- hot UTF-8 + length-cache inline core (relocated from spinel_rt.h;
19
+ each generated TU still inlines these identically). Length-cache state
20
+ (sp_str_lcache / SP_STR_LCACHE_*) lives in sp_alloc.h / sp_alloc.c. ---- */
21
+ static inline int sp_utf8_char_len(unsigned char c){if(c<0x80)return 1;if(c<0xC0)return 1;if(c<0xE0)return 2;if(c<0xF0)return 3;return 4;}
22
+ static inline int sp_utf8_advance(const char*p){int cn=sp_utf8_char_len((unsigned char)*p);int i=1;while(i<cn&&((unsigned char)p[i]&0xC0)==0x80)i++;return i;}
23
+ static inline int sp_utf8_decode(const char*p,uint32_t*out){unsigned char c=(unsigned char)p[0];if(c<0x80){*out=c;return 1;}if(c<0xC0){*out=c;return 1;}unsigned char c1=(unsigned char)p[1];if((c1&0xC0)!=0x80){*out=c;return 1;}if(c<0xE0){*out=((uint32_t)(c&0x1F)<<6)|(c1&0x3F);return 2;}unsigned char c2=(unsigned char)p[2];if((c2&0xC0)!=0x80){*out=c;return 1;}if(c<0xF0){*out=((uint32_t)(c&0x0F)<<12)|((uint32_t)(c1&0x3F)<<6)|(c2&0x3F);return 3;}unsigned char c3=(unsigned char)p[3];if((c3&0xC0)!=0x80){*out=c;return 1;}*out=((uint32_t)(c&0x07)<<18)|((uint32_t)(c1&0x3F)<<12)|((uint32_t)(c2&0x3F)<<6)|(c3&0x3F);return 4;}
24
+ static inline int sp_utf8_encode(uint32_t cp,char*out){if(cp<0x80){out[0]=(char)cp;return 1;}if(cp<0x800){out[0]=(char)(0xC0|(cp>>6));out[1]=(char)(0x80|(cp&0x3F));return 2;}if(cp<0x10000){out[0]=(char)(0xE0|(cp>>12));out[1]=(char)(0x80|((cp>>6)&0x3F));out[2]=(char)(0x80|(cp&0x3F));return 3;}out[0]=(char)(0xF0|(cp>>18));out[1]=(char)(0x80|((cp>>12)&0x3F));out[2]=(char)(0x80|((cp>>6)&0x3F));out[3]=(char)(0x80|(cp&0x3F));return 4;}
25
+ #define sp_str_lcache_hash sp_str_lcache_slot
26
+ /* 0xf1 is frozen -- an explicitly frozen heap string, and every string literal
27
+ (frozen string literals are permanent). Its contents can never change, so its
28
+ character length is the most cacheable of all; leaving it out meant #length,
29
+ #[] and every character index on a literal rescanned the whole string on each
30
+ call. 0xfd (the sp_String append buffer) is in as well: its length moves, but
31
+ every mutation goes through sp_fd_publish, which drops the entry -- without
32
+ it a scan loop over a built-up string rewalked the whole buffer per index. */
33
+ static inline int sp_str_cacheable(const char *s) {
34
+ unsigned char m = ((const unsigned char *)s)[-1];
35
+ return m == 0xfe || m == 0xfc || m == 0xff || m == 0xf1 || m == 0xfd || m == 0xfb || m == 0xfa || m == 0xf8;
36
+ }
37
+ /* Byte-exact comparison of two heap strings. strcmp stops at the first NUL,
38
+ so two strings that differ only after one compared equal and sorted equal --
39
+ `\0` is an ordinary byte in a Ruby String, and the representation carries
40
+ the real length (#3471, #3472). Ordering matches CRuby's: memcmp over the
41
+ common prefix, then the shorter string first. */
42
+ static inline int sp_str_cmp_bytes(const char *a, const char *b) {
43
+ size_t la = sp_str_byte_len(a), lb = sp_str_byte_len(b);
44
+ size_t n = la < lb ? la : lb;
45
+ int r = n ? memcmp(a, b, n) : 0;
46
+ if (r) return r < 0 ? -1 : 1;
47
+ if (la != lb) return la < lb ? -1 : 1;
48
+ /* identical bytes: CRuby falls back to the encoding index when the two are
49
+ not comparable, and ASCII-8BIT sorts before UTF-8 (rb_str_cmp). Both
50
+ ASCII-only stays 0, the way sp_str_eq answers true for it. */
51
+ { int ba = sp_str_is_binary(a), bb = sp_str_is_binary(b);
52
+ if (ba == bb) return 0;
53
+ for (size_t i = 0; i < la; i++)
54
+ if ((unsigned char)a[i] >= 0x80) return ba ? -1 : 1;
55
+ return 0; }
56
+ }
57
+ static inline void sp_str_split_push(sp_StrArray*a,const char*p,size_t n){
58
+ char*r=sp_str_alloc_raw(n+1);
59
+ memcpy(r,p,n);
60
+ r[n]=0;
61
+ /* record the real length: alloc_raw leaves it unset, so a piece holding an
62
+ embedded NUL would answer strlen and read short */
63
+ sp_str_set_len(r,n);
64
+ sp_StrArray_push(a,r);
65
+ }
66
+
67
+ int sp_utf8_set_has(const uint32_t*cps,size_t n,uint32_t cp);
68
+ uint32_t sp_uc_toupper(uint32_t cp);
69
+ uint32_t sp_uc_tolower(uint32_t cp);
70
+ sp_int sp_str_casecmp(const char*a,const char*b);
71
+ sp_bool sp_str_casecmp_p(const char*a,const char*b);
72
+ sp_bool sp_str_valid_encoding(const char*s);
73
+ const char*sp_str_field(const char*s,const char*sep,sp_int n);
74
+ sp_int sp_str_field_count(const char*s,const char*sep);
75
+ const char*sp_str_concat(const char*a,const char*b);
76
+ const char*sp_str_append_bytes(const char*a,const char*b);
77
+ const char*sp_str_concat3(const char*a,const char*b,const char*c);
78
+ const char*sp_str_concat4(const char*a,const char*b,const char*c,const char*d);
79
+ const char*sp_str_concat_arr(const char *const *parts,int n);
80
+ const char*sp_str_inspect(const char*s);
81
+ sp_bool sp_sym_plain_name_p(const char *p, sp_bool allow_suffix);
82
+ sp_bool sp_sym_simple_p(const char *n);
83
+ const char *sp_sym_inspect_name(const char *name);
84
+ const char *sp_sym_inspect_key(const char *name);
85
+ const char*sp_str_upcase(const char*s);
86
+ const char*sp_str_downcase(const char*s);
87
+ const char*sp_str_swapcase(const char*s);
88
+ const char*sp_str_upcase_ascii(const char*s);
89
+ const char*sp_str_downcase_ascii(const char*s);
90
+ const char*sp_str_swapcase_ascii(const char*s);
91
+ const char*sp_str_capitalize_ascii(const char*s);
92
+ const char*sp_str_dump(const char*s);
93
+ const char*sp_str_delete_prefix(const char*s,const char*p);
94
+ const char*sp_str_substr(const char*s,sp_int start,sp_int len);
95
+ const char *sp_str_append_grow(const char *s, const char *t);
96
+ const char *sp_str_append_grow_n(const char *s, const char *t, size_t lb); /* the first lb bytes of t */
97
+ const char*sp_str_delete_suffix(const char*s,const char*p);
98
+ const char*sp_str_strip(const char*s);
99
+ const char*sp_str_chomp(const char*s);
100
+ const char *sp_str_chomp_sep(const char *s, const char *sep);
101
+ const char*sp_str_chop(const char*s);
102
+ const char*sp_str_chr(const char*s);
103
+ sp_int sp_str_byte_to_char(const char*s,sp_int byteoff);
104
+ sp_bool sp_str_include(const char*s,const char*sub);
105
+ sp_bool sp_str_start_with(const char*s,const char*p);
106
+ sp_bool sp_str_end_with(const char*s,const char*suf);
107
+ sp_StrArray *sp_str_partition(const char *s, const char *sep);
108
+ sp_StrArray *sp_str_rpartition(const char *s, const char *sep);
109
+ sp_StrArray*sp_str_lines(const char*s);
110
+ sp_StrArray*sp_str_lines_sep(const char*s,const char*sep);
111
+ sp_StrArray*sp_str_lines_sep_chomp(const char*s,const char*sep);
112
+ sp_StrArray*sp_str_lines_chomp(const char*s);
113
+ const char*sp_str_byteslice(const char*s,sp_int start,sp_int len);
114
+ const char*sp_str_byteslice1(const char*s,sp_int i);
115
+ const char*sp_str_byteslice_range(const char*s,sp_int lo,sp_int hi,int excl,int lo_none,int hi_none);
116
+ const char*sp_str_bytesplice(const char*s,sp_int start,sp_int len,const char*val);
117
+ int sp_str_ascii_only(const char*s);
118
+ int sp_str_enc_step(int state, const char *acc, size_t acc_len, const char *part, size_t part_len, int part_bin);
119
+ /* the common step inline: a part in the encoding so far keeps it */
120
+ static inline int sp_str_enc_step_i(int state, const char *acc, size_t acc_len, const char *part, size_t part_len, int part_bin) {
121
+ if (state == part_bin) return state;
122
+ return sp_str_enc_step(state, acc, acc_len, part, part_len, part_bin);
123
+ }
124
+ const char*sp_str_format_strarr(const char*fmt,sp_StrArray*a);
125
+ const char*sp_str_sub(const char*s,const char*pat,const char*rep);
126
+ const char*sp_str_remove_first(const char*s,const char*pat);
127
+ const char*sp_str_capitalize(const char*s);
128
+ const char*sp_str_repeat(const char*s,sp_int n);
129
+ sp_IntArray*sp_str_bytes(const char*s);
130
+ const char *sp_str_crypt(const char *s, const char *salt);
131
+ const char*sp_str_lstrip(const char*s);
132
+ const char*sp_str_rstrip(const char*s);
133
+ const char*sp_str_dup(const char*s);
134
+ const char *sp_plain_char(unsigned char c);
135
+ const char *sp_bin_char(unsigned char c);
136
+ const char*sp_str_b(const char*s);
137
+
138
+ /* ---- utf8-dependent cold transforms (lib/sp_str.c) ---- */
139
+ /* nil-receiver raise: a nullable string carries nil as NULL, and CRuby
140
+ answers NoMethodError. The bare primitives below stay total over NULL
141
+ (runtime internals pass legitimately-nil elements); the _m/_p variants
142
+ carry Ruby receiver semantics at method call sites. */
143
+ SP_NORETURN SP_COLD void sp_nil_recv(const char *meth);
144
+ sp_int sp_str_length_m(const char *s);
145
+ sp_int sp_str_bytesize_m(const char *s);
146
+ sp_bool sp_str_empty_p(const char *s);
147
+ const char *sp_str_plus(const char *a, const char *b);
148
+ const char *sp_str_plus_lit(const char *a, const char *lit, size_t ll);
149
+ sp_int sp_str_count_chars(const char *s, size_t bl);
150
+ sp_int sp_str_length(const char*s);
151
+ sp_int sp_str_ord(const char*s);
152
+ size_t sp_utf8_byte_offset(const char*s,sp_int char_idx);
153
+ uint32_t*sp_utf8_decode_all(const char*s,size_t*out_n);
154
+ uint32_t*sp_utf8_decode_charset(const char*s,size_t*out_n);
155
+ uint32_t*sp_utf8_decode_charset_n(const char*s,size_t bl,size_t*out_n);
156
+ void sp_str_split_into(sp_StrArray*a,const char*s,const char*sep);
157
+ const char*sp_str_undump(const char*s);
158
+ const char*sp_str_succ_impl(const char*s);
159
+ const char*sp_str_succ(const char*s);
160
+ const char*sp_str_succ_n(const char*s,sp_int n);
161
+ sp_StrArray*sp_str_split(const char*s,const char*sep);
162
+ sp_StrArray*sp_str_split_drop_trailing(const char*s,const char*sep);
163
+ sp_StrArray*sp_str_split_limit(const char*s,const char*sep,sp_int n);
164
+ sp_StrArray*sp_str_split_ws(const char*s);
165
+ sp_StrArray*sp_str_split_ws_limit(const char*s,sp_int n);
166
+ sp_StrArray*sp_str_scan(const char*s,const char*pat);
167
+ sp_int sp_str_scan_at(const char*s,const char*pat,sp_int pos);
168
+ const char*sp_str_gsub(const char*s,const char*pat,const char*rep);
169
+ sp_int sp_str_index(const char*s,const char*sub);
170
+ sp_int sp_str_index_from(const char*s,const char*sub,sp_int start);
171
+ sp_int sp_str_rindex(const char*s,const char*sub);
172
+ sp_int sp_str_rindex_from(const char*s,const char*sub,sp_int pos);
173
+ sp_int sp_str_byteindex(const char*s,const char*sub);
174
+ sp_int sp_str_byteindex_from(const char*s,const char*sub,sp_int start);
175
+ sp_int sp_str_byterindex(const char*s,const char*sub);
176
+ sp_int sp_str_byterindex_from(const char*s,const char*sub,sp_int pos);
177
+ const char*sp_str_sub_range(const char*s,sp_int start,sp_int len);
178
+ const char*sp_str_char_at_or_nil(const char*s,sp_int i);
179
+ const char*sp_str_sub_range_len(const char*s,sp_int cl,sp_int start,sp_int len);
180
+ const char*sp_str_sub_range_r(const char*s,sp_int start,sp_int end_,sp_int excl);
181
+ const char*sp_str_sub_range_len_r(const char*s,sp_int cl,sp_int start,sp_int end_,sp_int excl);
182
+ const char*sp_str_reverse(const char*s);
183
+ sp_int sp_str_count(const char*s,const char*chars);
184
+ sp_int sp_str_count_n(const char*s,const char**chars,sp_int n);
185
+ sp_int sp_str_sum_bits(const char*s,sp_int bits); /* String#sum(bits=16) */
186
+ sp_IntArray*sp_str_codepoints(const char*s);
187
+ sp_IntArray*sp_str_codepoints_all(const char*s);
188
+ sp_StrArray*sp_str_chars(const char*s);
189
+ const char*sp_str_tr(const char*s,const char*from,const char*to);
190
+ const char*sp_str_tr_s(const char*s,const char*from,const char*to);
191
+ const char*sp_str_delete(const char*s,const char*chars);
192
+ const char*sp_str_squeeze(const char*s);
193
+ const char*sp_str_squeeze_chars(const char*s,const char*cs);
194
+ const char*sp_str_delete_n(const char*s,const char**chars,sp_int n);
195
+ const char*sp_str_squeeze_n(const char*s,const char**chars,sp_int n);
196
+ const char *sp_str_scrub(const char *s, const char *repl);
197
+ const char *sp_str_scrub_utf8(const char *s, const char *repl);
198
+ sp_int sp_str_scrub_bad(const char *s, sp_int bl, sp_int from, sp_int *len);
199
+ const char *sp_str_scrub_repl(const char *r);
200
+ const char *sp_str_encode(const char *s, sp_RbVal dst, sp_RbVal src, sp_RbVal invalid, sp_RbVal undef, sp_RbVal replace);
201
+ const char *sp_str_scrub_bang(const char *s, const char *repl);
202
+ const char*sp_str_ljust(const char*s,sp_int w);
203
+ const char*sp_str_rjust(const char*s,sp_int w);
204
+ const char*sp_str_center(const char*s,sp_int w);
205
+ const char*sp_str_ljust2(const char*s,sp_int w,const char*pad);
206
+ const char*sp_str_rjust2(const char*s,sp_int w,const char*pad);
207
+ const char*sp_str_center2(const char*s,sp_int w,const char*pad);
208
+ sp_int sp_str_index_opt(const char *s, const char *sub);
209
+ sp_int sp_str_index_from_opt(const char *s, const char *sub, sp_int start);
210
+ sp_int sp_str_rindex_opt(const char *s, const char *sub);
211
+
212
+ /* ---- relocated from spinel_rt.h: hash key primitives (sp_str_hash /
213
+ sp_str_eq / _sp_istr_idx) used by lib/sp_hash.c's StrInt/StrStr/IntStr/
214
+ IntIntHash accessors. Still static (inline / noinline) -- each including
215
+ TU (the single generated TU via spinel_rt.h, and sp_hash.c) gets its own
216
+ private copy, so this is a pure textual relocation with no codegen change. ---- */
217
+ /* NULL-safe string equality. ENV[] returns NULL for unset vars
218
+ (the dispatch is `sp_str_dup_external(getenv(...))`, which propagates
219
+ NULL), so emitted strcmp(...) on the result of `ENV["X"] == "1"` would
220
+ dereference NULL on either side. nil-vs-string equality is false in
221
+ Ruby; nil == nil is true, so falling back to pointer equality on the
222
+ NULL path covers both. */
223
+ static inline int sp_str_eq(const char*a,const char*b){
224
+ if(a==b)return 1;
225
+ if(!a||!b)return 0;
226
+ if(strcmp(a,b)!=0)return 0;
227
+ /* strcmp equality is only prefix equality when a length header records
228
+ an embedded NUL ("a\0b" vs "a"): confirm byte-exact equality. The
229
+ miss path above stays a single strcmp. */
230
+ size_t la=sp_str_byte_len(a);
231
+ if(la!=sp_str_byte_len(b)||memcmp(a,b,la)!=0)return 0;
232
+ /* CRuby's rb_str_comparable: equal bytes are equal strings only when the
233
+ encodings are comparable -- the same encoding, or both operands ASCII
234
+ only. spinel has two, so the question is the BINARY tag; the ASCII scan
235
+ runs only when the tags disagree about bytes that already matched, which
236
+ keeps every ordinary comparison at the two reads above. */
237
+ if(sp_str_is_binary(a)==sp_str_is_binary(b))return 1;
238
+ for(size_t i=0;i<la;i++)if((unsigned char)a[i]>=0x80)return 0;
239
+ return 1;
240
+ }
241
+ /* Compare a spinel string against a PLAIN C string (a stack buffer, a literal
242
+ with no marker byte). sp_str_eq reads a marker in front of BOTH operands to
243
+ recover a length that can carry an embedded NUL; handing it unmarked memory
244
+ reads the byte before the object, and when that byte happens to look like a
245
+ marker it reads a bogus header and answers a garbage length. The plain side
246
+ has no embedded NUL by construction, so its length is strlen. */
247
+ static inline sp_bool sp_str_eq_cstr(const char *marked, const char *plain) {
248
+ if (!marked || !plain) return (sp_bool)(marked == plain);
249
+ size_t lp = strlen(plain);
250
+ return (sp_bool)(sp_str_byte_len(marked) == lp && memcmp(marked, plain, lp) == 0);
251
+ }
252
+
253
+ /* String#valid_encoding? -- walks the buffer and accepts pure ASCII
254
+ or well-formed UTF-8 (RFC 3629 byte sequences with no overlong
255
+ forms, no surrogate halves, code points <= U+10FFFF). */
256
+ /* A BINARY string hashes apart from its UTF-8 twin, but only when it holds a
257
+ byte that makes them incomparable: "abc".b and "abc" are eql? in CRuby and
258
+ hash together, "caf\xC3\xA9" and "café" are neither. The high-byte question
259
+ is answered inside the walk the hash already makes, so this costs one
260
+ register. */
261
+ static inline uint64_t sp_str_hash_bytes(const char*s,int*saw_high){
262
+ uint64_t h=14695981039346656037ULL;int hi=0;
263
+ while(*s){unsigned char c=(unsigned char)*s++;hi|=(c>=0x80);h^=c;h*=1099511628211ULL;}
264
+ *saw_high=hi;return h;
265
+ }
266
+ static inline uint64_t sp_str_hash_compute(const char*s){
267
+ int hi=0;uint64_t h=sp_str_hash_bytes(s,&hi);
268
+ if(hi&&sp_str_is_binary(s))h^=0x9e3779b97f4a7c15ULL;
269
+ return h;
270
+ }
271
+ /* Cold path: compute (and, for a heap/heap-frozen string, cache) the FNV
272
+ hash. Kept out-of-line so sp_str_hash's inline fast path -- a cached-hash
273
+ read -- stays tiny and doesn't bloat every call site's code layout. */
274
+ static SP_NOINLINE uint64_t sp_str_hash_miss(const char*s,unsigned char m){
275
+ if(m==0xfe||m==0xfc||m==0xf1||m==0xfa||m==0xf8){
276
+ sp_str_hdr*hd=((sp_str_hdr*)(s-1))-1;
277
+ uint64_t h=sp_str_hash_compute(s);
278
+ hd->hash=h?h:1;
279
+ return hd->hash;
280
+ }
281
+ return sp_str_hash_compute(s);
282
+ }
283
+ static inline uint64_t sp_str_hash(const char*s){
284
+ /* nil hashes like any other absent key: a String-keyed lookup with a nil
285
+ key answers "not present" in Ruby, and reading the tag byte at s[-1]
286
+ off a NULL faulted instead (#3790). sp_str_eq already answers false
287
+ against NULL, so the probe walks past every occupied slot. */
288
+ if(!s)return 14695981039346656037ULL;
289
+ unsigned char m=((const unsigned char*)s)[-1];
290
+ if(m==0xfe||m==0xfc||m==0xf1||m==0xfa||m==0xf8){
291
+ uint64_t cached=(((sp_str_hdr*)(s-1))-1)->hash;
292
+ if(cached)return cached;
293
+ }
294
+ return sp_str_hash_miss(s,m);
295
+ }
296
+ static inline sp_int _sp_istr_idx(sp_int mask,sp_int k){return(sp_int)(((uint64_t)(unsigned long long)k*11400714819323198485ULL)&(uint64_t)mask);}
297
+
298
+ /* ---- more cold string ops relocated from spinel_rt.h (0 optcarrot
299
+ uses; deps already visible via sp_str.h's own sp_array.h include). ---- */
300
+ sp_bool sp_str_in_list(const char *m, const char *const *list);
301
+ sp_RbVal sp_str_index_poly(const char *s, const char *sub);
302
+ sp_RbVal sp_str_index_from_poly(const char *s, const char *sub, sp_int start);
303
+ sp_RbVal sp_str_rindex_poly(const char *s, const char *sub);
304
+ sp_PolyArray *sp_str_lines_poly(const char *s);
305
+
306
+ #endif /* SP_STR_H */
@@ -0,0 +1,23 @@
1
+ /* sp_str_crypt.c -- String#crypt, through libc crypt(3).
2
+ *
3
+ * An object of its own in libspinel_rt.a, so only a program that calls
4
+ * String#crypt pulls it in: in sp_str.o every program referenced crypt(),
5
+ * and the link line's -lcrypt made libcrypt a dependency of hello world
6
+ * (#6674). src/main.c adds -lcrypt only when the generated C calls
7
+ * sp_str_crypt. */
8
+ #include <string.h>
9
+ #include <stdlib.h>
10
+ #include "sp_str.h"
11
+ const char *sp_str_crypt(const char *s, const char *salt) {SP_GC_ROOT_STR(s);SP_GC_ROOT_STR(salt);
12
+ /* the real libc crypt(3) -- DES with a 2-char salt (or the platform's
13
+ extended schemes), byte-identical to CRuby's String#crypt (#2398).
14
+ Declared by hand: glibc hides it behind crypt.h/_XOPEN_SOURCE while
15
+ macOS ships it in unistd.h. */
16
+ extern char *crypt(const char *key, const char *slt);
17
+ if (!s) sp_nil_recv("crypt");
18
+ if (!salt || !salt[0] || !salt[1])
19
+ sp_raise_cls("ArgumentError", "salt too short (need >=2 bytes)");
20
+ char *d = crypt(s, salt);
21
+ if (!d) sp_raise_cls("ArgumentError", "invalid salt");
22
+ return sp_str_dup_external(d);
23
+ }
@@ -0,0 +1,92 @@
1
+ /* sp_string.c -- the cold sp_String in-place mutators (see sp_string.h).
2
+ prepend / insert / replace / dup are off the hot string-building path, so they
3
+ are compiled once here instead of inline in every generated TU. */
4
+ #include "sp_string.h"
5
+ #include <string.h>
6
+
7
+ void sp_String_prepend(sp_String*s,const char*t){SP_GC_ROOT(s);SP_GC_ROOT_STR(t);if(!s||!t)return;if(sp_String_is_frozen(s)){sp_raise_frozen_str(s->data);return;}int64_t tl=(int64_t)strlen(t);if(!sp_fd_grow(s,s->len+tl))return;memmove(s->data+tl,s->data,s->len+1);memcpy(s->data,t,tl);s->len+=tl;sp_fd_publish(s);}
8
+ /* String#insert(idx, str): insert at idx; negative idx is relative to len+1. */
9
+ void sp_String_insert(sp_String*s,int64_t idx,const char*t){SP_GC_ROOT(s);SP_GC_ROOT_STR(t);if(!s||!t)return;if(sp_String_is_frozen(s)){sp_raise_frozen_str(s->data);return;}int64_t tl=(int64_t)strlen(t);if(tl==0)return;if(idx<0)idx+=s->len+1;if(idx<0)idx=0;if(idx>s->len)idx=s->len;if(!sp_fd_grow(s,s->len+tl))return;memmove(s->data+idx+tl,s->data+idx,s->len-idx+1);memcpy(s->data+idx,t,tl);s->len+=tl;sp_fd_publish(s);}
10
+ /* String#replace(s): replace entire content. */
11
+ void sp_String_replace(sp_String*s,const char*t){SP_GC_ROOT(s);SP_GC_ROOT_STR(t);if(!s||!t)return;if(sp_String_is_frozen(s)){sp_raise_frozen_str(s->data);return;}int64_t tl=(int64_t)strlen(t);if(!sp_fd_grow(s,tl))return;memcpy(s->data,t,tl);s->data[tl]='\0';s->len=tl;sp_fd_publish(s);}
12
+ sp_String*sp_String_dup(sp_String*s){SP_GC_ROOT(s);return sp_String_new(s->data);}
13
+ /* The first growth past an inline payload (sp_String_new_inline_len) moves
14
+ it to a malloc'd block, which the object now owns: its bytes are counted
15
+ and the finalizer that frees them installed, as sp_String_new_len does
16
+ from the start. The inline room stays part of the object. Rare, and kept
17
+ out of sp_fd_grow so that one still inlines into every append. */
18
+ int sp_fd_grow_inline(sp_String *s, int64_t need){
19
+ sp_gc_hdr *h = (sp_gc_hdr *)((char *)s - sizeof(sp_gc_hdr));
20
+ int64_t new_cap = (need * 2) + 16;
21
+ sp_str_lcache_drop(s->data);
22
+ char *raw = (char *)malloc(SP_FD_OVH + new_cap);
23
+ if (!raw) return 0;
24
+ char *data = sp_fd_setup(raw);
25
+ memcpy(data, s->data, (size_t)s->len + 1);
26
+ s->cap = new_cap; s->data = data; sp_fd_own(s);
27
+ h->size += s->cap + SP_FD_OVH; sp_gc_bytes_add(s->cap + SP_FD_OVH);
28
+ if (!h->finalize) { h->finalize = sp_String_fin; sp_slab_set_fin(h); }
29
+ return 1;
30
+ }
31
+
32
+ /* A handle made from a chilled String records the symbol whose to_s it holds
33
+ (sp_String.chilled is its id + 1). */
34
+ void sp_String_chill(sp_String*r,const char*s){
35
+ if(sp_str_is_chilled(s))r->chilled=(unsigned)(sp_str_chilled_sym(s)+1);
36
+ }
37
+ /* Such a handle is chilled until its first mutation, which makes it plain
38
+ for good (CRuby's str_modify): sp_fd_publish clears the flag. A path that
39
+ changes the bytes without publishing is caught here, at +@, by the bytes
40
+ no longer being the symbol's name. */
41
+ int sp_String_chilled_now(sp_String*h){
42
+ const char*nm=sp_sym_name_fn?sp_sym_name_fn((sp_sym)(h->chilled-1)):NULL;
43
+ size_t n=nm?sp_str_byte_len(nm):0;
44
+ if(nm&&(size_t)h->len==n&&memcmp(h->data,nm,n)==0)return 1;
45
+ h->chilled=0;
46
+ return 0;
47
+ }
48
+ /* Symbol#to_s and #id2name: CRuby answers a new chilled String each time
49
+ (sp_str_is_chilled), so `s = sym.to_s; t = +s` copies and `t << x` leaves
50
+ s alone. The symbol table's own name is a plain static that a handle would
51
+ take as an ordinary String, so each symbol gets one chilled copy, built at
52
+ its first to_s and kept as long as the symbol is: static storage with a
53
+ header, which the collector neither marks nor sweeps, and the symbol ahead
54
+ of it (sp_str_chilled_obj). The table is guarded by the heap lock, as the
55
+ dedup table is. */
56
+ static const char**sp_sym_chilled_tab=NULL;
57
+ static sp_int sp_sym_chilled_cap=0;
58
+ const char*sp_sym_to_s_chilled(sp_sym id){
59
+ const char*nm=sp_sym_name_fn?sp_sym_name_fn(id):sp_str_empty;
60
+ if(id<0||!sp_sym_name_fn)return nm;
61
+ SP_HEAP_LOCK();
62
+ const char*hit=id<sp_sym_chilled_cap?sp_sym_chilled_tab[id]:NULL;
63
+ SP_HEAP_UNLOCK();
64
+ if(hit)return hit;
65
+ size_t n=sp_str_byte_len(nm);
66
+ sp_str_chilled_obj*o=(sp_str_chilled_obj*)malloc(sizeof(sp_str_chilled_obj)+n+1);
67
+ if(!o)return nm;
68
+ int ascii7=1;
69
+ for(size_t i=0;i<n;i++)if((unsigned char)nm[i]>=0x80)ascii7=0;
70
+ o->sym=id;
71
+ o->h.next=(sp_str_hdr*)&sp_str_chilled_tag;
72
+ o->h.size=(uint32_t)(n+1)|(ascii7?SP_STR_SIZE_ASCII7:0u)|(sp_str_is_binary(nm)?SP_STR_SIZE_BINARY:0u);
73
+ o->h.len=(uint32_t)n;o->h.hash=0;
74
+ o->m=0xfb;memcpy(o->d,nm,n);o->d[n]=0;
75
+ SP_HEAP_LOCK();
76
+ if(id>=sp_sym_chilled_cap){
77
+ sp_int nc=sp_sym_chilled_cap?sp_sym_chilled_cap:64;
78
+ while(nc<=id)nc*=2;
79
+ const char**nt=(const char**)realloc((void*)sp_sym_chilled_tab,(size_t)nc*sizeof(const char*));
80
+ if(nt){
81
+ memset((void*)(nt+sp_sym_chilled_cap),0,(size_t)(nc-sp_sym_chilled_cap)*sizeof(const char*));
82
+ sp_sym_chilled_tab=nt;sp_sym_chilled_cap=nc;
83
+ }
84
+ }
85
+ const char*r=o->d;
86
+ if(id<sp_sym_chilled_cap){
87
+ if(sp_sym_chilled_tab[id]){free(o);r=sp_sym_chilled_tab[id];} /* another thread won */
88
+ else sp_sym_chilled_tab[id]=r;
89
+ }
90
+ SP_HEAP_UNLOCK();
91
+ return r;
92
+ }