diff --git a/stage1/spl_ast2ir.c b/stage1/spl_ast2ir.c index 9c8cdcd..e1e5ac6 100644 --- a/stage1/spl_ast2ir.c +++ b/stage1/spl_ast2ir.c @@ -361,6 +361,10 @@ static spl_ir_node_ref_t ast2ir_intrin_cast(spl_ast2ir_t *a, spl_ast_node_t *n, static spl_ir_node_ref_t ast2ir_intrin_call(spl_ast2ir_t *a, spl_ast_node_t *n, spl_intrin_t in, spl_ast_node_ref_vec_t *args) { switch (in) { + case SPL_INTRIN_BITSIZEOF: { + spl_ast_node_t *arg = spl_ast_node(a->ast, vec_at(*args, 0)); + return spl_ir_builder_type_bitsizeof(a->ir, node_ir(a, arg)); + } case SPL_INTRIN_SIZEOF: { spl_ast_node_t *arg = spl_ast_node(a->ast, vec_at(*args, 0)); return spl_ir_builder_type_sizeof(a->ir, node_ir(a, arg)); @@ -501,9 +505,6 @@ static spl_ir_node_ref_t emit_lvalue(spl_ast2ir_t *a, spl_ast_node_ref_t ref) { spl_lang_type_kind_t obj_kind = obj_ln->kind; spl_lang_type_id_t obj_elem = (obj_kind == SPL_LANG_TYPE_PTR) ? obj_ln->extra.elem : 0; - if (spl_intrin_lookup_field(obj_kind, n->field.field) != SPL_INTRIN_NONE) - return 0; - spl_ir_node_ref_t base = 0; spl_lang_type_id_t agg_lang = 0; if (obj_kind == SPL_LANG_TYPE_PTR) { diff --git a/stage1/spl_intrin.c b/stage1/spl_intrin.c index 3cd85fc..34728fd 100644 --- a/stage1/spl_intrin.c +++ b/stage1/spl_intrin.c @@ -2,6 +2,8 @@ static const spl_intrin_info_t g_intrin_table[] = { [SPL_INTRIN_NONE] = {0}, + [SPL_INTRIN_BITSIZEOF] = {"@bitsizeof", SPL_INTRIN_BITSIZEOF, SPL_INTRIN_FORM_CALL, 1, 1, + SPL_INTRIN_RESULT_USIZE}, [SPL_INTRIN_SIZEOF] = {"@sizeof", SPL_INTRIN_SIZEOF, SPL_INTRIN_FORM_CALL, 1, 1, SPL_INTRIN_RESULT_USIZE}, [SPL_INTRIN_ALIGNOF] = {"@alignof", SPL_INTRIN_ALIGNOF, SPL_INTRIN_FORM_CALL, 1, 1, @@ -96,4 +98,4 @@ spl_intrin_t spl_intrin_lookup_cast(spl_type_t *type, spl_type_id_t ft, spl_type /* int float 刻意不在这里:只能通过显式 builtin 调用。 */ return SPL_INTRIN_NONE; -} \ No newline at end of file +} diff --git a/stage1/spl_intrin.h b/stage1/spl_intrin.h index f1552fa..24620c7 100644 --- a/stage1/spl_intrin.h +++ b/stage1/spl_intrin.h @@ -6,6 +6,7 @@ typedef enum { SPL_INTRIN_NONE = 0, + SPL_INTRIN_BITSIZEOF, SPL_INTRIN_SIZEOF, SPL_INTRIN_ALIGNOF, SPL_INTRIN_ASSERT, diff --git a/stage1/spl_sema.c b/stage1/spl_sema.c index 1f70584..6cd331f 100644 --- a/stage1/spl_sema.c +++ b/stage1/spl_sema.c @@ -746,8 +746,6 @@ static void sema_collect_types(spl_sema_t *sema, spl_ast_node_ref_t ref) { if (pat->kind == SPL_AST_PATTERN_IDENT) { if (d->resolved_def_id) sema_eval_decl(sema, d->resolved_def_id); - } else { - sema_do_type_unpack(sema, d, pat, d->type_decl.type_expr); } } break; @@ -772,6 +770,144 @@ static void sema_collect_signatures(spl_sema_t *sema) { } } +static int sema_def_is_type(const spl_def_node_t *def) { + if (!def) + return 0; + switch (def->kind) { + case SPL_DEF_SCALAR: + case SPL_DEF_AGG: + case SPL_DEF_DISTINCT: + case SPL_DEF_ALIAS: + case SPL_DEF_BUILTIN: + return 1; + default: + return 0; + } +} + +/* 解包声明本身不会在 collect_names 阶段建立符号,因此必须在构建模块根 + * 聚合之前处理。否则根字段无法引用通过 `type .{ ... } = source` 导入的类型。 */ +static void sema_collect_type_unpacks(spl_sema_t *sema, spl_ast_node_ref_t ref) { + spl_ast_node_t *n = spl_ast_node(sema->ast, ref); + if (!n) + return; + switch (n->kind) { + case SPL_AST_CONTAINER_MEMBERS: + for (usize i = 0; i < vec_size(n->container_members.declarations); ++i) { + spl_ast_node_ref_t dref = vec_at(n->container_members.declarations, i); + spl_ast_node_t *d = spl_ast_node(sema->ast, dref); + if (!d || d->kind != SPL_AST_TYPE_DECL) + continue; + spl_ast_node_t *pat = spl_ast_node(sema->ast, d->type_decl.pattern); + if (pat && pat->kind != SPL_AST_PATTERN_IDENT) + sema_do_type_unpack(sema, d, pat, d->type_decl.type_expr); + } + break; + case SPL_AST_TOP_LEVEL_BLOCK: + if (n->top_level_block.block) + sema_collect_type_unpacks(sema, n->top_level_block.block); + break; + default: + break; + } +} + +/* @sizeof/@bitsizeof/@alignof 的参数在语法层仍是普通 Expr。 + intrinsic 的类型上下文在这里解释表达式形态,不要求 parser 识别 intrinsic。 */ +static spl_lang_type_id_t sema_infer_type_context(spl_sema_t *sema, spl_ast_node_ref_t ref) { + spl_ast_node_t *n = spl_ast_node(sema->ast, ref); + if (!n) + return 0; + + switch (n->kind) { + case SPL_AST_EXPR_IDENT: { + spl_lang_type_id_t builtin = spl_type_lookup_builtin(sema->type, n->ident.name); + if (builtin) { + n->resolved_lang_tid = builtin; + return builtin; + } + + spl_symbol_t symbol; + if (!spl_scope_find(sema->scope, n->ident.name, &symbol) || + symbol.kind != SPL_SYMBOL_KIND_TYPE) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, + "`%s` is not a type", n->ident.name); + return 0; + } + spl_def_node_t *def = spl_def_node(sema->type, symbol.node); + if (!sema_def_is_type(def)) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, + "`%s` is not a type", n->ident.name); + return 0; + } + n->resolved_def_id = symbol.node; + spl_lang_type_id_t result = sema_eval_decl(sema, symbol.node); + n->resolved_lang_tid = result; + return result; + } + case SPL_AST_EXPR_FIELD: { + spl_lang_type_id_t owner_id = sema_infer_type_context(sema, n->field.obj); + spl_lang_type_node_t *owner = spl_lang_type_node(sema->type, owner_id); + if (!owner) + return 0; + for (usize i = 0; i < vec_size(owner->methods); ++i) { + spl_lang_method_t *member = &vec_at(owner->methods, i); + if (strcmp(member->name, n->field.field) != 0) + continue; + spl_def_node_t *def = spl_def_node(sema->type, member->def_id); + if (!sema_def_is_type(def)) + break; + n->resolved_def_id = member->def_id; + spl_lang_type_id_t result = sema_eval_decl(sema, member->def_id); + n->resolved_lang_tid = result; + return result; + } + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, + "`%s` is not a type member", n->field.field); + return 0; + } + case SPL_AST_EXPR_UNARY: + if (n->unary.op == SPL_UNARY_OP_DEREF) { + spl_lang_type_id_t pointee = sema_infer_type_context(sema, n->unary.operand); + if (!pointee) + return 0; + spl_lang_type_id_t result = spl_lang_make_ptr(sema->type, pointee); + n->resolved_lang_tid = result; + return result; + } + break; + case SPL_AST_EXPR_TUPLE: { + spl_lang_type_id_vec_t elements; + vec_init(elements); + for (usize i = 0; i < vec_size(n->tuple.elems); ++i) { + spl_lang_type_id_t element = + sema_infer_type_context(sema, vec_at(n->tuple.elems, i)); + if (!element) { + vec_free(elements); + return 0; + } + vec_push(elements, element); + } + spl_lang_type_id_t result = spl_lang_make_tuple( + sema->type, vec_unsafe_get_data(elements), vec_size(elements)); + vec_free(elements); + n->resolved_lang_tid = result; + return result; + } + case SPL_AST_EXPR_COMPTIME_CALL: { + spl_lang_type_id_t result = sema_infer(sema, ref); + n->resolved_lang_tid = result; + return result; + } + default: + break; + } + + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, + "intrinsic argument is not a type expression"); + return 0; +} + static spl_lang_type_id_t sema_intrin_call(spl_sema_t *sema, spl_ast_node_t *n, spl_intrin_t in, spl_ast_node_ref_vec_t *args) { const spl_intrin_info_t *info = spl_intrin_info(in); @@ -784,9 +920,11 @@ static spl_lang_type_id_t sema_intrin_call(spl_sema_t *sema, spl_ast_node_t *n, } switch (in) { + case SPL_INTRIN_BITSIZEOF: case SPL_INTRIN_SIZEOF: case SPL_INTRIN_ALIGNOF: { - sema_infer(sema, vec_at(*args, 0)); + if (!sema_infer_type_context(sema, vec_at(*args, 0))) + return 0; return spl_lang_make_int(sema->type, sizeof(void *) * 8, 0); } case SPL_INTRIN_ASSERT: { @@ -2600,6 +2738,7 @@ void spl_sema_run_module(spl_sema_t *sema, spl_ast_node_ref_t module_ref) { spl_lang_type_id_t saved_agg = sema->current_aggregate_lang; sema_collect_names(sema, module_ref); + sema_collect_type_unpacks(sema, module_ref); spl_lang_type_id_t container_lang = 0; if (mod->resolved_def_id) diff --git a/stage2/utils/ascii.spl b/stage2/utils/ascii.spl new file mode 100644 index 0000000..d538465 --- /dev/null +++ b/stage2/utils/ascii.spl @@ -0,0 +1,216 @@ +// Expect retcode = 0 +// ASCII 字符工具。输入和输出都是单个字节,不解释 UTF-8 或本地化规则。 + +type .{ .Error, .make } = @import("error.spl"); + +type DigitResult = enum { + Ok: u8, + Err: Error, +} + +fn is_ascii(c: u8): bool { + ret c <= 127; +} + +fn is_control(c: u8): bool { + ret c <= 31 || c == 127; +} + +fn is_space(c: u8): bool { + ret c == 32 || (c >= 9 && c <= 13); +} + +fn is_blank(c: u8): bool { + ret c == 32 || c == 9; +} + +fn is_digit(c: u8): bool { + ret c >= 48 && c <= 57; +} + +fn is_lower(c: u8): bool { + ret c >= 97 && c <= 122; +} + +fn is_upper(c: u8): bool { + ret c >= 65 && c <= 90; +} + +fn is_alpha(c: u8): bool { + ret is_lower(c) || is_upper(c); +} + +fn is_alnum(c: u8): bool { + ret is_alpha(c) || is_digit(c); +} + +fn is_hex_digit(c: u8): bool { + ret is_digit(c) || (c >= 65 && c <= 70) || (c >= 97 && c <= 102); +} + +fn is_print(c: u8): bool { + ret c >= 32 && c <= 126; +} + +fn is_graph(c: u8): bool { + ret c >= 33 && c <= 126; +} + +fn is_punct(c: u8): bool { + ret is_graph(c) && !is_alnum(c); +} + +fn to_lower(c: u8): u8 { + if is_upper(c) { ret c + 32; } + ret c; +} + +fn to_upper(c: u8): u8 { + if is_lower(c) { ret c - 32; } + ret c; +} + +// radix 必须位于 2..36。大小写字母均表示 10..35。 +fn digit_value(c: u8, radix: u8): DigitResult { + if radix < 2 || radix > 36 { + ret .Err(make(.InvalidArgument)); + } + + var value: u8 = 0; + if is_digit(c) { + value = c - 48; + } else if is_upper(c) { + value = c - 65 + 10; + } else if is_lower(c) { + value = c - 97 + 10; + } else { + ret .Err(make(.InvalidDigit)); + } + + if value >= radix { ret .Err(make(.InvalidDigit)); } + ret .Ok(value); +} + +// value 必须位于 0..35。uppercase 只影响 10..35 的字母形式。 +fn digit_char(value: u8, uppercase: bool): DigitResult { + if value >= 36 { ret .Err(make(.OutOfRange)); } + if value < 10 { ret .Ok(value + 48); } + if uppercase { ret .Ok(value - 10 + 65); } + ret .Ok(value - 10 + 97); +} + +fn expect_digit(result: DigitResult, expected: u8) { + match result { + .Ok(value) => { @assert(value == expected, "digit value"); }, + .Err(error) => { @assert(false, "unexpected digit error"); }, + } +} + +fn expect_error(result: DigitResult, expected_invalid_argument: bool) { + match result { + .Ok(value) => { @assert(false, "expected digit error"); }, + .Err(error) => { + match error.kind { + .InvalidArgument => { + @assert(expected_invalid_argument, "unexpected invalid radix"); + }, + .InvalidDigit => { + @assert(!expected_invalid_argument, "unexpected invalid digit"); + }, + _ => { @assert(false, "unexpected digit error kind"); }, + } + }, + } +} + +fn expect_range_error(result: DigitResult) { + match result { + .Ok(value) => { @assert(false, "expected digit range error"); }, + .Err(error) => { + match error.kind { + .OutOfRange => {}, + _ => { @assert(false, "unexpected digit range error kind"); }, + } + }, + } +} + +fn test_all_bytes() { + var i: usize = 0; + while i < 256 { + var c = i as u8; + @assert(is_ascii(c) == (i <= 127), "ASCII boundary"); + @assert(is_control(c) == (i <= 31 || i == 127), "control boundary"); + @assert(is_print(c) == (i >= 32 && i <= 126), "print boundary"); + @assert(is_graph(c) == (i >= 33 && i <= 126), "graph boundary"); + @assert(is_blank(c) == (i == 9 || i == 32), "blank set"); + @assert(is_space(c) == (i == 9 || i == 10 || i == 11 || i == 12 || i == 13 || i == 32), + "space set"); + @assert(is_digit(c) == (i >= 48 && i <= 57), "digit set"); + @assert(is_upper(c) == (i >= 65 && i <= 90), "upper set"); + @assert(is_lower(c) == (i >= 97 && i <= 122), "lower set"); + @assert(is_hex_digit(c) == ((i >= 48 && i <= 57) || + (i >= 65 && i <= 70) || + (i >= 97 && i <= 102)), + "hex digit set"); + @assert(is_alpha(c) == (is_upper(c) || is_lower(c)), "alpha relation"); + @assert(is_alnum(c) == (is_alpha(c) || is_digit(c)), "alnum relation"); + @assert(is_punct(c) == (is_graph(c) && !is_alnum(c)), "punct relation"); + + if is_upper(c) { + @assert(to_lower(c) == c + 32, "upper to lower"); + @assert(to_upper(to_lower(c)) == c, "case round trip"); + } else if is_lower(c) { + @assert(to_upper(c) == c - 32, "lower to upper"); + @assert(to_lower(to_upper(c)) == c, "case round trip"); + } else { + @assert(to_lower(c) == c, "lower preserves non-letter"); + @assert(to_upper(c) == c, "upper preserves non-letter"); + } + i = i + 1; + } +} + +fn test_digits() { + var radix: usize = 2; + while radix <= 36 { + var value: usize = 0; + while value < radix { + var lower: u8 = 0; + match digit_char(value as u8, false) { + .Ok(c) => { lower = c; }, + .Err(error) => { @assert(false, "lower digit generation"); }, + } + expect_digit(digit_value(lower, radix as u8), value as u8); + + var upper: u8 = 0; + match digit_char(value as u8, true) { + .Ok(c) => { upper = c; }, + .Err(error) => { @assert(false, "upper digit generation"); }, + } + expect_digit(digit_value(upper, radix as u8), value as u8); + if value < 10 { + @assert(lower == upper, "decimal digits ignore case"); + } else { + @assert(lower == upper + 32, "letter digit case distance"); + } + value = value + 1; + } + radix = radix + 1; + } + + expect_error(digit_value(48, 0), true); + expect_error(digit_value(48, 1), true); + expect_error(digit_value(48, 37), true); + expect_error(digit_value(47, 10), false); + expect_error(digit_value(58, 10), false); + expect_error(digit_value(50, 2), false); + expect_error(digit_value(103, 16), false); + expect_range_error(digit_char(36, false)); + expect_range_error(digit_char(255, true)); +} + +fn main() { + test_all_bytes(); + test_digits(); +} diff --git a/stage2/utils/byte_buffer.spl b/stage2/utils/byte_buffer.spl new file mode 100644 index 0000000..c374a9d --- /dev/null +++ b/stage2/utils/byte_buffer.spl @@ -0,0 +1,317 @@ +// Expect retcode = 0 +// 拥有字节存储的动态缓冲区。普通赋值会复制所有权,转移必须使用 take。 +// 借用切片和 z_ptr 返回的指针在下一次可能扩容的操作或 drop 后失效。 + +type .{ .Error, .Status } = @import("error.spl"); +type .{ .checked_add, .move } = @import("memory.spl"); +type .{ .Vec } = @import("vec.spl"); + +type ByteBuffer = @self; + +storage: Vec, + +type PtrResult = enum { + Ok: *u8, + Err: Error, +} + +fn new(): ByteBuffer { + match Vec.new(1, 1) { + .Ok(value) => { ret ByteBuffer { .storage = value }; }, + .Err(error) => { @assert(false, "fixed byte layout is valid"); }, + } + ret ByteBuffer { .storage = Vec.blank(1, 1) }; +} + +fn size(self: ByteBuffer): usize { + ret self.storage.len; +} + +fn capacity(self: ByteBuffer): usize { + ret self.storage.cap; +} + +fn is_empty(self: ByteBuffer): bool { + ret self.storage.len == 0; +} + +fn as_slice(self: ByteBuffer): []u8 { + ret .{ .ptr = self.storage.data, .len = self.storage.len }; +} + +fn reserve(self: *ByteBuffer, capacity_needed: usize): Status { + ret Vec.reserve(&self.storage, capacity_needed); +} + +fn append(self: *ByteBuffer, bytes: []u8): Status { + ret Vec.append(&self.storage, bytes.ptr, bytes.len); +} + +fn append_byte(self: *ByteBuffer, byte: u8): Status { + ret Vec.push(&self.storage, &byte); +} + +fn append_repeat(self: *ByteBuffer, byte: u8, count: usize): Status { + if count == 0 { ret .Ok; } + + var new_len: usize = 0; + match checked_add(self.storage.len, count) { + .Ok(value) => { new_len = value; }, + .Err(error) => { ret .Err(error); }, + } + match reserve(self, new_len) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + + var i = self.storage.len; + while i < new_len { + self.storage.data[i] = byte; + i = i + 1; + } + self.storage.len = new_len; + ret .Ok; +} + +// 扩展时用 fill 初始化新增字节;缩短只改变逻辑长度。 +fn resize(self: *ByteBuffer, new_len: usize, fill: u8): Status { + if new_len <= self.storage.len { + self.storage.len = new_len; + ret .Ok; + } + + var count = new_len - self.storage.len; + ret append_repeat(self, fill, count); +} + +fn clear(self: *ByteBuffer) { + Vec.clear(&self.storage); +} + +// 保证 data[len] 为零并返回 data。终止零不属于逻辑内容。 +// 缓冲区内容可以包含更早的零字节,调用方决定是否适合作为 C 字符串。 +fn z_ptr(self: *ByteBuffer): PtrResult { + var needed: usize = 0; + match checked_add(self.storage.len, 1) { + .Ok(value) => { needed = value; }, + .Err(error) => { ret .Err(error); }, + } + match reserve(self, needed) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + self.storage.data[self.storage.len] = 0; + ret .Ok(self.storage.data); +} + +// 成功时替换 out 的旧内容;失败保持 out 不变。允许 bytes 借用 out 自身。 +fn assign(out: *ByteBuffer, bytes: []u8): Status { + if bytes.len == 0 { + clear(out); + ret .Ok; + } + if bytes.ptr == null { ret .Err(Error.make(.InvalidArgument)); } + + var used = out.storage.len; + if used != 0 { + var base = out.storage.data as usize; + var source = bytes.ptr as usize; + if source >= base && source - base < used { + var offset = source - base; + if bytes.len > used - offset { + ret .Err(Error.make(.OutOfRange)); + } + match move(out.storage.data[0..bytes.len], bytes) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + out.storage.len = bytes.len; + ret .Ok; + } + } + + var fresh = new(); + match append(&fresh, bytes) { + .Ok => {}, + .Err(error) => { + drop(&fresh); + ret .Err(error); + }, + } + drop(out); + out.storage = Vec.take(&fresh.storage); + ret .Ok; +} + +// 深复制;允许 out 与 source 是同一对象。 +fn clone(out: *ByteBuffer, source: ByteBuffer): Status { + ret Vec.clone(&out.storage, source.storage); +} + +fn take(source: *ByteBuffer): ByteBuffer { + ret ByteBuffer { .storage = Vec.take(&source.storage) }; +} + +fn drop(self: *ByteBuffer) { + Vec.drop(&self.storage); +} + +fn expect_ok(result: Status) { + match result { + .Ok => {}, + .Err(error) => { + match error.kind { + .InvalidArgument => { @assert(false, "unexpected invalid argument"); }, + .OutOfRange => { @assert(false, "unexpected out of range"); }, + .Overflow => { @assert(false, "unexpected overflow"); }, + _ => { @assert(false, "unexpected byte buffer error"); }, + } + }, + } +} + +fn expect_overflow(result: Status) { + match result { + .Ok => { @assert(false, "expected byte buffer overflow"); }, + .Err(error) => { + match error.kind { + .Overflow => {}, + _ => { @assert(false, "unexpected byte buffer error kind"); }, + } + }, + } +} + +fn expect_bytes(self: ByteBuffer, expected: []u8) { + @assert(self.storage.len == expected.len, "byte buffer length"); + var i: usize = 0; + while i < expected.len { + @assert(self.storage.data[i] == expected.ptr[i], "byte buffer content"); + i = i + 1; + } +} + +fn expect_z(self: *ByteBuffer) { + var old_len = self.storage.len; + match z_ptr(self) { + .Ok(ptr) => { + @assert(ptr != null, "zero terminated pointer"); + @assert(ptr[old_len] == 0, "zero terminator"); + }, + .Err(error) => { @assert(false, "zero termination"); }, + } + @assert(self.storage.len == old_len, "terminator is outside logical content"); +} + +fn test_append_and_resize() { + var buffer = new(); + @assert(is_empty(buffer)); + @assert(size(buffer) == 0 && capacity(buffer) == 0); + expect_z(&buffer); + @assert(capacity(buffer) >= 1); + + expect_ok(append(&buffer, "abc")); + expect_ok(append_byte(&buffer, 0)); + expect_ok(append(&buffer, "de")); + @assert(buffer.storage.len == 6, "append logical length"); + var embedded: [6]u8 = .[97, 98, 99, 0, 100, 101]; + expect_bytes(buffer, embedded[0..6]); + expect_z(&buffer); + + expect_ok(resize(&buffer, 9, 255)); + @assert(buffer.storage.len == 9, "grow logical length"); + var grown: [9]u8 = .[97, 98, 99, 0, 100, 101, 255, 255, 255]; + expect_bytes(buffer, grown[0..9]); + expect_ok(resize(&buffer, 2, 17)); + @assert(buffer.storage.len == 2, "shrink logical length"); + expect_bytes(buffer, "ab"); + expect_ok(append_repeat(&buffer, 120, 4)); + @assert(buffer.storage.len == 6, "repeat logical length"); + expect_bytes(buffer, "abxxxx"); + + clear(&buffer); + @assert(is_empty(buffer) && capacity(buffer) != 0, "clear retains allocation"); + expect_z(&buffer); + drop(&buffer); +} + +fn test_growth_and_self_append() { + var buffer = new(); + var i: usize = 0; + while i < 8 { + expect_ok(append_byte(&buffer, (65 + i) as u8)); + i = i + 1; + } + @assert(buffer.storage.cap == 8, "prepare byte buffer growth boundary"); + + var own = as_slice(buffer); + expect_ok(append(&buffer, own)); + @assert(buffer.storage.len == 16); + i = 0; + while i < 16 { + @assert(buffer.storage.data[i] == (65 + i % 8) as u8, "self append content"); + i = i + 1; + } + expect_z(&buffer); + drop(&buffer); +} + +fn test_ownership_and_failures() { + var source = new(); + expect_ok(append(&source, "owned")); + + var copy = new(); + expect_ok(clone(©, source)); + @assert(copy.storage.len == 5, "clone logical length"); + source.storage.data[0] = 79; + expect_bytes(copy, "owned"); + + var before_self_clone = copy.storage.raw; + expect_ok(clone(©, copy)); + @assert(copy.storage.raw != before_self_clone, "self clone replaces nested allocation"); + expect_bytes(copy, "owned"); + + var before_external_assign = copy.storage.data; + expect_ok(assign(©, "xyz")); + @assert(copy.storage.len == 3, "external assign logical length"); + @assert(copy.storage.data != before_external_assign, "external assign replaces data"); + expect_bytes(copy, "xyz"); + before_external_assign = copy.storage.data; + expect_ok(assign(©, "owned")); + @assert(copy.storage.len == 5, "second external assign logical length"); + @assert(copy.storage.data != before_external_assign, "second external assign replaces data"); + + // assign 的输入借用目标自身,提交前必须保持旧分配有效。 + var middle = as_slice(copy); + middle.ptr = middle.ptr + 1; + middle.len = 3; + match assign(©, middle) { + .Ok => {}, + .Err(error) => { @assert(false, "self assign failed"); }, + } + @assert(copy.storage.len == 3, "assign logical length"); + expect_bytes(copy, "wne"); + + var moved = take(©); + @assert(copy.storage.raw == null && copy.storage.len == 0 && copy.storage.cap == 0); + expect_bytes(moved, "wne"); + + var old_raw = moved.storage.raw; + var old_len = moved.storage.len; + var old_cap = moved.storage.cap; + expect_overflow(append_repeat(&moved, 1, ~(0 as usize))); + @assert(moved.storage.raw == old_raw && moved.storage.len == old_len && + moved.storage.cap == old_cap, + "overflow preserves byte buffer"); + expect_bytes(moved, "wne"); + + drop(&moved); + drop(©); + drop(&source); +} + +fn main() { + test_append_and_resize(); + test_growth_and_self_append(); + test_ownership_and_failures(); +} diff --git a/stage2/utils/byte_view.spl b/stage2/utils/byte_view.spl new file mode 100644 index 0000000..1f6596f --- /dev/null +++ b/stage2/utils/byte_view.spl @@ -0,0 +1,268 @@ +// Expect retcode = 0 +// 借用的字节字符串视图;不拥有、不分配也不释放 ptr 指向的存储。 +// 非空视图要求 ptr 在整个借用期内可读。空视图统一表示为 null/0。 + +type .{ .Error, .make } = @import("error.spl"); + +type ByteView = @self; + +ptr: *u8, +len: usize, + +type ByteResult = enum { + Ok: u8, + Err: Error, +} + +type ViewResult = enum { + Ok: ByteView, + Err: Error, +} + +type FindResult = enum { + Found: usize, + NotFound, +} + +fn empty(): ByteView { + ret ByteView { .ptr = null, .len = 0 }; +} + +// 输入切片的存储仍由调用方拥有。零长度输入被规范化为空视图。 +fn from_slice(bytes: []u8): ByteView { + if bytes.len == 0 { ret empty(); } + ret ByteView { .ptr = bytes.ptr, .len = bytes.len }; +} + +fn as_slice(self: ByteView): []u8 { + ret .{ .ptr = self.ptr, .len = self.len }; +} + +fn size(self: ByteView): usize { + ret self.len; +} + +fn is_empty(self: ByteView): bool { + ret self.len == 0; +} + +fn byte_at(self: ByteView, index: usize): ByteResult { + if index >= self.len { ret .Err(make(.OutOfRange)); } + if self.ptr == null { ret .Err(make(.InvalidArgument)); } + ret .Ok(self.ptr[index]); +} + +fn eq(a: ByteView, b: ByteView): bool { + if a.len != b.len { ret false; } + var i: usize = 0; + while i < a.len { + if a.ptr[i] != b.ptr[i] { ret false; } + i = i + 1; + } + ret true; +} + +// 按无符号字节进行字典序比较,结果只承诺小于、等于或大于零。 +fn cmp(a: ByteView, b: ByteView): i32 { + var count = a.len; + if b.len < count { count = b.len; } + + var i: usize = 0; + while i < count { + var av = a.ptr[i]; + var bv = b.ptr[i]; + if av < bv { ret -1; } + if av > bv { ret 1; } + i = i + 1; + } + + if a.len < b.len { ret -1; } + if a.len > b.len { ret 1; } + ret 0; +} + +// 返回半开区间 [start, end)。失败不会产生部分视图。 +fn slice(self: ByteView, start: usize, end: usize): ViewResult { + if start > end || end > self.len { + ret .Err(make(.OutOfRange)); + } + if start == end { ret .Ok(empty()); } + if self.ptr == null { ret .Err(make(.InvalidArgument)); } + + var address = self.ptr as usize; + var limit: usize = ~(0 as usize); + if start > limit - address { ret .Err(make(.Overflow)); } + ret .Ok(ByteView { .ptr = (address + start) as *u8, .len = end - start }); +} + +fn find(haystack: ByteView, needle: ByteView): FindResult { + if needle.len == 0 { ret .Found(0); } + if needle.len > haystack.len { ret .NotFound; } + + var last = haystack.len - needle.len; + var start: usize = 0; + while start <= last { + var i: usize = 0; + while i < needle.len && haystack.ptr[start + i] == needle.ptr[i] { + i = i + 1; + } + if i == needle.len { ret .Found(start); } + start = start + 1; + } + ret .NotFound; +} + +fn contains(haystack: ByteView, needle: ByteView): bool { + match find(haystack, needle) { + .Found(index) => { ret true; }, + .NotFound => { ret false; }, + } +} + +fn starts_with(self: ByteView, prefix: ByteView): bool { + if prefix.len > self.len { ret false; } + var i: usize = 0; + while i < prefix.len { + if self.ptr[i] != prefix.ptr[i] { ret false; } + i = i + 1; + } + ret true; +} + +fn ends_with(self: ByteView, suffix: ByteView): bool { + if suffix.len > self.len { ret false; } + var start = self.len - suffix.len; + var i: usize = 0; + while i < suffix.len { + if self.ptr[start + i] != suffix.ptr[i] { ret false; } + i = i + 1; + } + ret true; +} + +fn expect_byte(result: ByteResult, expected: u8) { + match result { + .Ok(value) => { @assert(value == expected, "byte result"); }, + .Err(error) => { @assert(false, "unexpected byte error"); }, + } +} + +fn expect_byte_error(result: ByteResult, out_of_range: bool) { + match result { + .Ok(value) => { @assert(false, "expected byte error"); }, + .Err(error) => { + match error.kind { + .OutOfRange => { @assert(out_of_range, "unexpected byte range error"); }, + .InvalidArgument => { @assert(!out_of_range, "unexpected invalid view"); }, + _ => { @assert(false, "unexpected byte error kind"); }, + } + }, + } +} + +fn expect_slice(result: ViewResult, expected: ByteView) { + match result { + .Ok(value) => { @assert(eq(value, expected), "slice result"); }, + .Err(error) => { @assert(false, "unexpected slice error"); }, + } +} + +fn expect_slice_error(result: ViewResult) { + match result { + .Ok(value) => { @assert(false, "expected slice error"); }, + .Err(error) => { + match error.kind { + .OutOfRange => {}, + _ => { @assert(false, "unexpected slice error kind"); }, + } + }, + } +} + +fn expect_found(result: FindResult, expected: usize) { + match result { + .Found(index) => { @assert(index == expected, "find position"); }, + .NotFound => { @assert(false, "expected match"); }, + } +} + +fn expect_not_found(result: FindResult) { + match result { + .Found(index) => { @assert(false, "unexpected match"); }, + .NotFound => {}, + } +} + +fn test_construction_and_access() { + var canonical = empty(); + @assert(canonical.ptr == null && canonical.len == 0, "canonical empty"); + + var data: [5]u8 = .[104, 101, 0, 108, 111]; + var text = from_slice(data[0..5]); + @assert(size(text) == 5); + @assert(!is_empty(text)); + expect_byte(byte_at(text, 0), 104); + expect_byte(byte_at(text, 2), 0); + expect_byte(byte_at(text, 4), 111); + expect_byte_error(byte_at(text, 5), true); + + var normalized = from_slice(data[3..3]); + @assert(normalized.ptr == null && normalized.len == 0, "empty input normalized"); + expect_byte_error(byte_at(ByteView { .ptr = null, .len = 1 }, 0), false); + + var bytes = as_slice(text); + @assert(bytes.ptr == text.ptr && bytes.len == text.len, "slice conversion borrows"); +} + +fn test_compare_and_slice() { + var hello = from_slice("hello"); + var same = from_slice("hello"); + var help = from_slice("help"); + var high_data: [1]u8 = .[255]; + var low_data: [1]u8 = .[127]; + + @assert(eq(hello, same)); + @assert(!eq(hello, help)); + @assert(eq(empty(), from_slice(""))); + @assert(cmp(hello, same) == 0); + @assert(cmp(hello, help) < 0); + @assert(cmp(help, hello) > 0); + @assert(cmp(from_slice(low_data[0..1]), from_slice(high_data[0..1])) < 0, + "comparison uses unsigned bytes"); + + expect_slice(slice(hello, 0, 5), hello); + expect_slice(slice(hello, 1, 4), from_slice("ell")); + match slice(hello, 2, 2) { + .Ok(value) => { + @assert(value.ptr == null && value.len == 0, "empty subslice normalized"); + }, + .Err(error) => { @assert(false, "empty subslice"); }, + } + expect_slice_error(slice(hello, 4, 3)); + expect_slice_error(slice(hello, 0, 6)); +} + +fn test_search() { + var text = from_slice("ababa"); + expect_found(find(text, from_slice("aba")), 0); + expect_found(find(text, from_slice("bab")), 1); + expect_found(find(text, from_slice("ba")), 1); + expect_found(find(text, from_slice("")), 0); + expect_not_found(find(text, from_slice("abb"))); + expect_not_found(find(text, from_slice("ababaa"))); + + @assert(contains(text, from_slice("aba"))); + @assert(!contains(text, from_slice("xyz"))); + @assert(starts_with(text, from_slice("ab"))); + @assert(starts_with(text, empty())); + @assert(!starts_with(text, from_slice("ba"))); + @assert(ends_with(text, from_slice("ba"))); + @assert(ends_with(text, empty())); + @assert(!ends_with(text, from_slice("ab"))); +} + +fn main() { + test_construction_and_access(); + test_compare_and_slice(); + test_search(); +} diff --git a/stage2/utils/error.spl b/stage2/utils/error.spl new file mode 100644 index 0000000..5d0cf86 --- /dev/null +++ b/stage2/utils/error.spl @@ -0,0 +1,82 @@ +// Expect retcode = 0 +// 公共错误值不持有资源,可按值复制;不分配错误消息字符串。 +// position 是调用方输入中的从零开始的位置;仅在 has_position 时有效。 +// 未定位错误的 position 规范为 0,不能用 position == 0 判断是否有位置。 +// 数值解析位置按输入字节计数。可用内存耗尽仍由现有 VM 终止处理。 + +type Error = @self; + +type Kind = enum { + InvalidArgument, + OutOfRange, + Overflow, + DivisionByZero, + InvalidDigit, + InvalidFormat, + UnexpectedEnd, + InvalidAlignment, + WidthMismatch, + Io, +} + +kind: Kind, +has_position: bool, +position: usize, + +// 无成功载荷的结果。带成功值的模块定义自己的 Ok: T / Err: Error 枚举。 +type Status = enum { + Ok, + Err: Error, +} + +fn make(kind: Kind): Error { + ret Error { .kind = kind, .has_position = false, .position = 0 }; +} + +fn at(kind: Kind, position: usize): Error { + ret Error { .kind = kind, .has_position = true, .position = position }; +} + +fn test_status(result: Status, expected_error: bool) { + match result { + .Ok => { + @assert(!expected_error, "success variant"); + }, + .Err(error) => { + @assert(expected_error, "error variant"); + @assert(!error.has_position, "unlocated error"); + @assert(error.position == 0, "canonical absent position"); + match error.kind { + .Overflow => {}, + _ => { @assert(false, "error kind preserved in result"); }, + } + }, + } +} + +fn main() { + var success: Status = .Ok; + test_status(success, false); + var failure: Status = .Err(make(.Overflow)); + test_status(failure, true); + + var first = at(.InvalidDigit, 0); + @assert(first.has_position, "byte zero is a valid position"); + @assert(first.position == 0); + match first.kind { + .InvalidDigit => {}, + _ => { @assert(false, "positioned error kind"); }, + } + + var original = at(.UnexpectedEnd, 17); + var copy = original; + copy.position = 3; + @assert(original.position == 17, "error copy has independent fields"); + @assert(copy.position == 3); + @assert(copy.has_position); + + var max_position: usize = ~0 as usize; + var last = at(.OutOfRange, max_position); + @assert(last.has_position, "no reserved position sentinel"); + @assert(last.position == max_position); +} diff --git a/stage2/utils/memory.spl b/stage2/utils/memory.spl new file mode 100644 index 0000000..6fa23b3 --- /dev/null +++ b/stage2/utils/memory.spl @@ -0,0 +1,236 @@ +// Expect retcode = 0 +// 尺寸计算和字节移动;不分配、不释放内存,不改变任何资源的所有权。 + +type .{ .Error, .Status, .make } = @import("error.spl"); + +type SizeResult = enum { + Ok: usize, + Err: Error, +} + +fn checked_add(a: usize, b: usize): SizeResult { + var limit: usize = ~(0 as usize); + if b > limit - a { ret .Err(make(.Overflow)); } + ret .Ok(a + b); +} + +fn checked_mul(a: usize, b: usize): SizeResult { + if a == 0 { ret .Ok(0); } + var limit: usize = ~(0 as usize); + if b > limit / a { ret .Err(make(.Overflow)); } + ret .Ok(a * b); +} + +// alignment 必须是非零的二次幂。已对齐的值保持不变。 +fn align_up(size: usize, alignment: usize): SizeResult { + if alignment == 0 { ret .Err(make(.InvalidAlignment)); } + var mask = alignment - 1; + if (alignment & mask) != 0 { ret .Err(make(.InvalidAlignment)); } + var limit: usize = ~(0 as usize); + if size > limit - mask { ret .Err(make(.Overflow)); } + ret .Ok((size + mask) & ~mask); +} + +// 将 src.len 个字节复制到 dst 的开头,允许两个切片任意重叠。 +// 零长度源不访问内存,允许空指针。所有可恢复错误均在首次写入前返回。 +// 非空源要求可读,目标前 src.len 字节要求可写,生命周期由调用方保证。 +// 长度和地址计算检查不能证明底层分配大小,也不能识别悬空指针。 +fn move(dst: []u8, src: []u8): Status { + var count = src.len; + if count == 0 { ret .Ok; } + if dst.len < count { ret .Err(make(.OutOfRange)); } + if dst.ptr == null || src.ptr == null { + ret .Err(make(.InvalidArgument)); + } + + var dst_addr = dst.ptr as usize; + var src_addr = src.ptr as usize; + var limit: usize = ~(0 as usize); + var last_offset = count - 1; + if last_offset > limit - dst_addr || last_offset > limit - src_addr { + ret .Err(make(.Overflow)); + } + if dst_addr == src_addr { ret .Ok; } + + if dst_addr < src_addr { + var i: usize = 0; + while i < count { + dst.ptr[i] = src.ptr[i]; + i = i + 1; + } + } else { + var i = count; + while i > 0 { + i = i - 1; + dst.ptr[i] = src.ptr[i]; + } + } + ret .Ok; +} + +fn expect_size(result: SizeResult, expected: usize) { + match result { + .Ok(value) => { @assert(value == expected, "checked size result"); }, + .Err(error) => { @assert(false, "unexpected size error"); }, + } +} + +fn expect_overflow(result: SizeResult) { + match result { + .Ok(value) => { @assert(false, "expected overflow"); }, + .Err(error) => { + match error.kind { + .Overflow => {}, + _ => { @assert(false, "expected overflow kind"); }, + } + }, + } +} + +fn expect_alignment_error(result: SizeResult) { + match result { + .Ok(value) => { @assert(false, "expected invalid alignment"); }, + .Err(error) => { + match error.kind { + .InvalidAlignment => {}, + _ => { @assert(false, "expected invalid alignment kind"); }, + } + }, + } +} + +fn expect_ok(result: Status) { + match result { + .Ok => {}, + .Err(error) => { @assert(false, "unexpected move error"); }, + } +} + +fn expect_invalid_move(result: Status) { + match result { + .Ok => { @assert(false, "expected invalid pointer"); }, + .Err(error) => { + match error.kind { + .InvalidArgument => {}, + _ => { @assert(false, "expected invalid argument kind"); }, + } + }, + } +} + +fn expect_move_overflow(result: Status) { + match result { + .Ok => { @assert(false, "expected address overflow"); }, + .Err(error) => { + match error.kind { + .Overflow => {}, + _ => { @assert(false, "expected address overflow kind"); }, + } + }, + } +} + +fn test_sizes() { + var limit: usize = ~(0 as usize); + expect_size(checked_add(0, 0), 0); + expect_size(checked_add(17, 25), 42); + expect_size(checked_add(limit, 0), limit); + expect_size(checked_add(limit - 1, 1), limit); + expect_overflow(checked_add(limit, 1)); + expect_overflow(checked_add(1, limit)); + + expect_size(checked_mul(6, 7), 42); + expect_size(checked_mul(0, limit), 0); + expect_size(checked_mul(limit, 0), 0); + expect_size(checked_mul(limit, 1), limit); + expect_size(checked_mul(limit / 2, 2), limit - 1); + expect_overflow(checked_mul(limit / 2 + 1, 2)); + expect_overflow(checked_mul(limit, limit)); + + expect_size(align_up(0, 8), 0); + expect_size(align_up(1, 8), 8); + expect_size(align_up(8, 8), 8); + expect_size(align_up(9, 8), 16); + expect_size(align_up(limit, 1), limit); + expect_size(align_up(limit - 7, 8), limit - 7); + expect_overflow(align_up(limit - 6, 8)); + expect_overflow(align_up(limit, 2)); + expect_alignment_error(align_up(0, 0)); + expect_alignment_error(align_up(0, 3)); + expect_alignment_error(align_up(7, 6)); + + var highest_power = limit / 2 + 1; + expect_size(align_up(0, highest_power), 0); + expect_size(align_up(1, highest_power), highest_power); + expect_size(align_up(highest_power, highest_power), highest_power); + expect_overflow(align_up(highest_power + 1, highest_power)); +} + +// 穷举同一 8 字节数组内的所有合法源、目标和长度组合。 +// expected 从原始字节值建立快照,不使用被测 move 构造期望值。 +fn test_overlap() { + var src_start: usize = 0; + while src_start <= 8 { + var dst_start: usize = 0; + while dst_start <= 8 { + var count: usize = 0; + while count <= 8 - src_start && count <= 8 - dst_start { + var data: [8]u8 = .[0, 1, 2, 3, 4, 5, 6, 7]; + var expected: [8]u8 = .[0, 1, 2, 3, 4, 5, 6, 7]; + var i: usize = 0; + while i < count { + expected[dst_start + i] = (src_start + i) as u8; + i = i + 1; + } + expect_ok(move(data[dst_start..8], data[src_start..src_start + count])); + i = 0; + while i < 8 { + @assert(data[i] == expected[i], "overlap and surrounding bytes"); + i = i + 1; + } + count = count + 1; + } + dst_start = dst_start + 1; + } + src_start = src_start + 1; + } +} + +fn test_move_errors() { + var data: [4]u8 = .[10, 20, 30, 40]; + var empty: []u8 = .{ .ptr = null, .len = 0 }; + expect_ok(move(empty, empty)); + expect_ok(move(data[0..4], empty)); + + match move(data[0..1], data[2..4]) { + .Ok => { @assert(false, "expected short destination"); }, + .Err(error) => { + match error.kind { + .OutOfRange => {}, + _ => { @assert(false, "expected out of range kind"); }, + } + }, + } + + var invalid: []u8 = .{ .ptr = null, .len = 2 }; + expect_invalid_move(move(data[0..4], invalid)); + expect_invalid_move(move(invalid, data[0..2])); + + // 伪造仅用于验证拒绝路径的地址,必须在任何解引用之前报告错误。 + var limit: usize = ~(0 as usize); + var overflowing: []u8 = .{ .ptr = limit as *u8, .len = 2 }; + expect_move_overflow(move(data[0..4], overflowing)); + expect_move_overflow(move(overflowing, data[0..2])); + expect_move_overflow(move(overflowing, overflowing)); + + @assert(data[0] == 10, "failure preserves destination byte 0"); + @assert(data[1] == 20, "failure preserves destination byte 1"); + @assert(data[2] == 30, "failure preserves destination byte 2"); + @assert(data[3] == 40, "failure preserves destination byte 3"); +} + +fn main() { + test_sizes(); + test_overlap(); + test_move_errors(); +} diff --git a/stage2/utils/vec.spl b/stage2/utils/vec.spl new file mode 100644 index 0000000..28d8b02 --- /dev/null +++ b/stage2/utils/vec.spl @@ -0,0 +1,444 @@ +// Expect retcode = 0 +// 类型擦除的动态数组。元素按值复制,数组只释放自身存储,不释放元素内部资源。 +// Vec 拥有 raw 指向的分配;普通赋值会复制所有权,所有权转移必须使用 take。 + +type .{ .Error, .Kind, .Status } = @import("error.spl"); +type .{ .SizeResult, .checked_add, .checked_mul, .move } = @import("memory.spl"); +type .{ .alloc, .copy, .free } = @import("../sys/mem.spl"); + +type Vec = @self; + +raw: *u8, +data: *u8, +len: usize, +cap: usize, +elem_size: usize, +elem_align: usize, + +type VecResult = enum { + Ok: Vec, + Err: Error, +} + +type PtrResult = enum { + Ok: *u8, + Err: Error, +} + +fn blank(elem_size: usize, elem_align: usize): Vec { + ret Vec { + .raw = null, + .data = null, + .len = 0, + .cap = 0, + .elem_size = elem_size, + .elem_align = elem_align, + }; +} + +fn valid_alignment(alignment: usize): bool { + ret alignment != 0 && (alignment & (alignment - 1)) == 0; +} + +fn new(elem_size: usize, elem_align: usize): VecResult { + if elem_size == 0 { ret .Err(Error.make(.InvalidArgument)); } + if !valid_alignment(elem_align) { + ret .Err(Error.make(.InvalidAlignment)); + } + ret .Ok(blank(elem_size, elem_align)); +} + +fn valid(self: Vec): bool { + if self.elem_size == 0 || !valid_alignment(self.elem_align) { ret false; } + if self.len > self.cap { ret false; } + if self.cap == 0 { + ret self.raw == null && self.data == null && self.len == 0; + } + if self.raw == null || self.data == null { ret false; } + ret (self.data as usize & (self.elem_align - 1)) == 0; +} + +fn status_error(kind: Kind): Status { + ret .Err(Error.make(kind)); +} + +// 计算元素区字节数。错误时不修改任何 Vec。 +fn byte_count(count: usize, elem_size: usize): SizeResult { + ret checked_mul(count, elem_size); +} + +fn allocate(capacity: usize, elem_size: usize, elem_align: usize): VecResult { + if elem_size == 0 { ret .Err(Error.make(.InvalidArgument)); } + if !valid_alignment(elem_align) { + ret .Err(Error.make(.InvalidAlignment)); + } + if capacity == 0 { ret .Ok(blank(elem_size, elem_align)); } + + var bytes: usize = 0; + match byte_count(capacity, elem_size) { + .Ok(value) => { bytes = value; }, + .Err(error) => { ret .Err(error); }, + } + + var total: usize = 0; + match checked_add(bytes, elem_align - 1) { + .Ok(value) => { total = value; }, + .Err(error) => { ret .Err(error); }, + } + + var raw = alloc(total) as *u8; + var address = raw as usize; + var mask = elem_align - 1; + if address > (~(0 as usize)) - mask { + free(raw as *any); + ret .Err(Error.make(.Overflow)); + } + var aligned = (address + mask) & ~mask; + ret .Ok(Vec { + .raw = raw, + .data = aligned as *u8, + .len = 0, + .cap = capacity, + .elem_size = elem_size, + .elem_align = elem_align, + }); +} + +// 保证至少可容纳 need 个元素。所有检查在原对象改变前完成。 +fn reserve(self: *Vec, need: usize): Status { + if !valid(self.*) { ret status_error(.InvalidArgument); } + if need <= self.cap { ret .Ok; } + + var new_cap = self.cap; + if new_cap < 8 { new_cap = 8; } + while new_cap < need { + if new_cap > (~(0 as usize)) / 2 { + new_cap = need; + break; + } + new_cap = new_cap * 2; + } + + var fresh: Vec = undefined; + match allocate(new_cap, self.elem_size, self.elem_align) { + .Ok(value) => { fresh = value; }, + .Err(error) => { ret .Err(error); }, + } + + var used_bytes: usize = 0; + match byte_count(self.len, self.elem_size) { + .Ok(value) => { used_bytes = value; }, + .Err(error) => { + drop(&fresh); + ret .Err(error); + }, + } + if used_bytes != 0 { + copy(fresh.data as *any, self.data as *any, used_bytes); + } + fresh.len = self.len; + + var old_raw = self.raw; + self.* = fresh; + free(old_raw as *any); + ret .Ok; +} + +fn get(self: *Vec, index: usize): PtrResult { + if !valid(self.*) { ret .Err(Error.make(.InvalidArgument)); } + if index >= self.len { ret .Err(Error.make(.OutOfRange)); } + ret .Ok(self.data + index * self.elem_size); +} + +fn set(self: *Vec, index: usize, elem: *u8): Status { + if !valid(self.*) || elem == null { ret status_error(.InvalidArgument); } + if index >= self.len { ret status_error(.OutOfRange); } + var dst = self.data + index * self.elem_size; + ret move(dst[0..self.elem_size], elem[0..self.elem_size]); +} + +// 追加 count 个连续元素。elems 可以指向 self 当前已初始化的元素区。 +fn append(self: *Vec, elems: *u8, count: usize): Status { + if !valid(self.*) { ret status_error(.InvalidArgument); } + if count == 0 { ret .Ok; } + if elems == null { ret status_error(.InvalidArgument); } + + var copy_bytes: usize = 0; + match byte_count(count, self.elem_size) { + .Ok(value) => { copy_bytes = value; }, + .Err(error) => { ret .Err(error); }, + } + var new_len: usize = 0; + match checked_add(self.len, count) { + .Ok(value) => { new_len = value; }, + .Err(error) => { ret .Err(error); }, + } + + var used_bytes: usize = 0; + match byte_count(self.len, self.elem_size) { + .Ok(value) => { used_bytes = value; }, + .Err(error) => { ret .Err(error); }, + } + + var source_inside = false; + var source_offset: usize = 0; + if used_bytes != 0 { + var base = self.data as usize; + var source = elems as usize; + if source >= base && source - base < used_bytes { + source_offset = source - base; + if copy_bytes > used_bytes - source_offset { + ret status_error(.OutOfRange); + } + source_inside = true; + } + } + + match reserve(self, new_len) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + if source_inside { elems = self.data + source_offset; } + + var dst = self.data + used_bytes; + match move(dst[0..copy_bytes], elems[0..copy_bytes]) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + self.len = new_len; + ret .Ok; +} + +fn push(self: *Vec, elem: *u8): Status { + ret append(self, elem, 1); +} + +// 将末元素复制到 out,然后缩短数组。out 可以与元素区重叠。 +fn pop(self: *Vec, out: *u8): Status { + if !valid(self.*) || out == null { ret status_error(.InvalidArgument); } + if self.len == 0 { ret status_error(.OutOfRange); } + var source = self.data + (self.len - 1) * self.elem_size; + match move(out[0..self.elem_size], source[0..self.elem_size]) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + self.len = self.len - 1; + ret .Ok; +} + +fn clear(self: *Vec) { + self.len = 0; +} + +// 成功时先释放 out 原有存储再提交副本;失败保持 out 不变。 +// out 与 src 指向同一个 Vec 也允许。 +fn clone(out: *Vec, src: Vec): Status { + if !valid(out.*) || !valid(src) { ret status_error(.InvalidArgument); } + if out.elem_size != src.elem_size || out.elem_align != src.elem_align { + ret status_error(.InvalidArgument); + } + + var fresh = blank(src.elem_size, src.elem_align); + match reserve(&fresh, src.len) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + var bytes: usize = 0; + match byte_count(src.len, src.elem_size) { + .Ok(value) => { bytes = value; }, + .Err(error) => { + drop(&fresh); + ret .Err(error); + }, + } + if bytes != 0 { + copy(fresh.data as *any, src.data as *any, bytes); + } + fresh.len = src.len; + drop(out); + out.* = fresh; + ret .Ok; +} + +// 显式转移所有权,返回值必须由接收方最终 drop。 +fn take(source: *Vec): Vec { + var result = source.*; + source.* = blank(result.elem_size, result.elem_align); + ret result; +} + +fn drop(self: *Vec) { + var elem_size = self.elem_size; + var elem_align = self.elem_align; + free(self.raw as *any); + self.* = blank(elem_size, elem_align); +} + +fn expect_ok(result: Status) { + match result { + .Ok => {}, + .Err(error) => { @assert(false, "unexpected Vec error"); }, + } +} + +fn expect_error(result: Status, overflow: bool) { + match result { + .Ok => { @assert(false, "expected Vec error"); }, + .Err(error) => { + match error.kind { + .Overflow => { @assert(overflow, "unexpected overflow"); }, + .OutOfRange => { @assert(!overflow, "unexpected range error"); }, + _ => { @assert(false, "unexpected Vec error kind"); }, + } + }, + } +} + +fn make_vec(elem_size: usize, elem_align: usize): Vec { + match new(elem_size, elem_align) { + .Ok(value) => { ret value; }, + .Err(error) => { @assert(false, "Vec construction"); }, + } + ret blank(1, 1); +} + +fn expect_i32(self: *Vec, index: usize, expected: i32) { + match get(self, index) { + .Ok(ptr) => { @assert((ptr as *i32).* == expected, "Vec element value"); }, + .Err(error) => { @assert(false, "Vec element access"); }, + } +} + +fn test_construction_and_alignment() { + match new(0, 1) { + .Ok(value) => { @assert(false, "zero element size"); }, + .Err(error) => { + match error.kind { .InvalidArgument => {}, _ => { @assert(false); }, } + }, + } + match new(1, 3) { + .Ok(value) => { @assert(false, "invalid element alignment"); }, + .Err(error) => { + match error.kind { .InvalidAlignment => {}, _ => { @assert(false); }, } + }, + } + + var alignment: usize = 1; + while alignment <= 64 { + var v = make_vec(3, alignment); + expect_ok(reserve(&v, 9)); + @assert((v.data as usize & (alignment - 1)) == 0, "requested alignment"); + @assert(v.cap >= 9 && v.len == 0); + drop(&v); + @assert(v.raw == null && v.data == null && v.cap == 0); + alignment = alignment * 2; + } +} + +fn test_growth_and_access() { + var v = make_vec(@sizeof(i32), @alignof(i32)); + var i: i32 = 0; + while i < 1000 { + var value = i * 3 - 7; + expect_ok(push(&v, &value as *u8)); + i = i + 1; + } + @assert(v.len == 1000 && v.cap >= v.len); + i = 0; + while i < 1000 { + expect_i32(&v, i as usize, i * 3 - 7); + i = i + 1; + } + + var replacement: i32 = 123456; + expect_ok(set(&v, 500, &replacement as *u8)); + expect_i32(&v, 500, replacement); + expect_error(set(&v, 1000, &replacement as *u8), false); + + var popped: i32 = 0; + expect_ok(pop(&v, &popped as *u8)); + @assert(popped == 2990 && v.len == 999, "pop copies then removes"); + drop(&v); +} + +fn test_self_reference() { + var v = make_vec(@sizeof(i32), @alignof(i32)); + var i: i32 = 0; + while i < 8 { + expect_ok(push(&v, &i as *u8)); + i = i + 1; + } + @assert(v.len == 8 && v.cap == 8, "prepare growth boundary"); + + // 源指向旧分配,append 必须在扩容后重新定位它。 + expect_ok(append(&v, v.data, v.len)); + @assert(v.len == 16); + i = 0; + while i < 16 { + expect_i32(&v, i as usize, i % 8); + i = i + 1; + } + + var preserved_len = v.len; + expect_error(append(&v, v.data + (v.len - 1) * v.elem_size, 2), false); + @assert(v.len == preserved_len, "invalid self range preserves Vec"); + expect_i32(&v, 15, 7); + + var last_ptr: *u8 = null; + match get(&v, 15) { + .Ok(ptr) => { last_ptr = ptr; }, + .Err(error) => { @assert(false); }, + } + expect_ok(set(&v, 0, last_ptr)); + expect_i32(&v, 0, 7); + drop(&v); +} + +fn test_clone_take_and_failures() { + var source = make_vec(@sizeof(i32), @alignof(i32)); + var one: i32 = 1; + var two: i32 = 2; + expect_ok(push(&source, &one as *u8)); + expect_ok(push(&source, &two as *u8)); + + var copy = make_vec(@sizeof(i32), @alignof(i32)); + expect_ok(clone(©, source)); + expect_i32(©, 0, 1); + one = 99; + expect_ok(set(&source, 0, &one as *u8)); + expect_i32(©, 0, 1); + + // out 与 src 为同一对象仍产生内容相同的新分配。 + var old_data = copy.data; + expect_ok(clone(©, copy)); + @assert(copy.data != old_data, "self clone replaces allocation"); + expect_i32(©, 1, 2); + + var moved = take(©); + @assert(copy.raw == null && copy.data == null && copy.len == 0 && copy.cap == 0); + expect_i32(&moved, 0, 1); + + var old_raw = moved.raw; + var old_len = moved.len; + var old_cap = moved.cap; + expect_error(reserve(&moved, (~(0 as usize)) / moved.elem_size + 1), true); + @assert(moved.raw == old_raw && moved.len == old_len && moved.cap == old_cap, + "reserve failure preserves Vec"); + + clear(&moved); + @assert(moved.len == 0 && moved.cap == old_cap, "clear retains capacity"); + var output: i32 = 55; + expect_error(pop(&moved, &output as *u8), false); + @assert(output == 55, "failed pop preserves output"); + + drop(&moved); + drop(©); + drop(&source); +} + +fn main() { + test_construction_and_alignment(); + test_growth_and_access(); + test_self_reference(); + test_clone_take_and_failures(); +}