@scriptc/runtime 0.0.4 → 0.0.6
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.
- package/package.json +1 -1
- package/src/scr_array.c +6 -12
- package/src/scr_assert.c +32 -2
- package/src/scr_bytes.c +3 -6
- package/src/scr_bytes_io.c +1 -2
- package/src/scr_closure.c +1 -2
- package/src/scr_console.c +5 -0
- package/src/scr_cycle.c +1 -2
- package/src/scr_error.c +2 -4
- package/src/scr_events_emitter.c +1 -2
- package/src/scr_exception.c +87 -2
- package/src/scr_inspect.c +83 -2
- package/src/scr_json.c +157 -20
- package/src/scr_lib.c +50 -66
- package/src/scr_library.c +222 -0
- package/src/scr_map.c +2 -4
- package/src/scr_path.c +4 -8
- package/src/scr_regex.c +16 -25
- package/src/scr_runtime.h +127 -1
- package/src/scr_string.c +2 -4
- package/src/scr_symbol.c +2 -3
- package/src/scr_url.c +9 -16
- package/src/scr_url_params.c +6 -12
- package/src/scr_zlib.c +1 -2
package/package.json
CHANGED
package/src/scr_array.c
CHANGED
|
@@ -13,8 +13,7 @@ long scr_arr_live_count(void) { return scr_live_arrays; }
|
|
|
13
13
|
#endif
|
|
14
14
|
|
|
15
15
|
static void scr_arr_oom(void) {
|
|
16
|
-
|
|
17
|
-
abort();
|
|
16
|
+
scr_trap("scriptc: out of memory\n");
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
/* JS would return undefined for an OOB read and create holes for a far OOB
|
|
@@ -23,10 +22,8 @@ static void scr_arr_oom(void) {
|
|
|
23
22
|
static void scr_arr_trap_oob(double i, size_t len) {
|
|
24
23
|
char buf[32];
|
|
25
24
|
scr_f64_to_str(i, buf);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
buf, len);
|
|
29
|
-
abort();
|
|
25
|
+
scr_trap_fmt("scriptc: RangeError: array index %s out of bounds (length %zu)\n",
|
|
26
|
+
buf, len);
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
/* Validate i as an element index. limit is a->len for reads, a->len + 1 for
|
|
@@ -264,8 +261,7 @@ double scr_arr_push_ref(ScrArr *a, void *v) {
|
|
|
264
261
|
|
|
265
262
|
static uint64_t scr_arr_pop_slot(ScrArr *a) {
|
|
266
263
|
if (a->len == 0) {
|
|
267
|
-
|
|
268
|
-
abort();
|
|
264
|
+
scr_trap("scriptc: RangeError: pop() on an empty array\n");
|
|
269
265
|
}
|
|
270
266
|
return a->data[--a->len];
|
|
271
267
|
}
|
|
@@ -283,8 +279,7 @@ void *scr_arr_pop_ref(ScrArr *a) { return scr_slot_to_ptr(scr_arr_pop_slot(a));
|
|
|
283
279
|
* ownership moves out to the caller (no retain). */
|
|
284
280
|
static uint64_t scr_arr_shift_slot(ScrArr *a) {
|
|
285
281
|
if (a->len == 0) {
|
|
286
|
-
|
|
287
|
-
abort();
|
|
282
|
+
scr_trap("scriptc: internal error: shift() on an empty array\n");
|
|
288
283
|
}
|
|
289
284
|
uint64_t s = a->data[0];
|
|
290
285
|
a->len--;
|
|
@@ -456,8 +451,7 @@ ScrStr *scr_arr_join(ScrArr *a, ScrStr *sep) {
|
|
|
456
451
|
case SCR_ELEM_BYTES:
|
|
457
452
|
case SCR_ELEM_REF:
|
|
458
453
|
/* The compiler rejects join on ref-element arrays (SC1090). */
|
|
459
|
-
|
|
460
|
-
abort();
|
|
454
|
+
scr_trap("scriptc: internal error: join on a ref-element array\n");
|
|
461
455
|
}
|
|
462
456
|
}
|
|
463
457
|
ScrStr *out = scr_str_new(buf, len);
|
package/src/scr_assert.c
CHANGED
|
@@ -32,8 +32,7 @@
|
|
|
32
32
|
#include <string.h>
|
|
33
33
|
|
|
34
34
|
static void scr_assert_oom(void) {
|
|
35
|
-
|
|
36
|
-
abort();
|
|
35
|
+
scr_trap("scriptc: out of memory\n");
|
|
37
36
|
}
|
|
38
37
|
|
|
39
38
|
/* ── a tiny append-only byte buffer for message assembly ─────────────── */
|
|
@@ -151,6 +150,37 @@ void scr_assert_ok(bool pass, ScrStr *message) {
|
|
|
151
150
|
|
|
152
151
|
/* SameValue over doubles — Object.is, the comparison of strictEqual AND
|
|
153
152
|
* deepStrictEqual for numbers (NaN equals NaN; +0 and -0 differ). */
|
|
153
|
+
/* ── deepStrictEqual over cyclic values ────────────────────────────────
|
|
154
|
+
* RECURSIVE record types permit reference cycles; Node's deep equality
|
|
155
|
+
* memoizes (value1, value2) pairs so equal cyclic structures compare
|
|
156
|
+
* true instead of recursing forever. The compiler-emitted per-type
|
|
157
|
+
* helpers over cycle-capable types wrap their walks in enter/leave: a
|
|
158
|
+
* PAIR already being compared answers equal (the coinductive step —
|
|
159
|
+
* Node's memo behavior exactly). The stack is global (comparisons never
|
|
160
|
+
* interleave; the emitted walks cannot throw mid-compare). */
|
|
161
|
+
static struct { const void *a, *b; } *g_deq_stack;
|
|
162
|
+
static size_t g_deq_len;
|
|
163
|
+
static size_t g_deq_cap;
|
|
164
|
+
|
|
165
|
+
bool scr_assert_deq_enter(const void *a, const void *b) {
|
|
166
|
+
for (size_t i = 0; i < g_deq_len; i++) {
|
|
167
|
+
if (g_deq_stack[i].a == a && g_deq_stack[i].b == b) return true;
|
|
168
|
+
}
|
|
169
|
+
if (g_deq_len == g_deq_cap) {
|
|
170
|
+
g_deq_cap = g_deq_cap ? g_deq_cap * 2 : 8;
|
|
171
|
+
g_deq_stack = realloc(g_deq_stack, g_deq_cap * sizeof *g_deq_stack);
|
|
172
|
+
if (!g_deq_stack) scr_assert_oom();
|
|
173
|
+
}
|
|
174
|
+
g_deq_stack[g_deq_len].a = a;
|
|
175
|
+
g_deq_stack[g_deq_len].b = b;
|
|
176
|
+
g_deq_len++;
|
|
177
|
+
return false;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
void scr_assert_deq_leave(void) {
|
|
181
|
+
if (g_deq_len > 0) g_deq_len--;
|
|
182
|
+
}
|
|
183
|
+
|
|
154
184
|
bool scr_assert_same_value_f64(double a, double b) {
|
|
155
185
|
if (a == b) return a != 0.0 || signbit(a) == signbit(b);
|
|
156
186
|
return isnan(a) && isnan(b);
|
package/src/scr_bytes.c
CHANGED
|
@@ -17,8 +17,7 @@ long scr_bytes_live_count(void) { return scr_live_bytes; }
|
|
|
17
17
|
#endif
|
|
18
18
|
|
|
19
19
|
static void scr_bytes_oom(void) {
|
|
20
|
-
|
|
21
|
-
abort();
|
|
20
|
+
scr_trap("scriptc: out of memory\n");
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
size_t scr_bytes_elem_size(ScrBytesElem elem) {
|
|
@@ -98,10 +97,8 @@ static size_t scr_bytes_check_index(const ScrBytes *b, double i) {
|
|
|
98
97
|
if (!(i >= 0) || i != trunc(i) || i >= (double)b->len) {
|
|
99
98
|
char buf[32];
|
|
100
99
|
scr_f64_to_str(i, buf);
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
buf, b->len);
|
|
104
|
-
abort();
|
|
100
|
+
scr_trap_fmt("scriptc: RangeError: typed array index %s out of bounds (length %zu)\n",
|
|
101
|
+
buf, b->len);
|
|
105
102
|
}
|
|
106
103
|
return (size_t)i;
|
|
107
104
|
}
|
package/src/scr_bytes_io.c
CHANGED
package/src/scr_closure.c
CHANGED
|
@@ -146,8 +146,7 @@ void *scr_box_get_ref(ScrBox *b) {
|
|
|
146
146
|
case SCR_BOX_OBJ:
|
|
147
147
|
return b->obj_retain(p);
|
|
148
148
|
default:
|
|
149
|
-
|
|
150
|
-
abort();
|
|
149
|
+
scr_trap("scriptc: internal error: box_get_ref on a scalar box\n");
|
|
151
150
|
}
|
|
152
151
|
}
|
|
153
152
|
|
package/src/scr_console.c
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
static long scr_abandoned_fibers = 0;
|
|
15
15
|
void scr_note_abandoned_fibers(long n) { scr_abandoned_fibers = n; }
|
|
16
16
|
|
|
17
|
+
#ifndef SCR_LIB
|
|
17
18
|
#ifdef SCR_RC_AUDIT
|
|
18
19
|
extern long scr_str_live_count(void); /* scr_string.c */
|
|
19
20
|
extern long scr_arr_live_count(void); /* scr_array.c */
|
|
@@ -96,6 +97,10 @@ void scr_init(void) {
|
|
|
96
97
|
#endif
|
|
97
98
|
atexit(scr_collect_cycles_at_exit);
|
|
98
99
|
}
|
|
100
|
+
#endif /* !SCR_LIB — a library artifact never touches host stdio modes/buffering and
|
|
101
|
+
* registers no atexit handlers: scr_init and its exit hooks (the
|
|
102
|
+
* audit's _Exit(99) included) are executable-lane machinery; the
|
|
103
|
+
* library session reset lives in scr_library.c. */
|
|
99
104
|
|
|
100
105
|
/* ONE formatter for both console streams — console.error/warn print
|
|
101
106
|
* byte-identically to console.log in Node (same inspect rendering), only
|
package/src/scr_cycle.c
CHANGED
|
@@ -39,8 +39,7 @@
|
|
|
39
39
|
#define SCR_RC(obj) (*(size_t *)(obj))
|
|
40
40
|
|
|
41
41
|
static void scr_cyc_oom(void) {
|
|
42
|
-
|
|
43
|
-
abort();
|
|
42
|
+
scr_trap("scriptc: out of memory\n");
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
void *scr_cyc_alloc(size_t size, ScrTraceFn trace, ScrCycFreeFn free_fn) {
|
package/src/scr_error.c
CHANGED
|
@@ -14,8 +14,7 @@
|
|
|
14
14
|
#include <stdlib.h>
|
|
15
15
|
|
|
16
16
|
static void scr_error_oom(void) {
|
|
17
|
-
|
|
18
|
-
abort();
|
|
17
|
+
scr_trap("scriptc: out of memory\n");
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
static bool scr_error_traced = false;
|
|
@@ -247,8 +246,7 @@ void scr_undef_global_read(ScrStr *name) {
|
|
|
247
246
|
size_t len = name->len + sizeof suffix - 1;
|
|
248
247
|
char *msg = malloc(len + 1);
|
|
249
248
|
if (!msg) {
|
|
250
|
-
|
|
251
|
-
abort();
|
|
249
|
+
scr_trap("scriptc: out of memory\n");
|
|
252
250
|
}
|
|
253
251
|
memcpy(msg, name->data, name->len);
|
|
254
252
|
memcpy(msg + name->len, suffix, sizeof suffix);
|
package/src/scr_events_emitter.c
CHANGED
package/src/scr_exception.c
CHANGED
|
@@ -13,6 +13,28 @@
|
|
|
13
13
|
#include <stdio.h>
|
|
14
14
|
#include <stdlib.h>
|
|
15
15
|
|
|
16
|
+
#ifndef SCR_LIB
|
|
17
|
+
/* The trap funnel's EXECUTABLE expansion: exactly the historical
|
|
18
|
+
* fputs/vfprintf-to-stderr + abort every trap site used to open-code — the
|
|
19
|
+
* default lane's bytes and behavior are unchanged by construction. Library
|
|
20
|
+
* builds (-DSCR_LIB) get the sink-routing definitions from scr_library.c
|
|
21
|
+
* instead; this pair compiles out there. Lives HERE (the failure-channel
|
|
22
|
+
* unit, present in every link including the runtime's own unit-test
|
|
23
|
+
* subsets) rather than in the console unit. */
|
|
24
|
+
_Noreturn void scr_trap(const char *msg) {
|
|
25
|
+
fputs(msg, stderr);
|
|
26
|
+
abort();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
_Noreturn void scr_trap_fmt(const char *fmt, ...) {
|
|
30
|
+
va_list ap;
|
|
31
|
+
va_start(ap, fmt);
|
|
32
|
+
vfprintf(stderr, fmt, ap);
|
|
33
|
+
va_end(ap);
|
|
34
|
+
abort();
|
|
35
|
+
}
|
|
36
|
+
#endif /* !SCR_LIB */
|
|
37
|
+
|
|
16
38
|
static ScrExcCell scr_main_exc; /* fiber zero (the main stack) */
|
|
17
39
|
static ScrExcCell *scr_cur = &scr_main_exc;
|
|
18
40
|
|
|
@@ -60,8 +82,7 @@ void scr_exc_clear(void) { scr_exc_reset(); }
|
|
|
60
82
|
ScrCaught *scr_exc_take(void) {
|
|
61
83
|
ScrCaught *c = calloc(1, sizeof *c);
|
|
62
84
|
if (!c) {
|
|
63
|
-
|
|
64
|
-
abort();
|
|
85
|
+
scr_trap("scriptc: out of memory\n");
|
|
65
86
|
}
|
|
66
87
|
c->rc = 1;
|
|
67
88
|
c->kind = scr_exc_kind;
|
|
@@ -154,6 +175,70 @@ ScrStr *scr_caught_to_string(const ScrCaught *c) {
|
|
|
154
175
|
return scr_str_new("", 0);
|
|
155
176
|
}
|
|
156
177
|
|
|
178
|
+
#ifdef SCR_LIB
|
|
179
|
+
/* An exception that escaped user code into a generated entry wrapper: the
|
|
180
|
+
* host contract is that declared entries do not throw, so an escape is a
|
|
181
|
+
* contract-shaped failure exactly like a trap. Render the same "Uncaught
|
|
182
|
+
* ..." first line the executable epilogue prints (scr_exc_print_uncaught's
|
|
183
|
+
* arms, buffer-writing), release the payload, and route the text through
|
|
184
|
+
* the trap funnel — one failure channel at the boundary. */
|
|
185
|
+
void scr_library_check_exc(void) {
|
|
186
|
+
if (!scr_exc_pending()) return;
|
|
187
|
+
static char buf[1024]; /* the message is copied out before the payload dies */
|
|
188
|
+
size_t n = 0;
|
|
189
|
+
const char prefix[] = "Uncaught ";
|
|
190
|
+
memcpy(buf, prefix, sizeof prefix - 1);
|
|
191
|
+
n = sizeof prefix - 1;
|
|
192
|
+
const size_t cap = sizeof buf - 2; /* room for '\n' + NUL */
|
|
193
|
+
switch (scr_exc_kind) {
|
|
194
|
+
case SCR_EXC_F64:
|
|
195
|
+
n += scr_f64_to_str(scr_exc_f64, buf + n);
|
|
196
|
+
break;
|
|
197
|
+
case SCR_EXC_BOOL: {
|
|
198
|
+
const char *b = scr_exc_bool ? "true" : "false";
|
|
199
|
+
size_t bl = strlen(b);
|
|
200
|
+
memcpy(buf + n, b, bl);
|
|
201
|
+
n += bl;
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
case SCR_EXC_STR: {
|
|
205
|
+
const ScrStr *s = (const ScrStr *)scr_exc_payload;
|
|
206
|
+
size_t take = s->len > cap - n ? cap - n : s->len;
|
|
207
|
+
memcpy(buf + n, s->data, take);
|
|
208
|
+
n += take;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case SCR_EXC_OBJ:
|
|
212
|
+
if (scr_error_is(scr_exc_payload)) {
|
|
213
|
+
ScrStr *s = scr_error_to_string((ScrError *)scr_exc_payload);
|
|
214
|
+
size_t take = s->len > cap - n ? cap - n : s->len;
|
|
215
|
+
memcpy(buf + n, s->data, take);
|
|
216
|
+
n += take;
|
|
217
|
+
scr_str_release(s);
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
/* fall through: non-Error hierarchy objects render like other refs */
|
|
221
|
+
case SCR_EXC_REF: {
|
|
222
|
+
const char obj[] = "[object]";
|
|
223
|
+
memcpy(buf + n, obj, sizeof obj - 1);
|
|
224
|
+
n += sizeof obj - 1;
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
case SCR_EXC_NONE:
|
|
228
|
+
case SCR_EXC_GENRET: { /* unreachable through the pending() gate */
|
|
229
|
+
const char none[] = "(no exception)";
|
|
230
|
+
memcpy(buf + n, none, sizeof none - 1);
|
|
231
|
+
n += sizeof none - 1;
|
|
232
|
+
break;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
buf[n++] = '\n';
|
|
236
|
+
buf[n] = '\0';
|
|
237
|
+
scr_exc_reset(); /* the payload is released before the funnel poisons */
|
|
238
|
+
scr_trap(buf);
|
|
239
|
+
}
|
|
240
|
+
#endif /* SCR_LIB */
|
|
241
|
+
|
|
157
242
|
void scr_throw_f64(double v) {
|
|
158
243
|
scr_exc_reset();
|
|
159
244
|
scr_exc_kind = SCR_EXC_F64;
|
package/src/scr_inspect.c
CHANGED
|
@@ -31,8 +31,7 @@
|
|
|
31
31
|
#include <string.h>
|
|
32
32
|
|
|
33
33
|
static void insp_oom(void) {
|
|
34
|
-
|
|
35
|
-
abort();
|
|
34
|
+
scr_trap("scriptc: out of memory\n");
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
/* ── a tiny append-only byte buffer ──────────────────────────────────── */
|
|
@@ -246,10 +245,13 @@ static double g_cur_depth; /* ctx.currentDepth (last-entered composite) */
|
|
|
246
245
|
|
|
247
246
|
static InspFrame *insp_top(void) { return &g_frames[g_nframes - 1]; }
|
|
248
247
|
|
|
248
|
+
static void insp_circ_reset(void); /* circular-reference state, below */
|
|
249
|
+
|
|
249
250
|
/* Begin a non-empty composite: formatRaw's `recurseTimes += 1;
|
|
250
251
|
* ctx.currentDepth = recurseTimes` plus the uniform +2 the children
|
|
251
252
|
* format under. `recurse` is the CHILDREN's recursion depth. */
|
|
252
253
|
void scr_insp_begin(double recurse) {
|
|
254
|
+
if (recurse == 1) insp_circ_reset(); /* a fresh top-level value: Node's per-inspect ctx */
|
|
253
255
|
if (g_nframes == g_frames_cap) {
|
|
254
256
|
g_frames_cap = g_frames_cap ? g_frames_cap * 2 : 8;
|
|
255
257
|
g_frames = realloc(g_frames, g_frames_cap * sizeof(InspFrame));
|
|
@@ -278,6 +280,85 @@ void scr_insp_entry(ScrStr *s, bool is_num) {
|
|
|
278
280
|
if (!is_num) f->all_num = false;
|
|
279
281
|
}
|
|
280
282
|
|
|
283
|
+
/* ── circular references (Node's <ref *N> / [Circular *N]) ────────────
|
|
284
|
+
* Recursive record/class types permit runtime reference cycles, and Node
|
|
285
|
+
* renders them with formatValue's seen/circular machinery: a value
|
|
286
|
+
* already ON the current traversal stack renders as `[Circular *N]` (N
|
|
287
|
+
* assigned at first detection, in discovery order), and every rendering
|
|
288
|
+
* of a so-numbered value gets the `<ref *N> ` prefix at its close. The
|
|
289
|
+
* compiler-emitted helpers over CYCLE-CAPABLE composites drive exactly
|
|
290
|
+
* that protocol: circ_check first (before the empty-literal and depth
|
|
291
|
+
* answers — Node's order: a circular value beyond the depth budget still
|
|
292
|
+
* says Circular), seen_push after begin, ref_wrap around end's result.
|
|
293
|
+
* The circular MAP persists for one whole top-level inspect — begin(1)
|
|
294
|
+
* is the per-value reset (a root composite's first frame). */
|
|
295
|
+
typedef struct {
|
|
296
|
+
const void *ptr;
|
|
297
|
+
int id; /* circular id (0 while only on the stack) */
|
|
298
|
+
} InspSeenEnt;
|
|
299
|
+
|
|
300
|
+
static InspSeenEnt *g_seen;
|
|
301
|
+
static size_t g_nseen;
|
|
302
|
+
static size_t g_seen_cap;
|
|
303
|
+
/* Detection-numbered circular targets (persist across the call). */
|
|
304
|
+
static const void *g_circ[64];
|
|
305
|
+
static int g_ncirc;
|
|
306
|
+
|
|
307
|
+
static void insp_circ_reset(void) {
|
|
308
|
+
g_nseen = 0;
|
|
309
|
+
g_ncirc = 0;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
static int insp_circ_id(const void *v) {
|
|
313
|
+
for (int i = 0; i < g_ncirc; i++) {
|
|
314
|
+
if (g_circ[i] == v) return i + 1;
|
|
315
|
+
}
|
|
316
|
+
return 0;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
double scr_insp_circ_check(const void *v) {
|
|
320
|
+
for (size_t i = 0; i < g_nseen; i++) {
|
|
321
|
+
if (g_seen[i].ptr != v) continue;
|
|
322
|
+
int id = insp_circ_id(v);
|
|
323
|
+
if (id == 0 && g_ncirc < (int)(sizeof g_circ / sizeof *g_circ)) {
|
|
324
|
+
g_circ[g_ncirc++] = v;
|
|
325
|
+
id = g_ncirc;
|
|
326
|
+
}
|
|
327
|
+
return (double)id;
|
|
328
|
+
}
|
|
329
|
+
return 0;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
void scr_insp_seen_push(const void *v) {
|
|
333
|
+
if (g_nseen == g_seen_cap) {
|
|
334
|
+
g_seen_cap = g_seen_cap ? g_seen_cap * 2 : 8;
|
|
335
|
+
g_seen = realloc(g_seen, g_seen_cap * sizeof(InspSeenEnt));
|
|
336
|
+
if (!g_seen) insp_oom();
|
|
337
|
+
}
|
|
338
|
+
g_seen[g_nseen].ptr = v;
|
|
339
|
+
g_seen[g_nseen].id = 0;
|
|
340
|
+
g_nseen++;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
ScrStr *scr_insp_circular(double id) {
|
|
344
|
+
char buf[32];
|
|
345
|
+
int n = snprintf(buf, sizeof buf, "[Circular *%.0f]", id);
|
|
346
|
+
return scr_str_new(buf, (size_t)n);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
ScrStr *scr_insp_ref_wrap(const void *v, ScrStr *s) {
|
|
350
|
+
if (g_nseen > 0) g_nseen--;
|
|
351
|
+
int id = insp_circ_id(v);
|
|
352
|
+
if (id == 0) return scr_str_retain(s);
|
|
353
|
+
char buf[32];
|
|
354
|
+
int n = snprintf(buf, sizeof buf, "<ref *%d> ", id);
|
|
355
|
+
ScrStr *out = scr_str_alloc_raw(s->len + (size_t)n, s->len + (size_t)n);
|
|
356
|
+
memcpy(out->data, buf, (size_t)n);
|
|
357
|
+
memcpy(out->data + n, s->data, s->len);
|
|
358
|
+
out->data[out->len] = '\0';
|
|
359
|
+
return out;
|
|
360
|
+
}
|
|
361
|
+
|
|
281
362
|
/* remainingText: the "... N more items" tail entry. */
|
|
282
363
|
ScrStr *scr_insp_more_items(double remaining) {
|
|
283
364
|
char buf[64];
|