/
nv-lang
/
nova
Обзор
Документация
Войти
/
nv-lang
/
nova
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
main
std/src/encoding/utf16.nv
108 строк
5 KB
Evgeniy Golovin
merge: comment-hygiene-2 — чистка комментариев std (batch 1-20) + линт-свип 330→13
01 авг 2026, 18:13
01 авг 2026, 18:13
bb8b33d
Код
Авторство
О чём код?
// SPDX-License-Identifier: MIT OR Apache-2.0 // std/encoding/utf16.nv — UTF-16 / code-point interop. // // Дополняет байтовый слой (`str.from_bytes_*` / `@to_bytes` / `@as_bytes`) и // codepoint-линзу (`@as_chars`): даёт конверсии в/из UTF-16 (Windows API, // JS-interop, JSON `\uXXXX`) и доступ к сырым int-codepoint'ам для // низкоуровневых протоколов/таблиц. // // Инвариант R-UTF8: любой `str` на выходе — валидный UTF-8. // `from_utf16` проверяет surrogate-пары; невалидный UTF-16 (lone/усечённый // surrogate) → `Err(Utf16Error)`, str НЕ конструируется. // // `str`-методы (`@encode_utf16` / `@code_points` / `str.from_utf16`) — тонкие // делегаты в свободные функции этого модуля; импортируется явно // (`import std.encoding.utf16`), не prelude — это FFI/протокол-концерн. module encoding.utf16 // ─── Ошибка декодирования UTF-16 ─── /// Invalid sequence of UTF-16 code units. Stores the offending code /// unit (0..0xFFFF) for diagnostics. export type Utf16Error enum | LoneHighSurrogate(int) // high surrogate без следующего low | LoneLowSurrogate(int) // low surrogate без предшествующего high | TruncatedSurrogatePair(int) // high surrogate в самом конце входа // ─── Surrogate-помощники (UTF-16 spec) ─── /// High (leading) surrogate: U+D800..=U+DBFF. export fn is_high_surrogate(u int) -> bool => u >= 0xD800 && u <= 0xDBFF /// Low (trailing) surrogate: U+DC00..=U+DFFF. export fn is_low_surrogate(u int) -> bool => u >= 0xDC00 && u <= 0xDFFF /// Assemble a supplementary codepoint (U+10000..=U+10FFFF) from a valid surrogate /// pair. The caller guarantees `is_high_surrogate(hi) && is_low_surrogate(lo)`. export fn decode_surrogate_pair(hi int, lo int) -> int requires is_high_surrogate(hi) requires is_low_surrogate(lo) ensures result >= 0x10000 && result <= 0x10FFFF => 0x10000 + ((hi - 0xD800) << 10) + (lo - 0xDC00) // ─── str → UTF-16 ─── /// UTF-16 code units of the string. BMP codepoint → one u16; supplementary /// (cp > 0xFFFF) → surrogate pair (two u16). Analogous to Rust `str::encode_utf16` /// and the JS string representation. Roundtrip: `from_utf16(s.encode_utf16()) == Ok(s)`. export fn str @encode_utf16() -> []u16 { // capacity hint: code units ≤ byte_len (ASCII=1 байт=1 unit; не-ASCII — // меньше units чем байт, кроме supplementary 4 байта→2 units; byte_len — // безопасная верхняя граница без второго прохода). mut out []u16 = []u16.new(cap: @byte_len()) for c in @chars() { ro cp = c as int if cp < 0x10000 { out.push(cp as u16) } else { ro v = cp - 0x10000 out.push((0xD800 + (v >> 10)) as u16) // high surrogate out.push((0xDC00 + (v & 0x3FF)) as u16) // low surrogate } } out } // ─── UTF-16 → str (checked) ─── /// Decode UTF-16 code units into a `str`. Validates surrogate pairs: /// a lone high/low or truncated pair → `Err(Utf16Error)`. The result is valid /// UTF-8 (R-UTF8). Analogous to Rust `String::from_utf16`. /// /// Two-pass: first validate into `[]int` codepoints (an early `Err` is safe — /// no live consume-typed value), then build the `str` from codepoints /// via `StringBuilder` (consumed once on the only successful path). export fn str.from_utf16(units []u16) -> Result[str, Utf16Error] { ro n = units.len() mut cps []int = []int.new(cap: n) mut i = 0 while i < n { ro u = units[i] as int if is_high_surrogate(u) { if i + 1 >= n { return Err(TruncatedSurrogatePair(u)) } ro lo = units[i + 1] as int if !is_low_surrogate(lo) { return Err(LoneHighSurrogate(u)) } cps.push(decode_surrogate_pair(u, lo)) i += 2 } else if is_low_surrogate(u) { return Err(LoneLowSurrogate(u)) } else { cps.push(u) // BMP scalar (validated: не surrogate) i += 1 } } // Все code units валидны → строим str. StringBuilder создаётся ПОСЛЕ всех // ранних `return Err`, потребляется один раз через @as_str (consume-safe). // Capacity *4: supplementary codepoints encode to 4 UTF-8 bytes. consume sb = StringBuilder.new(cap: cps.len() * 4) for cp in cps { ro c = cp.to_char() ?? '\u{FFFD}' sb.append(c) } Ok(sb.into_str()) } // ─── str → raw code points ─── // Moved to runtime.string chars.nv as @to_code_points(). // Use `s.to_code_points()` — available without explicit import via runtime.string.