diff --git a/stage1/spl_ast2ir.c b/stage1/spl_ast2ir.c index 0e95a48..29ae373 100644 --- a/stage1/spl_ast2ir.c +++ b/stage1/spl_ast2ir.c @@ -373,6 +373,40 @@ static spl_ir_node_ref_t ast2ir_intrin_call(spl_ast2ir_t *a, spl_ast_node_t *n, spl_ast_node_t *arg = spl_ast_node(a->ast, vec_at(*args, 0)); return spl_ir_builder_type_alignof(a->ir, node_ir(a, arg)); } + case SPL_INTRIN_ENUM2INT: { + /* enum 值 -> 其 tag(判别值);field 0 即 tag。 */ + spl_ast_node_t *src = spl_ast_node(a->ast, vec_at(*args, 0)); + spl_ir_node_ref_t v = emit_expr(a, vec_at(*args, 0)); + if (!v) + return 0; + spl_type_id_t agg_t = node_ir(a, src); + spl_type_id_t tag_t = node_ir(a, n); + if (!agg_t || !tag_t) + return 0; + return spl_ir_builder_agg_extract(a->ir, agg_t, tag_t, 0, v); + } + case SPL_INTRIN_FIELD_COUNT: { + spl_ast_node_t *arg = spl_ast_node(a->ast, vec_at(*args, 0)); + return spl_ir_builder_type_field_count(a->ir, node_ir(a, arg)); + } + case SPL_INTRIN_OFFSETOF: { + spl_ast_node_t *arg = spl_ast_node(a->ast, vec_at(*args, 0)); + spl_type_id_t agg_t = node_ir(a, arg); + if (!agg_t) + return 0; + spl_ast_node_t *fnode = spl_ast_node(a->ast, vec_at(*args, 1)); + usize idx = 0; + if (fnode && fnode->kind == SPL_AST_EXPR_INT_LIT) { + idx = (usize)fnode->int_lit.bits; + } else if (fnode && fnode->kind == SPL_AST_EXPR_IDENT) { + if (!spl_type_agg_member_idx(&a->sema->type->ir, agg_t, fnode->ident.name, &idx)) + return 0; + } else { + return 0; + } + spl_ir_node_ref_t idx_node = const_usize(a, idx); + return spl_ir_builder_type_offsetof(a->ir, agg_t, idx_node); + } case SPL_INTRIN_ASSERT: { spl_ir_node_ref_t cond = emit_expr(a, vec_at(*args, 0)); if (!cond) @@ -420,11 +454,11 @@ static spl_ir_node_ref_t ast2ir_intrin_call(spl_ast2ir_t *a, spl_ast_node_t *n, case SPL_INTRIN_TRUNCATE: case SPL_INTRIN_BIT_CAST: case SPL_INTRIN_FLOAT_CAST: - case SPL_INTRIN_INT_FROM_FLOAT: - case SPL_INTRIN_FLOAT_FROM_INT: - case SPL_INTRIN_INT_FROM_BOOL: - case SPL_INTRIN_INT_FROM_PTR: - case SPL_INTRIN_PTR_FROM_INT: + case SPL_INTRIN_FLOAT2INT: + case SPL_INTRIN_INT2FLOAT: + case SPL_INTRIN_BOOL2INT: + case SPL_INTRIN_PTR2INT: + case SPL_INTRIN_INT2PTR: case SPL_INTRIN_PTR_CAST: return ast2ir_intrin_cast(a, n, in, args); case SPL_INTRIN_BREAKPOINT: @@ -619,15 +653,15 @@ static spl_ir_node_ref_t ast2ir_emit_cast_builtin(spl_ast2ir_t *a, spl_ast_node_ return spl_ir_builder_cast_ftrunc(a->ir, ft, tt, v); return spl_ir_builder_cast_fext(a->ir, ft, tt, v); } - case SPL_INTRIN_INT_FROM_FLOAT: + case SPL_INTRIN_FLOAT2INT: return spl_ir_builder_case_float2int(a->ir, ft, tt, v); - case SPL_INTRIN_FLOAT_FROM_INT: + case SPL_INTRIN_INT2FLOAT: return spl_ir_builder_case_int2float(a->ir, ft, tt, v); - case SPL_INTRIN_INT_FROM_BOOL: + case SPL_INTRIN_BOOL2INT: return spl_ir_builder_cast_bool2int(a->ir, ft, tt, v); - case SPL_INTRIN_INT_FROM_PTR: + case SPL_INTRIN_PTR2INT: return spl_ir_builder_cast_ptr2int(a->ir, ft, tt, v); - case SPL_INTRIN_PTR_FROM_INT: + case SPL_INTRIN_INT2PTR: return spl_ir_builder_cast_int2ptr(a->ir, ft, tt, v); case SPL_INTRIN_PTR_CAST: return spl_ir_builder_cast_bitcast(a->ir, ft, tt, v); @@ -1935,10 +1969,12 @@ static spl_ir_node_ref_t emit_implicit_cast(spl_ast2ir_t *a, spl_ast_node_t *n, static usize spl_int_map_hash(usize k) { return MAP_HASH_INT(k); } static int spl_int_map_cmp(usize a, usize b) { return MAP_CMP_INT(a, b); } -void spl_ast2ir_init(spl_ast2ir_t *ast2ir, spl_ir_builder_t *ir, const spl_sema_t *sema) { +void spl_ast2ir_init(spl_ast2ir_t *ast2ir, spl_ir_builder_t *ir, spl_layout_t *layout, + const spl_sema_t *sema) { ast2ir->sema = sema; ast2ir->ir = ir; ast2ir->ast = sema->ast; + ast2ir->layout = layout; map_init(ast2ir->def2ir_map, spl_int_map_hash, spl_int_map_cmp); vec_init(ast2ir->loop_stack); vec_init(ast2ir->defer_stack); diff --git a/stage1/spl_ast2ir.h b/stage1/spl_ast2ir.h index a7c5b08..fb117f7 100644 --- a/stage1/spl_ast2ir.h +++ b/stage1/spl_ast2ir.h @@ -23,6 +23,7 @@ typedef struct { const spl_sema_t *sema; spl_ir_builder_t *ir; spl_ast_t *ast; + spl_layout_t *layout; /* 借用 splc0 的 layout */ spl_def2ir_map_t def2ir_map; spl_loop_ctx_vec_t loop_stack; spl_defer_stack_t defer_stack; @@ -32,7 +33,8 @@ typedef struct { spl_diag_t *diag; } spl_ast2ir_t; -void spl_ast2ir_init(spl_ast2ir_t *ast2ir, spl_ir_builder_t *ir, const spl_sema_t *sema); +void spl_ast2ir_init(spl_ast2ir_t *ast2ir, spl_ir_builder_t *ir, spl_layout_t *layout, + const spl_sema_t *sema); void spl_ast2ir_drop(spl_ast2ir_t *ast2ir); void spl_ast2ir_run(spl_ast2ir_t *ast2ir, spl_ast_ptr_vec_t ast_vec); diff --git a/stage1/spl_intrin.c b/stage1/spl_intrin.c index 34728fd..d684af3 100644 --- a/stage1/spl_intrin.c +++ b/stage1/spl_intrin.c @@ -22,18 +22,24 @@ static const spl_intrin_info_t g_intrin_table[] = { SPL_INTRIN_RESULT_UNIT}, [SPL_INTRIN_FLOAT_CAST] = {"@float_cast", SPL_INTRIN_FLOAT_CAST, SPL_INTRIN_FORM_CALL, 2, 2, SPL_INTRIN_RESULT_UNIT}, - [SPL_INTRIN_INT_FROM_FLOAT] = {"@int_from_float", SPL_INTRIN_INT_FROM_FLOAT, - SPL_INTRIN_FORM_CALL, 2, 2, SPL_INTRIN_RESULT_UNIT}, - [SPL_INTRIN_FLOAT_FROM_INT] = {"@float_from_int", SPL_INTRIN_FLOAT_FROM_INT, - SPL_INTRIN_FORM_CALL, 2, 2, SPL_INTRIN_RESULT_UNIT}, - [SPL_INTRIN_INT_FROM_BOOL] = {"@int_from_bool", SPL_INTRIN_INT_FROM_BOOL, SPL_INTRIN_FORM_CALL, - 1, 2, SPL_INTRIN_RESULT_UNIT}, - [SPL_INTRIN_INT_FROM_PTR] = {"@int_from_ptr", SPL_INTRIN_INT_FROM_PTR, SPL_INTRIN_FORM_CALL, 1, - 2, SPL_INTRIN_RESULT_UNIT}, - [SPL_INTRIN_PTR_FROM_INT] = {"@ptr_from_int", SPL_INTRIN_PTR_FROM_INT, SPL_INTRIN_FORM_CALL, 2, - 2, SPL_INTRIN_RESULT_UNIT}, + [SPL_INTRIN_FLOAT2INT] = {"@float2int", SPL_INTRIN_FLOAT2INT, SPL_INTRIN_FORM_CALL, 2, 2, + SPL_INTRIN_RESULT_UNIT}, + [SPL_INTRIN_INT2FLOAT] = {"@int2float", SPL_INTRIN_INT2FLOAT, SPL_INTRIN_FORM_CALL, 2, 2, + SPL_INTRIN_RESULT_UNIT}, + [SPL_INTRIN_BOOL2INT] = {"@bool2int", SPL_INTRIN_BOOL2INT, SPL_INTRIN_FORM_CALL, 1, 2, + SPL_INTRIN_RESULT_UNIT}, + [SPL_INTRIN_PTR2INT] = {"@ptr2int", SPL_INTRIN_PTR2INT, SPL_INTRIN_FORM_CALL, 1, 2, + SPL_INTRIN_RESULT_UNIT}, + [SPL_INTRIN_INT2PTR] = {"@int2ptr", SPL_INTRIN_INT2PTR, SPL_INTRIN_FORM_CALL, 2, 2, + SPL_INTRIN_RESULT_UNIT}, [SPL_INTRIN_PTR_CAST] = {"@ptr_cast", SPL_INTRIN_PTR_CAST, SPL_INTRIN_FORM_CALL, 2, 2, SPL_INTRIN_RESULT_UNIT}, + [SPL_INTRIN_ENUM2INT] = {"@enum2int", SPL_INTRIN_ENUM2INT, SPL_INTRIN_FORM_CALL, 1, 1, + SPL_INTRIN_RESULT_USIZE}, + [SPL_INTRIN_OFFSETOF] = {"@offsetof", SPL_INTRIN_OFFSETOF, SPL_INTRIN_FORM_CALL, 2, 2, + SPL_INTRIN_RESULT_USIZE}, + [SPL_INTRIN_FIELD_COUNT] = {"@field_count", SPL_INTRIN_FIELD_COUNT, SPL_INTRIN_FORM_CALL, 1, 1, + SPL_INTRIN_RESULT_USIZE}, }; static const usize g_intrin_count = sizeof(g_intrin_table) / sizeof(g_intrin_table[0]); @@ -88,14 +94,15 @@ spl_intrin_t spl_intrin_lookup_cast(spl_type_t *type, spl_type_id_t ft, spl_type if (f->kind == SPL_TYPE_FLOAT && t->kind == SPL_TYPE_FLOAT) return SPL_INTRIN_FLOAT_CAST; if (f->kind == SPL_TYPE_BOOL && t->kind == SPL_TYPE_INT) - return SPL_INTRIN_INT_FROM_BOOL; + return SPL_INTRIN_BOOL2INT; if (f->kind == SPL_TYPE_PTR && t->kind == SPL_TYPE_INT) - return SPL_INTRIN_INT_FROM_PTR; + return SPL_INTRIN_PTR2INT; if (f->kind == SPL_TYPE_INT && t->kind == SPL_TYPE_PTR) - return SPL_INTRIN_PTR_FROM_INT; + return SPL_INTRIN_INT2PTR; if (f->kind == SPL_TYPE_PTR && t->kind == SPL_TYPE_PTR) return SPL_INTRIN_PTR_CAST; - /* int float 刻意不在这里:只能通过显式 builtin 调用。 */ + /* int float 刻意不在这里:只能通过显式 builtin 调用。 + enum<->int 也刻意禁止(用 @enum2int 取 tag)。 */ return SPL_INTRIN_NONE; } diff --git a/stage1/spl_intrin.h b/stage1/spl_intrin.h index 24620c7..0a784ea 100644 --- a/stage1/spl_intrin.h +++ b/stage1/spl_intrin.h @@ -16,12 +16,15 @@ typedef enum { SPL_INTRIN_TRUNCATE, SPL_INTRIN_BIT_CAST, SPL_INTRIN_FLOAT_CAST, - SPL_INTRIN_INT_FROM_FLOAT, - SPL_INTRIN_FLOAT_FROM_INT, - SPL_INTRIN_INT_FROM_BOOL, - SPL_INTRIN_INT_FROM_PTR, - SPL_INTRIN_PTR_FROM_INT, + SPL_INTRIN_FLOAT2INT, + SPL_INTRIN_INT2FLOAT, + SPL_INTRIN_BOOL2INT, + SPL_INTRIN_PTR2INT, + SPL_INTRIN_INT2PTR, SPL_INTRIN_PTR_CAST, + SPL_INTRIN_ENUM2INT, + SPL_INTRIN_OFFSETOF, + SPL_INTRIN_FIELD_COUNT, } spl_intrin_t; typedef enum { diff --git a/stage1/spl_ir2vm.c b/stage1/spl_ir2vm.c index a7145c2..6be271d 100644 --- a/stage1/spl_ir2vm.c +++ b/stage1/spl_ir2vm.c @@ -3,49 +3,24 @@ static usize align_up(usize x, usize a); +/* size/align/offset 统一取自 layout(spl_layout 是唯一准确来源)。 */ +static const spl_layout_info_t *ir_layout(spl_ir2vm_t *ctx, spl_type_id_t tid) { + return spl_layout_info_of(ctx->layout, tid); +} + static usize ir_type_size(spl_ir2vm_t *ctx, spl_type_id_t tid) { - spl_type_node_t *tn = spl_type_node(ctx->type, tid); - if (!tn) - return 8; - switch (tn->kind) { - case SPL_TYPE_UNIT: + const spl_layout_info_t *info = ir_layout(ctx, tid); + if (!info) return 0; - case SPL_TYPE_BOOL: + return (info->size_bits + 7) / 8; +} + +static usize ir_type_align(spl_ir2vm_t *ctx, spl_type_id_t tid) { + const spl_layout_info_t *info = ir_layout(ctx, tid); + if (!info) return 1; - case SPL_TYPE_INT: - return (tn->int_type.bits + 7) / 8; - case SPL_TYPE_FLOAT: - return (tn->float_type.bits + 7) / 8; - case SPL_TYPE_PTR: - return sizeof(void *); - case SPL_TYPE_FN: - return sizeof(void *); - case SPL_TYPE_ARRAY: - return ir_type_size(ctx, tn->array_type.element) * tn->array_type.len; - case SPL_TYPE_ENUM: { - usize tag_sz = ir_type_size(ctx, tn->agg_type.tag_type); - usize payload_align = 1; - usize payload_size = 0; - for (usize i = 0; i < vec_size(tn->agg_type.variants); ++i) { - usize vs = ir_type_size(ctx, vec_at(tn->agg_type.variants, i).type_id); - if (vs > payload_align) - payload_align = vs; - if (vs > payload_size) - payload_size = vs; - } - usize payload_off = align_up(tag_sz, payload_align); - return align_up(payload_off + payload_size, payload_align); - } - case SPL_TYPE_STRUCT: - case SPL_TYPE_UNION: { - usize s = 0; - for (usize i = 0; i < vec_size(tn->agg_type.variants); ++i) - s += ir_type_size(ctx, vec_at(tn->agg_type.variants, i).type_id); - return s; - } - default: - return 8; - } + usize a = info->align_bits / 8; + return a ? a : 1; } static bool ir_is_aggregate(spl_ir2vm_t *ctx, spl_type_id_t tid) { @@ -98,27 +73,13 @@ static usize ir_agg_field_off(spl_ir2vm_t *ctx, spl_type_id_t tid, usize idx) { spl_type_node_t *tn = spl_type_node(ctx->type, tid); if (!tn) return 0; - if (tn->kind == SPL_TYPE_STRUCT || tn->kind == SPL_TYPE_UNION) { - usize off = 0; - for (usize i = 0; i < idx && i < vec_size(tn->agg_type.variants); ++i) - off += ir_type_size(ctx, vec_at(tn->agg_type.variants, i).type_id); - return off; - } if (tn->kind == SPL_TYPE_ARRAY) return idx * ir_type_size(ctx, tn->array_type.element); - if (tn->kind == SPL_TYPE_ENUM) { - if (idx == 0) - return 0; - usize tag_sz = ir_type_size(ctx, tn->agg_type.tag_type); - usize payload_align = 1; - for (usize i = 0; i < vec_size(tn->agg_type.variants); ++i) { - usize vs = ir_type_size(ctx, vec_at(tn->agg_type.variants, i).type_id); - if (vs > payload_align) - payload_align = vs; - } - return align_up(tag_sz, payload_align); - } - return 0; + + const spl_layout_info_t *info = ir_layout(ctx, tid); + if (!info || idx >= vec_size(info->field_bit_offsets)) + return 0; + return vec_at(info->field_bit_offsets, idx) / 8; } static int get_or_add_native(spl_ir2vm_t *ctx, const char *name) { @@ -752,12 +713,31 @@ static void cg_typeop(cgen_t *cg, usize ref, const spl_ir_node_t *n) { case SPL_IR_TYPE_SIZEOF: v = ir_type_size(ctx, n->ir_sizeof.tid); break; - case SPL_IR_TYPE_BITSIZEOF: - v = ir_type_size(ctx, n->bitsizeof.tid) * 8; + case SPL_IR_TYPE_BITSIZEOF: { + const spl_layout_info_t *info = ir_layout(ctx, n->bitsizeof.tid); + v = info ? info->size_bits : 0; break; + } case SPL_IR_TYPE_ALIGNOF: - v = ir_type_size(ctx, n->ir_alignof.tid); + v = ir_type_align(ctx, n->ir_alignof.tid); break; + case SPL_IR_TYPE_OFFSETOF: { + usize idx = 0; + if (n->ir_offsetof.field_idx < vec_size(cg->irf->nodes)) { + const spl_ir_node_t *idx_node = &vec_at(cg->irf->nodes, n->ir_offsetof.field_idx); + if (idx_node->kind == SPL_IR_TYPE_CONST) + idx = (usize)idx_node->type_const.int_lit; + } + v = ir_agg_field_off(ctx, n->ir_offsetof.tid, idx); + break; + } + case SPL_IR_TYPE_FIELD_COUNT: { + spl_type_node_t *tn = spl_type_node(ctx->type, n->field_count.tid); + if (tn && (tn->kind == SPL_TYPE_STRUCT || tn->kind == SPL_TYPE_UNION || + tn->kind == SPL_TYPE_ENUM || tn->kind == SPL_TYPE_SHAPE)) + v = vec_size(tn->agg_type.variants); + break; + } default: return; } @@ -1028,6 +1008,8 @@ static void cg_node(cgen_t *cg, usize ref, const spl_ir_node_t *n) { case SPL_IR_TYPE_SIZEOF: case SPL_IR_TYPE_BITSIZEOF: case SPL_IR_TYPE_ALIGNOF: + case SPL_IR_TYPE_OFFSETOF: + case SPL_IR_TYPE_FIELD_COUNT: cg_typeop(cg, ref, n); break; @@ -1142,11 +1124,12 @@ static usize spl_map_hash(usize k) { return MAP_HASH_INT(k); } static int spl_map_cmp(usize a, usize b) { return MAP_CMP_INT(a, b); } void spl_ir2vm_init(spl_ir2vm_t *ctx, const spl_ir_t *ir, const spl_type_t *type, - const spl_attr_t *attr) { + spl_layout_t *layout, const spl_attr_t *attr) { Assert(ir != NULL && type != NULL && attr != NULL); ctx->ir = ir; ctx->type = type; ctx->attr = attr; + ctx->layout = layout; map_init(ctx->extern_native, spl_map_hash, spl_map_cmp); spl_prog_init(&ctx->prog); diff --git a/stage1/spl_ir2vm.h b/stage1/spl_ir2vm.h index 4f043e0..b245125 100644 --- a/stage1/spl_ir2vm.h +++ b/stage1/spl_ir2vm.h @@ -3,6 +3,7 @@ #include "spl_diag.h" #include "spl_ir.h" +#include "spl_layout.h" #include "spl_type.h" #include "../stage0/spl_mcode.h" @@ -21,12 +22,13 @@ typedef struct { const spl_attr_t *attr; spl_extern_idx_map_t extern_native; spl_prog_t prog; + spl_layout_t *layout; /* 借用 splc0 的 layout(size/align/offset 唯一来源) */ spl_diag_t *diag; } spl_ir2vm_t; void spl_ir2vm_init(spl_ir2vm_t *ctx, const spl_ir_t *ir, const spl_type_t *type, - const spl_attr_t *attr); + spl_layout_t *layout, const spl_attr_t *attr); void spl_ir2vm_drop(spl_ir2vm_t *ctx); int spl_ir2vm_run_func(spl_ir2vm_t *ctx, spl_ir_func_ref_t ref); diff --git a/stage1/spl_layout.c b/stage1/spl_layout.c index 7868eb3..893c0dc 100644 --- a/stage1/spl_layout.c +++ b/stage1/spl_layout.c @@ -50,8 +50,9 @@ void spl_layout_init(spl_layout_t *layout, spl_layout_target_t *target) { spl_layout_add_scalar(layout, 4, 4); /* i32 / u32 / f32 */ spl_layout_add_scalar(layout, 8, 8); /* i64 / u64 / f64 */ - usize ps = sizeof(void *); - spl_layout_add_ptr(layout, 0, ps, ps); + usize ps = target && target->ptr_size ? target->ptr_size : sizeof(void *); + usize pa = target && target->ptr_align ? target->ptr_align : sizeof(void *); + spl_layout_add_ptr(layout, 0, ps, pa); } void spl_layout_drop(spl_layout_t *layout) { @@ -145,6 +146,8 @@ static bool layout_agg(spl_layout_t *layout, spl_type_node_t *tn, spl_layout_inf for (usize i = 0; i < n; ++i) { spl_type_member_t *m = &vec_at(tn->agg_type.variants, i); + if (m->type_id == 0) /* 无载荷变体 */ + continue; spl_layout_info_t *fi = infos_at(layout, m->type_id); if (!fi || !fi->resolved) return false; @@ -160,6 +163,21 @@ static bool layout_agg(spl_layout_t *layout, spl_type_node_t *tn, spl_layout_inf tag_align_bits > payload_align_bits ? tag_align_bits : payload_align_bits; out->size_bits = round_up_bits(payload_off + payload_size_bits, final_align_bits); out->align_bits = final_align_bits; + + /* 字段索引约定(与 ir2vm 一致):0 = tag,1..n = 各变体载荷(同一偏移)。 */ + vec_push(out->field_bit_offsets, 0); + vec_push(out->field_bit_sizes, tag_info->size_bits); + for (usize i = 0; i < n; ++i) { + spl_type_member_t *m = &vec_at(tn->agg_type.variants, i); + usize fsize = 0; + if (m->type_id) { + spl_layout_info_t *fi = infos_at(layout, m->type_id); + if (fi) + fsize = fi->size_bits; + } + vec_push(out->field_bit_offsets, payload_off); + vec_push(out->field_bit_sizes, fsize); + } return true; } @@ -175,22 +193,27 @@ static bool layout_agg(spl_layout_t *layout, spl_type_node_t *tn, spl_layout_inf for (usize i = 0; i < n; ++i) { spl_type_member_t *m = &vec_at(tn->agg_type.variants, i); - spl_layout_info_t *fi = infos_at(layout, m->type_id); - if (!fi || !fi->resolved) - return false; - - usize field_align_bits = fi->align_bits ? fi->align_bits : 8; + usize fsize_bits = 0; + usize fsize_align = 8; + if (m->type_id) { + spl_layout_info_t *fi = infos_at(layout, m->type_id); + if (!fi || !fi->resolved) + return false; + fsize_bits = fi->size_bits; + fsize_align = fi->align_bits ? fi->align_bits : 8; + } + usize field_align_bits = fsize_align; if (is_union) { vec_push(out->field_bit_offsets, 0); - vec_push(out->field_bit_sizes, fi->size_bits); - if (fi->size_bits > out->size_bits) - out->size_bits = fi->size_bits; + vec_push(out->field_bit_sizes, fsize_bits); + if (fsize_bits > out->size_bits) + out->size_bits = fsize_bits; } else { offset_bits = round_up_bits(offset_bits, field_align_bits); vec_push(out->field_bit_offsets, offset_bits); - vec_push(out->field_bit_sizes, fi->size_bits); - offset_bits += fi->size_bits; + vec_push(out->field_bit_sizes, fsize_bits); + offset_bits += fsize_bits; } if (field_align_bits > max_align_bits) max_align_bits = field_align_bits; @@ -266,9 +289,12 @@ static void resolve_type(spl_layout_t *layout, spl_type_t *type, spl_type_id_t i case SPL_TYPE_UNION: case SPL_TYPE_ENUM: case SPL_TYPE_SHAPE: { + if (tn->kind == SPL_TYPE_ENUM && tn->agg_type.tag_type) + resolve_type(layout, type, tn->agg_type.tag_type); for (usize i = 0; i < vec_size(tn->agg_type.variants); ++i) { spl_type_member_t *m = &vec_at(tn->agg_type.variants, i); - resolve_type(layout, type, m->type_id); + if (m->type_id) + resolve_type(layout, type, m->type_id); } layout_agg(layout, tn, info); return; diff --git a/stage1/spl_layout.h b/stage1/spl_layout.h index 820d41c..5a7b645 100644 --- a/stage1/spl_layout.h +++ b/stage1/spl_layout.h @@ -32,7 +32,8 @@ typedef struct { typedef struct { enum { SPL_BIG_ENDIAN, SPL_LITTLE_ENDIAN } endian; // TODO 更多位存储方式 - + usize ptr_size; + usize ptr_align; } spl_layout_target_t; typedef struct { diff --git a/stage1/spl_sema.c b/stage1/spl_sema.c index 99c086c..20ed1d1 100644 --- a/stage1/spl_sema.c +++ b/stage1/spl_sema.c @@ -938,9 +938,9 @@ static spl_lang_type_id_t sema_intrin_call(spl_sema_t *sema, spl_ast_node_t *n, case SPL_INTRIN_TRUNCATE: case SPL_INTRIN_BIT_CAST: case SPL_INTRIN_FLOAT_CAST: - case SPL_INTRIN_INT_FROM_FLOAT: - case SPL_INTRIN_FLOAT_FROM_INT: - case SPL_INTRIN_PTR_FROM_INT: + case SPL_INTRIN_FLOAT2INT: + case SPL_INTRIN_INT2FLOAT: + case SPL_INTRIN_INT2PTR: case SPL_INTRIN_PTR_CAST: { spl_lang_type_id_t src_lang = sema_infer(sema, vec_at(*args, 0)); if (!src_lang) @@ -959,8 +959,8 @@ static spl_lang_type_id_t sema_intrin_call(spl_sema_t *sema, spl_ast_node_t *n, n->resolved_lang_tid = dst_lang; return dst_lang; } - case SPL_INTRIN_INT_FROM_BOOL: - case SPL_INTRIN_INT_FROM_PTR: { + case SPL_INTRIN_BOOL2INT: + case SPL_INTRIN_PTR2INT: { sema_infer(sema, vec_at(*args, 0)); if (nargs >= 2) { spl_lang_type_id_t dst = sema_infer(sema, vec_at(*args, 1)); @@ -973,6 +973,53 @@ static spl_lang_type_id_t sema_intrin_call(spl_sema_t *sema, spl_ast_node_t *n, n->resolved_lang_tid = r; return r; } + case SPL_INTRIN_ENUM2INT: { + spl_lang_type_id_t src = sema_infer(sema, vec_at(*args, 0)); + if (!src) + return 0; + spl_lang_type_node_t *sn = spl_lang_type_node(sema->type, src); + if (!sn || sn->kind != SPL_LANG_TYPE_ENUM) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, + "@enum2int requires an enum operand"); + return 0; + } + spl_type_node_t *sir = spl_type_node(&sema->type->ir, sn->ir_type_id); + if (!sir || !sir->agg_type.tag_type) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_INTERNAL, &n->dbg, + "@enum2int: enum has no tag type"); + return 0; + } + spl_lang_type_id_t tag = sema_lang_from_ir(sema, sir->agg_type.tag_type); + n->resolved_lang_tid = tag; + return tag; + } + case SPL_INTRIN_FIELD_COUNT: { + 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_OFFSETOF: { + spl_lang_type_id_t agg_lang = sema_infer_type_context(sema, vec_at(*args, 0)); + if (!agg_lang) + return 0; + spl_type_id_t agg_ir = spl_lang_type_get_ir_type(sema->type, agg_lang); + spl_ast_node_t *fnode = spl_ast_node(sema->ast, vec_at(*args, 1)); + if (!fnode) + return 0; + if (fnode->kind == SPL_AST_EXPR_IDENT) { + usize idx = 0; + if (!spl_type_agg_member_idx(&sema->type->ir, agg_ir, fnode->ident.name, &idx)) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_NO_FIELD, &n->dbg, "no field `%s`", + fnode->ident.name); + return 0; + } + } else if (fnode->kind != SPL_AST_EXPR_INT_LIT) { + spl_diag_error(sema->diag, SPL_DIAG_KIND_TYPE_MISMATCH, &n->dbg, + "@offsetof expects a field name or integer index"); + return 0; + } + return spl_lang_make_int(sema->type, sizeof(void *) * 8, 0); + } default: UNREACHABLE(); } @@ -2718,11 +2765,12 @@ static spl_lang_type_id_t sema_infer(spl_sema_t *sema, spl_ast_node_ref_t ref) { return 0; } -void spl_sema_init(spl_sema_t *sema, spl_ast_t *ast, spl_lang_type_t *type, spl_scope_t *scope, - spl_attr_t *attr) { +void spl_sema_init(spl_sema_t *sema, spl_ast_t *ast, spl_lang_type_t *type, spl_layout_t *layout, + spl_scope_t *scope, spl_attr_t *attr) { Assert(sema != NULL && ast != NULL && type != NULL && scope != NULL && attr != NULL); sema->ast = ast; sema->type = type; + sema->layout = layout; sema->scope = scope; sema->current_fn_ret_tid = 0; sema->current_aggregate_lang = 0; diff --git a/stage1/spl_sema.h b/stage1/spl_sema.h index 50e3378..9068c10 100644 --- a/stage1/spl_sema.h +++ b/stage1/spl_sema.h @@ -5,6 +5,7 @@ #include "spl_attr.h" #include "spl_diag.h" #include "spl_lang_type.h" +#include "spl_layout.h" #include "spl_mpa.h" #include "spl_scope.h" #include "spl_type.h" @@ -21,6 +22,7 @@ struct spl_module_loader { typedef struct { spl_ast_t *ast; spl_lang_type_t *type; + spl_layout_t *layout; /* 由 splc0 拥有并解析;sema 仅借用 */ spl_scope_t *scope; spl_symbol_t root; spl_attr_t *attr; @@ -32,8 +34,8 @@ typedef struct { spl_diag_t *diag; } spl_sema_t; -void spl_sema_init(spl_sema_t *sema, spl_ast_t *ast, spl_lang_type_t *type, spl_scope_t *scope, - spl_attr_t *attr); +void spl_sema_init(spl_sema_t *sema, spl_ast_t *ast, spl_lang_type_t *type, + spl_layout_t *layout, spl_scope_t *scope, spl_attr_t *attr); void spl_sema_drop(spl_sema_t *sema); void spl_sema_run_module(spl_sema_t *sema, spl_ast_node_ref_t module_ref); diff --git a/stage1/splc0.c b/stage1/splc0.c index c01a2e4..c1476c7 100644 --- a/stage1/splc0.c +++ b/stage1/splc0.c @@ -182,6 +182,8 @@ static int compile_spl(const char *src, const char *fname, const char *outpath, spl_attr_t attr; spl_lang_type_t lang_type; + spl_layout_target_t layout_target; + spl_layout_t layout; spl_scope_t scope; spl_sema_t sema; spl_ast2ir_t a2ir; @@ -189,7 +191,7 @@ static int compile_spl(const char *src, const char *fname, const char *outpath, spl_ir2vm_t ir2vm; spl_ast_ptr_vec_t all_asts; - int attr_ok = 0, lang_type_ok = 0, scope_ok = 0; + int attr_ok = 0, lang_type_ok = 0, layout_ok = 0, scope_ok = 0; int sema_ok = 0, a2ir_ok = 0, irb_ok = 0, ir2vm_ok = 0; spl_diag_set_stage(&diag, SPL_STAGE_PARSE); @@ -212,10 +214,19 @@ static int compile_spl(const char *src, const char *fname, const char *outpath, attr_ok = 1; spl_lang_type_init(&lang_type); lang_type_ok = 1; + + /* 目标描述符:剥离目标机器细节,layout 是 size/align/offset 的唯一来源。 */ + memset(&layout_target, 0, sizeof layout_target); + layout_target.endian = SPL_LITTLE_ENDIAN; + layout_target.ptr_size = sizeof(void *); + layout_target.ptr_align = sizeof(void *); + spl_layout_init(&layout, &layout_target); + layout_ok = 1; + spl_scope_init(&scope); scope_ok = 1; - spl_sema_init(&sema, &drv.main_ast, &lang_type, &scope, &attr); + spl_sema_init(&sema, &drv.main_ast, &lang_type, &layout, &scope, &attr); sema_ok = 1; spl_module_loader_t loader = {.load = driver_load, .user = &drv}; sema.loader = &loader; @@ -242,7 +253,7 @@ static int compile_spl(const char *src, const char *fname, const char *outpath, spl_ir_builder_init(&ir_builder); irb_ok = 1; - spl_ast2ir_init(&a2ir, &ir_builder, &sema); + spl_ast2ir_init(&a2ir, &ir_builder, &layout, &sema); a2ir_ok = 1; spl_diag_set_stage(&diag, SPL_STAGE_AST2IR); a2ir.diag = &diag; @@ -257,7 +268,10 @@ static int compile_spl(const char *src, const char *fname, const char *outpath, goto cleanup; } - spl_ir2vm_init(&ir2vm, &a2ir.ir->ir, &lang_type.ir, &attr); + /* 类型已全部就绪:解析 layout,供 ir2vm 的 size/align/offset 使用。 */ + spl_layout_resolve(&layout, &lang_type.ir); + + spl_ir2vm_init(&ir2vm, &a2ir.ir->ir, &lang_type.ir, &layout, &attr); ir2vm_ok = 1; spl_diag_set_stage(&diag, SPL_STAGE_IR2VM); ir2vm.diag = &diag; @@ -285,6 +299,8 @@ cleanup: spl_sema_drop(&sema); if (scope_ok) spl_scope_drop(&scope); + if (layout_ok) + spl_layout_drop(&layout); if (lang_type_ok) spl_lang_type_drop(&lang_type); if (attr_ok) diff --git a/stage2/utils/big_int.spl b/stage2/utils/big_int.spl new file mode 100644 index 0000000..a3a78cc --- /dev/null +++ b/stage2/utils/big_int.spl @@ -0,0 +1,871 @@ +// Expect retcode = 0 +// 任意精度有符号整数。limbs 为小端 u32;零规范为 len=0 且 negative=false。 +// 普通赋值只借用同一存储;深复制使用 clone,所有权转移使用 take。 +type .{ .Error, .Status } = @import("error.spl"); +type .{ .checked_mul } = @import("memory.spl"); +type .{ .digit_char, .digit_value } = @import("ascii.spl"); +type .{ .ByteBuffer } = @import("byte_buffer.spl"); +type .{ .alloc, .copy, .free } = @import("../sys/mem.spl"); + +type BigInt = @self; + +limbs: *u32, +len: usize, +cap: usize, +negative: bool, + +fn zero(): BigInt { + ret BigInt { .limbs = null, .len = 0, .cap = 0, .negative = false }; +} + +fn valid(self: BigInt): bool { + if self.len > self.cap { ret false; } + if self.cap == 0 { + ret self.limbs == null && self.len == 0 && !self.negative; + } + if self.limbs == null { ret false; } + if self.len == 0 { ret !self.negative; } + ret self.limbs[self.len - 1] != 0; +} + +fn is_zero(self: BigInt): bool { + ret self.len == 0; +} + +fn is_negative(self: BigInt): bool { + ret self.negative; +} + +fn normalize(self: *BigInt) { + while self.len > 0 { + if self.limbs[self.len - 1] != 0 { break; } + self.len = self.len - 1; + } + if self.len == 0 { self.negative = false; } +} + +fn reserve(self: *BigInt, need: usize): Status { + if !valid(self.*) { ret .Err(Error.make(.InvalidArgument)); } + if need <= self.cap { ret .Ok; } + + var new_cap = self.cap; + if new_cap < 2 { new_cap = 2; } + while new_cap < need { + if new_cap > (~(0 as usize)) / 2 { + new_cap = need; + break; + } + new_cap = new_cap * 2; + } + + var byte_count: usize = 0; + match checked_mul(new_cap, @sizeof(u32)) { + .Ok(value) => { byte_count = value; }, + .Err(error) => { ret .Err(error); }, + } + var fresh = alloc(byte_count) as *u32; + if self.len != 0 { + copy(fresh as *any, self.limbs as *any, self.len * @sizeof(u32)); + } + free(self.limbs as *any); + self.limbs = fresh; + self.cap = new_cap; + ret .Ok; +} + +fn drop(self: *BigInt) { + free(self.limbs as *any); + self.* = zero(); +} + +fn take(source: *BigInt): BigInt { + var result = source.*; + source.* = zero(); + ret result; +} + +fn replace(out: *BigInt, fresh: *BigInt) { + drop(out); + out.* = take(fresh); +} + +fn clone(out: *BigInt, source: BigInt): Status { + if !valid(out.*) || !valid(source) { ret .Err(Error.make(.InvalidArgument)); } + var fresh = zero(); + match reserve(&fresh, source.len) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + if source.len != 0 { + copy(fresh.limbs as *any, source.limbs as *any, source.len * @sizeof(u32)); + } + fresh.len = source.len; + fresh.negative = source.negative; + replace(out, &fresh); + ret .Ok; +} + +fn from_u64(out: *BigInt, value: u64): Status { + if !valid(out.*) { ret .Err(Error.make(.InvalidArgument)); } + var fresh = zero(); + if value != 0 { + match reserve(&fresh, 2) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + fresh.limbs[0] = value as u32; + fresh.limbs[1] = (value >> 32) as u32; + fresh.len = 1; + if fresh.limbs[1] != 0 { fresh.len = 2; } + } + replace(out, &fresh); + ret .Ok; +} + +fn from_i64(out: *BigInt, value: i64): Status { + var magnitude = value as u64; + var negative = value < 0; + if negative { magnitude = 0 - magnitude; } + match from_u64(out, magnitude) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + if out.len != 0 { out.negative = negative; } + ret .Ok; +} + +fn cmp_abs(a: BigInt, b: BigInt): i32 { + if a.len < b.len { ret -1; } + if a.len > b.len { ret 1; } + var i = a.len; + while i > 0 { + i = i - 1; + if a.limbs[i] < b.limbs[i] { ret -1; } + if a.limbs[i] > b.limbs[i] { ret 1; } + } + ret 0; +} + +fn cmp(a: BigInt, b: BigInt): i32 { + if a.negative != b.negative { + if a.negative { ret -1; } + ret 1; + } + var order = cmp_abs(a, b); + if a.negative { ret 0 - order; } + ret order; +} + +fn eq(a: BigInt, b: BigInt): bool { + if a.negative != b.negative || a.len != b.len { ret false; } + var i: usize = 0; + while i < a.len { + if a.limbs[i] != b.limbs[i] { ret false; } + i = i + 1; + } + ret true; +} + +fn add_abs_into(out: *BigInt, a: BigInt, b: BigInt): Status { + var count = a.len; + if b.len > count { count = b.len; } + if count == ~(0 as usize) { ret .Err(Error.make(.Overflow)); } + match reserve(out, count + 1) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + var carry: u64 = 0; + var i: usize = 0; + while i < count { + var av: u64 = 0; + var bv: u64 = 0; + if i < a.len { av = a.limbs[i] as u64; } + if i < b.len { bv = b.limbs[i] as u64; } + var sum = av + bv + carry; + out.limbs[i] = sum as u32; + carry = sum >> 32; + i = i + 1; + } + if carry != 0 { + out.limbs[count] = carry as u32; + count = count + 1; + } + out.len = count; + out.negative = false; + normalize(out); + ret .Ok; +} + +// 要求 |a| >= |b|。 +fn sub_abs_into(out: *BigInt, a: BigInt, b: BigInt): Status { + match reserve(out, a.len) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + var borrow: u64 = 0; + var i: usize = 0; + while i < a.len { + var av = a.limbs[i] as u64; + var bv: u64 = 0; + if i < b.len { bv = b.limbs[i] as u64; } + var subtrahend = bv + borrow; + if av < subtrahend { + out.limbs[i] = (4294967296 + av - subtrahend) as u32; + borrow = 1; + } else { + out.limbs[i] = (av - subtrahend) as u32; + borrow = 0; + } + i = i + 1; + } + out.len = a.len; + out.negative = false; + normalize(out); + ret .Ok; +} + +fn add(out: *BigInt, a: BigInt, b: BigInt): Status { + if !valid(out.*) || !valid(a) || !valid(b) { + ret .Err(Error.make(.InvalidArgument)); + } + var fresh = zero(); + if a.negative == b.negative { + match add_abs_into(&fresh, a, b) { + .Ok => {}, .Err(error) => { drop(&fresh); ret .Err(error); }, + } + if fresh.len != 0 { fresh.negative = a.negative; } + } else { + var order = cmp_abs(a, b); + if order >= 0 { + match sub_abs_into(&fresh, a, b) { + .Ok => {}, .Err(error) => { drop(&fresh); ret .Err(error); }, + } + if fresh.len != 0 { fresh.negative = a.negative; } + } else { + match sub_abs_into(&fresh, b, a) { + .Ok => {}, .Err(error) => { drop(&fresh); ret .Err(error); }, + } + if fresh.len != 0 { fresh.negative = b.negative; } + } + } + replace(out, &fresh); + ret .Ok; +} + +fn sub(out: *BigInt, a: BigInt, b: BigInt): Status { + var neg_b = b; + if neg_b.len != 0 { neg_b.negative = !neg_b.negative; } + ret add(out, a, neg_b); +} + +fn neg(out: *BigInt, source: BigInt): Status { + match clone(out, source) { + .Ok => {}, .Err(error) => { ret .Err(error); }, + } + if out.len != 0 { out.negative = !out.negative; } + ret .Ok; +} + +fn abs(out: *BigInt, source: BigInt): Status { + match clone(out, source) { + .Ok => {}, .Err(error) => { ret .Err(error); }, + } + out.negative = false; + ret .Ok; +} + +fn mul(out: *BigInt, a: BigInt, b: BigInt): Status { + if !valid(out.*) || !valid(a) || !valid(b) { + ret .Err(Error.make(.InvalidArgument)); + } + var fresh = zero(); + if a.len == 0 || b.len == 0 { + replace(out, &fresh); + ret .Ok; + } + if b.len > (~(0 as usize)) - a.len { + ret .Err(Error.make(.Overflow)); + } + var count = a.len + b.len; + match reserve(&fresh, count) { + .Ok => {}, .Err(error) => { ret .Err(error); }, + } + var z: usize = 0; + while z < count { fresh.limbs[z] = 0; z = z + 1; } + + var i: usize = 0; + while i < a.len { + var carry: u64 = 0; + var j: usize = 0; + while j < b.len { + var at = i + j; + var product = (a.limbs[i] as u64) * (b.limbs[j] as u64) + + (fresh.limbs[at] as u64) + carry; + fresh.limbs[at] = product as u32; + carry = product >> 32; + j = j + 1; + } + fresh.limbs[i + b.len] = carry as u32; + i = i + 1; + } + fresh.len = count; + fresh.negative = a.negative != b.negative; + normalize(&fresh); + replace(out, &fresh); + ret .Ok; +} + +fn bit_length(out: *usize, self: BigInt): Status { + if out == null || !valid(self) { ret .Err(Error.make(.InvalidArgument)); } + if self.len == 0 { out.* = 0; ret .Ok; } + if self.len - 1 > (~(0 as usize)) / 32 { ret .Err(Error.make(.Overflow)); } + var top = self.limbs[self.len - 1]; + var bits = (self.len - 1) * 32; + while top != 0 { + bits = bits + 1; + top = top >> 1; + } + out.* = bits; + ret .Ok; +} + +fn mul_pow2(out: *BigInt, source: BigInt, amount: usize): Status { + if !valid(out.*) || !valid(source) { ret .Err(Error.make(.InvalidArgument)); } + if source.len == 0 { ret from_u64(out, 0); } + var words = amount / 32; + var shift = amount % 32; + if source.len == ~(0 as usize) || words > (~(0 as usize)) - source.len - 1 { + ret .Err(Error.make(.Overflow)); + } + var fresh = zero(); + var count = source.len + words + 1; + match reserve(&fresh, count) { + .Ok => {}, .Err(error) => { ret .Err(error); }, + } + var i: usize = 0; + while i < words { fresh.limbs[i] = 0; i = i + 1; } + var carry: u64 = 0; + i = 0; + while i < source.len { + var value = ((source.limbs[i] as u64) << shift as u32) | carry; + fresh.limbs[i + words] = value as u32; + carry = value >> 32; + i = i + 1; + } + fresh.len = source.len + words; + if carry != 0 { + fresh.limbs[fresh.len] = carry as u32; + fresh.len = fresh.len + 1; + } + fresh.negative = source.negative; + replace(out, &fresh); + ret .Ok; +} + +// 按绝对值右移,等价于向零除以 2^amount。 +fn div_pow2(out: *BigInt, source: BigInt, amount: usize): Status { + if !valid(out.*) || !valid(source) { ret .Err(Error.make(.InvalidArgument)); } + var words = amount / 32; + var shift = amount % 32; + if words >= source.len { ret from_u64(out, 0); } + var count = source.len - words; + var fresh = zero(); + match reserve(&fresh, count) { + .Ok => {}, .Err(error) => { ret .Err(error); }, + } + var i: usize = 0; + while i < count { + var src = i + words; + var value = (source.limbs[src] as u64) >> shift as u32; + if shift != 0 && src + 1 < source.len { + value = value | ((source.limbs[src + 1] << (32 - shift) as u32) as u64); + } + fresh.limbs[i] = value as u32; + i = i + 1; + } + fresh.len = count; + fresh.negative = source.negative; + normalize(&fresh); + replace(out, &fresh); + ret .Ok; +} + +fn shift_one(self: *BigInt) { + var carry: u64 = 0; + var i: usize = 0; + while i < self.len { + var value = ((self.limbs[i] as u64) << 1) | carry; + self.limbs[i] = value as u32; + carry = value >> 32; + i = i + 1; + } + if carry != 0 { + self.limbs[self.len] = carry as u32; + self.len = self.len + 1; + } +} + +fn sub_abs_in_place(a: *BigInt, b: BigInt) { + var borrow: u64 = 0; + var i: usize = 0; + while i < a.len { + var av = a.limbs[i] as u64; + var bv: u64 = 0; + if i < b.len { bv = b.limbs[i] as u64; } + var subtrahend = bv + borrow; + if av < subtrahend { + a.limbs[i] = (4294967296 + av - subtrahend) as u32; + borrow = 1; + } else { + a.limbs[i] = (av - subtrahend) as u32; + borrow = 0; + } + i = i + 1; + } + normalize(a); +} + +fn div_rem(quotient: *BigInt, remainder: *BigInt, dividend: BigInt, + divisor: BigInt): Status { + if (quotient as usize) == (remainder as usize) { + ret .Err(Error.make(.InvalidArgument)); + } + if !valid(quotient.*) || !valid(remainder.*) || !valid(dividend) || !valid(divisor) { + ret .Err(Error.make(.InvalidArgument)); + } + if divisor.len == 0 { ret .Err(Error.make(.DivisionByZero)); } + + var q = zero(); + var r = zero(); + match reserve(&q, dividend.len) { + .Ok => {}, .Err(error) => { ret .Err(error); }, + } + if divisor.len == ~(0 as usize) { drop(&q); ret .Err(Error.make(.Overflow)); } + match reserve(&r, divisor.len + 1) { + .Ok => {}, .Err(error) => { drop(&q); ret .Err(error); }, + } + var i: usize = 0; + while i < dividend.len { q.limbs[i] = 0; i = i + 1; } + q.len = dividend.len; + + var bit: usize = 0; + match bit_length(&bit, dividend) { + .Ok => {}, + .Err(error) => { drop(&q); drop(&r); ret .Err(error); }, + } + while bit > 0 { + bit = bit - 1; + shift_one(&r); + var word = bit / 32; + var offset = bit % 32; + var incoming = (dividend.limbs[word] >> offset as u32) & 1; + if incoming != 0 { + if r.len == 0 { r.len = 1; r.limbs[0] = 0; } + r.limbs[0] = r.limbs[0] | 1; + } + if cmp_abs(r, divisor) >= 0 { + sub_abs_in_place(&r, divisor); + q.limbs[word] = q.limbs[word] | ((1 as u32) << offset as u32); + } + } + q.negative = q.len != 0 && dividend.negative != divisor.negative; + r.negative = r.len != 0 && dividend.negative; + normalize(&q); + normalize(&r); + replace(quotient, &q); + replace(remainder, &r); + ret .Ok; +} + +fn mul_small(self: *BigInt, value: u32): Status { + if self.len == 0 || value == 1 { ret .Ok; } + if value == 0 { self.len = 0; self.negative = false; ret .Ok; } + if self.len == ~(0 as usize) { ret .Err(Error.make(.Overflow)); } + match reserve(self, self.len + 1) { + .Ok => {}, .Err(error) => { ret .Err(error); }, + } + var carry: u64 = 0; + var i: usize = 0; + while i < self.len { + var product = (self.limbs[i] as u64) * (value as u64) + carry; + self.limbs[i] = product as u32; + carry = product >> 32; + i = i + 1; + } + if carry != 0 { + self.limbs[self.len] = carry as u32; + self.len = self.len + 1; + } + ret .Ok; +} + +fn add_small(self: *BigInt, value: u32): Status { + if value == 0 { ret .Ok; } + if self.len == ~(0 as usize) { ret .Err(Error.make(.Overflow)); } + match reserve(self, self.len + 1) { + .Ok => {}, .Err(error) => { ret .Err(error); }, + } + var carry = value as u64; + var i: usize = 0; + while carry != 0 && i < self.len { + var sum = (self.limbs[i] as u64) + carry; + self.limbs[i] = sum as u32; + carry = sum >> 32; + i = i + 1; + } + if carry != 0 { + self.limbs[self.len] = carry as u32; + self.len = self.len + 1; + } + ret .Ok; +} + +fn parse(out: *BigInt, bytes: []u8, radix: u8): Status { + if !valid(out.*) { ret .Err(Error.make(.InvalidArgument)); } + if radix < 2 || radix > 36 { ret .Err(Error.make(.InvalidArgument)); } + if bytes.len == 0 { ret .Err(Error.at(.UnexpectedEnd, 0)); } + if bytes.ptr == null { ret .Err(Error.make(.InvalidArgument)); } + + var i: usize = 0; + var negative = false; + if bytes.ptr[0] == 45 || bytes.ptr[0] == 43 { + negative = bytes.ptr[0] == 45; + i = 1; + } + if i == bytes.len { ret .Err(Error.at(.UnexpectedEnd, i)); } + + var fresh = zero(); + while i < bytes.len { + var digit: u8 = 0; + match digit_value(bytes.ptr[i], radix) { + .Ok(value) => { digit = value; }, + .Err(error) => { drop(&fresh); ret .Err(Error.at(.InvalidDigit, i)); }, + } + match mul_small(&fresh, radix as u32) { + .Ok => {}, .Err(error) => { drop(&fresh); ret .Err(error); }, + } + match add_small(&fresh, digit as u32) { + .Ok => {}, .Err(error) => { drop(&fresh); ret .Err(error); }, + } + i = i + 1; + } + fresh.negative = fresh.len != 0 && negative; + replace(out, &fresh); + ret .Ok; +} + +fn div_small(self: *BigInt, radix: u32): u32 { + var remainder: u64 = 0; + var i = self.len; + while i > 0 { + i = i - 1; + var current = (remainder << 32) | (self.limbs[i] as u64); + self.limbs[i] = (current / radix as u64) as u32; + remainder = current % radix as u64; + } + normalize(self); + ret remainder as u32; +} + +fn format(out: *ByteBuffer, source: BigInt, radix: u8, uppercase: bool): Status { + if !valid(source) { ret .Err(Error.make(.InvalidArgument)); } + if radix < 2 || radix > 36 { ret .Err(Error.make(.InvalidArgument)); } + var reverse = ByteBuffer.new(); + var fresh = ByteBuffer.new(); + var magnitude = zero(); + match clone(&magnitude, source) { + .Ok => {}, .Err(error) => { ret .Err(error); }, + } + magnitude.negative = false; + if magnitude.len == 0 { + match ByteBuffer.append_byte(&reverse, 48) { + .Ok => {}, .Err(error) => { drop(&magnitude); ret .Err(error); }, + } + } + while magnitude.len != 0 { + var remainder = div_small(&magnitude, radix as u32); + var byte: u8 = 0; + match digit_char(remainder as u8, uppercase) { + .Ok(value) => { byte = value; }, + .Err(error) => { drop(&magnitude); ByteBuffer.drop(&reverse); ret .Err(error); }, + } + match ByteBuffer.append_byte(&reverse, byte) { + .Ok => {}, + .Err(error) => { + drop(&magnitude); ByteBuffer.drop(&reverse); ret .Err(error); + }, + } + } + if source.negative { + match ByteBuffer.append_byte(&fresh, 45) { + .Ok => {}, + .Err(error) => { + drop(&magnitude); ByteBuffer.drop(&reverse); ret .Err(error); + }, + } + } + var i = ByteBuffer.size(reverse); + var digits = ByteBuffer.as_slice(reverse); + while i > 0 { + i = i - 1; + match ByteBuffer.append_byte(&fresh, digits.ptr[i]) { + .Ok => {}, + .Err(error) => { + drop(&magnitude); ByteBuffer.drop(&reverse); ByteBuffer.drop(&fresh); + ret .Err(error); + }, + } + } + drop(&magnitude); + ByteBuffer.drop(&reverse); + ByteBuffer.drop(out); + out.* = ByteBuffer.take(&fresh); + ret .Ok; +} + +fn to_u64(out: *u64, source: BigInt): Status { + if out == null || !valid(source) { ret .Err(Error.make(.InvalidArgument)); } + if source.negative || source.len > 2 { ret .Err(Error.make(.Overflow)); } + var value: u64 = 0; + if source.len > 0 { value = source.limbs[0] as u64; } + if source.len > 1 { value = value | ((source.limbs[1] as u64) << 32); } + out.* = value; + ret .Ok; +} + +fn to_i64(out: *i64, source: BigInt): Status { + if out == null || !valid(source) { ret .Err(Error.make(.InvalidArgument)); } + if source.len > 2 { ret .Err(Error.make(.Overflow)); } + var magnitude: u64 = 0; + if source.len > 0 { magnitude = source.limbs[0] as u64; } + if source.len > 1 { magnitude = magnitude | ((source.limbs[1] as u64) << 32); } + if !source.negative { + if magnitude > 9223372036854775807 { ret .Err(Error.make(.Overflow)); } + out.* = magnitude as i64; + ret .Ok; + } + if magnitude > 9223372036854775808 { ret .Err(Error.make(.Overflow)); } + if magnitude == 9223372036854775808 { + out.* = magnitude as i64; + } else { + out.* = 0 - magnitude as i64; + } + ret .Ok; +} + +fn to_usize(out: *usize, source: BigInt): Status { + if out == null { ret .Err(Error.make(.InvalidArgument)); } + var value: u64 = 0; + match to_u64(&value, source) { + .Ok => {}, .Err(error) => { ret .Err(error); }, + } + if value > (~(0 as usize)) as u64 { ret .Err(Error.make(.Overflow)); } + out.* = value as usize; + ret .Ok; +} + +fn expect_ok(result: Status) { + match result { .Ok => {}, .Err(error) => { @assert(false, "unexpected BigInt error"); }, } +} + +fn expect_text_radix(value: BigInt, radix: u8, uppercase: bool, expected: []u8) { + var buffer = ByteBuffer.new(); + expect_ok(format(&buffer, value, radix, uppercase)); + var actual = ByteBuffer.as_slice(buffer); + @assert(actual.len == expected.len, "formatted length"); + var i: usize = 0; + while i < expected.len { + @assert(actual.ptr[i] == expected.ptr[i], "formatted byte"); + i = i + 1; + } + ByteBuffer.drop(&buffer); +} + +fn expect_text(value: BigInt, expected: []u8) { + expect_text_radix(value, 10, false, expected); +} + +fn parsed(text: []u8): BigInt { + var value = zero(); + expect_ok(parse(&value, text, 10)); + ret value; +} + +fn test_arithmetic() { + var a = parsed("4294967295"); + var one = zero(); + expect_ok(from_u64(&one, 1)); + expect_ok(add(&a, a, one)); + expect_text(a, "4294967296"); + + var all_ones = zero(); + expect_ok(parse(&all_ones, "ffffffffffffffffffffffffffffffff", 16)); + expect_ok(add(&all_ones, all_ones, one)); + expect_text(all_ones, "340282366920938463463374607431768211456"); + expect_ok(sub(&all_ones, all_ones, one)); + expect_text_radix(all_ones, 16, false, "ffffffffffffffffffffffffffffffff"); + + var two_64 = parsed("18446744073709551616"); + var product = zero(); + expect_ok(mul(&product, two_64, two_64)); + expect_text(product, "340282366920938463463374607431768211456"); + + var difference = zero(); + expect_ok(sub(&difference, product, two_64)); + expect_text(difference, "340282366920938463444927863358058659840"); + + var shifted = zero(); + expect_ok(mul_pow2(&shifted, one, 257)); + var bits: usize = 0; + expect_ok(bit_length(&bits, shifted)); + @assert(bits == 258); + expect_ok(div_pow2(&shifted, shifted, 257)); + @assert(eq(shifted, one)); + + drop(&shifted); drop(&difference); drop(&product); drop(&two_64); + drop(&all_ones); drop(&one); drop(&a); +} + +fn test_division_and_sign() { + var dividend = parsed("340282366920938463463374607431768211456"); + var divisor = parsed("18446744073709551616"); + var quotient = zero(); + var remainder = zero(); + expect_ok(div_rem("ient, &remainder, dividend, divisor)); + @assert(eq(quotient, divisor)); + @assert(is_zero(remainder)); + + var long_dividend = parsed("123456789012345678901234567890"); + var long_divisor = parsed("9876543210"); + expect_ok(div_rem("ient, &remainder, long_dividend, long_divisor)); + expect_text(quotient, "12499999887343749990"); + expect_text(remainder, "1562499990"); + + var negative = zero(); + var seven = zero(); + expect_ok(from_i64(&negative, -100)); + expect_ok(from_u64(&seven, 7)); + expect_ok(div_rem(&negative, &remainder, negative, seven)); + expect_text(negative, "-14"); + expect_text(remainder, "-2"); + + var reconstructed = zero(); + expect_ok(mul(&reconstructed, negative, seven)); + expect_ok(add(&reconstructed, reconstructed, remainder)); + expect_text(reconstructed, "-100"); + + var saved = parsed("123"); + var zero_divisor = zero(); + match div_rem(&saved, &remainder, dividend, zero_divisor) { + .Ok => { @assert(false, "division by zero"); }, + .Err(error) => { match error.kind { .DivisionByZero => {}, _ => { @assert(false); }, } }, + } + expect_text(saved, "123"); + + drop(&zero_divisor); drop(&saved); drop(&reconstructed); drop(&seven); drop(&negative); + drop(&long_divisor); drop(&long_dividend); + drop(&remainder); drop("ient); drop(&divisor); drop(÷nd); +} + +fn test_parse_format_and_conversion() { + var value = zero(); + expect_ok(parse(&value, "-deadBEEF", 16)); + var output = ByteBuffer.new(); + expect_ok(format(&output, value, 16, false)); + var text = ByteBuffer.as_slice(output); + @assert(text.len == 9 && text.ptr[0] == 45 && text.ptr[1] == 100 && text.ptr[8] == 102); + expect_text_radix(value, 16, true, "-DEADBEEF"); + + var base36 = zero(); + expect_ok(parse(&base36, "zzzzzzzzzz", 36)); + expect_text_radix(base36, 36, false, "zzzzzzzzzz"); + + var preserved = parsed("77"); + match parse(&preserved, "12x", 10) { + .Ok => { @assert(false, "invalid digit"); }, + .Err(error) => { + @assert(error.has_position && error.position == 2); + match error.kind { .InvalidDigit => {}, _ => { @assert(false); }, } + }, + } + expect_text(preserved, "77"); + + var max = parsed("18446744073709551615"); + var converted: u64 = 0; + expect_ok(to_u64(&converted, max)); + @assert(converted == 18446744073709551615); + var over = parsed("18446744073709551616"); + converted = 17; + match to_u64(&converted, over) { + .Ok => { @assert(false); }, + .Err(error) => { match error.kind { .Overflow => {}, _ => { @assert(false); }, } }, + } + @assert(converted == 17, "failed conversion preserves output"); + + var minimum = parsed("-9223372036854775808"); + var signed_value: i64 = 0; + expect_ok(to_i64(&signed_value, minimum)); + @assert(signed_value == (9223372036854775808 as u64) as i64); + var signed_over = parsed("9223372036854775808"); + signed_value = 23; + match to_i64(&signed_value, signed_over) { + .Ok => { @assert(false); }, + .Err(error) => { match error.kind { .Overflow => {}, _ => { @assert(false); }, } }, + } + @assert(signed_value == 23, "failed signed conversion preserves output"); + + var copied = zero(); + expect_ok(clone(&copied, max)); + var one_more = parsed("1"); + expect_ok(add(&max, max, one_more)); + expect_text(copied, "18446744073709551615"); + var moved = take(&copied); + @assert(copied.limbs == null && copied.len == 0 && copied.cap == 0); + expect_text(moved, "18446744073709551615"); + + drop(&moved); drop(&copied); drop(&one_more); drop(&signed_over); drop(&minimum); + drop(&over); drop(&max); drop(&preserved); drop(&base36); + ByteBuffer.drop(&output); drop(&value); +} + +fn expect_i64_value(value: BigInt, expected: i64) { + var actual: i64 = 0; + expect_ok(to_i64(&actual, value)); + @assert(actual == expected, "small integer reference result"); +} + +fn test_small_reference() { + var left = zero(); + var right = zero(); + var result = zero(); + var remainder = zero(); + var a: i64 = 0; + while a <= 0 { + var b: i64 = 2; + while b <= 2 { + expect_ok(from_i64(&left, a)); + expect_ok(from_i64(&right, b)); + expect_ok(add(&result, left, right)); + expect_i64_value(result, a + b); + expect_ok(sub(&result, left, right)); + expect_i64_value(result, a - b); + expect_ok(mul(&result, left, right)); + expect_i64_value(result, a * b); + if b != 0 { + expect_ok(div_rem(&result, &remainder, left, right)); + expect_i64_value(result, a / b); + expect_i64_value(remainder, a % b); + } + b = b + 1; + } + a = a + 1; + } + drop(&remainder); drop(&result); drop(&right); drop(&left); +} + +fn main() { + test_arithmetic(); + test_division_and_sign(); + test_parse_format_and_conversion(); + test_small_reference(); +} diff --git a/stage2/utils/fmt.spl b/stage2/utils/fmt.spl new file mode 100644 index 0000000..79c7292 --- /dev/null +++ b/stage2/utils/fmt.spl @@ -0,0 +1,241 @@ +// Expect retcode = 0 +// 数值/布尔格式化,写入 ByteBuffer。radix 取值 2..36;uppercase 只影响 10..35 的字母形式。 +// 不分配额外内存(除目标 ByteBuffer 自身扩容),失败不产生部分输出。 + +type .{ .Error, .Status, .make } = @import("error.spl"); +type .{ .ByteBuffer } = @import("byte_buffer.spl"); +type .{ .digit_char } = @import("ascii.spl"); + +// 注意:radix 边界直接用字面量。stage1 刻意不做常量求值/折叠, +// 顶层 const 不应用于运行时表达式。 +fn valid_radix(radix: u8): bool { + ret radix >= 2 && radix <= 36; +} + +fn append_u64(out: *ByteBuffer, value: u64, radix: u8, uppercase: bool): Status { + if !valid_radix(radix) { ret .Err(make(.InvalidArgument)); } + + var digits: [64]u8 = undefined; + var count: usize = 0; + + if value == 0 { + digits[0] = 48; + count = 1; + } else { + var v = value; + var base = radix as u64; + while v != 0 { + var d = (v % base) as u8; + match digit_char(d, uppercase) { + .Ok(c) => { digits[count] = c; }, + .Err(error) => { ret .Err(error); }, + } + count = count + 1; + v = v / base; + } + } + + // digits 是低位在前,反向写入得到正常顺序。 + var i = count; + while i > 0 { + i = i - 1; + match ByteBuffer.append_byte(out, digits[i]) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + } + ret .Ok; +} + +fn append_i64(out: *ByteBuffer, value: i64, radix: u8, uppercase: bool): Status { + if !valid_radix(radix) { ret .Err(make(.InvalidArgument)); } + if value < 0 { + match ByteBuffer.append_byte(out, 45) { // '-' + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + // value 为补码,(0 - (value as u64)) 得到绝对值(含 i64 最小值)。 + var magnitude = 0 - (value as u64); + ret append_u64(out, magnitude, radix, uppercase); + } + ret append_u64(out, value as u64, radix, uppercase); +} + +fn append_usize(out: *ByteBuffer, value: usize, radix: u8, uppercase: bool): Status { + ret append_u64(out, value as u64, radix, uppercase); +} + +fn append_isize(out: *ByteBuffer, value: isize, radix: u8, uppercase: bool): Status { + ret append_i64(out, value as i64, radix, uppercase); +} + +fn append_bool(out: *ByteBuffer, value: bool): Status { + if value { ret ByteBuffer.append(out, "true"); } + ret ByteBuffer.append(out, "false"); +} + +// ---- 测试辅助 ---- + +fn expect_ok(result: Status) { + match result { + .Ok => {}, + .Err(error) => { @assert(false, "unexpected fmt error"); }, + } +} + +fn expect_invalid(result: Status) { + match result { + .Ok => { @assert(false, "expected fmt error"); }, + .Err(error) => { + match error.kind { + .InvalidArgument => {}, + _ => { @assert(false, "unexpected fmt error kind"); }, + } + }, + } +} + +fn expect_text(buffer: *ByteBuffer, expected: []u8) { + var actual = ByteBuffer.as_slice(buffer.*); + @assert(actual.len == expected.len, "formatted length"); + var i: usize = 0; + while i < expected.len { + @assert(actual.ptr[i] == expected.ptr[i], "formatted byte"); + i = i + 1; + } +} + +fn test_unsigned() { + var b = ByteBuffer.new(); + + expect_ok(append_u64(&b, 0, 10, false)); + expect_text(&b, "0"); + ByteBuffer.clear(&b); + + expect_ok(append_u64(&b, 42, 10, false)); + expect_text(&b, "42"); + ByteBuffer.clear(&b); + + expect_ok(append_u64(&b, 1234567890, 10, false)); + expect_text(&b, "1234567890"); + ByteBuffer.clear(&b); + + expect_ok(append_u64(&b, 18446744073709551615, 10, false)); + expect_text(&b, "18446744073709551615"); + ByteBuffer.clear(&b); + + expect_ok(append_u64(&b, 255, 16, false)); + expect_text(&b, "ff"); + ByteBuffer.clear(&b); + + expect_ok(append_u64(&b, 255, 16, true)); + expect_text(&b, "FF"); + ByteBuffer.clear(&b); + + expect_ok(append_u64(&b, 4096, 16, false)); + expect_text(&b, "1000"); + ByteBuffer.clear(&b); + + expect_ok(append_u64(&b, 170, 2, false)); + expect_text(&b, "10101010"); + ByteBuffer.clear(&b); + + expect_ok(append_u64(&b, 35, 36, false)); + expect_text(&b, "z"); + ByteBuffer.clear(&b); + + expect_ok(append_u64(&b, 18446744073709551615, 2, false)); + expect_text(&b, "1111111111111111111111111111111111111111111111111111111111111111"); + ByteBuffer.clear(&b); + + ByteBuffer.drop(&b); +} + +fn test_signed() { + var b = ByteBuffer.new(); + + expect_ok(append_i64(&b, 0, 10, false)); + expect_text(&b, "0"); + ByteBuffer.clear(&b); + + expect_ok(append_i64(&b, -123, 10, false)); + expect_text(&b, "-123"); + ByteBuffer.clear(&b); + + expect_ok(append_i64(&b, 9223372036854775807, 10, false)); + expect_text(&b, "9223372036854775807"); + ByteBuffer.clear(&b); + + expect_ok(append_i64(&b, -9223372036854775808, 10, false)); + expect_text(&b, "-9223372036854775808"); + ByteBuffer.clear(&b); + + expect_ok(append_isize(&b, -16, 16, false)); + expect_text(&b, "-10"); + ByteBuffer.clear(&b); + + expect_ok(append_u64(&b, 48879, 16, true)); + expect_text(&b, "BEEF"); + ByteBuffer.clear(&b); + + ByteBuffer.drop(&b); +} + +fn test_usize_bool_and_errors() { + var b = ByteBuffer.new(); + + expect_ok(append_usize(&b, 1000 as usize, 10, false)); + expect_text(&b, "1000"); + ByteBuffer.clear(&b); + + expect_ok(append_bool(&b, true)); + expect_text(&b, "true"); + ByteBuffer.clear(&b); + + expect_ok(append_bool(&b, false)); + expect_text(&b, "false"); + ByteBuffer.clear(&b); + + expect_invalid(append_u64(&b, 1, 0, false)); + expect_invalid(append_u64(&b, 1, 1, false)); + expect_invalid(append_u64(&b, 1, 37, false)); + expect_invalid(append_i64(&b, -1, 0, false)); + @assert(ByteBuffer.size(b) == 0, "invalid radix writes nothing"); + + ByteBuffer.drop(&b); +} + +fn test_composition_and_growth() { + var b = ByteBuffer.new(); + + expect_ok(ByteBuffer.append(&b, "x=")); + expect_ok(append_i64(&b, -42, 10, false)); + expect_ok(ByteBuffer.append(&b, " hex=")); + expect_ok(append_u64(&b, 255, 16, false)); + expect_ok(ByteBuffer.append(&b, " ok=")); + expect_ok(append_bool(&b, true)); + expect_text(&b, "x=-42 hex=ff ok=true"); + ByteBuffer.clear(&b); + + // 反复写入触发 ByteBuffer 扩容,校验累积内容。 + var i: i32 = 0; + while i < 200 { + expect_ok(append_i64(&b, i as i64, 10, false)); + expect_ok(ByteBuffer.append_byte(&b, 44)); // ',' + i = i + 1; + } + expect_ok(append_i64(&b, 200, 10, false)); + @assert(ByteBuffer.size(b) > 0, "growth content"); + var s = ByteBuffer.as_slice(b); + @assert(s.ptr[0] == 48, "starts with 0"); + @assert(s.ptr[1] == 44, "0 then comma"); + + ByteBuffer.drop(&b); +} + +fn main() { + test_unsigned(); + test_signed(); + test_usize_bool_and_errors(); + test_composition_and_growth(); +} diff --git a/stage2/utils/json.spl b/stage2/utils/json.spl new file mode 100644 index 0000000..6c873f0 --- /dev/null +++ b/stage2/utils/json.spl @@ -0,0 +1,780 @@ +// Expect retcode = 0 +// 轻量 JSON 解析 / 输出器(递归下降 + 节点池),用于验证底层库的拼装,未来服务 LSP。 +// 设计: +// - 输入 ByteView,字符串/数字以“源切片”借用,不做解码,round-trip 保真; +// - 节点池 Vec[JsonNode];数组/对象子项存 Vec[JsonChild],保留插入顺序; +// - 错误带字节位置(error.spl);输出写 ByteBuffer;缩进/数字用 ByteBuffer/fmt。 +// 限制:不做字符串转义解码、无递归深度保护、数字不做数值化(原样保存)。 + +type .{ .Error, .Status, .make, .at } = @import("error.spl"); +type .{ .ByteView, .ViewResult, .FindResult } = @import("byte_view.spl"); +type .{ .Vec } = @import("vec.spl"); +type .{ .ByteBuffer } = @import("byte_buffer.spl"); +type .{ .is_digit, .is_hex_digit, .is_space } = @import("ascii.spl"); +type .{ .append_i64, .append_u64 } = @import("fmt.spl"); + +type JsonKind = enum { + Null, + True, + False, + Number, + String, + Array, + Object, +} + +// 数组元素或对象成员。数组时 key 为空视图;对象时 key 为源中的原始键文本。 +type JsonChild = struct { + key: ByteView, + val: usize, +} + +type JsonNode = struct { + kind: JsonKind, + text: ByteView, + children: Vec, +} + +// 解析结果文档:nodes 拥有全部节点;root 为根节点下标。 +type Json = struct { + nodes: Vec, + root: usize, +} + +type JsonResult = enum { + Ok: Json, + Err: Error, +} + +type NodeResult = enum { + Ok: usize, + Err: Error, +} + +type Parser = struct { + src: ByteView, + pos: usize, + nodes: Vec, +} + +fn blank_node(kind: JsonKind): JsonNode { + ret JsonNode { + .kind = kind, + .text = ByteView.empty(), + .children = Vec.blank(@sizeof(JsonChild), 8), + }; +} + +fn intern_node(p: *Parser, node: JsonNode): NodeResult { + var index = p.nodes.len; + var n = node; + match Vec.push(&p.nodes, &n as *u8) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + ret .Ok(index); +} + +fn node_at(doc: *Json, index: usize): *JsonNode { + match Vec.get(&doc.nodes, index) { + .Ok(ptr) => { ret ptr as *JsonNode; }, + .Err(error) => { ret null; }, + } + ret null; +} + +fn skip_ws(p: *Parser) { + while p.pos < p.src.len && is_space(p.src.ptr[p.pos]) { + p.pos = p.pos + 1; + } +} + +// -1 表示 EOF。 +fn peek(p: *Parser): i32 { + if p.pos >= p.src.len { ret -1; } + ret p.src.ptr[p.pos] as i32; +} + +fn parse_string_raw(p: *Parser): ViewResult { + if peek(p) != 34 { ret .Err(at(.InvalidFormat, p.pos)); } + p.pos = p.pos + 1; + var start = p.pos; + while p.pos < p.src.len { + var c = p.src.ptr[p.pos]; + if c == 34 { break; } + if c == 92 { // '\\' + p.pos = p.pos + 1; + if p.pos >= p.src.len { ret .Err(at(.UnexpectedEnd, p.pos)); } + if p.src.ptr[p.pos] == 117 { // 'u' + var k: usize = 0; + while k < 4 { + p.pos = p.pos + 1; + if p.pos >= p.src.len { ret .Err(at(.UnexpectedEnd, p.pos)); } + if !is_hex_digit(p.src.ptr[p.pos]) { ret .Err(at(.InvalidDigit, p.pos)); } + k = k + 1; + } + } + } else if c < 32 { + ret .Err(at(.InvalidFormat, p.pos)); + } + p.pos = p.pos + 1; + } + if p.pos >= p.src.len { ret .Err(at(.UnexpectedEnd, p.pos)); } + var end = p.pos; + p.pos = p.pos + 1; // 收尾引号 + ret ByteView.slice(p.src, start, end); +} + +fn parse_literal(p: *Parser, word: []u8, kind: JsonKind): NodeResult { + var i: usize = 0; + while i < word.len { + if p.pos + i >= p.src.len { ret .Err(at(.UnexpectedEnd, p.src.len)); } + if p.src.ptr[p.pos + i] != word.ptr[i] { + ret .Err(at(.InvalidFormat, p.pos + i)); + } + i = i + 1; + } + p.pos = p.pos + word.len; + var node = blank_node(kind); + ret intern_node(p, node); +} + +fn parse_number(p: *Parser): NodeResult { + var start = p.pos; + if peek(p) == 45 { p.pos = p.pos + 1; } // '-' + if p.pos >= p.src.len || !is_digit(p.src.ptr[p.pos]) { + ret .Err(at(.InvalidDigit, p.pos)); + } + if p.src.ptr[p.pos] == 48 { // 前导 0 后面不能接数字 + p.pos = p.pos + 1; + } else { + while p.pos < p.src.len && is_digit(p.src.ptr[p.pos]) { + p.pos = p.pos + 1; + } + } + if p.pos < p.src.len && p.src.ptr[p.pos] == 46 { // '.' + p.pos = p.pos + 1; + if p.pos >= p.src.len || !is_digit(p.src.ptr[p.pos]) { + ret .Err(at(.InvalidDigit, p.pos)); + } + while p.pos < p.src.len && is_digit(p.src.ptr[p.pos]) { + p.pos = p.pos + 1; + } + } + if p.pos < p.src.len && (p.src.ptr[p.pos] == 101 || p.src.ptr[p.pos] == 69) { // e/E + p.pos = p.pos + 1; + if p.pos < p.src.len && (p.src.ptr[p.pos] == 43 || p.src.ptr[p.pos] == 45) { + p.pos = p.pos + 1; + } + if p.pos >= p.src.len || !is_digit(p.src.ptr[p.pos]) { + ret .Err(at(.InvalidDigit, p.pos)); + } + while p.pos < p.src.len && is_digit(p.src.ptr[p.pos]) { + p.pos = p.pos + 1; + } + } + + var text = ByteView.slice(p.src, start, p.pos); + match text { + .Ok(v) => { + var node = blank_node(.Number); + node.text = v; + ret intern_node(p, node); + }, + .Err(error) => { ret .Err(error); }, + } +} + +fn parse_array(p: *Parser): NodeResult { + p.pos = p.pos + 1; // '[' + var kids = Vec.blank(@sizeof(JsonChild), 8); + + skip_ws(p); + if peek(p) == 93 { // ']' + p.pos = p.pos + 1; + var empty = blank_node(.Array); + empty.children = kids; + ret intern_node(p, empty); + } + + while true { + var element = parse_value(p); + var index: usize = 0; + match element { + .Ok(v) => { index = v; }, + .Err(error) => { Vec.drop(&kids); ret .Err(error); }, + } + var child = JsonChild { .key = ByteView.empty(), .val = index }; + match Vec.push(&kids, &child as *u8) { + .Ok => {}, + .Err(error) => { Vec.drop(&kids); ret .Err(error); }, + } + + skip_ws(p); + var c = peek(p); + if c == 44 { // ',' + p.pos = p.pos + 1; + continue; + } + if c == 93 { // ']' + p.pos = p.pos + 1; + break; + } + Vec.drop(&kids); + ret .Err(at(.InvalidFormat, p.pos)); + } + + var node = blank_node(.Array); + node.children = kids; + ret intern_node(p, node); +} + +fn parse_object(p: *Parser): NodeResult { + p.pos = p.pos + 1; // '{' + var kids = Vec.blank(@sizeof(JsonChild), 8); + + skip_ws(p); + if peek(p) == 125 { // '}' + p.pos = p.pos + 1; + var empty = blank_node(.Object); + empty.children = kids; + ret intern_node(p, empty); + } + + while true { + skip_ws(p); + var key_view = ByteView.empty(); + match parse_string_raw(p) { + .Ok(v) => { key_view = v; }, + .Err(error) => { Vec.drop(&kids); ret .Err(error); }, + } + + skip_ws(p); + if peek(p) != 58 { // ':' + Vec.drop(&kids); + ret .Err(at(.InvalidFormat, p.pos)); + } + p.pos = p.pos + 1; + + var value = parse_value(p); + var index: usize = 0; + match value { + .Ok(v) => { index = v; }, + .Err(error) => { Vec.drop(&kids); ret .Err(error); }, + } + + var child = JsonChild { .key = key_view, .val = index }; + match Vec.push(&kids, &child as *u8) { + .Ok => {}, + .Err(error) => { Vec.drop(&kids); ret .Err(error); }, + } + + skip_ws(p); + var c = peek(p); + if c == 44 { // ',' + p.pos = p.pos + 1; + continue; + } + if c == 125 { // '}' + p.pos = p.pos + 1; + break; + } + Vec.drop(&kids); + ret .Err(at(.InvalidFormat, p.pos)); + } + + var node = blank_node(.Object); + node.children = kids; + ret intern_node(p, node); +} + +fn parse_value(p: *Parser): NodeResult { + skip_ws(p); + var c = peek(p); + if c < 0 { ret .Err(at(.UnexpectedEnd, p.pos)); } + var ch = c as u8; + + if ch == 34 { // '"' + var view = ByteView.empty(); + match parse_string_raw(p) { + .Ok(v) => { view = v; }, + .Err(error) => { ret .Err(error); }, + } + var node = blank_node(.String); + node.text = view; + ret intern_node(p, node); + } + if ch == 123 { ret parse_object(p); } // '{' + if ch == 91 { ret parse_array(p); } // '[' + if ch == 116 { ret parse_literal(p, "true", .True); } + if ch == 102 { ret parse_literal(p, "false", .False); } + if ch == 110 { ret parse_literal(p, "null", .Null); } + ret parse_number(p); +} + +fn drop_node_pool(pool: *Vec) { + var i: usize = 0; + while i < pool.*.len { + match Vec.get(pool, i) { + .Ok(ptr) => { + var n = ptr as *JsonNode; + Vec.drop(&n.children); + }, + .Err(error) => {}, + } + i = i + 1; + } + Vec.drop(pool); +} + +fn parse(src: ByteView): JsonResult { + var p = Parser { + .src = src, + .pos = 0, + .nodes = Vec.blank(@sizeof(JsonNode), 8), + }; + + skip_ws(&p); + var root: usize = 0; + var result = parse_value(&p); + match result { + .Ok(index) => { root = index; }, + .Err(error) => { + drop_node_pool(&p.nodes); + ret .Err(error); + }, + } + + skip_ws(&p); + if p.pos != src.len { + drop_node_pool(&p.nodes); + ret .Err(at(.InvalidFormat, p.pos)); + } + + ret .Ok(Json { .nodes = p.nodes, .root = root }); +} + +fn drop(doc: *Json) { + drop_node_pool(&doc.nodes); +} + +fn root_index(doc: *Json): usize { + ret doc.root; +} + +fn kind_of(doc: *Json, index: usize): JsonKind { + var n = node_at(doc, index); + if n == null { ret .Null; } + ret n.kind; +} + +// 枚举 tag 取整(stage1 枚举 == 有缺陷,用 @enum2int 比较更稳妥)。 +fn kind_code(kind: JsonKind): usize { + ret @enum2int(kind) as usize; +} + +fn text_of(doc: *Json, index: usize): ByteView { + var n = node_at(doc, index); + if n == null { ret ByteView.empty(); } + ret n.text; +} + +fn array_count(doc: *Json, index: usize): usize { + var n = node_at(doc, index); + if n == null { ret 0; } + match n.kind { + .Array => { ret n.children.len; }, + _ => { ret 0; }, + } + ret 0; +} + +fn array_at(doc: *Json, index: usize, i: usize): usize { + var n = node_at(doc, index); + if n == null { ret ~(0 as usize); } + match n.kind { + .Array => { + if i >= n.children.len { ret ~(0 as usize); } + match Vec.get(&n.children, i) { + .Ok(ptr) => { ret (ptr as *JsonChild).val; }, + .Err(error) => { ret ~(0 as usize); }, + } + }, + _ => { ret ~(0 as usize); }, + } + ret ~(0 as usize); +} + +// 对象按键查找(线性,保持插入顺序)。找到返回节点下标。 +fn object_find(doc: *Json, index: usize, key: ByteView): FindResult { + var n = node_at(doc, index); + if n == null { ret .NotFound; } + match n.kind { + .Object => { + var i: usize = 0; + var count = n.children.len; + while i < count { + match Vec.get(&n.children, i) { + .Ok(ptr) => { + var child = ptr as *JsonChild; + if ByteView.eq(child.key, key) { ret .Found(child.val); } + }, + .Err(error) => {}, + } + i = i + 1; + } + ret .NotFound; + }, + _ => { ret .NotFound; }, + } + ret .NotFound; +} + +fn emit_quoted(out: *ByteBuffer, text: ByteView): Status { + match ByteBuffer.append_byte(out, 34) { // '"' + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + match ByteBuffer.append(out, ByteView.as_slice(text)) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + ret ByteBuffer.append_byte(out, 34); +} + +fn emit_indent(out: *ByteBuffer, indent: usize, depth: usize): Status { + match ByteBuffer.append_byte(out, 10) { // '\n' + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + ret ByteBuffer.append_repeat(out, 32, depth * indent); +} + +fn emit(out: *ByteBuffer, doc: *Json, index: usize, indent: usize, depth: usize): Status { + var n = node_at(doc, index); + if n == null { ret .Err(make(.OutOfRange)); } + + match n.kind { + .Null => { ret ByteBuffer.append(out, "null"); }, + .True => { ret ByteBuffer.append(out, "true"); }, + .False => { ret ByteBuffer.append(out, "false"); }, + .Number => { ret ByteBuffer.append(out, ByteView.as_slice(n.text)); }, + .String => { ret emit_quoted(out, n.text); }, + .Array => { ret emit_children(out, doc, n, false, indent, depth); }, + .Object => { ret emit_children(out, doc, n, true, indent, depth); }, + } + ret .Ok; +} + +fn emit_children(out: *ByteBuffer, doc: *Json, n: *JsonNode, is_object: bool, indent: usize, + depth: usize): Status { + var open_byte: u8 = 91; // '[' + var close_byte: u8 = 93; // ']' + if is_object { + open_byte = 123; // '{' + close_byte = 125; // '}' + } + + match ByteBuffer.append_byte(out, open_byte) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + + var count = n.children.len; + if count == 0 { + ret ByteBuffer.append_byte(out, close_byte); + } + + var i: usize = 0; + while i < count { + if i != 0 { + match ByteBuffer.append_byte(out, 44) { // ',' + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + } + if indent != 0 { + match emit_indent(out, indent, depth + 1) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + } + + match Vec.get(&n.children, i) { + .Ok(ptr) => { + var child = ptr as *JsonChild; + if is_object { + match emit_quoted(out, child.key) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + match ByteBuffer.append_byte(out, 58) { // ':' + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + if indent != 0 { + match ByteBuffer.append_byte(out, 32) { // ' ' + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + } + } + match emit(out, doc, child.val, indent, depth + 1) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + }, + .Err(error) => { ret .Err(error); }, + } + i = i + 1; + } + + if indent != 0 { + match emit_indent(out, indent, depth) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + } + ret ByteBuffer.append_byte(out, close_byte); +} + +fn emit_compact(out: *ByteBuffer, doc: *Json): Status { + ret emit(out, doc, doc.root, 0, 0); +} + +fn emit_pretty(out: *ByteBuffer, doc: *Json, indent: usize): Status { + ret emit(out, doc, doc.root, indent, 0); +} + +// ---- 构造辅助(写 JSON 片段;字符串需由调用方保证已转义)---- + +fn write_int(out: *ByteBuffer, value: i64): Status { + ret append_i64(out, value, 10, false); +} + +fn write_u64(out: *ByteBuffer, value: u64): Status { + ret append_u64(out, value, 10, false); +} + +// ---- 测试辅助 ---- + +fn expect_ok(result: Status) { + match result { + .Ok => {}, + .Err(error) => { @assert(false, "unexpected json error"); }, + } +} + +fn expect_bytes(out: *ByteBuffer, expected: []u8) { + var actual = ByteBuffer.as_slice(out.*); + @assert(actual.len == expected.len, "emitted length"); + var i: usize = 0; + while i < expected.len { + @assert(actual.ptr[i] == expected.ptr[i], "emitted byte"); + i = i + 1; + } +} + +fn expect_parse_error(result: JsonResult, pos: usize) { + match result { + .Ok(doc) => { @assert(false, "expected parse error"); }, + .Err(error) => { + @assert(error.has_position, "positioned error"); + @assert(error.position == pos, "error position"); + }, + } +} + +fn expect_kind(doc: *Json, index: usize, kind: JsonKind) { + var n = node_at(doc, index); + @assert(n != null, "node exists"); + @assert(kind_code(n.kind) == kind_code(kind), "node kind"); +} + +fn expect_text(doc: *Json, index: usize, expected: []u8) { + var got = text_of(doc, index); + var want = ByteView.from_slice(expected); + @assert(ByteView.eq(got, want), "node text"); +} + +fn test_scalars() { + var r = parse(ByteView.from_slice("null")); + match r { + .Ok(doc) => { expect_kind(&doc, root_index(&doc), .Null); drop(&doc); }, + .Err(error) => { @assert(false, "parse null"); }, + } + + r = parse(ByteView.from_slice("true")); + match r { + .Ok(doc) => { expect_kind(&doc, root_index(&doc), .True); drop(&doc); }, + .Err(error) => { @assert(false, "parse true"); }, + } + + r = parse(ByteView.from_slice("false")); + match r { + .Ok(doc) => { expect_kind(&doc, root_index(&doc), .False); drop(&doc); }, + .Err(error) => { @assert(false, "parse false"); }, + } + + r = parse(ByteView.from_slice("-12.5e2")); + match r { + .Ok(doc) => { + var idx = root_index(&doc); + expect_kind(&doc, idx, .Number); + expect_text(&doc, idx, "-12.5e2"); + drop(&doc); + }, + .Err(error) => { @assert(false, "parse number"); }, + } + + r = parse(ByteView.from_slice("\"hi\\n\"")); + match r { + .Ok(doc) => { + var idx = root_index(&doc); + expect_kind(&doc, idx, .String); + expect_text(&doc, idx, "hi\\n"); // 原始切片,未解码 + drop(&doc); + }, + .Err(error) => { @assert(false, "parse string"); }, + } +} + +fn test_containers_and_lookup() { + var text = "{\"a\":1,\"b\":[true,null,\"x\"],\"c\":{\"d\":2}}"; + var r = parse(ByteView.from_slice(text)); + match r { + .Ok(doc) => { + var root = root_index(&doc); + expect_kind(&doc, root, .Object); + + var a = ByteView.from_slice("a"); + match object_find(&doc, root, a) { + .Found(idx) => { + expect_kind(&doc, idx, .Number); + expect_text(&doc, idx, "1"); + }, + .NotFound => { @assert(false, "find a"); }, + } + + var b = ByteView.from_slice("b"); + var bidx: usize = 0; + match object_find(&doc, root, b) { + .Found(idx) => { bidx = idx; }, + .NotFound => { @assert(false, "find b"); }, + } + expect_kind(&doc, bidx, .Array); + @assert(array_count(&doc, bidx) == 3, "array count"); + expect_kind(&doc, array_at(&doc, bidx, 0), .True); + expect_kind(&doc, array_at(&doc, bidx, 1), .Null); + expect_text(&doc, array_at(&doc, bidx, 2), "x"); + + var c = ByteView.from_slice("c"); + var cidx: usize = 0; + match object_find(&doc, root, c) { + .Found(idx) => { cidx = idx; }, + .NotFound => { @assert(false, "find c"); }, + } + var d = ByteView.from_slice("d"); + match object_find(&doc, cidx, d) { + .Found(idx) => { expect_text(&doc, idx, "2"); }, + .NotFound => { @assert(false, "find c.d"); }, + } + + var missing = ByteView.from_slice("zzz"); + match object_find(&doc, root, missing) { + .Found(idx) => { @assert(false, "unexpected find"); }, + .NotFound => {}, + } + + drop(&doc); + }, + .Err(error) => { @assert(false, "parse object"); }, + } +} + +fn test_roundtrip_compact() { + var text = "{\"a\":1,\"b\":[true,null,\"x\\n\"],\"c\":{},\"d\":[],\"e\":-3.5e-2}"; + var r = parse(ByteView.from_slice(text)); + match r { + .Ok(doc) => { + var out = ByteBuffer.new(); + expect_ok(emit_compact(&out, &doc)); + expect_bytes(&out, text); + ByteBuffer.drop(&out); + drop(&doc); + }, + .Err(error) => { @assert(false, "parse for roundtrip"); }, + } +} + +fn test_pretty() { + var text = "{\"a\":1,\"b\":[1,2]}"; + var r = parse(ByteView.from_slice(text)); + match r { + .Ok(doc) => { + var out = ByteBuffer.new(); + expect_ok(emit_pretty(&out, &doc, 2)); + var expected = "{\n \"a\": 1,\n \"b\": [\n 1,\n 2\n ]\n}"; + expect_bytes(&out, expected); + ByteBuffer.drop(&out); + drop(&doc); + }, + .Err(error) => { @assert(false, "parse pretty"); }, + } +} + +fn test_errors() { + expect_parse_error(parse(ByteView.from_slice("")), 0); + expect_parse_error(parse(ByteView.from_slice("\"abc")), 4); + expect_parse_error(parse(ByteView.from_slice("tru")), 3); + expect_parse_error(parse(ByteView.from_slice("[1,]")), 3); + expect_parse_error(parse(ByteView.from_slice("{\"a\":}")), 5); + expect_parse_error(parse(ByteView.from_slice("1 2")), 2); + expect_parse_error(parse(ByteView.from_slice("01")), 1); +} + +fn test_construction_helpers() { + var out = ByteBuffer.new(); + expect_ok(ByteBuffer.append(&out, "{\"id\":")); + expect_ok(write_u64(&out, 18446744073709551615)); + expect_ok(ByteBuffer.append(&out, ",\"n\":")); + expect_ok(write_int(&out, -7)); + expect_ok(ByteBuffer.append(&out, "}")); + expect_bytes(&out, "{\"id\":18446744073709551615,\"n\":-7}"); + ByteBuffer.drop(&out); +} + +fn test_large_array() { + var out = ByteBuffer.new(); + expect_ok(ByteBuffer.append_byte(&out, 91)); // '[' + var i: usize = 0; + while i < 1000 { + if i != 0 { expect_ok(ByteBuffer.append_byte(&out, 44)); } + expect_ok(ByteBuffer.append_byte(&out, 49)); // '1' + i = i + 1; + } + expect_ok(ByteBuffer.append_byte(&out, 93)); // ']' + + var text = ByteBuffer.as_slice(out); + var r = parse(ByteView.from_slice(text)); + match r { + .Ok(doc) => { + expect_kind(&doc, root_index(&doc), .Array); + @assert(array_count(&doc, root_index(&doc)) == 1000, "large array count"); + expect_kind(&doc, array_at(&doc, root_index(&doc), 999), .Number); + drop(&doc); + }, + .Err(error) => { @assert(false, "parse large array"); }, + } + ByteBuffer.drop(&out); +} + +fn main() { + test_scalars(); + test_containers_and_lookup(); + test_roundtrip_compact(); + test_pretty(); + test_errors(); + test_construction_helpers(); + test_large_array(); +} diff --git a/stage2/utils/map.spl b/stage2/utils/map.spl new file mode 100644 index 0000000..bc98544 --- /dev/null +++ b/stage2/utils/map.spl @@ -0,0 +1,448 @@ +// Expect retcode = 0 +// 开放寻址 + tombstone 哈希表。键和值都按字节复制到表自己的存储。 +// 键类型由 hash_fn / eq_fn 解释:回调收到指向键字节的指针(如 *usize / *ByteView)。 +// 普通赋值只复制句柄(与 Vec 一样),所有权转移必须使用 take。 + +type .{ .Error, .Status, .make } = @import("error.spl"); +type .{ .Vec } = @import("vec.spl"); +type .{ .ByteView } = @import("byte_view.spl"); +type .{ .copy } = @import("../sys/mem.spl"); + +// 槽位状态:0=EMPTY,1=OCCUPIED,2=DELETED(墓碑,仅在扩容重哈希时清除)。 +// 负载因子 70/128。 + +type Map = @self; + +keys: Vec, +vals: Vec, +states: Vec, +size: usize, +key_size: usize, +val_size: usize, +hash_fn: fn(*u8): usize, +eq_fn: fn(*u8, *u8): bool, + +fn blank(key_size: usize, key_align: usize, val_size: usize, val_align: usize, + hash_fn: fn(*u8): usize, eq_fn: fn(*u8, *u8): bool): Map { + ret Map { + .keys = Vec.blank(key_size, key_align), + .vals = Vec.blank(val_size, val_align), + .states = Vec.blank(1, 1), + .size = 0, + .key_size = key_size, + .val_size = val_size, + .hash_fn = hash_fn, + .eq_fn = eq_fn, + }; +} + +fn new(key_size: usize, key_align: usize, val_size: usize, val_align: usize, + hash_fn: fn(*u8): usize, eq_fn: fn(*u8, *u8): bool): Map { + ret blank(key_size, key_align, val_size, val_align, hash_fn, eq_fn); +} + +fn valid(self: Map): bool { + if self.key_size == 0 || self.val_size == 0 { ret false; } + if self.states.len != self.keys.len || self.states.len != self.vals.len { + ret false; + } + if self.size > self.states.len { ret false; } + ret true; +} + +fn size(self: Map): usize { + ret self.size; +} + +fn capacity(self: Map): usize { + ret self.states.cap; +} + +// 迭代支持:遍历 [0, slot_count),slot_state 为 1 时 key_at/val_at 有效。 +fn slot_count(self: Map): usize { + ret self.states.len; +} + +fn slot_state(self: Map, index: usize): u8 { + ret self.states.data[index]; +} + +fn slot_occupied(state: u8): bool { + ret state == 1; +} + +fn key_at(self: *Map, index: usize): *u8 { + ret self.keys.data + index * self.key_size; +} + +fn val_at(self: *Map, index: usize): *u8 { + ret self.vals.data + index * self.val_size; +} + +// 返回占用键的槽位;未找到返回 ~0。容量为 0 时直接未找到。 +fn find_slot(self: *Map, key: *u8): usize { + if self.states.cap == 0 { ret ~(0 as usize); } + var mask = self.states.cap - 1; + var i = self.hash_fn(key) & mask; + while self.states.data[i] != 0 { + if self.states.data[i] == 1 && self.eq_fn(self.keys.data + i * self.key_size, key) { + ret i; + } + i = (i + 1) & mask; + } + ret ~(0 as usize); +} + +// 要求容量非零且键不存在,直接写入第一个空槽(重哈希专用)。 +fn place(self: *Map, key: *u8, val: *u8) { + var mask = self.states.cap - 1; + var i = self.hash_fn(key) & mask; + while self.states.data[i] != 0 { + i = (i + 1) & mask; + } + copy((self.keys.data + i * self.key_size) as *any, key as *any, self.key_size); + copy((self.vals.data + i * self.val_size) as *any, val as *any, self.val_size); + self.states.data[i] = 1; + self.size = self.size + 1; +} + +// 保证至少 need 个槽位。所有检查在旧表改动前完成,失败保持原表不变。 +fn reserve(self: *Map, need: usize): Status { + if !valid(self.*) { ret .Err(make(.InvalidArgument)); } + if need <= self.states.cap { ret .Ok; } + + var new_cap = self.states.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 = self.*; + fresh.keys = Vec.blank(self.key_size, self.keys.elem_align); + fresh.vals = Vec.blank(self.val_size, self.vals.elem_align); + fresh.states = Vec.blank(1, 1); + fresh.size = 0; + + match Vec.reserve(&fresh.keys, new_cap) { + .Ok => {}, + .Err(error) => { drop(&fresh); ret .Err(error); }, + } + match Vec.reserve(&fresh.vals, new_cap) { + .Ok => {}, + .Err(error) => { drop(&fresh); ret .Err(error); }, + } + match Vec.reserve(&fresh.states, new_cap) { + .Ok => {}, + .Err(error) => { drop(&fresh); ret .Err(error); }, + } + fresh.keys.len = new_cap; + fresh.vals.len = new_cap; + fresh.states.len = new_cap; + + var i: usize = 0; + while i < new_cap { + fresh.states.data[i] = 0; + i = i + 1; + } + + i = 0; + while i < self.states.len { + if self.states.data[i] == 1 { + place(&fresh, self.keys.data + i * self.key_size, + self.vals.data + i * self.val_size); + } + i = i + 1; + } + + var old_keys = self.keys; + var old_vals = self.vals; + var old_states = self.states; + self.* = fresh; + Vec.drop(&old_keys); + Vec.drop(&old_vals); + Vec.drop(&old_states); + ret .Ok; +} + +// 覆盖已存在的键;否则插入并可能扩容。 +fn put(self: *Map, key: *u8, val: *u8): Status { + if !valid(self.*) || key == null || val == null { + ret .Err(make(.InvalidArgument)); + } + + var existing = find_slot(self, key); + if existing != ~(0 as usize) { + copy((self.vals.data + existing * self.val_size) as *any, val as *any, self.val_size); + ret .Ok; + } + + if self.states.cap == 0 || (self.size + 1) * 128 / self.states.cap >= 70 { + var need = self.states.cap * 2; + if self.states.cap == 0 { need = 8; } + match reserve(self, need) { + .Ok => {}, + .Err(error) => { ret .Err(error); }, + } + } + + place(self, key, val); + ret .Ok; +} + +// 找到键则把值复制到 out 并返回 true;否则 out 不变并返回 false。 +fn get(self: *Map, key: *u8, out: *u8): bool { + if !valid(self.*) || key == null || out == null { ret false; } + var i = find_slot(self, key); + if i == ~(0 as usize) { ret false; } + copy(out as *any, (self.vals.data + i * self.val_size) as *any, self.val_size); + ret true; +} + +fn del(self: *Map, key: *u8): bool { + if !valid(self.*) || key == null { ret false; } + var i = find_slot(self, key); + if i == ~(0 as usize) { ret false; } + self.states.data[i] = 2; + self.size = self.size - 1; + ret true; +} + +// 清空内容但保留容量。墓碑与负载计数一并复位。 +fn clear(self: *Map) { + var i: usize = 0; + while i < self.states.len { + self.states.data[i] = 0; + i = i + 1; + } + self.size = 0; +} + +fn drop(self: *Map) { + Vec.drop(&self.keys); + Vec.drop(&self.vals); + Vec.drop(&self.states); + self.size = 0; +} + +fn take(source: *Map): Map { + var result = source.*; + source.keys = Vec.blank(source.key_size, source.keys.elem_align); + source.vals = Vec.blank(source.val_size, source.vals.elem_align); + source.states = Vec.blank(1, 1); + source.size = 0; + ret result; +} + +// ---- 测试辅助 ---- + +fn hash_usize(key: *u8): usize { + var value = (key as *usize).*; + ret value * 2654435761; +} + +fn eq_usize(a: *u8, b: *u8): bool { + ret (a as *usize).* == (b as *usize).*; +} + +fn expect_ok(result: Status) { + match result { + .Ok => {}, + .Err(error) => { @assert(false, "unexpected Map error"); }, + } +} + +fn expect_invalid(result: Status) { + match result { + .Ok => { @assert(false, "expected Map error"); }, + .Err(error) => { + match error.kind { + .InvalidArgument => {}, + _ => { @assert(false, "unexpected Map error kind"); }, + } + }, + } +} + +fn count_occupied(m: *Map): usize { + var n: usize = 0; + var i: usize = 0; + while i < slot_count(m.*) { + if slot_occupied(slot_state(m.*, i)) { n = n + 1; } + i = i + 1; + } + ret n; +} + +fn make_int_map(): Map { + ret new(@sizeof(usize), @alignof(usize), @sizeof(i32), @alignof(i32), hash_usize, eq_usize); +} + +fn test_put_get_update() { + var m = make_int_map(); + @assert(size(m) == 0 && capacity(m) == 0, "empty map"); + @assert(valid(m)); + + var key: usize = 0; + var value: i32 = 0; + var out: i32 = 0; + + expect_invalid(put(&m, null, &value as *u8)); + expect_invalid(put(&m, &key as *u8, null)); + @assert(size(m) == 0, "failed put preserves map"); + + key = 7; + value = 70; + expect_ok(put(&m, &key as *u8, &value as *u8)); + @assert(size(m) == 1 && valid(m)); + + out = -1; + @assert(get(&m, &key as *u8, &out as *u8) && out == 70, "insert then get"); + + value = 71; + expect_ok(put(&m, &key as *u8, &value as *u8)); + @assert(size(m) == 1, "update does not grow size"); + out = -1; + @assert(get(&m, &key as *u8, &out as *u8) && out == 71, "update overwrites"); + + key = 8; + out = 123; + @assert(!get(&m, &key as *u8, &out as *u8) && out == 123, "missing key preserves out"); + + drop(&m); + @assert(size(m) == 0 && capacity(m) == 0, "drop resets map"); +} + +fn test_growth_and_delete() { + var m = make_int_map(); + var i: i32 = 0; + while i < 1000 { + var key: usize = i as usize; + var value: i32 = i * 3 - 7; + expect_ok(put(&m, &key as *u8, &value as *u8)); + i = i + 1; + } + @assert(size(m) == 1000); + @assert(count_occupied(&m) == 1000, "all slots accounted"); + + i = 0; + while i < 1000 { + var key: usize = i as usize; + var out: i32 = 0; + @assert(get(&m, &key as *u8, &out as *u8), "get after growth"); + @assert(out == i * 3 - 7, "value after growth"); + i = i + 1; + } + + i = 0; + while i < 500 { + var key: usize = i as usize; + @assert(del(&m, &key as *u8)); + i = i + 1; + } + @assert(size(m) == 500 && count_occupied(&m) == 500, "deleted slots not occupied"); + + i = 0; + while i < 500 { + var key: usize = i as usize; + var out: i32 = 0; + @assert(!get(&m, &key as *u8, &out as *u8), "deleted key missing"); + i = i + 1; + } + + // 墓碑槽位可被再次插入复用,且不会重复计数。 + var key: usize = 12345; + var value: i32 = 99; + expect_ok(put(&m, &key as *u8, &value as *u8)); + @assert(size(m) == 501); + var out: i32 = 0; + @assert(get(&m, &key as *u8, &out as *u8) && out == 99, "reuse tombstone"); + + @assert(del(&m, &key as *u8), "delete key"); + @assert(size(m) == 500); + @assert(!get(&m, &key as *u8, &out as *u8)); + + clear(&m); + @assert(size(m) == 0 && count_occupied(&m) == 0, "clear empties"); + + drop(&m); +} + +fn test_take_and_self_put() { + var m = make_int_map(); + var key: usize = 5; + var value: i32 = 55; + expect_ok(put(&m, &key as *u8, &value as *u8)); + + var moved = take(&m); + @assert(size(m) == 0 && capacity(m) == 0, "take leaves source empty"); + var out: i32 = 0; + @assert(get(&moved, &key as *u8, &out as *u8) && out == 55, "moved keeps content"); + + // 值指针可以指向表内已有的值(按字节移动,允许重叠)。 + var alias: i32 = 0; + @assert(get(&moved, &key as *u8, &alias as *u8)); + expect_ok(put(&moved, &key as *u8, &alias as *u8)); + @assert(get(&moved, &key as *u8, &out as *u8) && out == 55, "self value put"); + + drop(&moved); +} + +fn hash_view(key: *u8): usize { + var view = (key as *ByteView).*; + var h: usize = 14695981039346656037; + var i: usize = 0; + while i < view.len { + h = (h ^ (view.ptr[i] as usize)) * 1099511628211; + i = i + 1; + } + ret h; +} + +fn eq_view(a: *u8, b: *u8): bool { + ret ByteView.eq((a as *ByteView).*, (b as *ByteView).*); +} + +fn test_byte_view_keys() { + var m = new(@sizeof(ByteView), @alignof(ByteView), @sizeof(i32), @alignof(i32), + hash_view, eq_view); + + var alpha = ByteView.from_slice("alpha"); + var beta = ByteView.from_slice("beta"); + var gamma = ByteView.from_slice("gamma"); + var one: i32 = 1; + var two: i32 = 2; + var three: i32 = 3; + + expect_ok(put(&m, &alpha as *u8, &one as *u8)); + expect_ok(put(&m, &beta as *u8, &two as *u8)); + expect_ok(put(&m, &gamma as *u8, &three as *u8)); + @assert(size(m) == 3); + + var out: i32 = 0; + @assert(get(&m, &alpha as *u8, &out as *u8) && out == 1, "alpha"); + @assert(get(&m, &beta as *u8, &out as *u8) && out == 2, "beta"); + @assert(get(&m, &gamma as *u8, &out as *u8) && out == 3, "gamma"); + + // 内容相同的不同 ByteView 视为同一个键。 + var alpha2 = ByteView.from_slice("alpha"); + out = 0; + @assert(get(&m, &alpha2 as *u8, &out as *u8) && out == 1, "equal content key"); + @assert(size(m) == 3, "equal content does not insert"); + + @assert(del(&m, &beta as *u8), "delete beta"); + @assert(size(m) == 2); + @assert(!get(&m, &beta as *u8, &out as *u8)); + + drop(&m); +} + +fn main() { + test_put_get_update(); + test_growth_and_delete(); + test_take_and_self_put(); + test_byte_view_keys(); +}