From 7dcb98583a353e3a205f4bbbbac0c68843a7d8b3 Mon Sep 17 00:00:00 2001 From: Franz Date: Tue, 14 Jul 2026 13:20:45 +0200 Subject: [PATCH] Add `@(require_results)` where useful in `core:strings` --- core/strings/builder.odin | 14 +++++- core/strings/strings.odin | 101 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 109 insertions(+), 6 deletions(-) diff --git a/core/strings/builder.odin b/core/strings/builder.odin index 79ba6108f..f4cf7e86a 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -34,6 +34,7 @@ Returns: - res: The new Builder - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) builder_make_none :: proc(allocator := context.allocator, loc := #caller_location) -> (res: Builder, err: runtime.Allocator_Error) #optional_allocator_error { return Builder{buf=make([dynamic]byte, allocator, loc) or_return }, nil } @@ -50,6 +51,7 @@ Returns: - res: The new Builder - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) builder_make_len :: proc(len: int, allocator := context.allocator, loc := #caller_location) -> (res: Builder, err: runtime.Allocator_Error) #optional_allocator_error { return Builder{buf=make([dynamic]byte, len, allocator, loc) or_return }, nil } @@ -67,6 +69,7 @@ Returns: - res: The new Builder - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator, loc := #caller_location) -> (res: Builder, err: runtime.Allocator_Error) #optional_allocator_error { return Builder{buf=make([dynamic]byte, len, cap, allocator, loc) or_return }, nil } @@ -193,6 +196,7 @@ Inputs: Returns: - res: the io.Stream */ +@(require_results) to_stream :: proc(b: ^Builder) -> (res: io.Stream) { return io.Stream{procedure=_builder_stream_proc, data=b} } @@ -205,6 +209,7 @@ Inputs: Returns: - res: The io.Writer */ +@(require_results) to_writer :: proc(b: ^Builder) -> (res: io.Writer) { return io.to_writer(to_stream(b)) } @@ -267,6 +272,7 @@ Output: ab */ +@(require_results) builder_from_bytes :: proc(backing: []byte) -> (res: Builder) { return Builder{ buf = transmute([dynamic]byte)runtime.Raw_Dynamic_Array{ @@ -292,6 +298,7 @@ Inputs: Returns: - res: The contents of the Builder's buffer, as a string */ +@(require_results) to_string :: proc(b: Builder) -> (res: string) { return string(b.buf[:]) } @@ -306,6 +313,7 @@ Inputs: Returns: - res: A cstring of the Builder's buffer */ +@(require_results) unsafe_to_cstring :: proc(b: ^Builder, loc := #caller_location) -> (res: cstring) { append(&b.buf, 0, loc) pop(&b.buf) @@ -321,6 +329,7 @@ Returns: - res: A cstring of the Builder's buffer upon success - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) to_cstring :: proc(b: ^Builder, loc := #caller_location) -> (res: cstring, err: runtime.Allocator_Error) #optional_allocator_error { n := append(&b.buf, 0, loc) or_return if n != 1 { @@ -341,6 +350,7 @@ Inputs: Returns: - res: The length of the Builder's buffer */ +@(require_results) builder_len :: proc(b: Builder) -> (res: int) { return len(b.buf) } @@ -353,6 +363,7 @@ Inputs: Returns: - res: The capacity of the Builder's buffer */ +@(require_results) builder_cap :: proc(b: Builder) -> (res: int) { return cap(b.buf) } @@ -365,6 +376,7 @@ Inputs: Returns: - res: The available space left in the Builder's buffer */ +@(require_results) builder_space :: proc(b: Builder) -> (res: int) { return cap(b.buf) - len(b.buf) } @@ -966,4 +978,4 @@ builder_replace :: proc(b: ^Builder, old, new: string, n: int, loc := #caller_lo } } return -} \ No newline at end of file +} diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 40dc11e75..1193735dd 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -22,6 +22,7 @@ Returns: - res: The cloned string - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { c := make([]byte, len(s), allocator, loc) or_return copy(c, s) @@ -42,6 +43,7 @@ Returns: - res: A cloned cstring with an appended null-byte - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: cstring, err: runtime.Allocator_Error) #optional_allocator_error { c := make([]byte, len(s)+1, allocator, loc) or_return copy(c, s) @@ -61,6 +63,7 @@ NOTE: The created string is only valid as long as the pointer and length are val Returns: - res: A string created from the byte pointer and length */ +@(require_results) string_from_ptr :: proc(ptr: ^byte, len: int) -> (res: string) { return transmute(string)runtime.Raw_String{ptr, len} } @@ -78,6 +81,7 @@ Inputs: Returns: - res: A string created from the null-terminated byte pointer and length */ +@(require_results) string_from_null_terminated_ptr :: proc "contextless" (ptr: [^]byte, len: int) -> (res: string) { s := string(ptr[:len]) s = truncate_to_byte(s, 0) @@ -95,6 +99,7 @@ WARNING: This is unsafe because the original string may not contain a null-byte. Returns: - res: The converted cstring */ +@(require_results) unsafe_string_to_cstring :: proc(str: string) -> (res: cstring) { d := transmute(runtime.Raw_String)str return cstring(d.data) @@ -112,6 +117,7 @@ NOTE: Failure to find the byte results in returning the entire string. Returns: - res: The truncated string */ +@(require_results) truncate_to_byte :: proc "contextless" (str: string, b: byte) -> (res: string) #no_bounds_check { n := index_byte(str, b) if n < 0 { @@ -130,6 +136,7 @@ Inputs: Returns: - res: The truncated string */ +@(require_results) truncate_to_rune :: proc(str: string, r: rune) -> (res: string) #no_bounds_check { n := index_rune(str, r) if n < 0 { @@ -152,6 +159,7 @@ Returns: - res: The cloned string from the byte array with a null-byte - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error #no_bounds_check { c := make([]byte, len(s)+1, allocator, loc) or_return copy(c, s) @@ -173,6 +181,7 @@ Returns: - res: The cloned string from the cstring - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { return clone(string(s), allocator, loc) } @@ -194,6 +203,7 @@ Returns: - res: The cloned string from the byte pointer and length - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { s := string_from_ptr(ptr, len) return clone(s, allocator, loc) @@ -224,6 +234,7 @@ Returns: - res: The cloned string from the null-terminated cstring and byte length - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { s := string_from_ptr((^u8)(ptr), len) s = truncate_to_byte(s, 0) @@ -253,6 +264,7 @@ Inputs: Returns: - result: `true` if the rune `r` in the string `s`, `false` otherwise */ +@(require_results) contains_rune :: proc(s: string, r: rune) -> (result: bool) { for c in s { if c == r { @@ -290,6 +302,7 @@ Output: false */ +@(require_results) contains :: proc(s, substr: string) -> (res: bool) { return index(s, substr) >= 0 } @@ -324,11 +337,13 @@ Output: false */ +@(require_results) contains_any :: proc(s, chars: string) -> (res: bool) { return index_any(s, chars) >= 0 } +@(require_results) contains_space :: proc(s: string) -> (res: bool) { for c in s { if is_space(c) { @@ -363,6 +378,7 @@ Output: 5 */ +@(require_results) rune_count :: proc(s: string) -> (res: int) { return utf8.rune_count_in_string(s) } @@ -398,6 +414,7 @@ Output: false */ +@(require_results) equal_fold :: proc(u, v: string) -> (res: bool) #no_bounds_check { s, t := u, v loop: for s != "" && t != "" { @@ -476,6 +493,7 @@ Output: 0 */ +@(require_results) prefix_length :: proc "contextless" (a, b: string) -> (n: int) { RUNE_ERROR :: '\ufffd' RUNE_SELF :: 0x80 @@ -524,6 +542,7 @@ Output: */ +@(require_results) common_prefix :: proc(a, b: string) -> string { return a[:prefix_length(a, b)] } @@ -558,6 +577,7 @@ Output: false */ +@(require_results) has_prefix :: proc(s, prefix: string) -> (result: bool) { return len(s) >= len(prefix) && s[0:len(prefix)] == prefix } @@ -592,6 +612,7 @@ Output: true */ +@(require_results) has_suffix :: proc(s, suffix: string) -> (result: bool) #no_bounds_check { return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix } @@ -631,6 +652,7 @@ Output: a...b...c */ +@(require_results) join :: proc(a: []string, sep: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error #no_bounds_check { if len(a) == 0 { return "", nil @@ -678,6 +700,7 @@ Output: abc */ +@(require_results) concatenate :: proc(a: []string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error #no_bounds_check { if len(a) == 0 { return "", nil @@ -724,6 +747,7 @@ Output: example */ +@(require_results) cut :: proc(s: string, rune_offset := int(0), rune_length := int(0)) -> (res: string) { s := s; rune_length := rune_length @@ -785,6 +809,7 @@ Output: example */ +@(require_results) cut_clone :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { res = cut(s, rune_offset, rune_length) return clone(res, allocator, loc) @@ -811,7 +836,7 @@ Returns: - res: The slice of substrings - err: An optional allocator error if one occured, `nil` otherwise */ -@private +@(private, require_results) _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) #no_bounds_check { s, n := s_, n_ @@ -892,6 +917,7 @@ Output: ["aaa", "bbb", "ccc", "ddd", "eee"] */ +@(require_results) split :: proc(s, sep: string, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) #optional_allocator_error { return _split(s, sep, 0, -1, allocator, loc) } @@ -930,6 +956,7 @@ Output: ["aaa", "bbb", "ccc.ddd.eee"] */ +@(require_results) split_n :: proc(s, sep: string, n: int, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) #optional_allocator_error { return _split(s, sep, 0, n, allocator, loc) } @@ -967,6 +994,7 @@ Output: ["aaa.", "bbb.", "ccc.", "ddd.", "eee"] */ +@(require_results) split_after :: proc(s, sep: string, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) #optional_allocator_error { return _split(s, sep, len(sep), -1, allocator, loc) } @@ -1005,6 +1033,7 @@ Output: ["aaa.", "bbb.", "ccc.ddd.eee"] */ +@(require_results) split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) #optional_allocator_error { return _split(s, sep, len(sep), n, allocator, loc) } @@ -1024,7 +1053,7 @@ Returns: - res: The resulting substring - ok: `true` if an iteration result was returned, `false` if the iterator has reached the end */ -@private +@(private, require_results) _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) { m: int if sep == "" { @@ -1082,6 +1111,7 @@ Output: e */ +@(require_results) split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) { m := index_byte(s^, sep) #no_bounds_check if m < 0 { @@ -1129,6 +1159,7 @@ Output: e */ +@(require_results) split_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) { return _split_iterator(s, sep, 0) } @@ -1165,6 +1196,7 @@ Output: e */ +@(require_results) split_after_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) { return _split_iterator(s, sep, len(sep)) } @@ -1180,7 +1212,7 @@ Inputs: Returns: - res: The trimmed string as a slice of the original. */ -@(private) +@(private, require_results) _trim_cr :: proc(s: string) -> (res: string) #no_bounds_check { n := len(s) if n > 0 { @@ -1221,6 +1253,7 @@ Output: ["a", "b", "c", "d", "e"] */ +@(require_results) split_lines :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) #optional_allocator_error { sep :: "\n" lines := _split(s, sep, 0, -1, allocator, loc) or_return @@ -1263,6 +1296,7 @@ Output: ["a", "b", "c\nd\ne"] */ +@(require_results) split_lines_n :: proc(s: string, n: int, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) #optional_allocator_error { sep :: "\n" lines := _split(s, sep, 0, n, allocator, loc) or_return @@ -1304,6 +1338,7 @@ Output: ["a\n", "b\n", "c\n", "d\n", "e"] */ +@(require_results) split_lines_after :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) #optional_allocator_error { sep :: "\n" lines := _split(s, sep, len(sep), -1, allocator, loc) or_return @@ -1347,6 +1382,7 @@ Output: ["a\n", "b\n", "c\nd\ne"] */ +@(require_results) split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) #optional_allocator_error { sep :: "\n" lines := _split(s, sep, len(sep), n, allocator, loc) or_return @@ -1385,6 +1421,7 @@ Output: abcde */ +@(require_results) split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) { sep :: "\n" line = _split_iterator(s, sep, 0) or_return @@ -1423,6 +1460,7 @@ Output: e */ +@(require_results) split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) { sep :: "\n" line = _split_iterator(s, sep, len(sep)) or_return @@ -1460,6 +1498,7 @@ Output: -1 */ +@(require_results) index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) { return #force_inline bytes.index_byte(transmute([]u8)s, c) } @@ -1496,6 +1535,7 @@ Output: -1 */ +@(require_results) last_index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) { return #force_inline bytes.last_index_byte(transmute([]u8)s, c) } @@ -1539,6 +1579,7 @@ Output: 7 */ +@(require_results) index_rune :: proc(s: string, r: rune) -> (res: int) { switch { case u32(r) < utf8.RUNE_SELF: @@ -1591,6 +1632,7 @@ Output: -1 */ +@(require_results) index :: proc "contextless" (s, substr: string) -> (res: int) #no_bounds_check { hash_str_rabin_karp :: proc "contextless" (s: string) -> (hash: u32 = 0, pow: u32 = 1) #no_bounds_check { for i := 0; i < len(s); i += 1 { @@ -1671,6 +1713,7 @@ Output: -1 */ +@(require_results) last_index :: proc(s, substr: string) -> (res: int) #no_bounds_check { hash_str_rabin_karp_reverse :: proc "contextless" (s: string) -> (hash: u32 = 0, pow: u32 = 1) #no_bounds_check { for i := len(s) - 1; i >= 0; i -= 1 { @@ -1751,6 +1794,7 @@ Output: -1 */ +@(require_results) index_any :: proc(s, chars: string) -> (res: int) #no_bounds_check { if chars == "" { return -1 @@ -1815,6 +1859,7 @@ Output: -1 */ +@(require_results) last_index_any :: proc(s, chars: string) -> (res: int) #no_bounds_check { if chars == "" { return -1 @@ -1876,6 +1921,7 @@ Returns: - idx: the index of the first matching substring - width: the length of the found substring */ +@(require_results) index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) #no_bounds_check { idx = -1 if s == "" || len(substrs) <= 0 { @@ -1939,6 +1985,7 @@ Output: 0 */ +@(require_results) count :: proc(s, substr: string) -> (res: int) #no_bounds_check { if len(substr) == 0 { // special case return rune_count(s) + 1 @@ -2005,6 +2052,7 @@ Output: abcabc */ +@(require_results) repeat :: proc(s: string, count: int, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { if count < 0 { panic("strings: negative repeat count") @@ -2056,6 +2104,7 @@ Output: */ +@(require_results) replace_all :: proc(s, old, new: string, allocator := context.allocator, loc := #caller_location) -> (output: string, was_allocation: bool) { return replace(s, old, new, -1, allocator, loc) } @@ -2097,6 +2146,7 @@ Output: zzzz true */ +@(require_results) replace :: proc(s, old, new: string, n: int, allocator := context.allocator, loc := #caller_location) -> (output: string, was_allocation: bool) { if old == new || n == 0 { was_allocation = false @@ -2176,6 +2226,7 @@ Output: abcabc false */ +@(require_results) remove :: proc(s, key: string, n: int, allocator := context.allocator, loc := #caller_location) -> (output: string, was_allocation: bool) { return replace(s, key, "", n, allocator, loc) } @@ -2213,6 +2264,7 @@ Output: abcabc false */ +@(require_results) remove_all :: proc(s, key: string, allocator := context.allocator, loc := #caller_location) -> (output: string, was_allocation: bool) { return remove(s, key, -1, allocator, loc) } @@ -2229,6 +2281,7 @@ Inputs: Returns: -res: `true` if `r` is a whitespace character, `false` if otherwise */ +@(require_results) is_ascii_space :: proc(r: rune) -> (res: bool) { if r < utf8.RUNE_SELF { return _ascii_space[u8(r)] @@ -2245,6 +2298,7 @@ Inputs: Returns: -res: `true` if `r` is a whitespace character, `false` if otherwise */ +@(require_results) is_space :: proc(r: rune) -> (res: bool) { if r < 0x2000 { switch r { @@ -2272,6 +2326,7 @@ Inputs: Returns: -res: `true` if `r` is `0x0`, `false` if otherwise */ +@(require_results) is_null :: proc(r: rune) -> (res: bool) { return r == 0x0000 } @@ -2312,6 +2367,7 @@ Output: -1 */ +@(require_results) index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int) { for r, i in s { if p(r) == truth { @@ -2322,6 +2378,7 @@ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int } // Same as `index_proc`, but the procedure p takes a raw pointer for state +@(require_results) index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) { for r, i in s { if p(state, r) == truth { @@ -2332,6 +2389,7 @@ index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: r } // Finds the index of the *last* rune in the string s for which the procedure p returns the same value as truth +@(require_results) last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int) { // TODO(bill): Probably use Rabin-Karp Search for i := len(s); i > 0; { @@ -2345,6 +2403,7 @@ last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res } // Same as `index_proc_with_state`, runs through the string in reverse +@(require_results) last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) { // TODO(bill): Probably use Rabin-Karp Search for i := len(s); i > 0; { @@ -2384,6 +2443,7 @@ Output: testing */ +@(require_results) trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) { i := index_proc(s, p, false) if i == -1 { @@ -2403,6 +2463,7 @@ Inputs: Returns: - res: The trimmed string as a slice of the original */ +@(require_results) trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> (res: string) { i := index_proc_with_state(s, p, state, false) if i == -1 { @@ -2438,6 +2499,7 @@ Output: test */ +@(require_results) trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) #no_bounds_check { i := last_index_proc(s, p, false) if i >= 0 && s[i] >= utf8.RUNE_SELF { @@ -2460,6 +2522,7 @@ Inputs: Returns: - res: The trimmed string as a slice of the original, empty when no match */ +@(require_results) trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> (res: string) #no_bounds_check { i := last_index_proc_with_state(s, p, state, false) if i >= 0 && s[i] >= utf8.RUNE_SELF { @@ -2472,6 +2535,7 @@ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta } // Procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison +@(require_results) is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) { cutset := (^string)(state)^ for c in cutset { @@ -2492,6 +2556,7 @@ Inputs: Returns: - res: The trimmed string as a slice of the original */ +@(require_results) trim_left :: proc(s: string, cutset: string) -> (res: string) { if s == "" || cutset == "" { return s @@ -2510,6 +2575,7 @@ Inputs: Returns: - res: The trimmed string as a slice of the original */ +@(require_results) trim_right :: proc(s: string, cutset: string) -> (res: string) { if s == "" || cutset == "" { return s @@ -2528,6 +2594,7 @@ Inputs: Returns: - res: The trimmed string as a slice of the original */ +@(require_results) trim :: proc(s: string, cutset: string) -> (res: string) { return trim_right(trim_left(s, cutset), cutset) } @@ -2541,6 +2608,7 @@ Inputs: Returns: - res: The trimmed string as a slice of the original */ +@(require_results) trim_left_space :: proc(s: string) -> (res: string) { return trim_left_proc(s, is_space) } @@ -2554,6 +2622,7 @@ Inputs: Returns: - res: The trimmed string as a slice of the original */ +@(require_results) trim_right_space :: proc(s: string) -> (res: string) { return trim_right_proc(s, is_space) } @@ -2567,6 +2636,7 @@ Inputs: Returns: - res: The trimmed string as a slice of the original */ +@(require_results) trim_space :: proc(s: string) -> (res: string) { return trim_right_space(trim_left_space(s)) } @@ -2580,6 +2650,7 @@ Inputs: Returns: - res: The trimmed string as a slice of the original */ +@(require_results) trim_left_null :: proc(s: string) -> (res: string) { return trim_left_proc(s, is_null) } @@ -2593,6 +2664,7 @@ Inputs: Returns: - res: The trimmed string as a slice of the original */ +@(require_results) trim_right_null :: proc(s: string) -> (res: string) { return trim_right_proc(s, is_null) } @@ -2605,6 +2677,7 @@ Inputs: Returns: - res: The trimmed string as a slice of the original */ +@(require_results) trim_null :: proc(s: string) -> (res: string) { return trim_right_null(trim_left_null(s)) } @@ -2635,6 +2708,7 @@ Output: testing */ +@(require_results) trim_prefix :: proc(s, prefix: string) -> (res: string) #no_bounds_check { if has_prefix(s, prefix) { return s[len(prefix):] @@ -2668,6 +2742,7 @@ Output: todo.doc */ +@(require_results) trim_suffix :: proc(s, suffix: string) -> (res: string) #no_bounds_check { if has_suffix(s, suffix) { return s[:len(s)-len(suffix)] @@ -2708,6 +2783,7 @@ Output: ["testing", "this", "out", "nice", "done", "last"] */ +@(require_results) split_multi :: proc(s: string, substrs: []string, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) #optional_allocator_error #no_bounds_check { if s == "" || len(substrs) <= 0 { return nil, nil @@ -2783,6 +2859,7 @@ Output: last */ +@(require_results) split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok: bool) #no_bounds_check { if len(it) == 0 || len(substrs) <= 0 { return @@ -2839,6 +2916,7 @@ Output: Hello? */ +@(require_results) scrub :: proc(s: string, replacement: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { str := s b: Builder @@ -2901,6 +2979,7 @@ Output: abcxyz zyxcba */ +@(require_results) reverse :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { str := s n := len(str) @@ -2948,6 +3027,7 @@ Output: abc1 abc2 abc3 */ +@(require_results) expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { if tab_size <= 0 { panic("tab size must be positive") @@ -3028,6 +3108,7 @@ Output: true */ +@(require_results) partition :: proc(str, sep: string) -> (head, match, tail: string) #no_bounds_check { i := index(str, sep) if i == -1 { @@ -3060,6 +3141,7 @@ Returns: - res: A new string centered within a field of the specified length - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { n := rune_count(str) if n >= length || pad == "" { @@ -3097,6 +3179,7 @@ Returns: - res: A new string left-justified within a field of the specified length - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { n := rune_count(str) if n >= length || pad == "" { @@ -3133,6 +3216,7 @@ Returns: - res: A new string right-justified within a field of the specified length - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: runtime.Allocator_Error) #optional_allocator_error { n := rune_count(str) if n >= length || pad == "" { @@ -3194,6 +3278,7 @@ Returns: - res: A slice of substrings of the input string, or an empty slice if the input string only contains white space - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) fields :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) #optional_allocator_error #no_bounds_check { n := 0 was_space := 1 @@ -3260,6 +3345,7 @@ Returns: - res: A slice of substrings of the input string, or an empty slice if all code points in the input string satisfy the predicate or if the input string is empty - err: An optional allocator error if one occured, `nil` otherwise */ +@(require_results) fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator, loc := #caller_location) -> (res: []string, err: runtime.Allocator_Error) #optional_allocator_error #no_bounds_check { substrings := make([dynamic]string, 0, 32, allocator, loc) or_return @@ -3297,6 +3383,7 @@ Returns: - field: The first non-space substring found - ok: A boolean indicating if a non-space substring was found */ +@(require_results) fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) #no_bounds_check { start, end := -1, -1 for r, offset in s { @@ -3344,6 +3431,7 @@ Returns: NOTE: This implementation is a single-row-version of the Wagner–Fischer algorithm, based on C code by Martin Ettl. */ +@(require_results) levenshtein_distance :: proc(a, b: string, allocator := context.allocator, loc := #caller_location) -> (res: int, err: runtime.Allocator_Error) #optional_allocator_error { LEVENSHTEIN_DEFAULT_COSTS: []int : { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, @@ -3403,7 +3491,7 @@ levenshtein_distance :: proc(a, b: string, allocator := context.allocator, loc : return costs[n], nil } -@(private) +@(private, require_results) internal_substring :: proc(s: string, rune_start: int, rune_end: int) -> (sub: string, ok: bool) #no_bounds_check { sub = s ok = true @@ -3458,6 +3546,7 @@ Returns: - sub: the substring - ok: whether the rune indexes where in bounds of the original string */ +@(require_results) substring :: proc(s: string, rune_start: int, rune_end: int) -> (sub: string, ok: bool) { if rune_start < 0 || rune_end < 0 || rune_end < rune_start { return @@ -3479,6 +3568,7 @@ Returns: - sub: the substring - ok: whether the rune indexes where in bounds of the original string */ +@(require_results) substring_from :: proc(s: string, rune_start: int) -> (sub: string, ok: bool) { if rune_start < 0 { return @@ -3500,10 +3590,11 @@ Returns: - sub: the substring - ok: whether the rune indexes where in bounds of the original string */ +@(require_results) substring_to :: proc(s: string, rune_end: int) -> (sub: string, ok: bool) { if rune_end < 0 { return } return internal_substring(s, -1, rune_end) -} \ No newline at end of file +}