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,253 @@
1
+ #ifndef SP_STRING_H
2
+ #define SP_STRING_H
3
+ /* sp_string.h -- the mutable-String builder (sp_String), shared so both the
4
+ generated TU and cold lib C files (inspect helpers, ...) can build strings.
5
+
6
+ The hot core -- construction, append, and the sp_fd_* buffer mechanics that
7
+ sit on the string concat / interpolation path -- stays `static inline` here so
8
+ every TU inlines it (no perf cost vs. living in spinel_rt.h). The rarely-used
9
+ in-place mutators (prepend / insert / replace / dup) are cold, so they are
10
+ compiled once into libspinel_rt.a (lib/sp_string.c). */
11
+ #include "sp_alloc.h" /* sp_gc_alloc, sp_gc_bytes/hdr, sp_str_hdr, sp_str_byte_len, sp_raise_cls */
12
+ #include <string.h>
13
+
14
+ int sp_str_ascii_only(const char *s);
15
+
16
+ /* `binary` is the ASCII-8BIT tag, kept on the HANDLE: sp_fd_setup zeroes the
17
+ payload header on every grow, so the tag has to be re-stamped after each
18
+ mutation rather than living only in the bytes. `chilled` is nonzero for a
19
+ handle made from a chilled String (sp_str_is_chilled: what Symbol#to_s
20
+ answers): that symbol's id + 1, so sp_String_uplus can tell whether the
21
+ bytes are still its name -- +@ copies it then, as it copies a frozen one,
22
+ and a mutated one is an ordinary String, as CRuby's is. A GC allocation is
23
+ zeroed, so every other handle starts at 0 with no store. */
24
+ typedef struct { char *data; int64_t len; int64_t cap; unsigned binary; unsigned chilled; } sp_String;
25
+ /* sp_fd_publish reads `binary` and `chilled` as one word */
26
+ _Static_assert(offsetof(sp_String, chilled) == offsetof(sp_String, binary) + 4 && sizeof(unsigned) == 4,
27
+ "sp_String.binary and .chilled are adjacent 32-bit fields");
28
+
29
+ /* Per-mutable-string freeze flag rides in the GC header alongside `marked`. */
30
+ static inline sp_bool sp_String_is_frozen(sp_String*s){if(!s)return TRUE;sp_gc_hdr*h=(sp_gc_hdr*)((char*)s-sizeof(sp_gc_hdr));return h->frozen;}
31
+ static inline sp_String*sp_String_freeze(sp_String*s){if(s){sp_gc_hdr*h=(sp_gc_hdr*)((char*)s-sizeof(sp_gc_hdr));h->frozen=1;}return s;}
32
+
33
+ /* A mutable String's payload carries the same length-bearing sp_str_hdr that
34
+ 0xfe/0xfc heap strings use, so an escaped const char* is binary-safe (the
35
+ reader uses sp_str_byte_len, not strlen). Block layout:
36
+ [sp_str_hdr][0xfd marker][data ...][NUL]. s->data points at `data`. The ctor
37
+ and in-place mutators are also reached with bare C string literals (no marker
38
+ byte), so they size their operand with strlen, not a [-1]-marker reader. */
39
+ #define SP_FD_HDR (sizeof(sp_str_hdr)+1) /* header + marker byte */
40
+ #define SP_FD_OVH (sizeof(sp_str_hdr)+2) /* header + marker + NUL terminator */
41
+ static inline char *sp_fd_base(const char *data){return (char*)data-SP_FD_HDR;}
42
+ /* The payload's `next` field carries the owning sp_String rather than a heap
43
+ link: these blocks are malloc'd and never join the string heap, so nothing
44
+ walks that list, and a container holding the escaped `const char *` has no
45
+ other way back to the handle. Without it the mark reaches the bytes and
46
+ stops, the handle goes unreferenced, and sp_String_fin frees the bytes the
47
+ container still points at. sp_fd_own publishes it; every path that lays out
48
+ a payload has to call it. */
49
+ static inline char *sp_fd_setup(char *raw){
50
+ sp_str_hdr *h = (sp_str_hdr *)raw;
51
+ h->next = NULL; h->size = 0; h->len = 0; h->hash = 0;
52
+ char *body = (char *)(h + 1);
53
+ body[0] = (char)0xfd;
54
+ return body + 1;
55
+ }
56
+ static inline void sp_fd_own(sp_String *s){
57
+ ((sp_str_hdr *)sp_fd_base(s->data))->next = (sp_str_hdr *)(void *)s;
58
+ }
59
+ static inline void sp_fd_publish(sp_String *s){
60
+ sp_str_hdr *h = (sp_str_hdr *)sp_fd_base(s->data);
61
+ h->len = (uint32_t)s->len; h->hash = 0;
62
+ h->size &= ~SP_STR_SIZE_ASCII7; /* the bytes just changed */
63
+ /* Both flags are rare and read as one word, so a plain handle pays the one
64
+ load and branch the binary tag alone did. A mutation makes a chilled
65
+ handle plain for good, as CRuby's str_modify does, even one that leaves
66
+ the bytes the symbol's name again (`m << "!"; m.chop!`). */
67
+ uint64_t fl; memcpy(&fl, &s->binary, sizeof fl);
68
+ if (SP_EXPECT(fl != 0, 0)) {
69
+ if (s->binary) h->size |= SP_STR_SIZE_BINARY;
70
+ s->chilled = 0;
71
+ }
72
+ sp_str_lcache_drop(s->data);
73
+ }
74
+ /* A handle whose payload sits inside its own GC object, right after the
75
+ struct (sp_String_new_fresh): no malloc and no finalizer, which are most of
76
+ what a handle costs to make and to collect. Its first growth moves the
77
+ payload out (sp_fd_grow_inline, lib/sp_string.c), off the inlined path. */
78
+ static inline int sp_fd_is_inline(sp_String *s){return sp_fd_base(s->data)==(char*)(s+1);}
79
+ SP_COLD SP_NOINLINE int sp_fd_grow_inline(sp_String *s, int64_t need);
80
+ static inline int sp_fd_grow(sp_String *s, int64_t need){
81
+ if (need < s->cap) return 1;
82
+ if (SP_EXPECT(sp_fd_is_inline(s), 0)) return sp_fd_grow_inline(s, need);
83
+ sp_gc_hdr *h = (sp_gc_hdr *)((char *)s - sizeof(sp_gc_hdr));
84
+ int64_t new_cap = (need * 2) + 16;
85
+ sp_str_lcache_drop(s->data);
86
+ char *raw = (char *)realloc(sp_fd_base(s->data), SP_FD_OVH + new_cap);
87
+ if (!raw) return 0;
88
+ sp_gc_bytes_sub(s->cap + SP_FD_OVH); h->size -= s->cap + SP_FD_OVH;
89
+ s->cap = new_cap; s->data = sp_fd_setup(raw); sp_fd_own(s);
90
+ h->size += s->cap + SP_FD_OVH; sp_gc_bytes_add(s->cap + SP_FD_OVH);
91
+ return 1;
92
+ }
93
+ static inline void sp_String_fin(void*p){sp_str_lcache_drop(((sp_String*)p)->data);free(sp_fd_base(((sp_String*)p)->data));}
94
+ /* Build a handle over an explicit byte length. Everything the caller needs to
95
+ read out of `s` must be read BEFORE this returns: the copy below happens
96
+ ahead of sp_gc_alloc precisely because a collection there frees an `s` that
97
+ this stack frame is the only anchor for (roots are explicit -- the C stack is
98
+ not scanned), so `s` may be dangling by the time it comes back. */
99
+ static inline sp_String*sp_String_new_len(const char*s,int64_t len){
100
+ int64_t cap=(len*2)+16;
101
+ char*raw=(char*)malloc(SP_FD_OVH+cap);
102
+ char*data=sp_fd_setup(raw);
103
+ memcpy(data,s,len);data[len]=0;
104
+ sp_String*r=(sp_String*)sp_gc_alloc(sizeof(sp_String),sp_String_fin,NULL);
105
+ r->len=len;r->cap=cap;r->data=data;r->binary=0;sp_fd_own(r);
106
+ {sp_gc_hdr*h=(sp_gc_hdr*)((char*)r-sizeof(sp_gc_hdr));h->size+=r->cap+SP_FD_OVH;sp_gc_bytes_add(r->cap+SP_FD_OVH);}
107
+ sp_fd_publish(r);
108
+ return r;
109
+ }
110
+ static inline sp_String*sp_String_new(const char*s){return sp_String_new_len(s,(int64_t)strlen(s));}
111
+ /* The same over a payload inside the object, when it fits: for a String the
112
+ handle is made for and nobody else holds, a literal or a temporary handed to
113
+ a parameter that is the handle (#6179), made and dropped once per call. The
114
+ bytes are read into a stack copy first, for the reason sp_String_new_len
115
+ copies before it allocates. */
116
+ #define SP_FD_INLINE_MAX 192
117
+ static inline sp_String*sp_String_new_inline_len(const char*s,int64_t len){
118
+ int64_t cap=(len*2)+16;
119
+ size_t sz=sizeof(sp_String)+SP_FD_OVH+(size_t)cap;
120
+ if(sz>SP_FD_INLINE_MAX)return sp_String_new_len(s,len);
121
+ char tmp[SP_FD_INLINE_MAX];
122
+ memcpy(tmp,s,(size_t)len);
123
+ sp_String*r=(sp_String*)sp_gc_alloc(sz,NULL,NULL);
124
+ char*data=sp_fd_setup((char*)(r+1));
125
+ memcpy(data,tmp,(size_t)len);data[len]=0;
126
+ r->len=len;r->cap=cap;r->data=data;r->binary=0;sp_fd_own(r);
127
+ sp_fd_publish(r);
128
+ return r;
129
+ }
130
+ /* Shared append core: `tl` is the operand byte length (strlen for the
131
+ bare-literal-safe entry, sp_str_byte_len for the binary one). */
132
+ static inline void sp_fd_append_len(sp_String*s,const char*t,int64_t tl){if(!sp_fd_grow(s,s->len+tl))return;memcpy(s->data+s->len,t,tl);s->len+=tl;s->data[s->len]=0;sp_fd_publish(s);}
133
+ static inline void sp_String_append(sp_String*s,const char*t){if(!s||!t)return;if(sp_String_is_frozen(s)){sp_raise_frozen_str(s->data);return;}sp_fd_append_len(s,t,(int64_t)strlen(t));}
134
+ /* Binary-safe append: sizes the operand with the header length so an embedded
135
+ NUL is preserved (Ruby String#<< / concat on a marked spinel string). */
136
+ /* Replace the buffer contents in place (the handle stays stable, so every
137
+ alias and container holding it observes the new value; #3227). */
138
+ static inline void sp_String_set_bin(sp_String*s,const char*t){if(!s||!t)return;if(sp_String_is_frozen(s)){sp_raise_frozen_str(s->data);return;}s->len=0;sp_fd_append_len(s,t,(int64_t)sp_str_byte_len(t));}
139
+ /* The bytes a read put into its (len, outbuf) buffer: binary-safe, and the
140
+ buffer becomes ASCII-8BIT, as CRuby's readpartial/sysread/read_nonblock
141
+ leave it. */
142
+ static inline void sp_String_set_read_bytes(sp_String*s,const char*t){if(!s||!t)return;sp_String_set_bin(s,t);s->binary=1;sp_fd_publish(s);}
143
+ /* the first tl bytes of t: the append form of an interpolation (emit_interp_append) */
144
+ static inline void sp_String_append_n(sp_String*s,const char*t,size_t tl){if(!s||!t)return;if(sp_String_is_frozen(s)){sp_raise_frozen_str(s->data);return;}sp_fd_append_len(s,t,(int64_t)tl);}
145
+ /* append_as_bytes preserves the handle's encoding as well as embedded NULs. */
146
+ static inline void sp_String_append_bytes(sp_String*s,const char*t){if(!s||!t)return;if(sp_String_is_frozen(s)){sp_raise_frozen_str(s->data);return;}sp_fd_append_len(s,t,(int64_t)sp_str_byte_len(t));}
147
+ static inline void sp_String_append_bin(sp_String*s,const char*t){
148
+ if(!s||!t)return;
149
+ if(sp_String_is_frozen(s)){sp_raise_frozen_str(s->data);return;}
150
+ if (s->binary && !sp_str_is_binary(t) &&
151
+ !sp_str_ascii_only(t) && sp_str_ascii_only(s->data)) {
152
+ s->binary=0;
153
+ sp_str_as_text(s->data);
154
+ }
155
+ sp_fd_append_len(s,t,(int64_t)sp_str_byte_len(t));
156
+ }
157
+ /* Handle wrap for CODEGEN-emitted sources only: every spinel-emitted string
158
+ carries a marker byte at s[-1], so the frozen state (0xf1: an explicit
159
+ .freeze / frozen_string_literal) can be inherited safely. Runtime-internal
160
+ callers pass bare C literals with NO marker -- they must use the plain
161
+ sp_String_new above (reading s[-1] there is OOB and, under clang's rodata
162
+ layout, misreads as frozen; cf. the #282 marker-probe lesson). */
163
+ /* Chilled Strings (sp_str_is_chilled), compiled once in lib/sp_string.c and
164
+ kept off the inlined constructors and +@: sp_String_chill names the symbol
165
+ a new handle came from (a 0xfb static is rare, a chilled one rarer),
166
+ sp_String_chilled_now asks whether a handle is still that symbol's name,
167
+ and sp_sym_to_s_chilled is Symbol#to_s. */
168
+ SP_COLD void sp_String_chill(sp_String*r,const char*s);
169
+ int sp_String_chilled_now(sp_String*h);
170
+ const char*sp_sym_to_s_chilled(sp_sym id);
171
+ static inline sp_String*sp_String_new_shared(const char*s){
172
+ if(!s)return NULL; /* nil into a shared-string slot stays nil (a nullable String, #4567) */
173
+ /* Read every property of `s` HERE, before the allocation below: the handle's
174
+ constructor can collect, and `s` is typically an unrooted temporary (the
175
+ codegen hands this `sp_IntArray_pack(...)` directly), so touching it
176
+ afterwards is a read of freed memory.
177
+
178
+ The ASCII-8BIT tag is inherited, not just the frozen bit: a pack / String#b
179
+ result captured by a block becomes one of these handles, and dropping the
180
+ tag put its bytes back on the character-counting path. The LENGTH has to
181
+ come with it. strlen stops at the first NUL, so sizing a binary payload
182
+ that way truncated it: `Array.new(n, 0).pack("C*")` became the empty string
183
+ the moment it was stored anywhere that promotes it, and the next setbyte
184
+ raised "index 0 out of string" against a zero-length buffer.
185
+
186
+ The length comes from the header for EVERY marked string, not only a
187
+ binary-tagged one. A string of NUL bytes is all-ASCII, so the binary bit
188
+ is off and strlen answered 0 for it too: `Box.new("\0" * 8)` whose ivar is
189
+ later mutated through its reader -- the promotion that brings a handle
190
+ here -- arrived empty, and the setbyte raised against the zero-length
191
+ buffer. sp_str_byte_len reads the header length and falls back to strlen
192
+ only for an UNMARKED string, which this constructor is never given. */
193
+ int bin=sp_str_is_binary(s);
194
+ int frozen=sp_str_is_frozen_val(s);
195
+ int mk=((const unsigned char*)s)[-1];
196
+ int64_t len=(int64_t)sp_str_byte_len(s);
197
+ sp_String*r=sp_String_new_len(s,len);
198
+ if(bin){r->binary=1;sp_fd_publish(r);}
199
+ if(frozen){sp_gc_hdr*h=(sp_gc_hdr*)((char*)r-sizeof(sp_gc_hdr));h->frozen=1;}
200
+ if(SP_UNLIKELY(mk==0xfb))sp_String_chill(r,s); /* a static: still there after the allocation */
201
+ return r;
202
+ }
203
+ /* An expression of either String face as the shared handle: a handle is
204
+ itself, a plain String is wrapped in a fresh one. The emitter writes it where
205
+ a typed handle parameter is filled from an expression whose own emitter
206
+ answers either (`+""`, a fresh String's builtin), and does not know which. */
207
+ static inline sp_String*sp_string_handle_id(sp_String*h){return h;}
208
+ #define SP_AS_STRING_HANDLE(x) _Generic((x), sp_String *: sp_string_handle_id, default: sp_String_new_shared)(x)
209
+ /* sp_String_new_shared for a String no one else holds (a literal's copy, a
210
+ temporary, a plain String a handle parameter reads off the boxed channel):
211
+ the same length and marks, over a payload inside the object. */
212
+ static inline sp_String*sp_String_new_fresh(const char*s){
213
+ if(!s)return NULL;
214
+ int bin=sp_str_is_binary(s);
215
+ int frozen=sp_str_is_frozen_val(s);
216
+ int mk=((const unsigned char*)s)[-1];
217
+ int64_t len=(int64_t)sp_str_byte_len(s);
218
+ sp_String*r=sp_String_new_inline_len(s,len);
219
+ if(bin){r->binary=1;sp_fd_publish(r);}
220
+ if(frozen){sp_gc_hdr*h=(sp_gc_hdr*)((char*)r-sizeof(sp_gc_hdr));h->frozen=1;}
221
+ if(SP_UNLIKELY(mk==0xfb))sp_String_chill(r,s); /* a static: still there after the allocation */
222
+ return r;
223
+ }
224
+ /* ...and for `+"lit"`: a mutable copy of a literal, which is frozen itself */
225
+ static inline sp_String*sp_String_new_unfrozen(const char*s){
226
+ if(!s)return NULL;
227
+ int bin=sp_str_is_binary(s);
228
+ sp_String*r=sp_String_new_inline_len(s,(int64_t)sp_str_byte_len(s));
229
+ if(bin){r->binary=1;sp_fd_publish(r);}
230
+ return r;
231
+ }
232
+ /* force_encoding / encode! on a handle: the ASCII-8BIT tag goes on the
233
+ handle, where every later growth re-stamps it from (sp_fd_publish), and
234
+ into the bytes now; mode 1 sets it, 0 clears it, -1 (another encoding)
235
+ leaves it. A frozen String refuses either way, as CRuby does (#3334). */
236
+ static inline sp_String*sp_String_force_encoding(sp_String*s,int mode){
237
+ if(sp_String_is_frozen(s)){sp_raise_frozen_str(s->data);return s;}
238
+ if(mode<0)return s;
239
+ s->binary=(unsigned)mode;
240
+ sp_str_hdr*h=(sp_str_hdr*)sp_fd_base(s->data);
241
+ if(mode)h->size|=SP_STR_SIZE_BINARY;else h->size&=~SP_STR_SIZE_BINARY;
242
+ sp_str_lcache_drop(s->data);
243
+ return s;
244
+ }
245
+ static inline const char*sp_String_cstr(sp_String*s){return s->data;}
246
+ static inline int64_t sp_String_length(sp_String*s){return s->len;}
247
+
248
+ /* Cold in-place mutators (compiled once in lib/sp_string.c). */
249
+ void sp_String_prepend(sp_String*s,const char*t);
250
+ void sp_String_insert(sp_String*s,int64_t idx,const char*t);
251
+ void sp_String_replace(sp_String*s,const char*t);
252
+ sp_String*sp_String_dup(sp_String*s);
253
+ #endif /* SP_STRING_H */
@@ -0,0 +1,78 @@
1
+ /* sp_system.c -- system()/backtick support in libspinel_rt.a.
2
+ * See sp_system.h.
3
+ *
4
+ * Self-contained (libc + OS process API only); does not include
5
+ * spinel_rt.h, so it carries its own sp_bool/TRUE/FALSE locally. */
6
+ #include "sp_system.h"
7
+ #include <stdlib.h>
8
+ #include <string.h>
9
+ #include <stdio.h>
10
+ #include <ctype.h>
11
+ #include <errno.h>
12
+ #include <unistd.h>
13
+ #include <sys/wait.h>
14
+ #if defined(_WIN32)
15
+ #include <spawn.h>
16
+ #endif
17
+
18
+ typedef int sp_bool;
19
+ #ifndef TRUE
20
+ #define TRUE 1
21
+ #endif
22
+ #ifndef FALSE
23
+ #define FALSE 0
24
+ #endif
25
+
26
+ int sp_last_status = 0;
27
+ int sp_last_pid = 0;
28
+
29
+
30
+ int sp_system_args(int argc, const char *const *argv) {
31
+ if (argc <= 0 || argv == NULL || argv[0] == NULL) {
32
+ sp_last_status = -1;
33
+ return FALSE;
34
+ }
35
+ fflush(NULL);
36
+ #if defined(_WIN32)
37
+ /* posix_spawn where there is no fork (see sp_process.c): a program that
38
+ cannot be started leaves $? at the 127 its failed exec exits with */
39
+ pid_t pid = 0;
40
+ {
41
+ int rc;
42
+ if (argc == 1) {
43
+ char *av[] = { (char *)"sh", (char *)"-c", (char *)argv[0], NULL };
44
+ rc = posix_spawn(&pid, "/bin/sh", NULL, NULL, av, environ);
45
+ }
46
+ else rc = posix_spawnp(&pid, argv[0], NULL, NULL, (char *const *)argv, environ);
47
+ if (rc != 0) { sp_last_status = 127 << 8; return FALSE; }
48
+ }
49
+ #else
50
+ pid_t pid = fork();
51
+ if (pid < 0) {
52
+ sp_last_status = -1;
53
+ return FALSE;
54
+ }
55
+ if (pid == 0) {
56
+ if (argc == 1) {
57
+ execl("/bin/sh", "sh", "-c", argv[0], (char *)NULL);
58
+ }
59
+ else {
60
+ execvp(argv[0], (char * const *)argv);
61
+ }
62
+ _exit(127);
63
+ }
64
+ #endif
65
+ /* Same rule as Process.waitpid2 (#4381): a blocking wait answers for the OS
66
+ worker, and a started green thread is pinned to its worker, so it would
67
+ stall the very thread that may have to drain this child's output before it
68
+ can exit. sp_sched_wait_child polls and hands the scheduler back while any
69
+ other thread is alive, and keeps the blocking wait when none is. */
70
+ int status = 0;
71
+ {
72
+ extern int sp_sched_wait_child(int pid, int *status);
73
+ if (sp_sched_wait_child((int)pid, &status) < 0) { sp_last_status = -1; return FALSE; }
74
+ }
75
+ sp_last_status = status;
76
+ sp_last_pid = (int)pid;
77
+ return (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? TRUE : FALSE;
78
+ }
@@ -0,0 +1,18 @@
1
+ /* sp_system.h -- system()/backtick support in libspinel_rt.a.
2
+ *
3
+ * `sp_last_status` backs Ruby's `$?` (the exit status of the last
4
+ * `system` / backtick). `sp_system_args` runs argv[0] with the rest as
5
+ * arguments (the multi-arg `system(...)` form), setting `sp_last_status`
6
+ * and returning whether the child exited 0. The bool result is declared
7
+ * as `int` to stay decoupled from the runtime's typedefs.
8
+ */
9
+ #ifndef SP_SYSTEM_H
10
+ #define SP_SYSTEM_H
11
+
12
+ extern int sp_last_status;
13
+ /* the pid of the child sp_last_status is from; 0 until a child is waited
14
+ for, when `$?` is nil */
15
+ extern int sp_last_pid;
16
+ int sp_system_args(int argc, const char *const *argv);
17
+
18
+ #endif