/
nv-lang
/
nova
Обзор
Документация
Войти
/
nv-lang
/
nova
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
compiler-codegen/nova_rt/array.h
1 182 строки
56 KB
Evgeniy Golovin
fix(codegen): CancelError typedef always available regardless of prelude subset
16 июл 2026, 00:00
16 июл 2026, 00:00
b7be1a4
Код
Авторство
О чём код?
#ifndef NOVA_RT_ARRAY_H #define NOVA_RT_ARRAY_H #include "alloc.h" #include <stdint.h> #include <stdio.h> /* snprintf — Plan 96 Ф.1 nv_panic_index_oob diagnostic */ #include <string.h> /* * Generic growable array for Nova codegen. * * Each element type T gets its own concrete struct via the NOVA_ARRAY_DECL * macro, which emits: * * typedef struct NovaArray_T { * T* data; * int64_t len; * int64_t cap; * } NovaArray_T; * * Helper functions (push, get) are generated by NOVA_ARRAY_IMPL. * * Usage in generated C: * NOVA_ARRAY_DECL(nova_int) * NOVA_ARRAY_IMPL(nova_int) * * The codegen emits these declarations at the top of the file for each * element type actually used. * * nova_array_new_T(cap) — allocate with initial capacity * nova_array_push_T(a, v) — append element, growing if needed * nova_array_get_T(a, i) — bounds-checked access, returns Option-like tag */ /* ---- Option<nova_int> for array.get() ---- * * * We use a simple tagged struct rather than a full sum-type to keep * the array header self-contained. Generated code for `.get(i)` uses * this struct directly; the match arms check `.tag`. */ /* Tag values shared across all NovaOpt_T structs */ #define NOVA_TAG_Option_None 0 #define NOVA_TAG_Option_Some 1 /* Declare just the Option<T> struct for element type T — split out (Plan * 172.12 A8) so a primitive that needs Option[T] but NOT the legacy * NovaArray_T growable-array struct (dead: `[]T` is Vec[T]-backed since the * Plan 172.12 A7/A8 flip, except the `nova_int` erasure-sentinel and * `void_p` closure-array — see NOVA_ARRAY_DECL below) can get ONLY the * Option struct declared. */ #define NOVA_OPT_DECL(T) \ typedef struct NovaOpt_##T { \ int tag; \ T value; \ } NovaOpt_##T; /* Declare the array struct AND Option<T> struct for element type T. * T_NAME is the C identifier used in the struct/function names (e.g. nova_int). * Plan 172.12 A8: only instantiated for `nova_int` (int64-slot erasure * sentinel for unresolved generic-mono `[]T` — an orthogonal mechanism from * the real per-element Vec[T] representation, untouched by the A7/A8 flip) * and `void_p` (closure-array / `[]Protocol` heterogeneous-box storage). */ #define NOVA_ARRAY_DECL(T) \ typedef struct NovaArray_##T { \ T* data; \ int64_t len; \ int64_t cap; \ } NovaArray_##T; \ NOVA_OPT_DECL(T) /* Implement helper functions for element type T. * Requires nova_alloc to be available. */ #define NOVA_ARRAY_IMPL(T) \ static NovaArray_##T* nova_array_new_##T(int64_t init_cap) { \ NovaArray_##T* a = (NovaArray_##T*)nova_alloc(sizeof(NovaArray_##T)); \ a->cap = init_cap > 0 ? init_cap : 8; \ a->len = 0; \ a->data = (T*)nova_alloc((size_t)(a->cap) * sizeof(T)); \ return a; \ } \ static void nova_array_push_##T(NovaArray_##T* a, T v) { \ if (a->len >= a->cap) { \ int64_t new_cap = a->cap * 2; \ T* new_data = (T*)nova_alloc((size_t)new_cap * sizeof(T)); \ memcpy(new_data, a->data, (size_t)(a->len) * sizeof(T)); \ a->data = new_data; \ a->cap = new_cap; \ } \ a->data[a->len++] = v; \ } \ static NovaOpt_##T nova_array_get_##T(NovaArray_##T* a, int64_t i) { \ NovaOpt_##T r; \ if (i >= 0 && i < a->len) { r.tag = NOVA_TAG_Option_Some; r.value = a->data[i]; } \ else { r.tag = NOVA_TAG_Option_None; r.value = (T){0}; } \ return r; \ } \ static NovaOpt_##T nova_array_pop_##T(NovaArray_##T* a) { \ NovaOpt_##T r; \ if (a->len > 0) { a->len--; r.tag = NOVA_TAG_Option_Some; r.value = a->data[a->len]; } \ else { r.tag = NOVA_TAG_Option_None; r.value = (T){0}; } \ return r; \ } \ static nova_bool nova_array_eq_##T(NovaArray_##T* a, NovaArray_##T* b) { \ if (a->len != b->len) return 0; \ for (int64_t _i = 0; _i < a->len; _i++) { if (a->data[_i] != b->data[_i]) return 0; } \ return 1; \ } \ static void nova_array_copy_from_##T(NovaArray_##T* dst, NovaArray_##T* src) { \ /* Plan 90.1: strict equal-length + always memmove (overlap safe). \ * Breaking change: old silent-truncation removed. \ * Truncation idiom: dst[..n].copy_from(src[..n]) via Plan 96 slicing. \ * memmove vs memcpy: overlap-safe by default, paritет с Go; no UB. */ \ if (src->len != dst->len) { \ nv_panic((nova_str){ \ .ptr = "copy_from: length mismatch (use dst[..n].copy_from(src[..n]) for partial copy)", \ .len = sizeof("copy_from: length mismatch (use dst[..n].copy_from(src[..n]) for partial copy)") - 1 }); } \ memmove(dst->data, src->data, (size_t)(dst->len) * sizeof(T)); \ } \ /* Plan 90.1 (D141 amendment): append — bulk-add to end with 2x growth, memmove (self-extend safe). \ * Renamed from extend_from. */ \ static void nova_array_append_##T(NovaArray_##T* dst, NovaArray_##T* src) { \ int64_t src_len = src->len; /* snapshot before potential realloc */ \ T* src_data = src->data; /* snapshot before potential realloc */ \ int64_t needed = dst->len + src_len; \ if (needed > dst->cap) { \ int64_t new_cap = dst->cap * 2; \ if (new_cap < needed) new_cap = needed; \ T* new_data = (T*)nova_alloc((size_t)new_cap * sizeof(T)); \ memcpy(new_data, dst->data, (size_t)(dst->len) * sizeof(T)); \ dst->data = new_data; \ dst->cap = new_cap; \ /* src_data/src_len are snapshots — valid even if src==dst (self-extend). \ * With Boehm GC, old allocation is retained until next collection. */ \ } \ /* memmove handles src == dst[X..Y] overlap cases correctly. */ \ memmove(dst->data + dst->len, src_data, (size_t)src_len * sizeof(T)); \ dst->len = needed; \ } \ /* Plan 90.1 (D141 amendment): insert — bulk-insert at position i with growth + memmove tail. \ * Renamed from insert_from. */ \ static void nova_array_insert_##T(NovaArray_##T* dst, int64_t i, NovaArray_##T* src) { \ if (i < 0 || i > dst->len) nv_panic_insert_oob(i, dst->len); \ int64_t src_len = src->len; /* snapshot before potential realloc */ \ T* src_data = src->data; /* snapshot before potential realloc */ \ int64_t needed = dst->len + src_len; \ int64_t tail_len = dst->len - i; \ if (needed > dst->cap) { \ /* Alloc new: copy prefix [0..i] + leave hole [i..i+src_len] + copy tail. */ \ int64_t new_cap = dst->cap * 2; \ if (new_cap < needed) new_cap = needed; \ T* new_data = (T*)nova_alloc((size_t)new_cap * sizeof(T)); \ memcpy(new_data, dst->data, (size_t)i * sizeof(T)); \ memcpy(new_data + i + src_len, dst->data + i, (size_t)tail_len * sizeof(T)); \ dst->data = new_data; \ dst->cap = new_cap; \ } else { \ /* In-place: memmove tail right by src_len slots. */ \ memmove(dst->data + i + src_len, dst->data + i, (size_t)tail_len * sizeof(T)); \ } \ /* memmove src into hole — handles overlap if src is a view into dst. */ \ memmove(dst->data + i, src_data, (size_t)src_len * sizeof(T)); \ dst->len = needed; \ } \ /* Plan 90.1: reserve — preallocate hint; len unchanged, only cap grows. */ \ static void nova_array_reserve_##T(NovaArray_##T* dst, int64_t extra) { \ if (extra < 0) nv_panic_negative_reserve(extra); \ int64_t needed = dst->len + extra; \ if (needed > dst->cap) { \ int64_t new_cap = dst->cap * 2; \ if (new_cap < needed) new_cap = needed; \ T* new_data = (T*)nova_alloc((size_t)new_cap * sizeof(T)); \ memcpy(new_data, dst->data, (size_t)(dst->len) * sizeof(T)); \ dst->data = new_data; \ dst->cap = new_cap; \ } \ /* len unchanged — only cap grows. View detach lint (W_VIEW_EXTEND_DETACH) \ * at call-site covers the case where a view becomes dangling after realloc.*/ \ } \ static void nova_array_copy_within_##T(NovaArray_##T* a, int64_t src_from, int64_t dst_from, int64_t len) { \ if (len < 0 || src_from < 0 || dst_from < 0 || \ src_from + len > a->len || dst_from + len > a->len) { \ nv_panic((nova_str){ .ptr = "copy_within: range out of bounds", \ .len = sizeof("copy_within: range out of bounds") - 1 }); } \ memmove(a->data + dst_from, a->data + src_from, (size_t)len * sizeof(T)); \ } \ static void nova_array_fill_##T(NovaArray_##T* a, T v) { \ /* Plan 90 Ф.4.5 + perf: single-byte T → memset path (compile-time DCE'd \ * через sizeof(T) constant per-instantiation). nova_byte/i8 → memset; \ * остальные T → scalar loop с auto-vectorization potential. */ \ if (sizeof(T) == 1) { \ memset(a->data, (unsigned char)v, (size_t)a->len); \ } else { \ for (int64_t _i = 0; _i < a->len; _i++) { a->data[_i] = v; } \ } \ } \ /* []T @append_zero(n): extend by N zero-initialized elements. 2x growth + \ * memset для tail новой памяти. Zero bytes = valid zero-init для primitives \ * (int/u8/f64 → 0; nova_ptr → NULL; bool → false). Полиморфно по T в отличие \ * от fill (memset на любом T, не только single-byte). Use-case: reserve \ * write-window в encoders, padding, length-prefix patching. Returns @ \ * через codegen для fluent chain (Plan 91.7 D181). */ \ static void nova_array_append_zero_##T(NovaArray_##T* a, int64_t n) { \ if (n < 0) { nv_panic((nova_str){ \ .ptr = "append_zero: n must be >= 0", \ .len = sizeof("append_zero: n must be >= 0") - 1 }); } \ if (n == 0) return; \ int64_t new_len = a->len + n; \ if (new_len > a->cap) { \ int64_t new_cap = a->cap * 2; \ if (new_cap < new_len) new_cap = new_len; \ T* new_data = (T*)nova_alloc((size_t)new_cap * sizeof(T)); \ memcpy(new_data, a->data, (size_t)(a->len) * sizeof(T)); \ a->data = new_data; \ a->cap = new_cap; \ } \ memset(a->data + a->len, 0, (size_t)n * sizeof(T)); \ a->len = new_len; \ } \ /* StringBuilder @truncate / []T @truncate: reduce len to new_len if shorter. */ \ static void nova_array_truncate_##T(NovaArray_##T* a, int64_t new_len) { \ if (new_len >= 0 && new_len < a->len) a->len = new_len; \ } \ /* Plan 96 Ф.4 — sub-slice view `arr[a..b]`. O(1): новый header (24 байта) * с data = orig->data + from (interior pointer); len = cap = to - from * (D-cap-len: push на view → realloc → silent detach, parent НЕ затронут). * GC: backing держится через interior-pointer (Boehm GC_set_all_interior_pointers). * Bounds: from < 0 || to < from || to > len → nv_panic (D-neg-panic). * Empty slice (from == to) валиден (D-empty-ok). */ \ static NovaArray_##T* nova_array_slice_##T(NovaArray_##T* a, int64_t from, int64_t to) { \ if (from < 0 || to < from || to > a->len) { \ nv_panic_slice_oob(from, to, a->len); \ } \ NovaArray_##T* v = (NovaArray_##T*)nova_alloc(sizeof(NovaArray_##T)); \ v->data = a->data + from; /* interior pointer */ \ v->len = to - from; \ v->cap = to - from; /* D-cap-len: push → realloc → detach */ \ return v; \ } /* Plan 95.bis Ф.2: Nova_Option_method_or_<T> убран из NOVA_ARRAY_IMPL — * перенесён на Nova-body в std/prelude/core.nv. Routing через * MethodRouting::DeclaredBody → mono'd Nova_Option_method_or_<T> с тем * же C-именем (C-redefinition collision если оставить здесь). */ /* ---- Default instantiations. * * Plan 172.12 A7/A8: `[]T` is Vec[T]-backed for every CONCRETE element type * (D239) — the legacy `NovaArray_T` growable-array struct + its * nova_array_new / push / get / pop / eq / compare / copy_from / * copy_within / append / insert / reserve / fill / truncate / slice * helpers are dead for every primitive except: * - `nova_int` — the int64-slot erasure SENTINEL for a genuinely * unresolved generic-mono `[]T` element (orthogonal mechanism, untouched * by the Vec flip — see emit_c.rs `elem_is_erased` / receiver-dispatch). * - `void_p` — closure-array (`[]fn(...)`) / `[]Protocol` heterogeneous * box storage (intentionally NOT ported to Vec — different * representation, out of A7/A8 scope). * Every other primitive still needs its `NovaOpt_T` (Option[T] tagged * struct) — that's independent of the array machinery — via the narrower * `NOVA_OPT_DECL`. */ NOVA_ARRAY_DECL(nova_int) NOVA_ARRAY_IMPL(nova_int) /* Plan 70.3 / 152.8: nova_char distinct typedef = uint32_t (D128 AMEND). * Codepoints fit in 21 bits; uint32_t is the natural type. Distinct C type * prevents Option[char]↔Option[int] structural collapse in mono mangling. */ NOVA_OPT_DECL(nova_char) NOVA_OPT_DECL(nova_byte) NOVA_OPT_DECL(nova_bool) NOVA_OPT_DECL(nova_f64) /* Plan 70.4: nova_f32 distinct from nova_f64 — ABI difference (4 vs 8 bytes). */ NOVA_OPT_DECL(nova_f32) /* Plan 70.4 Ф.2: sized-int Option — distinct packed storage (ABI-real). */ NOVA_OPT_DECL(int32_t) NOVA_OPT_DECL(int16_t) NOVA_OPT_DECL(int8_t) NOVA_OPT_DECL(uint32_t) NOVA_OPT_DECL(uint16_t) NOVA_OPT_DECL(uint64_t) /* ---- void_p — array of opaque pointers (used for closures via NovaClosBase*). * * Plan 55 Ф.1: `[]fn(...) -> T` (array of closures) → NovaArray_void_p*. * Closure stored as void* (= NovaClos_X*); call-site routes through * NOVA_CLOS_CALL_* / NovaClosBase dispatch. Boehm conservative-scan * sees each data[i] as potential pointer → env retained correctly. */ typedef void* void_p; NOVA_ARRAY_DECL(void_p) static NovaArray_void_p* nova_array_new_void_p(int64_t init_cap) { NovaArray_void_p* a = (NovaArray_void_p*)nova_alloc(sizeof(NovaArray_void_p)); a->cap = init_cap > 0 ? init_cap : 8; a->len = 0; a->data = (void_p*)nova_alloc((size_t)(a->cap) * sizeof(void_p)); return a; } static void nova_array_push_void_p(NovaArray_void_p* a, void_p v) { if (a->len >= a->cap) { int64_t new_cap = a->cap * 2; void_p* new_data = (void_p*)nova_alloc((size_t)new_cap * sizeof(void_p)); memcpy(new_data, a->data, (size_t)(a->len) * sizeof(void_p)); a->data = new_data; a->cap = new_cap; } a->data[a->len++] = v; } static NovaOpt_void_p nova_array_get_void_p(NovaArray_void_p* a, int64_t i) { NovaOpt_void_p r; if (i >= 0 && i < a->len) { r.tag = NOVA_TAG_Option_Some; r.value = a->data[i]; } else { r.tag = NOVA_TAG_Option_None; r.value = (void_p)0; } return r; } static NovaOpt_void_p nova_array_pop_void_p(NovaArray_void_p* a) { NovaOpt_void_p r; if (a->len > 0) { a->len--; r.tag = NOVA_TAG_Option_Some; r.value = a->data[a->len]; } else { r.tag = NOVA_TAG_Option_None; r.value = (void_p)0; } return r; } static nova_bool nova_array_eq_void_p(NovaArray_void_p* a, NovaArray_void_p* b) { if (a->len != b->len) return 0; for (int64_t _i = 0; _i < a->len; _i++) { if (a->data[_i] != b->data[_i]) return 0; } return 1; } /* ---- nova_str: Option[str] only (Plan 172.12 A8) — the legacy * `NovaArray_nova_str` growable-array struct + its hand-rolled * new/push/get/pop (never macro'd: `eq` needed `nova_str_eq`, not `==`) is * dead — `[]str` is Vec[str]-backed since Plan 172.12 A6/A7 (str-family * moved onto the Vec substrate first, ahead of the primitive flip). */ NOVA_OPT_DECL(nova_str) /* ---- Option constructors for nova_int (for match compatibility) ---- */ /* Both naming conventions supported: Option and NovaOpt_nova_int */ static inline NovaOpt_nova_int nova_make_Option_Some(nova_int v) { NovaOpt_nova_int r; r.tag = NOVA_TAG_Option_Some; r.value = v; return r; } static inline NovaOpt_nova_int nova_make_Option_None(void) { NovaOpt_nova_int r; r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } static inline NovaOpt_nova_int nova_make_NovaOpt_nova_int_Some(nova_int v) { NovaOpt_nova_int r; r.tag = NOVA_TAG_Option_Some; r.value = v; return r; } static inline NovaOpt_nova_int nova_make_NovaOpt_nova_int_None(void) { NovaOpt_nova_int r; r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } static inline nova_bool nova_opt_eq_nova_int(NovaOpt_nova_int a, NovaOpt_nova_int b) { if (a.tag != b.tag) return 0; if (a.tag == NOVA_TAG_Option_None) return 1; return a.value == b.value; } /* Plan 70.3: nova_char Option constructors/eq mirror nova_int. */ static inline NovaOpt_nova_char nova_make_NovaOpt_nova_char_Some(nova_char v) { NovaOpt_nova_char r; r.tag = NOVA_TAG_Option_Some; r.value = v; return r; } static inline NovaOpt_nova_char nova_make_NovaOpt_nova_char_None(void) { NovaOpt_nova_char r; r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } static inline nova_bool nova_opt_eq_nova_char(NovaOpt_nova_char a, NovaOpt_nova_char b) { if (a.tag != b.tag) return 0; if (a.tag == NOVA_TAG_Option_None) return 1; return a.value == b.value; } /* Plan 95.bis Ф.2: unwrap_or → Nova-body (std/prelude/core.nv). * is_some/is_none — Plan 95 Ф.4.2. Все три убраны. */ static inline nova_bool nova_opt_eq_nova_str(NovaOpt_nova_str a, NovaOpt_nova_str b) { if (a.tag != b.tag) return 0; if (a.tag == NOVA_TAG_Option_None) return 1; return nova_str_eq(a.value, b.value); } static inline nova_bool nova_opt_eq_nova_bool(NovaOpt_nova_bool a, NovaOpt_nova_bool b) { if (a.tag != b.tag) return 0; if (a.tag == NOVA_TAG_Option_None) return 1; return a.value == b.value; } /* Plan 70.4 Ф.4: nova_byte Option constructors + methods mirror nova_int. */ static inline NovaOpt_nova_byte nova_make_NovaOpt_nova_byte_Some(nova_byte v) { NovaOpt_nova_byte r; r.tag = NOVA_TAG_Option_Some; r.value = v; return r; } static inline NovaOpt_nova_byte nova_make_NovaOpt_nova_byte_None(void) { NovaOpt_nova_byte r; r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } static inline nova_bool nova_opt_eq_nova_byte(NovaOpt_nova_byte a, NovaOpt_nova_byte b) { if (a.tag != b.tag) return 0; if (a.tag == NOVA_TAG_Option_None) return 1; return a.value == b.value; } /* Plan 95 Ф.4.2 + 95.bis Ф.2: is_some/is_none + unwrap_or → Nova-body. */ static inline nova_bool nova_opt_eq_nova_f64(NovaOpt_nova_f64 a, NovaOpt_nova_f64 b) { if (a.tag != b.tag) return 0; if (a.tag == NOVA_TAG_Option_None) return 1; return a.value == b.value; } /* D26 Option methods for nova_f64. Plan 95 Ф.4.2 + 95.bis Ф.2: * is_some/is_none + unwrap_or → Nova-body. */ /* Plan 70.4: nova_f32 Option constructors/eq mirror nova_f64. */ static inline NovaOpt_nova_f32 nova_make_NovaOpt_nova_f32_Some(nova_f32 v) { NovaOpt_nova_f32 r; r.tag = NOVA_TAG_Option_Some; r.value = v; return r; } static inline NovaOpt_nova_f32 nova_make_NovaOpt_nova_f32_None(void) { NovaOpt_nova_f32 r; r.tag = NOVA_TAG_Option_None; r.value = 0.0f; return r; } static inline nova_bool nova_opt_eq_nova_f32(NovaOpt_nova_f32 a, NovaOpt_nova_f32 b) { if (a.tag != b.tag) return 0; if (a.tag == NOVA_TAG_Option_None) return 1; return a.value == b.value; } /* Plan 70.4: D26 Option methods for nova_f32. Plan 95 Ф.4.2 + 95.bis * Ф.2: is_some/is_none + unwrap_or → Nova-body. */ /* Plan 70.4 Ф.2: sized-int Option constructors/eq/methods — one block per type. */ static inline NovaOpt_int32_t nova_make_NovaOpt_int32_t_Some(int32_t v) { NovaOpt_int32_t r; r.tag = NOVA_TAG_Option_Some; r.value = v; return r; } static inline NovaOpt_int32_t nova_make_NovaOpt_int32_t_None(void) { NovaOpt_int32_t r; r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } static inline nova_bool nova_opt_eq_int32_t(NovaOpt_int32_t a, NovaOpt_int32_t b) { if (a.tag != b.tag) return 0; if (a.tag == NOVA_TAG_Option_None) return 1; return a.value == b.value; } /* Plan 95 Ф.4.2 + 95.bis Ф.2: is_some/is_none + unwrap_or → Nova-body. */ static inline NovaOpt_int16_t nova_make_NovaOpt_int16_t_Some(int16_t v) { NovaOpt_int16_t r; r.tag = NOVA_TAG_Option_Some; r.value = v; return r; } static inline NovaOpt_int16_t nova_make_NovaOpt_int16_t_None(void) { NovaOpt_int16_t r; r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } static inline nova_bool nova_opt_eq_int16_t(NovaOpt_int16_t a, NovaOpt_int16_t b) { if (a.tag != b.tag) return 0; if (a.tag == NOVA_TAG_Option_None) return 1; return a.value == b.value; } /* Plan 95 Ф.4.2 + 95.bis Ф.2: is_some/is_none + unwrap_or → Nova-body. */ static inline NovaOpt_int8_t nova_make_NovaOpt_int8_t_Some(int8_t v) { NovaOpt_int8_t r; r.tag = NOVA_TAG_Option_Some; r.value = v; return r; } static inline NovaOpt_int8_t nova_make_NovaOpt_int8_t_None(void) { NovaOpt_int8_t r; r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } static inline nova_bool nova_opt_eq_int8_t(NovaOpt_int8_t a, NovaOpt_int8_t b) { if (a.tag != b.tag) return 0; if (a.tag == NOVA_TAG_Option_None) return 1; return a.value == b.value; } /* Plan 95 Ф.4.2 + 95.bis Ф.2: is_some/is_none + unwrap_or → Nova-body. */ static inline NovaOpt_uint32_t nova_make_NovaOpt_uint32_t_Some(uint32_t v) { NovaOpt_uint32_t r; r.tag = NOVA_TAG_Option_Some; r.value = v; return r; } static inline NovaOpt_uint32_t nova_make_NovaOpt_uint32_t_None(void) { NovaOpt_uint32_t r; r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } static inline nova_bool nova_opt_eq_uint32_t(NovaOpt_uint32_t a, NovaOpt_uint32_t b) { if (a.tag != b.tag) return 0; if (a.tag == NOVA_TAG_Option_None) return 1; return a.value == b.value; } /* Plan 95 Ф.4.2 + 95.bis Ф.2: is_some/is_none + unwrap_or → Nova-body. */ static inline NovaOpt_uint16_t nova_make_NovaOpt_uint16_t_Some(uint16_t v) { NovaOpt_uint16_t r; r.tag = NOVA_TAG_Option_Some; r.value = v; return r; } static inline NovaOpt_uint16_t nova_make_NovaOpt_uint16_t_None(void) { NovaOpt_uint16_t r; r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } static inline nova_bool nova_opt_eq_uint16_t(NovaOpt_uint16_t a, NovaOpt_uint16_t b) { if (a.tag != b.tag) return 0; if (a.tag == NOVA_TAG_Option_None) return 1; return a.value == b.value; } /* Plan 95 Ф.4.2 + 95.bis Ф.2: is_some/is_none + unwrap_or → Nova-body. */ static inline NovaOpt_uint64_t nova_make_NovaOpt_uint64_t_Some(uint64_t v) { NovaOpt_uint64_t r; r.tag = NOVA_TAG_Option_Some; r.value = v; return r; } static inline NovaOpt_uint64_t nova_make_NovaOpt_uint64_t_None(void) { NovaOpt_uint64_t r; r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } static inline nova_bool nova_opt_eq_uint64_t(NovaOpt_uint64_t a, NovaOpt_uint64_t b) { if (a.tag != b.tag) return 0; if (a.tag == NOVA_TAG_Option_None) return 1; return a.value == b.value; } /* Plan 95 Ф.4.2 + 95.bis Ф.2: is_some/is_none + unwrap_or → Nova-body. */ /* ---- D26 Option methods ---- (Plan 95 Ф.4.2 is_some/is_none + 95.bis * Ф.2 unwrap_or + or — все перенесены на Nova-body в std/prelude/core.nv, * dispatch через MethodRouting::DeclaredBody с тем же C-именем * Nova_Option_method_<m>_<T_sani>. C-redefinition collision если * оставить здесь.) * * Note: @unwrap() throws Fail on None — реализация в codegen через * Nova_Fail_fail (effects.h инклюдится после array.h в nova_rt.h); * codegen emits inline check + fail call. */ /* ---- D26 string search: find / rfind (codepoint-offsets, school B) ---- * * * Возвращают Option[int] с **codepoint-offset** первого/последнего * вхождения needle (школа B — Python/Swift). Empty needle → Some(0) * для find, Some(s.char_len) для rfind. needle.len в байтах > s.len * → None. Совпадение проверяется byte-wise (memcmp), но возвращаемая * позиция — codepoint-индекс начала match. Считается, что needle * начинается на границе UTF-8 codepoint (если нет — match не найдётся * на границе, и мы вернём None / следующее найденное вхождение). */ static inline NovaOpt_nova_int nova_str_find(nova_str s, nova_str needle) { NovaOpt_nova_int r; if (needle.len == 0) { r.tag = NOVA_TAG_Option_Some; r.value = 0; return r; } if (needle.len > s.len) { r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } /* Walk UTF-8: i — byte offset, cp — codepoint offset. * Совпадение допустимо только на codepoint-границе. */ nova_int cp = 0; for (size_t i = 0; i + needle.len <= s.len; ) { if (memcmp(s.ptr + i, needle.ptr, needle.len) == 0) { r.tag = NOVA_TAG_Option_Some; r.value = cp; return r; } unsigned char b = (unsigned char)s.ptr[i]; if (b < 0x80) i += 1; else if ((b & 0xE0) == 0xC0) i += 2; else if ((b & 0xF0) == 0xE0) i += 3; else if ((b & 0xF8) == 0xF0) i += 4; else i += 1; cp++; } r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } static inline NovaOpt_nova_int nova_str_rfind(nova_str s, nova_str needle) { NovaOpt_nova_int r; /* Сначала посчитаем общее число codepoint'ов в s. */ nova_int total_cp = 0; for (size_t i = 0; i < s.len; i++) { unsigned char c = (unsigned char)s.ptr[i]; if ((c & 0xC0) != 0x80) total_cp++; } if (needle.len == 0) { r.tag = NOVA_TAG_Option_Some; r.value = total_cp; return r; } if (needle.len > s.len) { r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } /* Идём слева направо, запоминаем последний match как codepoint-индекс. */ nova_int last_cp = -1; nova_int cp = 0; for (size_t i = 0; i + needle.len <= s.len; ) { if (memcmp(s.ptr + i, needle.ptr, needle.len) == 0) { last_cp = cp; } unsigned char b = (unsigned char)s.ptr[i]; if (b < 0x80) i += 1; else if ((b & 0xE0) == 0xC0) i += 2; else if ((b & 0xF0) == 0xE0) i += 3; else if ((b & 0xF8) == 0xF0) i += 4; else i += 1; cp++; } if (last_cp < 0) { r.tag = NOVA_TAG_Option_None; r.value = 0; return r; } r.tag = NOVA_TAG_Option_Some; r.value = last_cp; return r; } /* ---- byte_len (= len) — Plan 108 D26 rev: str @len() = bytes O(1). * nova_str_byte_len → str @len() : O(1), байты. * nova_str_char_len → str @char_len(): O(n), defined in nova_rt.h. */ static inline nova_int nova_str_byte_len(nova_str s) { return (nova_int)s.len; } /* ---- str-family []T producers (to_bytes / bytes / as_bytes / split / * to_chars / chars) — REMOVED in Plan 172.12 A6 (Vec-canon substrate). * * These `NovaArray_*`-returning helpers were retired from codegen routing by * Plan 139.2 (the str type-methods `@bytes`/`@chars`/`@as_bytes`/`@split` and * `str.from_bytes_*` are now Nova-body, building real `Vec[u8]`/`Vec[str]` via * `Vec[T].from_raw_parts`). Verified dead (2026-07-07): zero call sites across * generated C and zero FFI/extern decls — every reference is a doc comment. * Owner decision (2026-07-08): NovaArray dies wholesale → these dead runtime * bodies are removed here (byte-identical: nova_rt headers are `#include`d, not * spliced into the generated `.c`; the functions were `static inline` + unused * → no object-code change). The live O(1) primitives `nova_str_byte_len` / * `nova_str_char_at` above/below are untouched. */ /* ---- nova_str_char_at: codepoint по codepoint-индексу. * Возвращает Option[char] = NovaOpt_nova_char (Plan 70.3: distinct typedef). * None если idx out-of-range или невалидный UTF-8. O(idx) — линейная * итерация по UTF-8. */ static inline NovaOpt_nova_char nova_str_char_at(nova_str s, nova_int idx) { NovaOpt_nova_char r; r.tag = NOVA_TAG_Option_None; r.value = 0; if (idx < 0) return r; nova_int cp_idx = 0; for (size_t i = 0; i < s.len; ) { unsigned char b = (unsigned char)s.ptr[i]; nova_int cp = 0; size_t step = 1; if (b < 0x80) { cp = b; step = 1; } else if ((b & 0xE0) == 0xC0 && i + 1 < s.len) { cp = ((nova_int)(b & 0x1F) << 6) | ((nova_int)((unsigned char)s.ptr[i+1] & 0x3F)); step = 2; } else if ((b & 0xF0) == 0xE0 && i + 2 < s.len) { cp = ((nova_int)(b & 0x0F) << 12) | ((nova_int)((unsigned char)s.ptr[i+1] & 0x3F) << 6) | ((nova_int)((unsigned char)s.ptr[i+2] & 0x3F)); step = 3; } else if ((b & 0xF8) == 0xF0 && i + 3 < s.len) { cp = ((nova_int)(b & 0x07) << 18) | ((nova_int)((unsigned char)s.ptr[i+1] & 0x3F) << 12) | ((nova_int)((unsigned char)s.ptr[i+2] & 0x3F) << 6) | ((nova_int)((unsigned char)s.ptr[i+3] & 0x3F)); step = 4; } else { cp = b; step = 1; } if (cp_idx == idx) { r.tag = NOVA_TAG_Option_Some; r.value = cp; return r; } cp_idx++; i += step; } return r; } /* ---- Built-in Result type (Ok carries nova_int, Err carries nova_str) ---- * * Plan 61 followup #3: typed Err payload via err_typed_payload + tid. * Backward compat: legacy `Err(nova_str)` использует payload.Err._0; * custom `Err(T)` (T ≠ nova_str) использует err_typed_payload + tid. * `!!` dispatch по tid (NOVA_TID_NONE → legacy string path; иначе typed * throw через nova_throw_typed). * * Full mono'd NovaResult_<T>_<E> per-(T,E) struct — Plan 14/56 territory * (sum-type monomorphization pass). Hybrid дает equivalent semantics для * custom Err без полного mono refactor. */ #define NOVA_TAG_Result_Ok 0 #define NOVA_TAG_Result_Err 1 /* Plan 59 Ф.7.5 D3: канонический mono'd Result для erased (T,E) = * (int, str). Раньше назывался `Nova_Result` (single hardcoded * representation). Теперь — `NovaRes_nova_int_nova_str`, инстанс * семейства `NovaRes_<ok>_<err>` (per-(T,E) мономорфизация). Определён * руками здесь (а не lazy-generated в `__NOVARES_TYPEDEFS__`), потому * что runtime-заголовки (`read_buffer.h`, `string_builder.h`, `cast.h`) * ссылаются на него, а они включаются ДО splice-маркера. * * `Nova_Result` / `nova_make_Result_*` / `Nova_Result_method_*` — * back-compat алиасы на время перехода (шаг E их удалит). */ typedef struct NovaRes_nova_int_nova_str { int tag; union { struct { nova_int _0; } Ok; struct { nova_str _0; } Err; } payload; /* Plan 61 followup #3: typed Err payload (NULL для legacy string Err). */ void* err_typed_payload; NovaTypeId err_typed_type_id; } NovaRes_nova_int_nova_str; #define Nova_Result NovaRes_nova_int_nova_str static inline NovaRes_nova_int_nova_str* nova_make_NovaRes_nova_int_nova_str_Ok(nova_int v) { NovaRes_nova_int_nova_str* r = (NovaRes_nova_int_nova_str*)nova_alloc(sizeof(NovaRes_nova_int_nova_str)); r->tag = NOVA_TAG_Result_Ok; r->payload.Ok._0 = v; r->err_typed_payload = NULL; r->err_typed_type_id = NOVA_TID_NONE; return r; } static inline NovaRes_nova_int_nova_str* nova_make_NovaRes_nova_int_nova_str_Err(nova_str v) { NovaRes_nova_int_nova_str* r = (NovaRes_nova_int_nova_str*)nova_alloc(sizeof(NovaRes_nova_int_nova_str)); r->tag = NOVA_TAG_Result_Err; r->payload.Err._0 = v; r->err_typed_payload = NULL; r->err_typed_type_id = NOVA_TID_NONE; return r; } /* Plan 61 followup #3: typed Err constructor. payload — heap-allocated * copy of typed value (caller responsible — обычно codegen эмитит * `T* p = nova_alloc(sizeof(T)); *p = val;` inline). tid = NOVA_TID_<T>. */ static inline NovaRes_nova_int_nova_str* nova_make_NovaRes_nova_int_nova_str_Err_typed(void* payload, NovaTypeId tid) { NovaRes_nova_int_nova_str* r = (NovaRes_nova_int_nova_str*)nova_alloc(sizeof(NovaRes_nova_int_nova_str)); r->tag = NOVA_TAG_Result_Err; r->payload.Err._0 = (nova_str){.ptr = "<typed err>", .len = 11}; /* diag fallback */ r->err_typed_payload = payload; r->err_typed_type_id = tid; return r; } /* Back-compat алиасы (шаг E удалит). */ #define nova_make_Result_Ok nova_make_NovaRes_nova_int_nova_str_Ok #define nova_make_Result_Err nova_make_NovaRes_nova_int_nova_str_Err #define nova_make_Result_Err_typed nova_make_NovaRes_nova_int_nova_str_Err_typed static inline nova_bool nova_result_eq(NovaRes_nova_int_nova_str* a, NovaRes_nova_int_nova_str* b) { if (a->tag != b->tag) return 0; if (a->tag == NOVA_TAG_Result_Ok) return a->payload.Ok._0 == b->payload.Ok._0; return nova_str_eq(a->payload.Err._0, b->payload.Err._0); } /* ---- D26 Result methods: unwrap_or / ok ---- * Plan 59 Ф.7.5 D3: суффикс `_nova_int_nova_str` — инстанс семейства * `Nova_Result_method_*_<n>`. Back-compat алиасы без суффикса ниже. * * Plan 95 Ф.5.2: `is_ok` / `is_err` УДАЛЕНЫ — перенесены на Nova-body * в std/prelude/core.nv; mono'd через DeclaredBody-dispatch + worklist * drain. C-redefinition collision (тот же C-symbol) если оставить. */ /* Plan 95.bis Ф.2: unwrap_or + ok трамплины + back-compat алиасы * убраны — перенесены на Nova-body в std/prelude/core.nv через * MethodRouting::DeclaredBody. Mono'd C-имя * Nova_Result_method_<m>_<n> эмитится из Nova-body; legacy * `Nova_Result*` callsites не существуют (Plan 59 mono'd всё). * is_ok/is_err — Plan 95 Ф.5.2. err — был inline emit * (emit_c.rs:11903+), теперь Nova-body. */ /* Plan 08 Ф.1: D73/D77 prelude конверсии (str↔numeric, char↔str, etc.). * Подключаем здесь — после определения nova_alloc (alloc.h) и nova_str * (nova_rt.h), чтобы conv.h мог их использовать. */ #include "conv.h" /* ---- D26 prelude: Error — record для quick-and-dirty ошибок с msg ---- */ typedef struct Nova_Error { nova_str msg; } Nova_Error; /* ---- D90 §7 amend / Plan 110 prelude: CancelError — typed cancel-as-error * payload surfaced to a consume `@cleanup(outcome ScopeOutcome)` on a CANCEL * unwind (`err is CancelError` narrowing, D54/174.3). Single `reason: str` * field mirrors the canonical `.nv` declaration (`std/prelude/errors.nv`: * `export type CancelError { ro reason str }`). Hand-written here (like * Error/RuntimeError above) and listed in `RUNTIME_DEFINED_TYPES` * (emit_c.rs §0) so the D314 consume-cleanup codegen desugar * (`assign_scope_outcome_from_frame`) can hand-alloc `Nova_CancelError` * UNCONDITIONALLY on every consume-cleanup CANCEL/FromFrame exit path — * even in a compile unit whose `#prelude(...)` subset selection never * merges `std/prelude/errors.nv` (`ScopeOutcome`, needed for the `@cleanup` * signature itself, lives in the separate, always-on `core.nv` sub-module — * Plan 62.F split decouples the two) — [M-consume-block-cancelerror-bare-cu]. */ typedef struct Nova_CancelError { nova_str reason; } Nova_CancelError; static inline Nova_Error* Nova_Error_static_new(nova_str msg) { Nova_Error* e = (Nova_Error*)nova_alloc(sizeof(Nova_Error)); e->msg = msg; return e; } /* ---- D26 prelude: RuntimeError — sum-тип встроенных runtime-сбоев ---- * Бросается встроенными операциями (D65): * - DivByZero — `a / 0` * - Overflow — численное переполнение * - IndexOutOfBounds {i, n} — arr[i] на out-of-range * - TypeMismatch(str) — runtime type-check провален * - AssertFailed(str) — assert(...) провален * - NoHandler(str) — эффект без handler'а * * StackOverflow и OutOfMemory не входят — это panic, не Fail (D13). * * Bootstrap: представлены как pointer-to-tag-union. Конструкторы * `nova_make_RuntimeError_<Variant>` создают boxed копию. */ #define NOVA_TAG_RuntimeError_DivByZero 0 #define NOVA_TAG_RuntimeError_Overflow 1 #define NOVA_TAG_RuntimeError_IndexOutOfBounds 2 #define NOVA_TAG_RuntimeError_TypeMismatch 3 #define NOVA_TAG_RuntimeError_AssertFailed 4 #define NOVA_TAG_RuntimeError_NoHandler 5 typedef struct Nova_RuntimeError { int tag; union { struct { int _dummy; } DivByZero; struct { int _dummy; } Overflow; struct { nova_int index; nova_int length; } IndexOutOfBounds; struct { nova_str _0; } TypeMismatch; struct { nova_str _0; } AssertFailed; struct { nova_str _0; } NoHandler; } payload; } Nova_RuntimeError; static inline Nova_RuntimeError* nova_make_RuntimeError_DivByZero(void) { Nova_RuntimeError* e = (Nova_RuntimeError*)nova_alloc(sizeof(Nova_RuntimeError)); e->tag = NOVA_TAG_RuntimeError_DivByZero; return e; } static inline Nova_RuntimeError* nova_make_RuntimeError_Overflow(void) { Nova_RuntimeError* e = (Nova_RuntimeError*)nova_alloc(sizeof(Nova_RuntimeError)); e->tag = NOVA_TAG_RuntimeError_Overflow; return e; } static inline Nova_RuntimeError* nova_make_RuntimeError_IndexOutOfBounds(nova_int idx, nova_int len) { Nova_RuntimeError* e = (Nova_RuntimeError*)nova_alloc(sizeof(Nova_RuntimeError)); e->tag = NOVA_TAG_RuntimeError_IndexOutOfBounds; e->payload.IndexOutOfBounds.index = idx; e->payload.IndexOutOfBounds.length = len; return e; } static inline Nova_RuntimeError* nova_make_RuntimeError_TypeMismatch(nova_str msg) { Nova_RuntimeError* e = (Nova_RuntimeError*)nova_alloc(sizeof(Nova_RuntimeError)); e->tag = NOVA_TAG_RuntimeError_TypeMismatch; e->payload.TypeMismatch._0 = msg; return e; } static inline Nova_RuntimeError* nova_make_RuntimeError_AssertFailed(nova_str msg) { Nova_RuntimeError* e = (Nova_RuntimeError*)nova_alloc(sizeof(Nova_RuntimeError)); e->tag = NOVA_TAG_RuntimeError_AssertFailed; e->payload.AssertFailed._0 = msg; return e; } static inline Nova_RuntimeError* nova_make_RuntimeError_NoHandler(nova_str msg) { Nova_RuntimeError* e = (Nova_RuntimeError*)nova_alloc(sizeof(Nova_RuntimeError)); e->tag = NOVA_TAG_RuntimeError_NoHandler; e->payload.NoHandler._0 = msg; return e; } /* Plan 95.bis Ф.2: Nova_Option_method_or_nova_str — explicit * specialization тоже убрана. Метод or перенесён на Nova-body в * std/prelude/core.nv; mono'd Nova_Option_method_or_<T> с тем же * C-именем эмитится из Nova-body через MethodRouting::DeclaredBody. */ /* Plan 96 Ф.1 — bounds-check для raw arr[i] (D27 §1632 drift fix). * * До Plan 96 codegen эмитил `(arr)->data[i]` без проверки границ — * controlled buffer overflow на запись, UB на чтение. D27 §1632 спека * требовала panic при OOB. Wrapper форматирует диагностику и вызывает * nv_panic (D13 — смерть текущего fiber'а). Сообщение формата * "array: index N out of bounds for length L" — паритет с Go/Rust. * * Allocation через nova_alloc — Boehm thread-safe; panic-path, cost * нерелевантен; буфер становится GC-mature после nv_panic (longjmp/exit). */ static inline void nv_panic_index_oob(nova_int idx, nova_int len) { char* buf = (char*)nova_alloc(96); int n = snprintf(buf, 96, "array: index %lld out of bounds for length %lld", (long long)idx, (long long)len); if (n < 0) n = 0; if (n > 95) n = 95; nv_panic((nova_str){ .ptr = buf, .len = (size_t)n }); } /* Plan 145 — portable bounds-checked element access (MSVC C2059 fix). * * Раньше codegen эмитил GNU statement-expression * (*({ __typeof__(arr) _a = (arr); nova_int _i = (i); * if (_i < 0 || _i >= _a->len) nv_panic_index_oob(_i, _a->len); * &_a->data[_i]; })) * — `({ ... })` и `__typeof__` это расширения GCC/Clang, cl.exe их не * поддерживает → C2059, MSVC ломался на большинстве индексаций. * * Все array/Vec структуры Nova имеют идентичный начальный layout * { T* data; int64_t len; int64_t cap } * (NovaArray_T и Nova_Vec____T), поэтому общий header-каст через `void*` * даёт portable доступ к len/data при любом T. Параметр `arr` объявлен * `void*` — это «лаундерит» исходный тип, так что чтение через NovaArrHdr* * безопасно относительно strict-aliasing (void* алиасит всё). Сайт * передаёт sizeof(T) и кастует результат `(T*)`, восстанавливая lvalue: * *(T*)nova_idx_chk((void*)(arr), (i), sizeof(T)) * Оба подвыражения (arr, idx) вычисляются ровно один раз (аргументы fn). * * nova_idx_chk — с bounds-check (panic при OOB); default индексаций. * nova_idx_nochk — без проверки, для сайтов, доказанных in-range * верификатором (Plan 140.2 D257 elision). */ typedef struct NovaArrHdr { void* data; int64_t len; int64_t cap; } NovaArrHdr; static inline void* nova_idx_chk(void* arr, nova_int i, size_t esz) { NovaArrHdr* h = (NovaArrHdr*)arr; if (i < 0 || i >= h->len) nv_panic_index_oob(i, h->len); return (char*)h->data + (size_t)i * esz; } static inline void* nova_idx_nochk(void* arr, nova_int i, size_t esz) { NovaArrHdr* h = (NovaArrHdr*)arr; return (char*)h->data + (size_t)i * esz; } /* [M-fixed-array-value-semantics] (2026-07-10, D27-амендмент) — bounds-checked * element access for `[N]T` INLINE mono structs (`_NovaFixArr_<N>_<L>_<T>`, * `typedef struct { T data[N]; } ...;` — codegen `resolved_type_to_c`/ * `register_mono_fixed_array`). UNLIKE `nova_idx_chk` above, there is NO * `{data,len,cap}` header to read `len` from — `[N]T` carries no runtime * length at all (`N` is a compile-time literal baked into the type). The * call site passes `data` (the struct's `.data`/`->data` array member, * already decayed to a pointer by C) and `n` (the literal `N`) directly. * *(T*)nova_fixarr_idx_chk((void*)((v).data), (i), (N), sizeof(T)) * * nova_fixarr_idx_chk — bounds-check (panic on OOB); default indexing. * nova_fixarr_idx_nochk — no check, for verifier-proven in-range sites * (Plan 140.2 D257 elision, same policy as `nova_idx_nochk`). */ static inline void* nova_fixarr_idx_chk(void* data, nova_int i, nova_int n, size_t esz) { if (i < 0 || i >= n) nv_panic_index_oob(i, n); return (char*)data + (size_t)i * esz; } static inline void* nova_fixarr_idx_nochk(void* data, nova_int i, size_t esz) { return (char*)data + (size_t)i * esz; } /* Plan 186 (D412) — hex-blob `x"…"` / `embed("path")` materialization. * * The blob bytes live in one interned `static const uint8_t nova_blob_<h>[]` * (.rodata, emitted at the interned-literals preamble marker). * * nova_blob_view — zero-copy `[]u8` view: 24-byte Vec header allocated on * the GC heap, data → the static blob, len == cap == N (D262 view model; * NO memcpy of the payload). Boehm ignores pointers outside its heap, so * the static blob is never collected/moved; a push on the view (len == * cap) triggers a normal grow into a FRESH heap buffer — the static * memory is never realloc'd or written. * nova_blob_copy — mut/consume-binding materialization: payload copied * into a fresh GC-heap buffer at the binding point; the result is an * ordinary writable/growable Vec[u8] buffer. */ static inline void* nova_blob_view(const uint8_t* p, int64_t n) { NovaArrHdr* h = (NovaArrHdr*)nova_alloc(sizeof(NovaArrHdr)); h->data = (void*)p; h->len = n; h->cap = n; return h; } /* [M-fixed-array-value-semantics] — срез value-массива `[N]T[a..b]`: КОПИЯ * диапазона в свежий Vec (value-семантика [N]T; вид в стек-хранилище дал бы * dangling). Форма-прецедент — nova_blob_copy (D412). */ static inline void* nova_fixarr_slice_copy(const void* data, nova_int total, nova_int from, nova_int to, size_t esz) { if (from < 0 || to < from || to > total) { char buf[96]; int n = snprintf(buf, 96, "fixed array: slice [%lld..%lld] out of bounds for length %lld", (long long)from, (long long)to, (long long)total); if (n < 0) n = 0; if (n > 95) n = 95; nv_panic((nova_str){ .ptr = (const uint8_t*)buf, .len = (nova_int)n }); } NovaArrHdr* h = (NovaArrHdr*)nova_alloc(sizeof(NovaArrHdr)); nova_int cnt = to - from; void* buf2 = cnt > 0 ? nova_alloc((size_t)cnt * esz) : (void*)0; if (cnt > 0) memcpy(buf2, (const char*)data + (size_t)from * esz, (size_t)cnt * esz); h->data = buf2; h->len = cnt; h->cap = cnt; return h; } static inline void* nova_blob_copy(const uint8_t* p, int64_t n) { NovaArrHdr* h = (NovaArrHdr*)nova_alloc(sizeof(NovaArrHdr)); void* buf = n > 0 ? nova_alloc((size_t)n) : (void*)0; if (n > 0) memcpy(buf, p, (size_t)n); h->data = buf; h->len = n; h->cap = n; return h; } /* Plan 145 — portable bit-reinterpret nova_int -> nova_f64 (MSVC C2059). * Заменяет GNU statement-expression union-pun * `({ union { nova_int i; nova_f64 f; } _u; _u.i = (v); _u.f; })`. * Union type-punning well-defined в C (в отличие от C++). */ static inline nova_f64 nova_bits_i2f(nova_int i) { union { nova_int i; nova_f64 f; } u; u.i = i; return u.f; } /* Plan 145 — portable heap-box (MSVC). Копирует sz байт из *src в свежий * GC-блок, возвращает указатель. Заменяет stmt-expr * `({ T* _p = nova_alloc(sizeof T); *_p = *src; (void*)_p; })` для * АДРЕСУЕМОГО src (lvalue, напр. поле payload.Err._0). */ static inline void* nova_box_value(const void* src, size_t sz) { void* p = nova_alloc(sz); memcpy(p, src, sz); return p; } /* Plan 145 — portable Vec[T] zero-copy sub-view slice (MSVC). Все Vec * структуры имеют начальный layout NovaArrHdr; новый view alloc'ится и * указывает внутрь исходного буфера (data + from*esz). _chk — с * bounds-check (panic), _nochk — proven in-range (Plan 140.2 элизия). */ /* Plan 172.14 (sret/_out, дизайн-секция 172.14 §2): *_out-формы — callee * конструирует дескриптор ПО МЕСТУ в готовом `out` (caller решает * размещение: стек-слот при не-эскейпе / GC-куча иначе). Классические * формы — тонкие обёртки с alloc (единственная аллокация на верху * цепочки). Безопасно при out == src: чтения полей src предшествуют * записям out. */ static inline void* nova_vec_slice_chk_out(void* src, nova_int from, nova_int to, size_t esz, void* out) { NovaArrHdr* s = (NovaArrHdr*)src; if (from < 0 || to < from || to > s->len) { char buf[96]; int n = snprintf(buf, 96, "Vec: slice [%lld..%lld] out of bounds for length %lld", (long long)from, (long long)to, (long long)s->len); if (n < 0) n = 0; if (n > 95) n = 95; nv_panic((nova_str){ .ptr = (const uint8_t*)buf, .len = (nova_int)n }); } NovaArrHdr* r = (NovaArrHdr*)out; r->data = (char*)s->data + (size_t)from * esz; r->len = to - from; r->cap = to - from; return r; } static inline void* nova_vec_slice_nochk_out(void* src, nova_int from, nova_int to, size_t esz, void* out) { NovaArrHdr* s = (NovaArrHdr*)src; NovaArrHdr* r = (NovaArrHdr*)out; r->data = (char*)s->data + (size_t)from * esz; r->len = to - from; r->cap = to - from; return r; } static inline void* nova_vec_slice_chk(void* src, nova_int from, nova_int to, size_t esz) { return nova_vec_slice_chk_out(src, from, to, esz, nova_alloc(sizeof(NovaArrHdr))); } static inline void* nova_vec_slice_nochk(void* src, nova_int from, nova_int to, size_t esz) { return nova_vec_slice_nochk_out(src, from, to, esz, nova_alloc(sizeof(NovaArrHdr))); } /* Plan 145 — portable str byte-range slice (MSVC). Plan 152.1 byte-range * zero-copy sub-view. _chk: byte bounds (panic) + UTF-8 codepoint-boundary * guard. _nochk: bounds элидированы (proven), boundary guard остаётся * (data-dependent). *_to_end_* варианты — для open-ended `s[from..]`: * конец = s.len, single-eval `s` (избегаем double-eval исходного выраж.). */ static inline void nova_str_slice_utf8_guard(nova_str s, nova_int from, nova_int to) { if ((from < s.len && (((unsigned char)s.ptr[from]) & 0xC0) == 0x80) || (to < s.len && (((unsigned char)s.ptr[to]) & 0xC0) == 0x80)) { const char* m = "str: slice splits a UTF-8 codepoint"; nv_panic((nova_str){ .ptr = (const uint8_t*)m, .len = (nova_int)strlen(m) }); } } static inline void nova_str_slice_bounds(nova_str s, nova_int from, nova_int to) { if (from < 0 || to < from || to > s.len) { char buf[112]; int n = snprintf(buf, 112, "str: slice [%lld..%lld] out of bounds for byte-length %lld", (long long)from, (long long)to, (long long)s.len); if (n < 0) n = 0; if (n > 111) n = 111; nv_panic((nova_str){ .ptr = (const uint8_t*)buf, .len = (nova_int)n }); } } static inline nova_str nova_str_slice_chk(nova_str s, nova_int from, nova_int to) { nova_str_slice_bounds(s, from, to); nova_str_slice_utf8_guard(s, from, to); { nova_str r; r.ptr = s.ptr + from; r.len = to - from; return r; } } static inline nova_str nova_str_slice_nochk(nova_str s, nova_int from, nova_int to) { nova_str_slice_utf8_guard(s, from, to); { nova_str r; r.ptr = s.ptr + from; r.len = to - from; return r; } } static inline nova_str nova_str_slice_to_end_chk(nova_str s, nova_int from) { nova_str_slice_bounds(s, from, s.len); nova_str_slice_utf8_guard(s, from, s.len); { nova_str r; r.ptr = s.ptr + from; r.len = s.len - from; return r; } } static inline nova_str nova_str_slice_to_end_nochk(nova_str s, nova_int from) { nova_str_slice_utf8_guard(s, from, s.len); { nova_str r; r.ptr = s.ptr + from; r.len = s.len - from; return r; } } /* Plan 145.1 — portable repack тэггированного NovaOpt_nova_int в NPO * (single-pointer) Option-payload (MSVC). Извлекает указатель-или-NULL за * ОДИН доступ к `t` (source single-eval); сайт оборачивает результат в * `(NovaOpt_X){ .value = (X)nova_npo_from_tagged_int(...) }` (compound * literal — без GNU statement-expression). Заменяет * `({ NovaOpt_nova_int t = …; NovaOpt_X r; r.value = …; r; })`. */ static inline void* nova_npo_from_tagged_int(NovaOpt_nova_int t) { return t.tag == NOVA_TAG_Option_Some ? (void*)(intptr_t)t.value : (void*)0; } /* Plan 138 Ф.3 (D238): str[i] → char — panicking codepoint accessor. * Wraps nova_str_char_at; panics с nv_panic_index_oob если idx OOB или * невалидный UTF-8. O(idx) — линейная итерация по UTF-8. */ static inline nova_char nova_str_index_panic(nova_str s, nova_int idx) { NovaOpt_nova_char r = nova_str_char_at(s, idx); if (r.tag == NOVA_TAG_Option_None) { /* Determine char_len for OOB message — iterate to count codepoints */ nova_int char_len = 0; for (size_t i = 0; i < s.len; ) { unsigned char b = (unsigned char)s.ptr[i]; if (b < 0x80) { i += 1; } else if ((b & 0xE0) == 0xC0) { i += 2; } else if ((b & 0xF0) == 0xE0) { i += 3; } else if ((b & 0xF8) == 0xF0) { i += 4; } else { i += 1; } char_len++; } nv_panic_index_oob(idx, char_len); } return r.value; } /* Plan 96 Ф.4 — bounds-check для slice creation (arr[a..b]). * Отдельное сообщение от raw-index OOB — диапазон вместо одного индекса. */ static inline void nv_panic_slice_oob(nova_int from, nova_int to, nova_int len) { char* buf = (char*)nova_alloc(112); int n = snprintf(buf, 112, "array: slice [%lld..%lld] out of bounds for length %lld", (long long)from, (long long)to, (long long)len); if (n < 0) n = 0; if (n > 111) n = 111; nv_panic((nova_str){ .ptr = buf, .len = (size_t)n }); } /* D178 (Plan 91 Ф.2.6): nova_str_compare — lexicographic comparison. * Returns negative / 0 / positive like C strcmp, but length-aware (memcmp). * Compares raw UTF-8 bytes; consistent with equality semantics. */ static inline nova_int nova_str_compare(nova_str a, nova_str b) { size_t min_len = a.len < b.len ? a.len : b.len; if (min_len > 0) { int r = __builtin_memcmp(a.ptr, b.ptr, min_len); if (r != 0) return (nova_int)r; } if (a.len < b.len) return (nova_int)-1; if (a.len > b.len) return (nova_int) 1; return (nova_int)0; } /* Plan 91 Ф.2: nova_str_parse_int — parse decimal int с optional ±-prefix. * Retained for legacy call sites; Nova-body parse_int(radix=10) is preferred. * Returns Some(n) на успех, None при error (empty, non-digit, overflow). */ static inline NovaOpt_nova_int nova_str_parse_int(nova_str s) { NovaOpt_nova_int r; r.tag = NOVA_TAG_Option_None; r.value = 0; if (s.len == 0) return r; size_t i = 0; int neg = 0; if (s.ptr[0] == '-') { neg = 1; i = 1; } else if (s.ptr[0] == '+') { i = 1; } if (i >= s.len) return r; /* "-" / "+" alone */ nova_int acc = 0; for (; i < s.len; i++) { char c = s.ptr[i]; if (c < '0' || c > '9') return r; nova_int d = (nova_int)(c - '0'); /* Overflow check: acc * 10 + d > INT64_MAX. */ if (acc > (9223372036854775807LL - d) / 10) return r; acc = acc * 10 + d; } r.tag = NOVA_TAG_Option_Some; r.value = neg ? -acc : acc; return r; } /* Plan 90.1 — bounds-check для insert(i, src) position. * i должен быть в [0, dst.len] (включая len — append-at-end допустим). */ static inline void nv_panic_insert_oob(nova_int i, nova_int len) { char* buf = (char*)nova_alloc(96); int n = snprintf(buf, 96, "insert: index %lld out of bounds for length %lld (valid range [0, len])", (long long)i, (long long)len); if (n < 0) n = 0; if (n > 95) n = 95; nv_panic((nova_str){ .ptr = buf, .len = (size_t)n }); } /* Plan 90.1 — bounds-check для reserve(extra) argument. * extra < 0 не имеет смысла и указывает на баг в коде пользователя. */ static inline void nv_panic_negative_reserve(nova_int extra) { char* buf = (char*)nova_alloc(80); int n = snprintf(buf, 80, "reserve: extra must be >= 0, got %lld", (long long)extra); if (n < 0) n = 0; if (n > 79) n = 79; nv_panic((nova_str){ .ptr = buf, .len = (size_t)n }); } #endif /* NOVA_RT_ARRAY_H */