stage2 更多的工具库
This commit is contained in:
1 parent
4141e7d574
commit
16a1f030d4
15 files changed
+1914
-109
No files matched your search
@@ -0,0 +1,238 @@
|
||||
// Expect retcode = 0
|
||||
// 拥有型字节字符串:在 ByteBuffer 之上提供字符串常用操作。
|
||||
// 普通赋值复制句柄(共享存储),所有权转移用 take;失败不产生部分状态。
|
||||
|
||||
type .{ .Error, .Status, .make } = @import("error.spl");
|
||||
type .{ .ByteView, .FindResult } = @import("byte_view.spl");
|
||||
type .{ .ByteBuffer } = @import("byte_buffer.spl");
|
||||
|
||||
type Str = @self;
|
||||
|
||||
storage: ByteBuffer,
|
||||
|
||||
type StrResult = enum {
|
||||
Ok: Str,
|
||||
Err: Error,
|
||||
}
|
||||
|
||||
fn new(): Str {
|
||||
ret Str { .storage = ByteBuffer.new() };
|
||||
}
|
||||
|
||||
// 复制 view 指向的字节。
|
||||
fn from_view(view: ByteView): StrResult {
|
||||
var s = new();
|
||||
match append_view(&s, view) {
|
||||
.Ok => {},
|
||||
.Err(error) => { drop(&s); ret .Err(error); },
|
||||
}
|
||||
ret .Ok(s);
|
||||
}
|
||||
|
||||
fn from_slice(bytes: []u8): StrResult {
|
||||
ret from_view(ByteView.from_slice(bytes));
|
||||
}
|
||||
|
||||
fn size(self: Str): usize {
|
||||
ret ByteBuffer.size(self.storage);
|
||||
}
|
||||
|
||||
fn is_empty(self: Str): bool {
|
||||
ret ByteBuffer.is_empty(self.storage);
|
||||
}
|
||||
|
||||
fn as_view(self: *Str): ByteView {
|
||||
ret ByteView.from_slice(ByteBuffer.as_slice(self.storage));
|
||||
}
|
||||
|
||||
fn as_slice(self: *Str): []u8 {
|
||||
ret ByteBuffer.as_slice(self.storage);
|
||||
}
|
||||
|
||||
fn reserve(self: *Str, capacity: usize): Status {
|
||||
ret ByteBuffer.reserve(&self.storage, capacity);
|
||||
}
|
||||
|
||||
fn append_view(self: *Str, view: ByteView): Status {
|
||||
ret ByteBuffer.append(&self.storage, ByteView.as_slice(view));
|
||||
}
|
||||
|
||||
fn append(self: *Str, bytes: []u8): Status {
|
||||
ret ByteBuffer.append(&self.storage, bytes);
|
||||
}
|
||||
|
||||
fn append_str(self: *Str, other: Str): Status {
|
||||
var o = other;
|
||||
ret append_view(self, as_view(&o));
|
||||
}
|
||||
|
||||
fn append_byte(self: *Str, byte: u8): Status {
|
||||
ret ByteBuffer.append_byte(&self.storage, byte);
|
||||
}
|
||||
|
||||
fn clear(self: *Str) {
|
||||
ByteBuffer.clear(&self.storage);
|
||||
}
|
||||
|
||||
fn eq(a: Str, b: Str): bool {
|
||||
var x = a;
|
||||
var y = b;
|
||||
ret ByteView.eq(as_view(&x), as_view(&y));
|
||||
}
|
||||
|
||||
fn eq_slice(a: Str, bytes: []u8): bool {
|
||||
var x = a;
|
||||
ret ByteView.eq(as_view(&x), ByteView.from_slice(bytes));
|
||||
}
|
||||
|
||||
fn cmp(a: Str, b: Str): i32 {
|
||||
var x = a;
|
||||
var y = b;
|
||||
ret ByteView.cmp(as_view(&x), as_view(&y));
|
||||
}
|
||||
|
||||
fn starts_with(self: Str, prefix: Str): bool {
|
||||
var x = self;
|
||||
var p = prefix;
|
||||
ret ByteView.starts_with(as_view(&x), as_view(&p));
|
||||
}
|
||||
|
||||
fn ends_with(self: Str, suffix: Str): bool {
|
||||
var x = self;
|
||||
var s = suffix;
|
||||
ret ByteView.ends_with(as_view(&x), as_view(&s));
|
||||
}
|
||||
|
||||
fn find(self: Str, needle: Str): FindResult {
|
||||
var x = self;
|
||||
var n = needle;
|
||||
ret ByteView.find(as_view(&x), as_view(&n));
|
||||
}
|
||||
|
||||
// 深复制;允许 out 与 source 为同一对象。
|
||||
fn clone(out: *Str, source: Str): Status {
|
||||
ret ByteBuffer.clone(&out.storage, source.storage);
|
||||
}
|
||||
|
||||
fn take(source: *Str): Str {
|
||||
ret Str { .storage = ByteBuffer.take(&source.storage) };
|
||||
}
|
||||
|
||||
fn drop(self: *Str) {
|
||||
ByteBuffer.drop(&self.storage);
|
||||
}
|
||||
|
||||
// ---- 测试辅助 ----
|
||||
|
||||
fn expect_ok(result: Status) {
|
||||
match result {
|
||||
.Ok => {},
|
||||
.Err(error) => { @assert(false, "unexpected str error"); },
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_text(s: *Str, expected: []u8) {
|
||||
var view = as_view(s);
|
||||
@assert(ByteView.eq(view, ByteView.from_slice(expected)), "str text");
|
||||
}
|
||||
|
||||
fn test_construction() {
|
||||
var s = new();
|
||||
@assert(is_empty(s) && size(s) == 0, "empty new");
|
||||
expect_ok(append(&s, "hello"));
|
||||
expect_ok(append_byte(&s, ' '));
|
||||
expect_ok(append(&s, "world"));
|
||||
expect_text(&s, "hello world");
|
||||
@assert(size(s) == 11, "size");
|
||||
|
||||
var r = from_slice("copy");
|
||||
match r {
|
||||
.Ok(copy) => {
|
||||
expect_text(©, "copy");
|
||||
@assert(eq(copy, copy), "self eq");
|
||||
drop(©);
|
||||
},
|
||||
.Err(error) => { @assert(false, "from_slice"); },
|
||||
}
|
||||
drop(&s);
|
||||
}
|
||||
|
||||
fn test_compare_and_search() {
|
||||
var hello_r = from_slice("hello");
|
||||
var hello: Str = new();
|
||||
match hello_r {
|
||||
.Ok(v) => { hello = v; },
|
||||
.Err(error) => { @assert(false, "hello"); },
|
||||
}
|
||||
var help_r = from_slice("help");
|
||||
var help: Str = new();
|
||||
match help_r {
|
||||
.Ok(v) => { help = v; },
|
||||
.Err(error) => { @assert(false, "help"); },
|
||||
}
|
||||
|
||||
@assert(eq(hello, hello), "eq");
|
||||
@assert(!eq(hello, help), "ne");
|
||||
@assert(eq_slice(hello, "hello"), "eq_slice");
|
||||
@assert(cmp(hello, help) < 0, "cmp lt");
|
||||
@assert(cmp(help, hello) > 0, "cmp gt");
|
||||
|
||||
@assert(starts_with(hello, hello), "starts_with");
|
||||
@assert(ends_with(hello, hello), "ends_with");
|
||||
var pre_r = from_slice("he");
|
||||
var pre: Str = new();
|
||||
match pre_r { .Ok(v) => { pre = v; }, .Err(e) => { @assert(false, "pre"); } }
|
||||
@assert(starts_with(hello, pre), "starts he");
|
||||
@assert(!ends_with(hello, pre), "!ends he");
|
||||
|
||||
var needle_r = from_slice("ll");
|
||||
var needle: Str = new();
|
||||
match needle_r { .Ok(v) => { needle = v; }, .Err(e) => { @assert(false, "needle"); } }
|
||||
match find(hello, needle) {
|
||||
.Found(index) => { @assert(index == 2, "find ll"); },
|
||||
.NotFound => { @assert(false, "ll not found"); },
|
||||
}
|
||||
match find(hello, pre) {
|
||||
.Found(index) => { @assert(index == 0, "find he"); },
|
||||
.NotFound => { @assert(false, "he not found"); },
|
||||
}
|
||||
|
||||
drop(&needle);
|
||||
drop(&pre);
|
||||
drop(&help);
|
||||
drop(&hello);
|
||||
}
|
||||
|
||||
fn test_clone_take_and_failures() {
|
||||
var source = new();
|
||||
expect_ok(append(&source, "owned"));
|
||||
|
||||
var copy = new();
|
||||
expect_ok(clone(©, source));
|
||||
expect_text(©, "owned");
|
||||
expect_ok(append_byte(&source, '!')); // '!'
|
||||
expect_text(©, "owned"); // 独立
|
||||
|
||||
var before = ByteBuffer.as_slice(copy.storage).ptr;
|
||||
expect_ok(clone(©, copy));
|
||||
@assert(ByteBuffer.as_slice(copy.storage).ptr != before, "self clone replaces allocation");
|
||||
expect_text(©, "owned");
|
||||
|
||||
var moved = take(©);
|
||||
@assert(ByteBuffer.size(copy.storage) == 0 && ByteBuffer.capacity(copy.storage) == 0,
|
||||
"take empties source");
|
||||
expect_text(&moved, "owned");
|
||||
|
||||
clear(&moved);
|
||||
@assert(is_empty(moved) && ByteBuffer.capacity(moved.storage) != 0, "clear keeps capacity");
|
||||
|
||||
drop(&moved);
|
||||
drop(©);
|
||||
drop(&source);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
test_construction();
|
||||
test_compare_and_search();
|
||||
test_clone_take_and_failures();
|
||||
}
|
||||
Reference in new issue
Block a user