339 lines
10 KiB
Plaintext
339 lines
10 KiB
Plaintext
// Expect retcode = 0
|
||
|
||
// 类型擦除的动态数组。元素按值复制,数组只释放自身存储,不释放元素内部资源。
|
||
// Vec 拥有 raw 指向的分配;普通赋值会复制所有权,所有权转移必须使用 take。
|
||
|
||
type .{ .Error, .Kind, .Status } = @import("error.spl");
|
||
type .{ .SizeResult, .checked_add, .checked_mul, .grow_capacity, .move } = @import("memory.spl");
|
||
type .{ .alloc, .copy, .free } = @import("../sys/mem.spl");
|
||
|
||
#extern(vm) fn vm_vec_valid(v: *any): i32;
|
||
#extern(vm) fn vm_vec_push_raw(v: *any, elem: *any): i32;
|
||
#extern(vm) fn vm_vec_push(v: *any, elem: *any): i32;
|
||
#extern(vm) fn vm_vec_get(v: *any, index: usize, out: *any): i32;
|
||
#extern(vm) fn vm_vec_allocate(out: *any, capacity: usize, elem_size: usize, elem_align: usize): i32;
|
||
#extern(vm) fn vm_vec_reserve_raw(v: *any, need: usize): i32;
|
||
#extern(vm) fn vm_vec_append(v: *any, elems: *any, count: usize): i32;
|
||
#extern(vm) fn vm_vec_set(v: *any, index: usize, elem: *any): i32;
|
||
#extern(vm) fn vm_vec_pop(v: *any, out: *any): i32;
|
||
#extern(vm) fn vm_vec_clone(out: *any, src: *any): i32;
|
||
#extern(vm) fn vm_vec_drop(v: *any): void;
|
||
|
||
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));
|
||
}
|
||
|
||
// 按指针接收,避免每次调用复制整个 Vec;内联对齐检查,省一次函数调用。
|
||
fn valid(self: *Vec): bool {
|
||
ret vm_vec_valid(self as *any) != 0;
|
||
}
|
||
|
||
fn status_error(kind: Kind): Status {
|
||
ret .Err(Error.make(kind));
|
||
}
|
||
|
||
// native 错误码 -> Error.Kind(0 ok / 1 InvalidArgument / 2 OutOfRange / 3 Overflow / 4 InvalidAlignment)
|
||
fn kind_from_code(code: i32): Kind {
|
||
if code == 2 { ret .OutOfRange; }
|
||
if code == 3 { ret .Overflow; }
|
||
if code == 4 { ret .InvalidAlignment; }
|
||
ret .InvalidArgument;
|
||
}
|
||
|
||
fn status_from_code(code: i32): Status {
|
||
if code == 0 { ret .Ok; }
|
||
ret status_error(kind_from_code(code));
|
||
}
|
||
|
||
// 计算元素区字节数。错误时不修改任何 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 {
|
||
var v: Vec = undefined;
|
||
var rc = vm_vec_allocate(&v as *any, capacity, elem_size, elem_align);
|
||
if rc == 0 { ret .Ok(v); }
|
||
ret .Err(Error.make(kind_from_code(rc)));
|
||
}
|
||
|
||
// 与 reserve 相同,但假定调用方已校验 self(内部热路径用,省去重复 valid)。
|
||
fn reserve_raw(self: *Vec, need: usize): Status {
|
||
ret status_from_code(vm_vec_reserve_raw(self as *any, need));
|
||
}
|
||
|
||
// 保证至少可容纳 need 个元素(外部入口,含校验)。
|
||
fn reserve(self: *Vec, need: usize): Status {
|
||
if !valid(self) { ret status_error(.InvalidArgument); }
|
||
ret reserve_raw(self, need);
|
||
}
|
||
|
||
// 预分配恰好 capacity 个元素(等价“with_capacity”)。
|
||
fn with_capacity(elem_size: usize, elem_align: usize, capacity: usize): VecResult {
|
||
ret allocate(capacity, elem_size, elem_align);
|
||
}
|
||
|
||
fn get(self: *Vec, index: usize): PtrResult {
|
||
var out: *u8 = null;
|
||
var rc = vm_vec_get(self as *any, index, &out as *any);
|
||
if rc == 0 { ret .Ok(out); }
|
||
if rc == 2 { ret .Err(Error.make(.OutOfRange)); }
|
||
ret .Err(Error.make(.InvalidArgument));
|
||
}
|
||
|
||
fn set(self: *Vec, index: usize, elem: *u8): Status {
|
||
ret status_from_code(vm_vec_set(self as *any, index, elem as *any));
|
||
}
|
||
|
||
// 追加 count 个连续元素。elems 可以指向 self 当前已初始化的元素区。
|
||
fn append(self: *Vec, elems: *u8, count: usize): Status {
|
||
ret status_from_code(vm_vec_append(self as *any, elems as *any, count));
|
||
}
|
||
|
||
fn push(self: *Vec, elem: *u8): Status {
|
||
ret status_from_code(vm_vec_push(self as *any, elem as *any));
|
||
}
|
||
|
||
// 热路径:假定 self 有效且已有容量时直接写入;容量不足才回退到 append。
|
||
fn push_raw(self: *Vec, elem: *u8): bool {
|
||
if elem == null { ret false; }
|
||
if vm_vec_push_raw(self as *any, elem as *any) != 0 { ret true; }
|
||
match append(self, elem, 1) {
|
||
.Ok => { ret true; },
|
||
.Err(error) => { ret false; },
|
||
}
|
||
ret false;
|
||
}
|
||
|
||
// 将末元素复制到 out,然后缩短数组。out 可以与元素区重叠。
|
||
fn pop(self: *Vec, out: *u8): Status {
|
||
ret status_from_code(vm_vec_pop(self as *any, out as *any));
|
||
}
|
||
|
||
fn clear(self: *Vec) {
|
||
self.len = 0;
|
||
}
|
||
|
||
// 成功时先释放 out 原有存储再提交副本;失败保持 out 不变。
|
||
// out 与 src 指向同一个 Vec 也允许。
|
||
fn clone(out: *Vec, src: Vec): Status {
|
||
ret status_from_code(vm_vec_clone(out as *any, &src as *any));
|
||
}
|
||
|
||
// 显式转移所有权,返回值必须由接收方最终 drop。
|
||
fn take(source: *Vec): Vec {
|
||
var result = source.*;
|
||
source.* = blank(result.elem_size, result.elem_align);
|
||
ret result;
|
||
}
|
||
|
||
fn drop(self: *Vec) {
|
||
vm_vec_drop(self as *any);
|
||
}
|
||
|
||
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));
|
||
defer { drop(&v); }
|
||
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");
|
||
}
|
||
|
||
fn test_self_reference() {
|
||
var v = make_vec(@sizeof(i32), @alignof(i32));
|
||
defer { drop(&v); }
|
||
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);
|
||
}
|
||
|
||
fn test_clone_take_and_failures() {
|
||
var source = make_vec(@sizeof(i32), @alignof(i32));
|
||
defer { drop(&source); }
|
||
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));
|
||
defer { drop(©); }
|
||
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(©);
|
||
defer { drop(&moved); }
|
||
@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");
|
||
}
|
||
|
||
fn main() {
|
||
test_construction_and_alignment();
|
||
test_growth_and_access();
|
||
test_self_reference();
|
||
test_clone_take_and_failures();
|
||
}
|