|
|
|
|
@@ -1,6 +1,4 @@
|
|
|
|
|
// bytes — slice operations over []u8. Mirrors Hare's bytes module
|
|
|
|
|
// (ref/hare/bytes/) for the in-tree subset: search/equality/prefix
|
|
|
|
|
// helpers used by lib/encoding, lib/bufio, lib/memio.
|
|
|
|
|
// Hare port of the in-tree subset; see ref/hare/bytes/.
|
|
|
|
|
//
|
|
|
|
|
// Documented divergences from Hare:
|
|
|
|
|
// - index_slice / rindex_slice use naive O(n·m); Hare specialises
|
|
|
|
|
@@ -29,17 +27,16 @@ import types;
|
|
|
|
|
// (utf8.ww:36). Plain `void` (not `!void`): continuation signal.
|
|
|
|
|
export type done = void;
|
|
|
|
|
|
|
|
|
|
// tokenizer — cursor over an input slice. Layout mirrors
|
|
|
|
|
// ref/hare/bytes/tokenize.ha:6-10. `p` is the cached peek-position;
|
|
|
|
|
// I64_MAX (forward) / I64_MIN (reverse) are the unprimed sentinels.
|
|
|
|
|
// p < 0 also identifies a reverse-direction iterator.
|
|
|
|
|
// Layout mirrors ref/hare/bytes/tokenize.ha:6-10. `p` is the cached
|
|
|
|
|
// peek-position; I64_MAX (forward) / I64_MIN (reverse) are the
|
|
|
|
|
// unprimed sentinels. p < 0 also identifies a reverse-direction
|
|
|
|
|
// iterator.
|
|
|
|
|
export type tokenizer = struct {
|
|
|
|
|
in: []u8,
|
|
|
|
|
delim: []u8,
|
|
|
|
|
p: i64,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// equal — true iff `a` and `b` have the same length and contents.
|
|
|
|
|
// ref/hare/bytes/equal.ha:9.
|
|
|
|
|
export fn equal(a: []u8, b: []u8) bool = {
|
|
|
|
|
if (a.len != b.len) { return false; };
|
|
|
|
|
@@ -51,8 +48,6 @@ export fn equal(a: []u8, b: []u8) bool = {
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// index — first offset of `needle` in `s`. u8 needle scans for the
|
|
|
|
|
// byte; []u8 needle scans for the substring. void if absent.
|
|
|
|
|
// ref/hare/bytes/index.ha:6.
|
|
|
|
|
export fn index(s: []u8, needle: (u8 | []u8)) (i32 | void) = {
|
|
|
|
|
match (needle) {
|
|
|
|
|
@@ -85,9 +80,8 @@ export fn index(s: []u8, needle: (u8 | []u8)) (i32 | void) = {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// rindex — last offset of `needle` in `s`. Empty []u8 needle returns
|
|
|
|
|
// s.len (ref/hare/bytes/index.ha:103 — Hare's loop yields r-0 at i=0).
|
|
|
|
|
// ref/hare/bytes/index.ha:86.
|
|
|
|
|
// Empty []u8 needle returns s.len (ref/hare/bytes/index.ha:103 —
|
|
|
|
|
// Hare's loop yields r-0 at i=0). ref/hare/bytes/index.ha:86.
|
|
|
|
|
export fn rindex(s: []u8, needle: (u8 | []u8)) (i32 | void) = {
|
|
|
|
|
match (needle) {
|
|
|
|
|
case let c: u8 => {
|
|
|
|
|
@@ -118,7 +112,6 @@ export fn rindex(s: []u8, needle: (u8 | []u8)) (i32 | void) = {
|
|
|
|
|
return;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// contains — true iff any of `needles` (byte or sub-slice) appears in `s`.
|
|
|
|
|
// ref/hare/bytes/contains.ha:6.
|
|
|
|
|
export fn contains(s: []u8, needles: (u8 | []u8)...) bool = {
|
|
|
|
|
let i: i32 = 0;
|
|
|
|
|
@@ -142,8 +135,7 @@ export fn contains(s: []u8, needles: (u8 | []u8)...) bool = {
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ltrim — borrowed view of `in` with leading bytes in `trim` stripped.
|
|
|
|
|
// `trim` must be non-empty. ref/hare/bytes/trim.ha:7.
|
|
|
|
|
// ref/hare/bytes/trim.ha:7. Borrowed view of `in` — caller must not free.
|
|
|
|
|
export fn ltrim(in: []u8, trim: u8...) []u8 = {
|
|
|
|
|
assert(trim.len > 0, "bytes.ltrim called with empty trim set");
|
|
|
|
|
let i: i32 = 0;
|
|
|
|
|
@@ -155,8 +147,7 @@ export fn ltrim(in: []u8, trim: u8...) []u8 = {
|
|
|
|
|
return r;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// rtrim — borrowed view of `in` with trailing bytes in `trim` stripped.
|
|
|
|
|
// `trim` must be non-empty. ref/hare/bytes/trim.ha:17. Hare's loop uses
|
|
|
|
|
// ref/hare/bytes/trim.ha:17. Borrowed view of `in`. Hare's loop uses
|
|
|
|
|
// `size` underflow at i==0 to terminate; ww indices are signed i32, so
|
|
|
|
|
// the equivalent termination is spelled `i >= 0` explicitly.
|
|
|
|
|
export fn rtrim(in: []u8, trim: u8...) []u8 = {
|
|
|
|
|
@@ -170,13 +161,11 @@ export fn rtrim(in: []u8, trim: u8...) []u8 = {
|
|
|
|
|
return r;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// trim — borrowed view of `in` with both ends in `trim` stripped.
|
|
|
|
|
// ref/hare/bytes/trim.ha:27.
|
|
|
|
|
// ref/hare/bytes/trim.ha:27. Borrowed view of `in`.
|
|
|
|
|
export fn trim(in: []u8, trim: u8...) []u8 = {
|
|
|
|
|
return ltrim(rtrim(in, trim...), trim...);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// hasprefix — true iff `s` starts with `pre`.
|
|
|
|
|
// ref/hare/bytes/contains.ha:21.
|
|
|
|
|
export fn hasprefix(s: []u8, pre: []u8) bool = {
|
|
|
|
|
if (pre.len > s.len) { return false; };
|
|
|
|
|
@@ -188,7 +177,6 @@ export fn hasprefix(s: []u8, pre: []u8) bool = {
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// hassuffix — true iff `s` ends with `suf`.
|
|
|
|
|
// ref/hare/bytes/contains.ha:35.
|
|
|
|
|
export fn hassuffix(s: []u8, suf: []u8) bool = {
|
|
|
|
|
if (suf.len > s.len) { return false; };
|
|
|
|
|
@@ -201,7 +189,7 @@ export fn hassuffix(s: []u8, suf: []u8) bool = {
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// reverse — in-place reverse of `s`. ref/hare/bytes/reverse.ha:5.
|
|
|
|
|
// ref/hare/bytes/reverse.ha:5.
|
|
|
|
|
export fn reverse(s: []u8) void = {
|
|
|
|
|
let i: i32 = 0;
|
|
|
|
|
let j: i32 = s.len - 1;
|
|
|
|
|
@@ -214,7 +202,7 @@ export fn reverse(s: []u8) void = {
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// zero — set every byte of `s` to 0. ref/hare/bytes/zero.ha:5.
|
|
|
|
|
// ref/hare/bytes/zero.ha:5.
|
|
|
|
|
export fn zero(s: []u8) void = {
|
|
|
|
|
let i: i32 = 0;
|
|
|
|
|
for (i < s.len) {
|
|
|
|
|
@@ -223,8 +211,6 @@ export fn zero(s: []u8) void = {
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// tokenize — iterator yielding tokens from `in` separated by any byte
|
|
|
|
|
// in `delim`. Leading / trailing / adjacent delims yield empty tokens.
|
|
|
|
|
// `delim` is borrowed; caller keeps it valid for the tokenizer's
|
|
|
|
|
// lifetime. ref/hare/bytes/tokenize.ha:22.
|
|
|
|
|
export fn tokenize(in: []u8, delim: u8...) tokenizer = {
|
|
|
|
|
@@ -242,8 +228,7 @@ export fn tokenize(in: []u8, delim: u8...) tokenizer = {
|
|
|
|
|
return t;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// rtokenize — reverse-direction tokenize. First nexttoken yields the
|
|
|
|
|
// last token, last nexttoken yields the first. ref/hare/bytes/tokenize.ha:40.
|
|
|
|
|
// ref/hare/bytes/tokenize.ha:40.
|
|
|
|
|
export fn rtokenize(in: []u8, delim: u8...) tokenizer = {
|
|
|
|
|
assert(delim.len > 0, "bytes.rtokenize called with empty slice");
|
|
|
|
|
assert((in.len: i64) < types.I64_MAX,
|
|
|
|
|
@@ -259,9 +244,8 @@ export fn rtokenize(in: []u8, delim: u8...) tokenizer = {
|
|
|
|
|
return t;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// peektoken — next token without advancing the cursor. Returns done
|
|
|
|
|
// once `s.delim` has been zeroed by a prior past-end nexttoken.
|
|
|
|
|
// ref/hare/bytes/tokenize.ha:91.
|
|
|
|
|
// Returns done once `s.delim` has been zeroed by a prior past-end
|
|
|
|
|
// nexttoken. ref/hare/bytes/tokenize.ha:91.
|
|
|
|
|
export fn peektoken(s: *tokenizer) ([]u8 | done) = {
|
|
|
|
|
if (s.delim.len == 0) {
|
|
|
|
|
let d: done; return d;
|
|
|
|
|
@@ -338,7 +322,6 @@ export fn peektoken(s: *tokenizer) ([]u8 | done) = {
|
|
|
|
|
return r;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// nexttoken — current token, then advance past it and the delim.
|
|
|
|
|
// Once the input is exhausted, returns done and zeros `s.delim` so
|
|
|
|
|
// subsequent peeks short-circuit. ref/hare/bytes/tokenize.ha:59.
|
|
|
|
|
export fn nexttoken(s: *tokenizer) ([]u8 | done) = {
|
|
|
|
|
@@ -380,15 +363,13 @@ export fn nexttoken(s: *tokenizer) ([]u8 | done) = {
|
|
|
|
|
return b;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// remainingtokens — the unconsumed portion of `s.in`. Read-only view.
|
|
|
|
|
// ref/hare/bytes/tokenize.ha:145.
|
|
|
|
|
// ref/hare/bytes/tokenize.ha:145. Read-only borrowed view.
|
|
|
|
|
export fn remainingtokens(s: *tokenizer) []u8 = {
|
|
|
|
|
return s.in;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// splitn — split `in` on any byte in `delim`, returning up to `n`
|
|
|
|
|
// tokens via forward iteration. The trailing slot (when more than
|
|
|
|
|
// `n - 1` tokens exist) holds the unconsumed remainder.
|
|
|
|
|
// The trailing slot (when more than `n - 1` tokens exist) holds the
|
|
|
|
|
// unconsumed remainder.
|
|
|
|
|
//
|
|
|
|
|
// The caller frees the returned slice via
|
|
|
|
|
// `os.free(r.ptr: *void, (r.cap: u64) * 24u64)`. Element bytes are
|
|
|
|
|
@@ -425,9 +406,8 @@ export fn splitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|
|
|
|
return toks;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// rsplitn — reverse-direction counterpart to [[splitn]]: tokens are
|
|
|
|
|
// collected from the end of `in`. The trailing slot holds the
|
|
|
|
|
// unconsumed prefix (everything before the n-th-from-last delim hit).
|
|
|
|
|
// The trailing slot holds the unconsumed prefix (everything before
|
|
|
|
|
// the n-th-from-last delim hit).
|
|
|
|
|
//
|
|
|
|
|
// When the input has fewer than n tokens, the `done` short-circuit
|
|
|
|
|
// returns toks UN-reversed (in last-token-first order). Mirrors Hare
|
|
|
|
|
@@ -486,8 +466,7 @@ export fn rsplitn(in: []u8, delim: []u8, n: i32) [][]u8 = {
|
|
|
|
|
return toks;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// split — full split of `in` on `delim` (no token cap). Mirrors
|
|
|
|
|
// `splitn(in, delim, types::SIZE_MAX)`. ww uses `types.I32_MAX`
|
|
|
|
|
// Mirrors `splitn(in, delim, types::SIZE_MAX)`. ww uses `types.I32_MAX`
|
|
|
|
|
// because the index type is i32 (lib/CLAUDE.md).
|
|
|
|
|
//
|
|
|
|
|
// ref/hare/bytes/tokenize.ha:225.
|
|
|
|
|
@@ -495,10 +474,9 @@ export fn split(in: []u8, delim: []u8) [][]u8 = {
|
|
|
|
|
return splitn(in, delim, types.I32_MAX);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// cut — split `in` along the first instance of `delim`, returning the
|
|
|
|
|
// portion before and the portion after the delimiter as a borrowed
|
|
|
|
|
// tuple. When `delim` is absent, the whole input is the first half and
|
|
|
|
|
// the second is empty. ref/hare/bytes/tokenize.ha:392.
|
|
|
|
|
// When `delim` is absent, the whole input is the first half and the
|
|
|
|
|
// second is empty. Both halves are borrowed views — caller must not
|
|
|
|
|
// free them. ref/hare/bytes/tokenize.ha:392.
|
|
|
|
|
//
|
|
|
|
|
// Delim is spelled (u8 | []u8) to match index/rindex (bytes.ww:57/91);
|
|
|
|
|
// the tagged union is an unordered set, so this is the same type as
|
|
|
|
|
@@ -525,7 +503,6 @@ export fn cut(in: []u8, delim: (u8 | []u8)) ([]u8, []u8) = {
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// rcut — like [[cut]] but splits along the last instance of `delim`.
|
|
|
|
|
// ref/hare/bytes/tokenize.ha:413.
|
|
|
|
|
export fn rcut(in: []u8, delim: (u8 | []u8)) ([]u8, []u8) = {
|
|
|
|
|
let ln: i32 = match (delim) {
|
|
|
|
|
|