Files

339 lines
10 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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(&copy); }
expect_ok(clone(&copy, source));
expect_i32(&copy, 0, 1);
one = 99;
expect_ok(set(&source, 0, &one as *u8));
expect_i32(&copy, 0, 1);
// out 与 src 为同一对象仍产生内容相同的新分配。
var old_data = copy.data;
expect_ok(clone(&copy, copy));
@assert(copy.data != old_data, "self clone replaces allocation");
expect_i32(&copy, 1, 2);
var moved = take(&copy);
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();
}