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.
- checksums.yaml +7 -0
- data/LICENSE-mruby.txt +22 -0
- data/LICENSE-spinel.txt +19 -0
- data/LICENSE.txt +21 -0
- data/README.md +61 -0
- data/ext/qr_reader_native/SPINEL_VERSION +1 -0
- data/ext/qr_reader_native/extconf.rb +8 -0
- data/ext/qr_reader_native/mruby/error.h +13 -0
- data/ext/qr_reader_native/mruby/internal.h +145 -0
- data/ext/qr_reader_native/mruby/string.h +22 -0
- data/ext/qr_reader_native/mruby.h +93 -0
- data/ext/qr_reader_native/mruby_shim.h +118 -0
- data/ext/qr_reader_native/qr_reader_native.c +13513 -0
- data/ext/qr_reader_native/qr_reader_native.h +29 -0
- data/ext/qr_reader_native/qr_reader_native_ext.c +598 -0
- data/ext/qr_reader_native/re_cased.h +71 -0
- data/ext/qr_reader_native/re_compile.c +5784 -0
- data/ext/qr_reader_native/re_ctype.h +3540 -0
- data/ext/qr_reader_native/re_exec.c +2190 -0
- data/ext/qr_reader_native/re_internal.h +671 -0
- data/ext/qr_reader_native/re_prop.h +4629 -0
- data/ext/qr_reader_native/re_spinel.c +363 -0
- data/ext/qr_reader_native/re_spinel.h +22 -0
- data/ext/qr_reader_native/re_spinel_utf8.inc +97 -0
- data/ext/qr_reader_native/re_utf8.c +156 -0
- data/ext/qr_reader_native/sp_alloc.c +1447 -0
- data/ext/qr_reader_native/sp_alloc.h +1196 -0
- data/ext/qr_reader_native/sp_argf.h +33 -0
- data/ext/qr_reader_native/sp_array.c +1052 -0
- data/ext/qr_reader_native/sp_array.h +456 -0
- data/ext/qr_reader_native/sp_bigint.c +5826 -0
- data/ext/qr_reader_native/sp_bigint.h +117 -0
- data/ext/qr_reader_native/sp_cold.c +5259 -0
- data/ext/qr_reader_native/sp_compat.h +402 -0
- data/ext/qr_reader_native/sp_core.c +818 -0
- data/ext/qr_reader_native/sp_core.h +56 -0
- data/ext/qr_reader_native/sp_crypto.c +877 -0
- data/ext/qr_reader_native/sp_crypto.h +169 -0
- data/ext/qr_reader_native/sp_dtoa.c +1533 -0
- data/ext/qr_reader_native/sp_dtoa.h +29 -0
- data/ext/qr_reader_native/sp_encoding_names.h +172 -0
- data/ext/qr_reader_native/sp_enum.h +86 -0
- data/ext/qr_reader_native/sp_exc.c +1185 -0
- data/ext/qr_reader_native/sp_exc.h +186 -0
- data/ext/qr_reader_native/sp_exc_ctx.h +56 -0
- data/ext/qr_reader_native/sp_fiber.c +889 -0
- data/ext/qr_reader_native/sp_fiber.h +162 -0
- data/ext/qr_reader_native/sp_fiber_ctx.h +84 -0
- data/ext/qr_reader_native/sp_format.c +695 -0
- data/ext/qr_reader_native/sp_format.h +83 -0
- data/ext/qr_reader_native/sp_gc.c +1793 -0
- data/ext/qr_reader_native/sp_gc.h +762 -0
- data/ext/qr_reader_native/sp_hash.c +131 -0
- data/ext/qr_reader_native/sp_hash.h +131 -0
- data/ext/qr_reader_native/sp_inspect.c +215 -0
- data/ext/qr_reader_native/sp_inspect.h +128 -0
- data/ext/qr_reader_native/sp_io.c +1676 -0
- data/ext/qr_reader_native/sp_io.h +272 -0
- data/ext/qr_reader_native/sp_iobuffer.c +1014 -0
- data/ext/qr_reader_native/sp_iobuffer.h +167 -0
- data/ext/qr_reader_native/sp_marshal.c +487 -0
- data/ext/qr_reader_native/sp_marshal.h +60 -0
- data/ext/qr_reader_native/sp_net.c +1029 -0
- data/ext/qr_reader_native/sp_net.h +205 -0
- data/ext/qr_reader_native/sp_pack.c +1254 -0
- data/ext/qr_reader_native/sp_poly_cold.c +1220 -0
- data/ext/qr_reader_native/sp_proc.c +410 -0
- data/ext/qr_reader_native/sp_proc.h +327 -0
- data/ext/qr_reader_native/sp_process.c +547 -0
- data/ext/qr_reader_native/sp_process_status.c +211 -0
- data/ext/qr_reader_native/sp_process_status.h +66 -0
- data/ext/qr_reader_native/sp_random.c +287 -0
- data/ext/qr_reader_native/sp_random.h +52 -0
- data/ext/qr_reader_native/sp_range.h +144 -0
- data/ext/qr_reader_native/sp_re.c +1379 -0
- data/ext/qr_reader_native/sp_re.h +186 -0
- data/ext/qr_reader_native/sp_re_names.h +59 -0
- data/ext/qr_reader_native/sp_sched.c +4044 -0
- data/ext/qr_reader_native/sp_sched.h +295 -0
- data/ext/qr_reader_native/sp_slab.c +1507 -0
- data/ext/qr_reader_native/sp_str.c +1462 -0
- data/ext/qr_reader_native/sp_str.h +306 -0
- data/ext/qr_reader_native/sp_str_crypt.c +23 -0
- data/ext/qr_reader_native/sp_string.c +92 -0
- data/ext/qr_reader_native/sp_string.h +253 -0
- data/ext/qr_reader_native/sp_system.c +78 -0
- data/ext/qr_reader_native/sp_system.h +18 -0
- data/ext/qr_reader_native/sp_time.c +860 -0
- data/ext/qr_reader_native/sp_time.h +100 -0
- data/ext/qr_reader_native/sp_types.h +292 -0
- data/ext/qr_reader_native/spinel_rt.h +19017 -0
- data/ext/qr_reader_native/unicase.c +399 -0
- data/ext/qr_reader_native/unicase.h +901 -0
- data/lib/qr_reader/native.rb +6 -0
- 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
|