export StrView, * use Cnile use Iter (IntoIter(*), map, sum, drop, head) use Slice (Slice, subslice) use StrInst as Str Str(*), const-str-len use Alloc (Allocator) use Error (or-fail) use List ### Iter & Str in Str.kkc fn from-const-str (s ConstStr) len = const-str-len(s) return StrView { contents: Slice { ptr: s Mem.cast(), count: len } } # (this one also adds a nullchar!) fn from-str (s Str, al) l =& List.mk(al) buf =& Mem.uninit-buf() for c in s chars() l List.add-all(c Char.as-u8-slice(buf)) l List.add(Char.nullchar() Char.char-u8()) return StrView { contents: l& List.to-slice() } fn from-ascii-slice (sl Slice Cnile.AsciiChar): StrView { contents: sl Slice.cast-slice() } fn from-bytes (sl Slice U8): StrView { contents: sl } # watch out, if it's a dynamic char, it won't work. how do I solve this? # fn from-char (char Char): StrView { contents: Slice { ptr: char.ptr, count: char.num-bytes } } # special clone, where StrView is compatible with C-style ConstStr fn clone-0(s StrView, al Allocator) -> StrView cnt = s.contents.count nus = al Alloc.allocate(cnt + 1) s.contents Slice.copy-to(nus) nus Slice.set(cnt, '\0' Char.char-u8()) return StrView { contents: Slice { ptr: nus.ptr, count: cnt } } # DANGEROUS! use it only after clone-0() or with constant strings. fn as-const-str (s StrView) -> ConstStr: s.contents.ptr Mem.cast() fn clone(s StrView, al Allocator): clone-0(s, al) fn free (s StrView, al Allocator) al Alloc.free({ ptr: s.contents.ptr, count: 0 }) fn substr (s StrView, from Size, to Size) from-bs = 0 for _ in 0 Iter.to (Mem.size-i32(from) - 1) if from-bs >= s.contents.count break from-bs <= from-bs + Char.next-char(s.contents.ptr Mem.offset-ptr'(from-bs)) to-bs = from-bs for _ in 0 Iter.to (Mem.size-i32(to) - Mem.size-i32(from) - 1) if to-bs >= s.contents.count break to-bs <= to-bs + Char.next-char(s.contents.ptr Mem.offset-ptr'(to-bs)) return StrView { contents: s.contents subslice(from-bs, to-bs) } fn byte-substr (s StrView, from Size, to Size): StrView { contents: s.contents Slice.subslice(from, to) } fn byte-substr-from (s StrView, from Size): StrView { contents: s.contents Slice.from(from) } fn num-bytes (self StrView): self.contents.count fn nth-char (self StrView, i Size): self chars() drop(i) head() or-fail('(StrView.nth-char) StrView does not have enough chars (wanted \(i)-th char)') fn char-at-offset (self StrView, i Size): StrView { contents: self.contents Slice.from(i) } chars() head() or-fail('(StrView.char-at-offset) off too great for this StrView (\(i))') fn take-while (self StrView, fun Char -> Bool) -> StrView bi = self chars() Iter.take-while(fun) map(fn c: c.num-bytes) sum() return self byte-substr(0, bi) fn null (sv StrView): sv num-bytes() == 0 fn drop-while (sv StrView, fun Char -> Bool) -> StrView from = 0 for c in sv chars() if not fun(c) break from <= from + c.num-bytes return sv byte-substr-from(from) #### Instances inst Eq StrView eq (l _, r _): l.contents == r.contents #### Iterators SplitIter og StrView last Size fun Char -> Bool fn split-by-filter (s StrView, fun Char -> Bool): SplitIter { og: s, last: 0, fun } fn split-by-each (s StrView, c Char): split-by-filter(s, fn cc: cc == c) inst IntoIter SplitIter into-iter (self SplitIter): self inst Iter SplitIter next (self Ptr SplitIter) -> Maybe StrView if self&.last > self&.og.contents.count return None # iterator is left after last encountered newline i = self&.last while i < self&.og.contents.count ptr = self&.og.contents Slice.get-ptr(i) c = ptr Char.scan-from-mem() if self&.fun(c) break i <= i + ptr Char.next-char() line = self&.og byte-substr(self&.last, i) i <= i + 1 # skip past the newline # modify the state self <&.last= i return Just(line) ######## LineIter og StrView last Size fn lines (s StrView): LineIter { og: s, last: 0 } inst IntoIter LineIter into-iter (self LineIter): self inst Iter LineIter next (self Ptr LineIter) -> Maybe StrView # should consume the last trailing newline if self&.last >= self&.og.contents.count return None # iterator is left after last encountered newline i = self&.last while i < self&.og.contents.count and self&.og.contents Slice.get(i) /= '\n' Char.char-u8() i <= i + 1 line = self&.og byte-substr(self&.last, i) # NOTE: we can do byte substr, since we're substr-ing to the nearest newline. this is faster, because we don't have to "re-walk" if i < self&.og.contents.count # when i < slice.count, then we encountered a newline. i <= i + 1 # skip past the newline # modify the state self <&.last= i return Just(line)