Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Data.String

String operations, byte-oriented and ASCII-accurate.

Built over the primitive UTF-8 string operations. Base includes this module.

Two indexing families exist and they do not cost the same. char_at, substring, and str_len are counted in codepoints, so each call walks the string’s UTF-8 encoding from the start and costs time proportional to the length. A scanner that advances an index one position at a time through them pays that walk on every step and is quadratic in its input, which is invisible on a line and ruinous on a file. byte_at and byte_len are constant-time raw-byte access and are the first choice for a scanner, a tokenizer, or a hash. Reach for the codepoint family only when the answer itself must be counted in characters.

In the bounds below, n and m are byte lengths and c is a codepoint count. Builder bounds assume the accumulator is threaded linearly.

Functions and Values

str_join

str_join : (String, List(String)) -> String

Join a list of strings, placing sep between adjacent elements. Time complexity: O(kN) in the worst case for k strings and N output bytes, because the immutable suffix is copied repeatedly.

str_join(", ", ["a", "b", "c"])
a, b, c

str_repeat

str_repeat : (String, Int) -> String

s repeated n times (the empty string when n <= 0). Time complexity: O(n^2 * |s|), because each immutable suffix is copied again.

str_repeat("ab", 3)
ababab

pad_left

pad_left : (String, Int) -> String

Right-align s to width w by prepending spaces (unchanged if already wider). Time complexity: O(|s| + p^2), where p is the padding width.

pad_left("42", 5)
   42

pad_right

pad_right : (String, Int) -> String

Left-align s to width w by appending spaces (unchanged if already wider). Time complexity: O(|s| + p^2), where p is the padding width.

pad_right("42", 5)
42

lines_of

lines_of : (List(String)) -> String

Join a list of strings with newlines between them. Time complexity: the same O(kN) worst-case bound as str_join.

lines_of(["one", "two"])
one
two

occurs_at

occurs_at : (String, String, Int, Int) -> Bool

Helper for the substring queries: whether needle sits at byte offset j in s, comparing from position k. Time complexity: O(m - k) in the worst case, where m is needle’s byte length.

starts_with

starts_with : (String, String) -> Bool

True when s begins with prefix. Time complexity: O(min(n, m)) in the worst case.

starts_with("foo", "foobar")
true

ends_with

ends_with : (String, String) -> Bool

True when s ends with suffix. Time complexity: O(m) in the worst case.

ends_with("bar", "foobar")
true

index_of_go

index_of_go : (String, String, Int) -> Int

Helper for index_of: search for needle in s from byte offset j. Time complexity: O((n - j)m) in the worst case.

index_of

index_of : (String, String) -> Int

The byte offset of the first occurrence of needle in s, or -1 if absent. Time complexity: O(nm) in the worst case.

index_of("bar", "foobar")
3

contains

contains : (String, String) -> Bool

True when needle occurs anywhere in s. Time complexity: O(nm) in the worst case.

contains("oob", "foobar")
true

map_case

map_case : (String, Int, Buf, Bool) -> Buf

Helper for to_upper/to_lower: fold ASCII case mapping over s into a byte buffer (up selects upper- vs lower-casing). Time complexity: O(n - i).

to_upper

to_upper : (String) -> String

ASCII upper-case of s (non-letters unchanged). Time complexity: O(n).

to_upper("Hello")
HELLO

to_lower

to_lower : (String) -> String

ASCII lower-case of s (non-letters unchanged). Time complexity: O(n).

to_lower("Hello")
hello

ltrim_idx

ltrim_idx : (String, Int) -> Int

Helper for trim: the first non-whitespace byte index at or after i. Time complexity: O(n - i).

rtrim_idx

rtrim_idx : (String, Int) -> Int

Helper for trim: the index just past the last non-whitespace byte before i. Time complexity: O(i).

slice_bytes

slice_bytes : (String, Int, Int, Buf) -> Buf

Helper for trim: collect the bytes of s in [lo, hi) into buf. Time complexity: O(hi - lo).

str_slice

str_slice : (String, Int, Int) -> String

The bytes of s in [lo, hi), clamped to the string’s bounds.

The byte-indexed counterpart of substring: both endpoints are byte offsets and reaching one is constant time, so a scanner that slices as it advances stays linear where the codepoint form is quadratic. The endpoints must fall on character boundaries, which they do when they come from comparisons against ASCII bytes or from a span the compiler emitted; a window that splits a character is repaired rather than rejected, so the result is always a well-formed String.

The span shares the parent’s bytes rather than copying them, and the parent stays alive for as long as the span does, so slicing costs the same whether the string is three bytes or three megabytes. Time complexity: O(1).

str_slice("foobar", 3, 6)
bar

trim

trim : (String) -> String

Strip leading and trailing ASCII whitespace. Time complexity: O(n); the returned slice itself is O(1).

trim("  hi  ")
hi

index_of_from

index_of_from : (Int, String, Int) -> Int

The index of character c in s at or after position i, or -1 if absent.

The codepoint index is the answer, so the walk that finds it is the walk this returns a position into. Each char_at starts at the beginning, however, so the repeated walks are quadratic on an ASCII string. index_of is the byte-offset counterpart. Time complexity: O(nc), worst-case O(n^2), for c codepoints examined.

split_from

split_from : (Int, String, Int) -> List(String)

Helper for split: split s on c, starting from position i.

Splits at a character, so it addresses the string the way index_of_from answers. A caller splitting a large document wants byte offsets from index_of and slices from str_slice. Time complexity: O(nc), worst-case O(n^2), including the codepoint-indexed search and substrings.

split

split : (Int, String) -> List(String)

Split s into the pieces between each occurrence of character c. Time complexity: O(nc), worst-case O(n^2).

split(char_at(",", 0), "a,b,c")
[a, b, c]

str_of_char

str_of_char : (Char) -> String

The single-character string containing c. Time complexity: O(1); a Unicode scalar encodes to at most four bytes.

str_of_char(chr(65))
A

chars_from

chars_from : (String, Int) -> List(Char)

Helper for chars: the characters of s from position i onward.

Decoding every character is what this is for, and each one is asked for by its character position; a byte-level pass would have to decode the encoding itself to answer the same question. Time complexity: O(nc), worst-case O(n^2), because each char_at restarts at the beginning.

chars

chars : (String) -> List(Char)

The list of characters in s. Time complexity: O(nc), worst-case O(n^2).

chars("hi")
[104, 105]