@scriptc/runtime 0.0.8 → 0.0.10
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_assert.c +43 -4
- package/src/scr_bytes.c +65 -18
- package/src/scr_bytes_io.c +507 -1
- package/src/scr_dc.c +6 -0
- package/src/scr_dgram.c +176 -1
- package/src/scr_dyn_handle.c +8 -66
- package/src/scr_dyn_invoke.c +96 -2
- package/src/scr_error.c +15 -0
- package/src/scr_events_emitter.c +34 -0
- package/src/scr_exception.c +4 -1
- package/src/scr_inspect.c +27 -3
- package/src/scr_island.c +417 -3
- package/src/scr_json.c +643 -6
- package/src/scr_lib.c +122 -4
- package/src/scr_library.c +105 -2
- package/src/scr_net.c +112 -0
- package/src/scr_number.c +29 -19
- package/src/scr_runtime.h +358 -9
- package/src/scr_stream.c +49 -2
- package/src/scr_tls.c +172 -0
package/src/scr_lib.c
CHANGED
|
@@ -1347,7 +1347,27 @@ static double scr_net_autosel_timeout_ms = 250;
|
|
|
1347
1347
|
|
|
1348
1348
|
double scr_net_get_autosel_timeout(void) { return scr_net_autosel_timeout_ms; }
|
|
1349
1349
|
|
|
1350
|
-
|
|
1350
|
+
/* Node's setDefaultAutoSelectFamilyAttemptTimeout: validateInt32(value,
|
|
1351
|
+
* 'value', 1), then the sub-10ms floor (Node clamps small budgets to
|
|
1352
|
+
* 10ms). Throws ERR_OUT_OF_RANGE catchably. */
|
|
1353
|
+
void scr_net_set_autosel_timeout(double ms) {
|
|
1354
|
+
char recv[48], msg[160];
|
|
1355
|
+
if (!(isfinite(ms) && trunc(ms) == ms)) {
|
|
1356
|
+
scr_num_received(ms, recv);
|
|
1357
|
+
int len = snprintf(msg, sizeof msg,
|
|
1358
|
+
"The value of \"value\" is out of range. It must be an integer. Received %s", recv);
|
|
1359
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
if (ms < 1 || ms > 2147483647.0) {
|
|
1363
|
+
scr_num_received(ms, recv);
|
|
1364
|
+
int len = snprintf(msg, sizeof msg,
|
|
1365
|
+
"The value of \"value\" is out of range. It must be >= 1 && <= 2147483647. Received %s", recv);
|
|
1366
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
scr_net_autosel_timeout_ms = ms < 10 ? 10 : ms;
|
|
1370
|
+
}
|
|
1351
1371
|
|
|
1352
1372
|
/* ── fs error formatting ─────────────────────────────────────────────
|
|
1353
1373
|
* Node's fs errors read "<ERRNO>: <text>, <syscall> '<path>'"
|
|
@@ -1647,7 +1667,7 @@ double scr_fs_read_sync(double fd, ScrBytes *buf, double offset, double length)
|
|
|
1647
1667
|
mlen = snprintf(msg, sizeof msg,
|
|
1648
1668
|
"The value of \"offset\" is out of range. It must be >= 0 && <= 9007199254740991. Received %s",
|
|
1649
1669
|
numbuf);
|
|
1650
|
-
|
|
1670
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
1651
1671
|
return 0;
|
|
1652
1672
|
}
|
|
1653
1673
|
size_t off = (size_t)offset;
|
|
@@ -1656,7 +1676,7 @@ double scr_fs_read_sync(double fd, ScrBytes *buf, double offset, double length)
|
|
|
1656
1676
|
mlen = snprintf(msg, sizeof msg,
|
|
1657
1677
|
"The value of \"length\" is out of range. It must be <= %zu. Received %s",
|
|
1658
1678
|
bytelen - off, numbuf);
|
|
1659
|
-
|
|
1679
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
1660
1680
|
return 0;
|
|
1661
1681
|
}
|
|
1662
1682
|
size_t want = (size_t)length;
|
|
@@ -2528,7 +2548,7 @@ ScrStr *scr_crypto_random_string(double n, ScrStr *enc) {
|
|
|
2528
2548
|
int mlen = snprintf(msg, sizeof msg,
|
|
2529
2549
|
"The value of \"size\" is out of range. It must be >= 0 && <= 2147483647. Received %.*s",
|
|
2530
2550
|
(int)numlen, num);
|
|
2531
|
-
|
|
2551
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)mlen, "ERR_OUT_OF_RANGE");
|
|
2532
2552
|
return NULL;
|
|
2533
2553
|
}
|
|
2534
2554
|
size_t size = (size_t)n;
|
|
@@ -3486,6 +3506,104 @@ ScrStr *scr_num_to_fixed0(double x) {
|
|
|
3486
3506
|
return r;
|
|
3487
3507
|
}
|
|
3488
3508
|
|
|
3509
|
+
/* Increment a decimal digit string in place. Returns true on overflow —
|
|
3510
|
+
* the value becomes 1 followed by len zeros (the caller folds the zeros
|
|
3511
|
+
* into its scale); an EMPTY string increments to "1" the same way (the
|
|
3512
|
+
* round-up-from-nothing case: 0.0005 at 3 fraction digits). */
|
|
3513
|
+
static bool scr_dec_inc(char *d, int len) {
|
|
3514
|
+
for (int i = len - 1; i >= 0; i--) {
|
|
3515
|
+
if (d[i] != '9') {
|
|
3516
|
+
d[i]++;
|
|
3517
|
+
return false;
|
|
3518
|
+
}
|
|
3519
|
+
d[i] = '0';
|
|
3520
|
+
}
|
|
3521
|
+
d[0] = '1';
|
|
3522
|
+
return true;
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3525
|
+
/* Intl.NumberFormat("en-US").format(x) / x.toLocaleString("en-US") with
|
|
3526
|
+
* DEFAULT options: decimal notation, minimum 0 / maximum 3 fraction
|
|
3527
|
+
* digits, "," grouping every three integer digits, "∞"/"NaN" texts, and
|
|
3528
|
+
* "-0" whenever the input is negative or negative zero even after
|
|
3529
|
+
* rounding to zero. Rounding is half-up ON THE SHORTEST ROUND-TRIPPING
|
|
3530
|
+
* DECIMAL — ICU's rounding input, probed against Node: format(1.0005) is
|
|
3531
|
+
* "1.001" although the double is 1.000499... and toFixed(3) answers
|
|
3532
|
+
* "1.000"; format(1e23) prints the shortest form's trailing zeros, not
|
|
3533
|
+
* the double's exact expansion. The en-US/latn symbols (",", ".", "∞",
|
|
3534
|
+
* "NaN", group size 3) are the whole embedded locale surface. Verified
|
|
3535
|
+
* differentially against Node. Result +1; never throws. */
|
|
3536
|
+
ScrStr *scr_intl_num_format_en_us(double x) {
|
|
3537
|
+
if (isnan(x)) return scr_str_new("NaN", 3);
|
|
3538
|
+
if (isinf(x)) {
|
|
3539
|
+
return x < 0 ? scr_str_new("-\xE2\x88\x9E", 4) : scr_str_new("\xE2\x88\x9E", 3);
|
|
3540
|
+
}
|
|
3541
|
+
bool neg = signbit(x) != 0;
|
|
3542
|
+
if (x == 0) return neg ? scr_str_new("-0", 2) : scr_str_new("0", 1);
|
|
3543
|
+
double a = neg ? -x : x;
|
|
3544
|
+
|
|
3545
|
+
/* Shortest digits: value = 0.d × 10^n (no trailing zeros, k ≤ 17). */
|
|
3546
|
+
char d[18];
|
|
3547
|
+
int n;
|
|
3548
|
+
int k = scr_f64_digits(a, d, &n);
|
|
3549
|
+
|
|
3550
|
+
/* Round at 3 fraction digits: fraction position p is digit index
|
|
3551
|
+
* n+p-1, so index n+3 is the first DROPPED digit. Half-up on the
|
|
3552
|
+
* decimal digits — the shortest string ends right after them, so
|
|
3553
|
+
* "first dropped digit ≥ 5" IS the whole decision. */
|
|
3554
|
+
int keep = n + 3;
|
|
3555
|
+
if (keep < k) {
|
|
3556
|
+
bool up = keep >= 0 && d[keep] >= '5';
|
|
3557
|
+
k = keep < 0 ? 0 : keep;
|
|
3558
|
+
if (up && scr_dec_inc(d, k)) {
|
|
3559
|
+
/* Carried out (all nines, or the round-up-from-nothing 0.0005
|
|
3560
|
+
* case): one leading 1, the dropped nines fold into the scale. */
|
|
3561
|
+
k = 1;
|
|
3562
|
+
n += 1;
|
|
3563
|
+
} else if (k == 0) {
|
|
3564
|
+
/* Everything rounded away: ±0 with the sign preserved. */
|
|
3565
|
+
return neg ? scr_str_new("-0", 2) : scr_str_new("0", 1);
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
|
|
3569
|
+
/* Assemble: integer digits (indices [0, n)), zero-padded past k, then
|
|
3570
|
+
* the ≤ 3 fraction digits (indices n..n+2, '0' outside [0, k)) with
|
|
3571
|
+
* trailing zeros trimmed, then commas every three integer digits. */
|
|
3572
|
+
char frac[3];
|
|
3573
|
+
int flen = 0;
|
|
3574
|
+
for (int p = 1; p <= 3; p++) {
|
|
3575
|
+
int idx = n + p - 1;
|
|
3576
|
+
frac[flen++] = (idx >= 0 && idx < k) ? d[idx] : '0';
|
|
3577
|
+
}
|
|
3578
|
+
while (flen > 0 && frac[flen - 1] == '0') flen--;
|
|
3579
|
+
|
|
3580
|
+
char out[512];
|
|
3581
|
+
int o = 0;
|
|
3582
|
+
if (neg) out[o++] = '-';
|
|
3583
|
+
if (n <= 0) {
|
|
3584
|
+
out[o++] = '0';
|
|
3585
|
+
} else {
|
|
3586
|
+
for (int i = 0; i < n; i++) {
|
|
3587
|
+
if (i > 0 && (n - i) % 3 == 0) out[o++] = ',';
|
|
3588
|
+
out[o++] = (i < k) ? d[i] : '0';
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
if (flen > 0) {
|
|
3592
|
+
out[o++] = '.';
|
|
3593
|
+
memcpy(out + o, frac, (size_t)flen);
|
|
3594
|
+
o += flen;
|
|
3595
|
+
}
|
|
3596
|
+
return scr_str_new(out, (size_t)o);
|
|
3597
|
+
}
|
|
3598
|
+
|
|
3599
|
+
/* Object.is over two numbers — the spec's SameValue on doubles: NaN
|
|
3600
|
+
* equals NaN, +0 differs from -0, everything else is ==. */
|
|
3601
|
+
bool scr_num_same_value(double a, double b) {
|
|
3602
|
+
if (a != a) return b != b;
|
|
3603
|
+
if (a == 0 && b == 0) return signbit(a) == signbit(b);
|
|
3604
|
+
return a == b;
|
|
3605
|
+
}
|
|
3606
|
+
|
|
3489
3607
|
bool scr_num_is_nan(double x) { return isnan(x) != 0; }
|
|
3490
3608
|
|
|
3491
3609
|
bool scr_num_is_integer(double x) { return isfinite(x) && trunc(x) == x; }
|
package/src/scr_library.c
CHANGED
|
@@ -40,7 +40,19 @@ void scr_library_set_sink(ScrLibSinkFn fn, void *ctx) {
|
|
|
40
40
|
* conforming survival pattern), then deliver exactly once, then abort:
|
|
41
41
|
* before registration, or if the sink returns (the ruled host-contract
|
|
42
42
|
* violation). The address is the funnel frame's return address — the trap
|
|
43
|
-
* site — 0 where the toolchain cannot supply one.
|
|
43
|
+
* site — 0 where the toolchain cannot supply one.
|
|
44
|
+
*
|
|
45
|
+
* Delivery shape (the ratified structured trap-teaching encoding): a
|
|
46
|
+
* message that already begins with the 0x01 marker — a facade-authored
|
|
47
|
+
* structured throw riding the verbatim rule, or the wrapper's compile-
|
|
48
|
+
* time-assembled SC4012 contract trap — is delivered byte-for-byte. Every
|
|
49
|
+
* OTHER message is a trap the runtime DETECTED, and the funnel assembles
|
|
50
|
+
* it here into 0x01 text 0x1F code 0x1F symbol [0x1F remediation]: the
|
|
51
|
+
* baseline human line becomes field 0 unchanged (so plain-text hosts read
|
|
52
|
+
* exactly what they always read), the code classifies the trap kind, the
|
|
53
|
+
* symbol is the entry the trapping call came through (recorded by the
|
|
54
|
+
* entry prologue below), and the remediation is the profile's for that
|
|
55
|
+
* code when the program TU's overlay table declares one. */
|
|
44
56
|
|
|
45
57
|
#if defined(__GNUC__) || defined(__clang__)
|
|
46
58
|
#define SCR_TRAP_ADDR() ((uint64_t)(uintptr_t)__builtin_return_address(0))
|
|
@@ -48,8 +60,95 @@ void scr_library_set_sink(ScrLibSinkFn fn, void *ctx) {
|
|
|
48
60
|
#define SCR_TRAP_ADDR() ((uint64_t)0)
|
|
49
61
|
#endif
|
|
50
62
|
|
|
63
|
+
/* The current-entry slot: every generated entry's prologue records its
|
|
64
|
+
* external symbol before dispatching into core code. A single static slot
|
|
65
|
+
* is sound — exactly one core is live per process (the one-live-core rule),
|
|
66
|
+
* entries never nest, and a trap can only fire while an entry is on the
|
|
67
|
+
* stack. NULL (never entered) renders as the empty symbol field. */
|
|
68
|
+
static const char *scr_library_entry_symbol = NULL;
|
|
69
|
+
|
|
70
|
+
/* Detected-trap classification: the runtime's trap sites self-classify
|
|
71
|
+
* through their message conventions (the exact bytes the executable lane
|
|
72
|
+
* prints), so the kind → code mapping keys on those prefixes. The codes are
|
|
73
|
+
* the compiler registry's runtime family (diagnostics/diagnostic.ts —
|
|
74
|
+
* documented beside SC4012); SC4019 is the family's residual for detected
|
|
75
|
+
* traps outside the named kinds (environment failures, unsupported
|
|
76
|
+
* operations, the RC audit). */
|
|
77
|
+
static const struct {
|
|
78
|
+
const char *prefix;
|
|
79
|
+
const char *code;
|
|
80
|
+
} scr_library_trap_kinds[] = {
|
|
81
|
+
{"Uncaught ", "SC4013"}, /* escaped exception at an entry */
|
|
82
|
+
{"scriptc: RangeError: ", "SC4014"}, /* range trap */
|
|
83
|
+
{"scriptc: TypeError: ", "SC4015"}, /* type trap */
|
|
84
|
+
{"scriptc: SyntaxError: ", "SC4016"}, /* syntax trap (regex compile) */
|
|
85
|
+
{"scriptc: out of memory", "SC4017"}, /* allocation failure */
|
|
86
|
+
{"scriptc: internal error: ", "SC4018"}, /* internal invariant failure */
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
static const char *scr_library_trap_code(const char *msg, size_t len) {
|
|
90
|
+
for (size_t i = 0; i < sizeof scr_library_trap_kinds / sizeof scr_library_trap_kinds[0]; i++) {
|
|
91
|
+
size_t plen = strlen(scr_library_trap_kinds[i].prefix);
|
|
92
|
+
if (len >= plen && memcmp(msg, scr_library_trap_kinds[i].prefix, plen) == 0) {
|
|
93
|
+
return scr_library_trap_kinds[i].code;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return "SC4019"; /* other detected trap */
|
|
97
|
+
}
|
|
98
|
+
|
|
51
99
|
static _Noreturn void scr_library_trap_deliver(const char *msg, size_t len, uint64_t addr) {
|
|
52
100
|
scr_library_poisoned = true;
|
|
101
|
+
if (!(len > 0 && (uint8_t)msg[0] == 0x01)) {
|
|
102
|
+
/* A detected trap: assemble the structured message. Static buffer —
|
|
103
|
+
* no malloc on the failure path; text truncates before structure ever
|
|
104
|
+
* would (codes and symbols are short; an oversized remediation drops
|
|
105
|
+
* whole, never split). */
|
|
106
|
+
static char buf[2048];
|
|
107
|
+
const char *code = scr_library_trap_code(msg, len);
|
|
108
|
+
const char *text = msg;
|
|
109
|
+
size_t text_len = len;
|
|
110
|
+
const char *rem = NULL;
|
|
111
|
+
for (size_t i = 0; i < scr_library_trap_overlays_len; i++) {
|
|
112
|
+
const char *const *t = &scr_library_trap_overlays[3 * i];
|
|
113
|
+
if (strcmp(t[0], code) == 0) {
|
|
114
|
+
if (t[1] != NULL) {
|
|
115
|
+
text = t[1];
|
|
116
|
+
text_len = strlen(t[1]);
|
|
117
|
+
}
|
|
118
|
+
rem = t[2];
|
|
119
|
+
break;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
const char *sym = scr_library_entry_symbol != NULL ? scr_library_entry_symbol : "";
|
|
123
|
+
size_t tail = 2 + strlen(code) + strlen(sym);
|
|
124
|
+
if (rem != NULL) {
|
|
125
|
+
if (tail + 1 + strlen(rem) > sizeof buf - 1) rem = NULL; /* drop whole, keep structure */
|
|
126
|
+
else tail += 1 + strlen(rem);
|
|
127
|
+
}
|
|
128
|
+
size_t n = 0;
|
|
129
|
+
buf[n++] = '\x01';
|
|
130
|
+
size_t cap = sizeof buf - 1 - tail;
|
|
131
|
+
if (text_len > cap) text_len = cap;
|
|
132
|
+
for (size_t i = 0; i < text_len; i++) {
|
|
133
|
+
/* The encoding reserves 0x01/0x1F; runtime messages never contain
|
|
134
|
+
* them, but an escaped exception's rendered text embeds user bytes. */
|
|
135
|
+
char c = text[i];
|
|
136
|
+
buf[n++] = (c == '\x01' || c == '\x1f') ? ' ' : c;
|
|
137
|
+
}
|
|
138
|
+
buf[n++] = '\x1f';
|
|
139
|
+
memcpy(buf + n, code, strlen(code));
|
|
140
|
+
n += strlen(code);
|
|
141
|
+
buf[n++] = '\x1f';
|
|
142
|
+
memcpy(buf + n, sym, strlen(sym));
|
|
143
|
+
n += strlen(sym);
|
|
144
|
+
if (rem != NULL) {
|
|
145
|
+
buf[n++] = '\x1f';
|
|
146
|
+
memcpy(buf + n, rem, strlen(rem));
|
|
147
|
+
n += strlen(rem);
|
|
148
|
+
}
|
|
149
|
+
msg = buf;
|
|
150
|
+
len = n;
|
|
151
|
+
}
|
|
53
152
|
if (scr_library_sink != NULL) {
|
|
54
153
|
scr_library_sink(scr_library_sink_ctx, (const uint8_t *)msg, len, addr);
|
|
55
154
|
}
|
|
@@ -80,7 +179,11 @@ __attribute__((noinline)) _Noreturn void scr_trap_fmt(const char *fmt, ...) {
|
|
|
80
179
|
|
|
81
180
|
/* ── entry prologues ──────────────────────────────────────────────────── */
|
|
82
181
|
|
|
83
|
-
void scr_library_entry(bool reset_arena) {
|
|
182
|
+
void scr_library_entry(bool reset_arena, const char *entry_symbol) {
|
|
183
|
+
/* Record the entry symbol FIRST so even a poisoned-abort's core dump
|
|
184
|
+
* names the entry; a trap anywhere below (the arena reset's OOM
|
|
185
|
+
* included) then reports the right symbol. */
|
|
186
|
+
scr_library_entry_symbol = entry_symbol;
|
|
84
187
|
/* A poisoned library's entries abort deterministically — never through the
|
|
85
188
|
* sink again (it received its exactly-once message when the trap fired),
|
|
86
189
|
* never into a heap whose invariants already failed. */
|
package/src/scr_net.c
CHANGED
|
@@ -92,6 +92,7 @@
|
|
|
92
92
|
#include "scr_runtime.h"
|
|
93
93
|
|
|
94
94
|
#include <errno.h>
|
|
95
|
+
#include <math.h>
|
|
95
96
|
#include <fcntl.h>
|
|
96
97
|
#include <stdio.h>
|
|
97
98
|
#include <stdlib.h>
|
|
@@ -1601,6 +1602,117 @@ ScrNetSocket *scr_net_connect(double port, ScrStr *host /*borrowed, nullable*/,
|
|
|
1601
1602
|
return s;
|
|
1602
1603
|
}
|
|
1603
1604
|
|
|
1605
|
+
/* ── the connect option-bag validation ladders (checked-dynamic lane) ──
|
|
1606
|
+
* Node-order validation over DOM option values with Node's exact typed
|
|
1607
|
+
* errors; the honest tail (connect for the validated forms, the
|
|
1608
|
+
* compiler-rendered fence for bags with unmodeled keys) runs only after
|
|
1609
|
+
* every validation passes. */
|
|
1610
|
+
|
|
1611
|
+
static bool scr_net_attempt_timeout_chk(const ScrDyn *t, const char *name) {
|
|
1612
|
+
if (t->kind != SCR_DYN_NUM) {
|
|
1613
|
+
scr_dyn_prop_type_fail(name, "of type number", t);
|
|
1614
|
+
return false;
|
|
1615
|
+
}
|
|
1616
|
+
char recv[48], msg[192];
|
|
1617
|
+
if (!(isfinite(t->v.num) && trunc(t->v.num) == t->v.num)) {
|
|
1618
|
+
scr_num_received(t->v.num, recv);
|
|
1619
|
+
int len = snprintf(msg, sizeof msg,
|
|
1620
|
+
"The value of \"%s\" is out of range. It must be an integer. Received %s",
|
|
1621
|
+
name, recv);
|
|
1622
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
|
|
1623
|
+
return false;
|
|
1624
|
+
}
|
|
1625
|
+
if (t->v.num < 1 || t->v.num > 2147483647.0) {
|
|
1626
|
+
scr_num_received(t->v.num, recv);
|
|
1627
|
+
int len = snprintf(msg, sizeof msg,
|
|
1628
|
+
"The value of \"%s\" is out of range. It must be >= 1 && <= 2147483647. Received %s",
|
|
1629
|
+
name, recv);
|
|
1630
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
|
|
1631
|
+
return false;
|
|
1632
|
+
}
|
|
1633
|
+
return true;
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
/* connect({ ..., autoSelectFamilyAttemptTimeout }): the budget validates
|
|
1637
|
+
* (validateInt32 from 1, Node's exact texts) and is then inert — this
|
|
1638
|
+
* slice's single dial has nothing to time, the same simplification the
|
|
1639
|
+
* autoSelectFamily flag already takes. NULL with the throw pending. */
|
|
1640
|
+
ScrNetSocket *scr_net_connect_attempt(double port, ScrStr *host, const ScrDyn *t) {
|
|
1641
|
+
if (!scr_net_attempt_timeout_chk(t, "options.autoSelectFamilyAttemptTimeout")) return NULL;
|
|
1642
|
+
return scr_net_connect(port, host, NULL);
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1645
|
+
static bool scr_net_dyn_truthy(const ScrDyn *v) {
|
|
1646
|
+
switch (v->kind) {
|
|
1647
|
+
case SCR_DYN_UNDEF:
|
|
1648
|
+
case SCR_DYN_NULL: return false;
|
|
1649
|
+
case SCR_DYN_BOOL: return v->v.b;
|
|
1650
|
+
case SCR_DYN_NUM: return v->v.num == v->v.num && v->v.num != 0;
|
|
1651
|
+
case SCR_DYN_STR: return v->v.str->len > 0;
|
|
1652
|
+
default: return true;
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
/* net.connect/createConnection over a RUNTIME option bag (computed keys
|
|
1657
|
+
* — the invalid-input probes): Node's Socket-constructor order — the
|
|
1658
|
+
* objectMode trio throws ERR_INVALID_ARG_VALUE first, then the port
|
|
1659
|
+
* (validatePort), the host's string contract, autoSelectFamily's boolean
|
|
1660
|
+
* contract, and the attempt budget. A bag that survives everything meets
|
|
1661
|
+
* the compiler-rendered fence: an unmodeled key must refuse loudly, never
|
|
1662
|
+
* silently drop. Always leaves an exception pending. */
|
|
1663
|
+
void scr_net_connect_opts_chk(const ScrDyn *opts, const ScrStr *fence) {
|
|
1664
|
+
if (opts == NULL || opts->kind != SCR_DYN_OBJ) {
|
|
1665
|
+
scr_dyn_arg_type_fail("options", "of type object",
|
|
1666
|
+
opts ? opts : scr_dyn_undefined());
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
static const char *const om[] = { "objectMode", "readableObjectMode", "writableObjectMode" };
|
|
1670
|
+
for (size_t i = 0; i < 3; i++) {
|
|
1671
|
+
const ScrDyn *v = scr_dyn_obj_get(opts, om[i], strlen(om[i]));
|
|
1672
|
+
if (v != NULL && scr_net_dyn_truthy(v)) {
|
|
1673
|
+
char name[48];
|
|
1674
|
+
snprintf(name, sizeof name, "options.%s", om[i]);
|
|
1675
|
+
scr_dyn_arg_value_fail(name, "is not supported", v);
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
const ScrDyn *port = scr_dyn_obj_get(opts, "port", 4);
|
|
1680
|
+
if (port != NULL && port->kind != SCR_DYN_UNDEF) {
|
|
1681
|
+
bool ok = port->kind == SCR_DYN_NUM && trunc(port->v.num) == port->v.num &&
|
|
1682
|
+
port->v.num >= 0 && port->v.num < 65536;
|
|
1683
|
+
if (!ok && port->kind == SCR_DYN_STR) {
|
|
1684
|
+
ScrStr *ps = scr_str_retain(port->v.str);
|
|
1685
|
+
double n = scr_string_to_number(ps);
|
|
1686
|
+
scr_str_release(ps);
|
|
1687
|
+
ok = n == n && trunc(n) == n && n >= 0 && n < 65536 && port->v.str->len > 0;
|
|
1688
|
+
}
|
|
1689
|
+
if (!ok) {
|
|
1690
|
+
char detail[64], msg[160];
|
|
1691
|
+
const char *d = scr_dyn_specific_type(port, detail, sizeof detail);
|
|
1692
|
+
int len = snprintf(msg, sizeof msg,
|
|
1693
|
+
"options.port should be >= 0 and < 65536. Received %s", d);
|
|
1694
|
+
scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_SOCKET_BAD_PORT");
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
const ScrDyn *host = scr_dyn_obj_get(opts, "host", 4);
|
|
1699
|
+
if (host != NULL && host->kind != SCR_DYN_UNDEF && host->kind != SCR_DYN_STR) {
|
|
1700
|
+
scr_dyn_prop_type_fail("options.host", "of type string", host);
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
const ScrDyn *asf = scr_dyn_obj_get(opts, "autoSelectFamily", 16);
|
|
1704
|
+
if (asf != NULL && asf->kind != SCR_DYN_UNDEF && asf->kind != SCR_DYN_BOOL) {
|
|
1705
|
+
scr_dyn_prop_type_fail("options.autoSelectFamily", "of type boolean", asf);
|
|
1706
|
+
return;
|
|
1707
|
+
}
|
|
1708
|
+
const ScrDyn *att = scr_dyn_obj_get(opts, "autoSelectFamilyAttemptTimeout", 30);
|
|
1709
|
+
if (att != NULL && att->kind != SCR_DYN_UNDEF &&
|
|
1710
|
+
!scr_net_attempt_timeout_chk(att, "options.autoSelectFamilyAttemptTimeout")) {
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
scr_throw_lowering_fence(fence);
|
|
1714
|
+
}
|
|
1715
|
+
|
|
1604
1716
|
/* ── the caller-lookup dial (net.connect with a lookup option) ─────────
|
|
1605
1717
|
*
|
|
1606
1718
|
* portless's createLoopbackConnection: connect({ host, port,
|
package/src/scr_number.c
CHANGED
|
@@ -24,27 +24,19 @@
|
|
|
24
24
|
* unchanged. Provides d2d(), d2d_small_int(), decimalLength17(), div10(). */
|
|
25
25
|
#include "../vendor/ryu/d2s.c"
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (x < 0) {
|
|
36
|
-
*out++ = '-';
|
|
37
|
-
x = -x;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/* Shortest round-tripping digits via Ryū. Mirrors d2s_buffered_n's
|
|
41
|
-
* dispatch: the exact small-integer fast path first (trailing decimal
|
|
42
|
-
* zeros folded into the exponent), the full algorithm otherwise. */
|
|
27
|
+
/* The Ryū digit core, shared by the ECMA placement below and the Intl
|
|
28
|
+
* en-US number formatter (scr_lib.c): the shortest round-tripping digit
|
|
29
|
+
* string for a positive finite double — value = 0.digits × 10^n with no
|
|
30
|
+
* trailing zeros. Returns k (the digit count, ≤ 17); digits is
|
|
31
|
+
* NUL-terminated. Mirrors d2s_buffered_n's dispatch: the exact
|
|
32
|
+
* small-integer fast path first (trailing decimal zeros folded into the
|
|
33
|
+
* exponent), the full algorithm otherwise. */
|
|
34
|
+
int scr_f64_digits(double x, char digits[18], int *n_out) {
|
|
43
35
|
uint64_t bits;
|
|
44
36
|
memcpy(&bits, &x, sizeof bits);
|
|
45
37
|
const uint64_t ieeeMantissa = bits & ((1ull << DOUBLE_MANTISSA_BITS) - 1);
|
|
46
38
|
const uint32_t ieeeExponent =
|
|
47
|
-
(uint32_t)(bits >> DOUBLE_MANTISSA_BITS); /* sign
|
|
39
|
+
(uint32_t)(bits >> DOUBLE_MANTISSA_BITS); /* sign must be stripped */
|
|
48
40
|
floating_decimal_64 v;
|
|
49
41
|
if (d2d_small_int(ieeeMantissa, ieeeExponent, &v)) {
|
|
50
42
|
for (;;) {
|
|
@@ -60,9 +52,8 @@ size_t scr_f64_to_str(double x, char *buf) {
|
|
|
60
52
|
|
|
61
53
|
/* Ryū's (mantissa, exponent) → ECMA's (digits, k, n): the k mantissa
|
|
62
54
|
* digits have no trailing zeros, and value = 0.digits * 10^n. */
|
|
63
|
-
char digits[18];
|
|
64
55
|
int k = (int)decimalLength17(v.mantissa);
|
|
65
|
-
|
|
56
|
+
*n_out = v.exponent + k;
|
|
66
57
|
digits[k] = '\0';
|
|
67
58
|
uint64_t m = v.mantissa;
|
|
68
59
|
for (int i = k - 1; i >= 0; i--) {
|
|
@@ -70,6 +61,25 @@ size_t scr_f64_to_str(double x, char *buf) {
|
|
|
70
61
|
digits[i] = (char)('0' + (uint32_t)m - 10 * (uint32_t)q);
|
|
71
62
|
m = q;
|
|
72
63
|
}
|
|
64
|
+
return k;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
size_t scr_f64_to_str(double x, char *buf) {
|
|
68
|
+
if (isnan(x)) return (size_t)(stpcpy(buf, "NaN") - buf);
|
|
69
|
+
if (x == 0) return (size_t)(stpcpy(buf, "0") - buf); /* covers -0 */
|
|
70
|
+
if (isinf(x)) {
|
|
71
|
+
return (size_t)(stpcpy(buf, x < 0 ? "-Infinity" : "Infinity") - buf);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
char *out = buf;
|
|
75
|
+
if (x < 0) {
|
|
76
|
+
*out++ = '-';
|
|
77
|
+
x = -x;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
char digits[18];
|
|
81
|
+
int n;
|
|
82
|
+
int k = scr_f64_digits(x, digits, &n);
|
|
73
83
|
|
|
74
84
|
if (k <= n && n <= 21) {
|
|
75
85
|
/* Integer: digits followed by n-k zeros. */
|