Skip to content

std/string

std/string — string receiver methods. Methods are migrated here gradually from the auto-injected magic prelude per docs/PRELUDE-TO-MODULES.md. Files declare import "std/string"; to bring the methods into scope. During the transition the prelude itself does the same import so existing single-file programs keep working unchanged.

pub function (s: string) is_empty(): boolean

s.is_empty() is a length-zero shorthand. The IR’s len fold resolves len(s) to a single i32.load at compile time when s is statically known to be a string, so this typically folds to one i32.eqz at the call site.

pub function (s: string) to_string(): string

(s: string) to_string() — identity on strings. Lets the f-string lexer’s blanket (<expr>).to_string() desugar work uniformly on every interpolant, regardless of whether the interpolant is already a string. Has no runtime cost: the IR’s inliner collapses the call to its operand.

pub function (s: string) repeat(n: i32): string

s.repeat(n)n copies of s concatenated. n <= 0 returns empty; n == 1 returns a fresh independent copy of s (the + lowering allocates). Quadratic on large n because each + allocates a fresh buffer; if a benchmark shows that’s a real bottleneck, drop to a single-pass memcpy via a __memcpy shim.

pub function (s: string) starts_with(prefix: string): boolean
pub function (s: string) ends_with(suffix: string): boolean
pub function (s: string) contains(needle: string): boolean
pub function (s: string) index_of(needle: string): i32
pub function (s: string) last_index_of(needle: string): i32

s.last_index_of(needle) — rightmost occurrence; -1 if absent. Empty needle returns len(s) to match Python rfind / Go LastIndex conventions (the “match at every gap” trick). Walks bytes from the right; O(n*m) worst case like index_of.

pub function (s: string) capitalize(): string

s.capitalize() — uppercase the first byte, leave the rest unchanged. ASCII-only: non-letter first bytes pass through. Different from to_upper (which folds every letter) and from Python’s str.capitalize (which also LOWERCASES the rest — our version intentionally preserves the tail since the lossy fold is rarely what callers want).

pub function (s: string) word_count(): i32

s.word_count() — number of whitespace-separated words. Same notion as fields(): runs of any ASCII whitespace count as a single separator, empty pieces dropped. Empty / all-whitespace input returns 0.

pub function (s: string) escape_html(): string

s.escape_html() — escape the five characters that have special meaning in HTML / XML: & < > ” ’. Produces a string safe to splice into element bodies and attribute values; matches Go html.EscapeString.

pub function (s: string) strip_quotes(): Option[string]

s.strip_quotes() — if s starts and ends with the same quote character (either ” or ’), return Some(inner). Otherwise None. Useful for parsing config files / CLI flags / JSON strings where quotes are optional.

pub function (s: string) leading_count(b: i32): i32

s.leading_count(b) — number of leading bytes equal to b. ” hello”.leading_count(32) == 4 (count of spaces). Useful for indent detection / column alignment.

pub function (s: string) trailing_count(b: i32): i32

s.trailing_count(b) — number of trailing bytes equal to b. “hello “.trailing_count(32) == 3. Symmetric to leading_count.

pub function (s: string) hash_fnv32(): u32

s.hash_fnv32() — FNV-1a 32-bit hash of the byte content. Non-cryptographic; good for hash-table bucket selection and fingerprinting. Constant-time per input length (no data-dependent branches). Reference: http://www.isthe.com/ chongo/tech/comp/fnv/.

Returns u32 because the multiply / xor mixing is defined over unsigned arithmetic.

pub function (s: string) hash_djb2(): u32

(s: string) hash_djb2() — Bernstein’s djb2 hash. Alternate non-cryptographic hash to FNV-1a; sometimes a better fit for short keys due to the differing mixing. Initial value 5381; multiplier 33. Returns u32.

pub function (s: string) escape_c(): string

s.escape_c() — C-style escape: backslash + double-quote each become \ ”, and the four whitespace bytes (\n \t \r \0) become their two-char escapes. Other bytes pass through unchanged. Standard shape for emitting source- code-ready string literals.

pub function (s: string) escape_shell(): string

(s: string) escape_shell() — POSIX-shell-safe quoting. Wraps s in single quotes and replaces any interior ' with the standard close-quote / escaped-single-quote / reopen-quote dance: ''\''. The result can be concatenated into a shell command line without word splitting or substitution.

pub function (s: string) snake_case(): string

(s: string) snake_case() / kebab_case() — convert camelCase or PascalCase to lower-case-with-separator names. Inserts an underscore (or hyphen) before each internal capital letter; lowercases everything; leaves existing separators (_, -, space) alone besides the case fold.

pub function (s: string) kebab_case(): string
pub function (s: string) is_valid_identifier(): boolean

s.is_valid_identifier() — true if s matches lang’s / C / JS identifier pattern: starts with a letter or underscore, continues with letters / digits / underscores. Empty string returns false.

pub function (s: string) is_ipv4(): boolean

s.is_ipv4() — true if s parses as a dotted-decimal IPv4 address: four octets, each 0..255, separated by dots. No leading-zero check; “0.0.0.0” / “255.255.255.255” / common shapes all pass.

Octet parse is a manual digit-walk rather than parse_int — parse_int uses i64 internally and hits the native i64-comparison-across-i32-boundary bug for small positive inputs (returns None on arm64 / x86-64 for “127”).

pub function (s: string) is_email_like(): boolean

s.is_email_like() — basic syntactic check: exactly one ’@’ with non-empty local + domain parts, and the domain contains at least one dot. Not RFC 5322 compliant (real email validation needs a parser); useful as a quick sanity check before downstream validation.

Tuple fields are bound to locals before passing to lenlen(p.0) directly on a (string, string) tuple-field access crashes the arm64 backend (the load folds incorrectly). Binding through var is a safe workaround pending a codegen fix.

pub function (s: string) is_url_like(): boolean

s.is_url_like() — true if s starts with http:// or https:// and contains at least one dot after the scheme (so localhost-style “http://nodot” fails). Very loose shape check, like is_email_like; defer to a real URL parser when one lands.

pub function (s: string) is_json_like(): boolean

s.is_json_like() — true if s (after trimming whitespace) starts and ends with matching {} or []. Not a real JSON validator — just a fast pre-flight check before attempting a parse. Empty strings return false.

pub function (s: string) common_prefix(other: string): string

s.common_prefix(other) — longest shared leading substring. Returns "" when the first byte differs or either argument is empty.

pub function (s: string) common_suffix(other: string): string

s.common_suffix(other) — longest shared trailing substring. Returns "" when no overlap or either side is empty.

pub function (s: string) starts_with_any(prefixes: string[]): boolean

s.starts_with_any(prefixes) — true if any entry in prefixes is a prefix of s. Empty prefixes array → false. An empty-string prefix always matches (consistent with starts_with("")).

pub function (s: string) ends_with_any(suffixes: string[]): boolean

s.ends_with_any(suffixes) — true if any entry in suffixes is a suffix of s. Empty suffixes array → false.

pub function (s: string) replace_first(old: string, new: string): string

s.replace_first(old, new) — replace only the first occurrence of old with new. Convenience wrapper over replace_n(old, new, 1). Empty old is a no-op (matches replace’s semantics).

pub function (s: string) word_at(i: i32): Option[string]

s.word_at(i) — Option[string] of the i-th word in s using whitespace as separator. Multiple consecutive whitespace bytes collapse (driven by fields()). Negative indices and indices >= word_count return None.

pub function (s: string) word_count_min(min_len: i32): i32

s.word_count_min(min_len) — count of whitespace- delimited words at least min_len bytes long. min_len <= 0 counts every word.

pub function (s: string) longest_word(): Option[string]

s.longest_word() — Option[string] of the longest whitespace-delimited word. Ties go to the first occurrence. Empty / all-whitespace input → None.

pub function (s: string) is_quoted(): boolean

s.is_quoted() — true if s has at least 2 bytes and the first and last bytes are matching quote characters (”, `, or ’). Doesn’t validate escaping inside the quotes — purely a shape predicate. Empty / single-char strings return false (need at least an opening and closing quote).

pub function (s: string) is_kebab_case(): boolean

s.is_kebab_case() — true if s is non-empty, lowercase / digit / hyphen only, no leading or trailing hyphen, and no consecutive hyphens. Matches the “url-slug” shape kebab-case usually denotes.

pub function (s: string) is_snake_case(): boolean

s.is_snake_case() — true if s is non-empty, lowercase / digit / underscore only, no leading or trailing underscore, and no consecutive underscores.

pub function (s: string) shift_byte(delta: i32): string

s.shift_byte(delta) — Caesar-style byte shift: each byte += delta, wrapping at 256. Negative deltas are supported. Useful for simple obfuscation / toy ciphers; don’t rely on it for security.

pub function (s: string) without_chars(set: string): string

s.without_chars(set) — remove every byte from s that appears anywhere in set. Empty set → unchanged. ASCII-level character class semantics.

pub function (s: string) contains_only(set: string): boolean

s.contains_only(set) — true if every byte in s is in set. Empty s → true (vacuous). Empty set with non-empty s → false (no characters allowed). ASCII-level.

pub function (s: string) count_chars_in(set: string): i32

s.count_chars_in(set) — count bytes of s that appear in set. Counterpart to without_chars / contains_only. Empty set → 0.

pub function (s: string) split_at(i: i32): (string, string)

s.split_at(i) — split s into two parts at byte index i. Returns a (string, string) tuple. Negative i clamps to 0; i >= len(s) puts everything in the first part.

pub function (s: string) replace_byte(from: i32, to: i32): string

s.replace_byte(from, to) — return a copy of s with every byte equal to from replaced by to. ASCII-level; for multi-byte sequences use replace(old, new). Both args are i32 byte values (use the literal character code or a helper like b'-' as i32).

pub function (s: string) to_acronym(): string

s.to_acronym() — first byte of each whitespace- separated token, uppercased and concatenated. “hello world” → “HW”. Empty / all-whitespace input → "". ASCII upper-fold only.

pub function (s: string) title_case(): string

s.title_case() — uppercase the first byte of every space-separated word, leave the rest untouched. Splits on the literal ’ ’ byte (preserves multi-space gaps as trailing spaces on the previous word the way Go’s strings.Title does). ASCII-only; “FOX” stays “FOX”.

pub function (s: string) lines_non_empty(): string[]

s.lines_non_empty() — like lines() but drops empty entries. Useful for parsing text that uses blank lines as separators where you don’t care about them.

pub function (s: string) is_ascii_only(): boolean
pub function (s: string) is_numeric(): boolean
pub function (s: string) is_alpha_only(): boolean
pub function (s: string) is_alnum_only(): boolean
pub function (s: string) is_int(): boolean

s.is_int() — true if s parses as a decimal integer (optional leading + / -, then one-or-more digits). Doesn’t check for i32 overflow — s.parse_int() is the validating + parsing form.

pub function (s: string) is_float(): boolean

s.is_float() — true if s looks like a decimal float: optional sign, digits, optional .digits. No exponent form (1e10) — that’s a follow-up. Pure-int strings are also accepted (5 → true). Used as a quick “looks numeric” check before invoking parse_float.

pub function (s: string) wrap(prefix: string, suffix: string): string

s.wrap(prefix, suffix) — concatenate prefix + s + suffix. Saves the multi-+ call site when wrapping a value in brackets / quotes / tags. "x".wrap("[", "]")"[x]".

pub function (s: string) is_blank(): boolean

s.is_blank() — true if s is empty or contains only ASCII whitespace. Differs from is_empty by also matching strings of pure tabs / spaces / newlines. Useful for “skip blank lines” passes.

pub function (s: string) is_hex_string(): boolean

s.is_hex_string() — every byte is a hex digit (case- insensitive). Empty string returns false (mirrors the is_numeric / is_alpha_only conventions). Useful for validating user-supplied hex hashes / hex-color values.

pub function (s: string) indent(prefix: string): string

s.indent(prefix) — prepend prefix to every line of s. Empty lines get the prefix too (matches Python textwrap indent’s default; the predicate-gated variant is a follow-up). Lines are determined by ‘\n’; CRLF input gets prefix between the CR and the LF only on the first line and after each ‘\n’, which matches the bytewise model.

pub function (s: string) trim(): string
pub function (s: string) trim_start(): string

s.trim_start() / trim_end() — one-sided whitespace strip. The bare trim already covers both ends; these are the asymmetric variants useful for trimming a prompt prefix without eating trailing data, or vice versa.

pub function (s: string) trim_end(): string
pub function (s: string) rstrip_newline(): string

s.rstrip_newline() — strip a single trailing newline. Removes “\n” or “\r\n” (the CRLF pair) but stops there — runs of multiple newlines are preserved beyond the first strip. Use trim_end for full whitespace strip.

pub function (s: string) trim_chars(chars: string): string

s.trim_chars(chars) — strip any byte that appears in chars from both ends. Empty chars is a no-op. Useful for stripping a chosen set of delimiters / wrapping characters in one pass.

pub function (s: string) trim_start_chars(chars: string): string

s.trim_start_chars(chars) / s.trim_end_chars(chars) — one-sided variants of trim_chars. Strip any byte in chars from the start / end only.

pub function (s: string) trim_end_chars(chars: string): string
pub function (s: string) starts_with_ci(prefix: string): boolean

s.starts_with_ci(prefix) / s.ends_with_ci(suffix) — ASCII case-insensitive variants of starts_with / ends_with. Folds A-Z to a-z per byte.

pub function (s: string) ends_with_ci(suffix: string): boolean
pub function (s: string) bytes(): u8[]

s.bytes() — returns a fresh u8[] whose contents are independently owned. Use s.as_bytes() (slice form) when you don’t need ownership; that’s the zero-copy companion.

pub function (s: string) to_lower(): string
pub function (s: string) to_upper(): string
pub function (s: string) split(sep: string): string[]

s.split(sep) — splits on every non-overlapping occurrence of sep. Empty pieces around adjacent separators are preserved (",a,b,".split(",")["", "a", "b", ""]). Empty separator splits into single-byte strings (matches JS’s split("")). Result is built incrementally via __array_append_string.

pub function (s: string) splitn(sep: string, n: i32): string[]

s.splitn(sep, n) — like split but caps the result at n pieces; the last piece carries the unsplit tail. n <= 0 returns the empty array; n == 1 returns [s] unchanged. Useful for “first token, rest” parsing — HTTP request lines, URL scheme separation, header key: value.

pub function (s: string) first(): Option[i32]

s.first() / s.last() — first / last byte as Option[i32]. None on empty. Convenience over s.at(0) / s.at(len(s) - 1) — the indices are easy to mis-write.

pub function (s: string) last(): Option[i32]
pub function (s: string) take(n: i32): string

s.take(n) / s.drop(n) — prefix and suffix slicing with bounds clamping. take(big) returns the whole string; drop(big) returns empty. take(neg) / drop(neg) treat the count as 0. Saves the s[0:min(n, len(s))] / s[min(n, len(s)):len(s)] boilerplate at every call site.

pub function (s: string) drop(n: i32): string
pub function (s: string) chunks(size: i32): string[]

s.chunks(size) — split into consecutive size-byte chunks. The last chunk may be shorter. size <= 0 returns an array containing just s (or empty if s is empty). Useful for base64 line-wrapping, hex-dump formatting, paged output.

pub function (s: string) lines(): string[]

s.lines() — splits on ‘\n’, strips a trailing ‘\r’ from each line (so CRLF is treated as LF), and drops the trailing empty line that a final ‘\n’ would otherwise produce. Matches Python’s str.splitlines() and Go’s bufio.Scanner with ScanLines:

“a\nb\nc” → [“a”, “b”, “c”] “a\nb\n” → [“a”, “b”] (trailing ‘\n’ eaten) “a\r\nb” → [“a”, “b”] (CR stripped) “\n” → [""] (one empty line) "" → [] (no lines at all)

O(n) single-pass byte walk; uses index-of-byte (s[i]) and substring slicing rather than the per-byte s[i:i+1] == "\n" pattern, so the inner loop allocates nothing.

pub function (s: string) fields(): string[]

s.fields() — split on runs of ASCII whitespace, dropping empty pieces. Differs from split(" ") which preserves adjacent-separator empties and only matches the single space byte. Whitespace classifier matches the trim() one (space / tab / CR / LF / VT / FF). Result is the run of non-empty fields in order. Empty / all-whitespace input returns an empty array.

Matches Go strings.Fields, Roc Str.split (whitespace flavour), Python str.split() (no-arg).

pub function (s: string) eq_ignore_ascii_case(other: string): boolean

s.eq_ignore_ascii_case(other) — equality with ASCII A-Z folded to a-z on both sides. Non-ASCII bytes (>= 0x80) only match byte-exact — multibyte UTF-8 sequences and Unicode case folding aren’t handled. Standard shape for HTTP header keys (“Content-Type” vs “content-type”) where the fold is strictly ASCII per RFC 7230.

pub function (s: string) strip_prefix(prefix: string): Option[string]

s.strip_prefix(p) — Some(rest) if s starts with p, None otherwise. The Some payload is s[len(p):], which under lang’s arena model is a fresh independent slice (string slicing allocates). Companion to starts_with that ALSO gives you the trailing chunk in a single call.

pub function (s: string) strip_suffix(suffix: string): Option[string]

s.strip_suffix(s) — Some(head) if s ends with the suffix. Symmetric to strip_prefix; returns s[0:len(s)-len(suffix)].

pub function (s: string) remove_prefix(prefix: string): string

s.remove_prefix(prefix) / s.remove_suffix(suffix) — like strip_prefix / strip_suffix but return s unchanged when the prefix / suffix doesn’t match, instead of Option. Avoids the match boilerplate when the unchanged value is the right fallback.

pub function (s: string) remove_suffix(suffix: string): string
pub function (s: string) is_uuid(): boolean

s.is_uuid() — true if s matches the canonical UUID format: 8-4-4-4-12 hex digits separated by dashes, total 36 characters. Doesn’t validate the version or variant nibble — just the shape.

pub function (s: string) pad_start(n: i32, ch: string): string

s.pad_start(n, ch) / s.pad_end(n, ch) — left- and right- pad the string with ch (the first byte is used) so the result is at least n bytes long. Already-long strings pass through unchanged. Standard alignment shape for tables, hex dumps, fixed-width column output.

ch is a single-byte string; multibyte fill characters would need codepoint handling we don’t have yet. Empty ch degenerates to no padding (returns the original).

pub function (s: string) pad_end(n: i32, ch: string): string
pub function (s: string) split_once(sep: string): Option[(string, string)]

s.split_once(sep) — splits at the FIRST occurrence of sep, returning Some((head, tail)). The separator is dropped from the output. Returns None when sep doesn’t appear or sep is empty. Useful for “key=value”, “name:port”, “scheme://rest” patterns where you only want one split.

pub function (s: string) trim_start_matches(sub: string): string

s.trim_start_matches(sub) / s.trim_end_matches(sub) — peel sub off the beginning / end as many times as it appears, contiguously. Symmetric with the regular trim() that strips whitespace.

pub function (s: string) trim_end_matches(sub: string): string
pub function (s: string) contains_ci(needle: string): boolean

s.contains_ci(needle) / index_of_ci(needle) — case- insensitive ASCII versions of contains / index_of. Folds A-Z to a-z on both sides per byte. Multibyte UTF-8 matches only as exact bytes (no Unicode case folding).

pub function (s: string) index_of_ci(needle: string): i32
pub function (s: string) pad_start_str(width: i32, fill: string): string

s.pad_start_str(width, fill) / pad_end_str(width, fill) — left- and right-pad to a minimum BYTE width using the multi-byte fill string as the padding unit. The pad repeats; if the byte-count overshoots, the LAST occurrence is truncated at the boundary. The simpler pad_start(n, ch) from earlier always uses a single-byte fill — these are the cousins for prefix dashes / line rules / ”…” kinds of decorative padding.

pub function (s: string) pad_end_str(width: i32, fill: string): string
pub function (s: string) center(width: i32, ch: string): string

s.center(width, ch) — center s within a width-byte field, padding equally on both sides with the single byte ch. Odd-padding splits leave the extra byte on the right. ch is a single-byte string (only first byte used). Already- long strings pass through unchanged.

pub function (s: string) reverse_words(): string

s.reverse_words() — split on ASCII whitespace, reverse the resulting word list, join with a single space. Drops the original whitespace structure (multiple spaces / tabs / newlines all collapse to single ’ ’). Empty input returns "".

pub function (s: string) truncate(n: i32, ellipsis: string): string

s.truncate(n, ellipsis) — if len(s) > n, return the first (n - len(ellipsis)) bytes followed by ellipsis. If len(s) <= n, return s unchanged. If n is smaller than the ellipsis itself, just hard-truncate s to n bytes (the ellipsis would otherwise overflow).

pub function (s: string) ellipsis(n: i32): string

s.ellipsis(n) — fit s into n bytes, marking truncation with ”…”. When len(s) <= n, returns s unchanged. Otherwise: if n >= 3, return the first (n-3) bytes followed by ”…” (so an n=3 cap on a long string is just ”…” — different from truncate(3, "...") which would hard-truncate the source). If n < 3, return the first n bytes of ”…” as a degenerate placeholder.

pub function (s: string) first_line(): Option[string]

s.first_line() — Option[string] containing the prefix up to (but not including) the first ‘\n’. Returns None for the empty string. If there’s no newline, returns Some(s).

pub function (s: string) at(i: i32): Option[i32]

s.at(i) — bounds-checked byte access. Returns Some(byte) for in-range i or None if i is out of [0, len). Counterpart to s[i] which traps on OOB; useful in tokeniser loops where the end check would otherwise need an explicit bounds guard at every step.

pub function (s: string) chars(): i32[]

s.chars() — byte array of the string’s contents as i32 values (one per byte). Byte-level, not codepoint — multibyte UTF-8 sequences come out as separate i32s in order. Convenience over s.bytes() (which returns u8[]) when callers want to do i32 arithmetic / comparisons on each byte without manually widening. The bytes() function stays as the canonical way to get a u8[] slice; chars() is the i32 sibling.

pub function (s: string) to_array(): string[]

s.to_array() — string[] one element per byte (each a single-byte string). Companion to chars() but with string elements; useful when later operations need string semantics rather than raw byte values.

pub function (s: string) reverse_bytes(): string

s.reverse_bytes() — fresh string with the byte order reversed. NOT a Unicode-aware reverse — multibyte UTF-8 sequences will scramble their internal byte order, producing invalid UTF-8. The name carries the warning. For pure-ASCII inputs (HTTP headers, hex / base64 / log lines, parser fragments) the operation is well-defined.

pub function (s: string) count(sub: string): i32

s.count(sub) — number of non-overlapping occurrences of sub in s. Empty needle returns 0 to avoid infinite-loop surprises; behaviour mirrors Rust str::matches().count().

pub function (s: string) count_byte(b: i32): i32

s.count_byte(b) — number of bytes equal to b. O(n) single-pass; cheaper than count(byte.to_ascii_string()) when the needle is known to be a single byte.

pub function (s: string) replace_n(old: string, new: string, n: i32): string

s.replace_n(old, new, n) — like replace() but caps at the first n occurrences. Useful when only the leading substitutions are interesting (e.g. unquoting the first few escapes, masking PII while keeping later context).

pub function (s: string) remove_all(needle: string): string

s.remove_all(needle) — replace every occurrence of needle with empty. Sugar for replace(needle, "").

pub function (s: string) before(sep: string): string

s.before(sep) — substring before the first occurrence of sep. Returns s unchanged if sep not present, "" if s starts with sep. Doesn’t include sep in the result.

pub function (s: string) after(sep: string): string

s.after(sep) — substring after the first occurrence of sep. Returns "" if sep not present, s unchanged if sep is empty. Doesn’t include sep in the result.

pub function (s: string) between(start: string, end: string): Option[string]

s.between(start, end) — Some(substring) for the content between the first start and the first end AFTER that start. None if either marker is missing (or empty). Useful for extracting tag bodies, parenthesised content, brace- delimited fields.

pub function (s: string) count_lines(): i32

s.count_lines() — number of lines in s. Empty string returns 0; lines are separated by ‘\n’. A trailing newline is NOT counted as an extra empty line (matches Go’s bufio.Scanner ScanLines + lines() prelude function).

pub function (s: string) repeat_with_sep(n: i32, sep: string): string

s.repeat_with_sep(n, sep) — like repeat() but with sep between every pair. "x".repeat_with_sep(3, ", ") → “x, x, x”. n <= 0 returns empty.

pub function (s: string) replace(old: string, new: string): string

s.replace(old, new) — substitutes every non-overlapping occurrence of old with new. Empty old is a no-op (returns the source unchanged) to match the JS / Go conventions and avoid the “insert N copies” pathology. Builds the result by concatenation; the IR’s + lowering handles the allocation.

pub function (s: string) parse_bool(): Option[boolean]

(s: string) parse_bool() — accepts the canonical truthy / falsy spellings: “true” / “1” → Some(true), “false” / “0” → Some(false). Anything else returns None (no implicit lowercasing — callers stop their own canonicalisation at the boundary). Empty string is None.

pub function (s: string) is_http_safe_method(): boolean

(s: string) is_http_safe_method() — true for GET / HEAD / OPTIONS / TRACE (RFC 9110 section 9.2.1). Safe methods don’t mutate state; ETag caching / replay logic depends on the distinction.

pub function (s: string) is_http_idempotent_method(): boolean

(s: string) is_http_idempotent_method() — true for any safe method plus PUT / DELETE. Idempotent methods can be safely retried after a network error without doubling the effect.

pub function (s: string) parse_int(): Option[i32]

s.parse_int() — decimal i32 parser. Optional leading -; rejects empty input, lone -, any non-digit character, embedded whitespace, and out-of-range values (-2^31..=2^31-1). Internal accumulator is i64 so the bound check is exact. Returns None on failure, Some(v) on success.

Replaced the ~190-line wat helper that previously lived in wasm.go; the lang version compiles to roughly the same code via the IR.

pub function (s: string) parse_hex_int(): Option[i32]

s.parse_hex_int() / s.parse_bin_int() — sugar for parse_int_radix(s, 16) / parse_int_radix(s, 2).

pub function (s: string) parse_bin_int(): Option[i32]
pub function (s: string) parse_float(): Option[f32]

s.parse_float() — decimal f32 parser. Grammar: [-][.][(e|E)[+-]?] At least one of integer or fraction digits required; trailing garbage rejected. Mantissa accumulates into an i64 with a saturation cap at 1e15; beyond that, extra digits are skipped while exp_adj keeps the magnitude right. Final value = (f32) mantissa * 10^exp_adj, sign applied via 0.0 - v.

pub function repeat_char(ch: i32, n: i32): string

repeat_char(ch, n) — fresh string of n copies of the byte ch. Faster than (c).chr().repeat(n) would be (no chr helper exists yet; this is the substitute). n <= 0 returns empty.

pub function (s: string) replace_at(i: i32, new: string): string

s.replace_at(i, new) — return a copy of s with the byte at index i replaced by the first byte of new. If new is empty, the byte at i is dropped. Out-of-range i (negative or >= len(s)) is a no-op and returns s unchanged.

pub function (s: string) without_byte(b: i32): string

s.without_byte(b) — copy of s with every byte equal to b removed. ASCII-level; for multi-byte sequences use replace_byte or replace(old, new).

pub function (s: string) repeat_to(target: i32): string

s.repeat_to(target) — concatenate copies of s until the result reaches at least target bytes, then truncate at the last full copy that fits. Empty s or target <= 0 → "". The truncate-not-overrun shape matches a common “fill to width without exceeding” pattern.