From bf82c40964b3d7458f1be3b23370914c5dd7862f Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Mon, 27 Mar 2023 20:09:51 -0700 Subject: [PATCH 1/8] string code docs --- core/strings/ascii_set.odin | 24 +- core/strings/builder.odin | 459 +++++++++--- core/strings/conversion.odin | 246 ++++-- core/strings/intern.odin | 69 +- core/strings/reader.odin | 150 +++- core/strings/strings.odin | 1375 +++++++++++++++++++++++++--------- 6 files changed, 1810 insertions(+), 513 deletions(-) diff --git a/core/strings/ascii_set.odin b/core/strings/ascii_set.odin index 9b59666f3..7b6f46f5b 100644 --- a/core/strings/ascii_set.odin +++ b/core/strings/ascii_set.odin @@ -3,9 +3,22 @@ package strings import "core:unicode/utf8" +/* +Ascii_Set is designed to store ASCII characters efficiently as a bit-array +Each bit in the array corresponds to a specific ASCII character, where the value of the bit (0 or 1) +indicates if the character is present in the set or not. +*/ Ascii_Set :: distinct [8]u32 +/* +Creates an Ascii_Set with unique characters from the input string. -// create an ascii set of all unique characters in the string +Inputs: +- chars: A string containing characters to include in the Ascii_Set. + +Returns: +- as: An Ascii_Set with unique characters from the input string. +- ok: false if any character in the input string is not a valid ASCII character. +*/ ascii_set_make :: proc(chars: string) -> (as: Ascii_Set, ok: bool) #no_bounds_check { for i in 0.. (as: Ascii_Set, ok: bool) #no_bounds_ch ok = true return } +/* +Determines if a given char is contained within an Ascii_Set. -// returns true when the `c` byte is contained in the `as` ascii set +Inputs: +- as: The Ascii_Set to search. +- c: The char to check for in the Ascii_Set. + +Returns: A boolean indicating if the byte is contained in the Ascii_Set (true) or not (false). +*/ ascii_set_contains :: proc(as: Ascii_Set, c: byte) -> bool #no_bounds_check { return as[c>>5] & (1<<(c&31)) != 0 } \ No newline at end of file diff --git a/core/strings/builder.odin b/core/strings/builder.odin index a6d5b78b4..f405f285b 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -4,68 +4,126 @@ import "core:runtime" import "core:unicode/utf8" import "core:strconv" import "core:io" - -Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool) - /* - dynamic byte buffer / string builder with helper procedures - the dynamic array is wrapped inside the struct to be more opaque - you can use `fmt.sbprint*` procedures with a `^strings.Builder` directly +Type definition for a procedure that flushes a Builder + +Inputs: +- b: A pointer to the Builder + +Returns: A boolean indicating whether the Builder should be reset +*/ +Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool) +/* +A dynamic byte buffer / string builder with helper procedures +The dynamic array is wrapped inside the struct to be more opaque +You can use `fmt.sbprint*` procedures with a `^strings.Builder` directly */ Builder :: struct { buf: [dynamic]byte, } +/* +Produces a Builder with a default length of 0 and cap of 16 -// return a builder, default length 0 / cap 16 are done through make +*Allocates Using Provided Allocator* + +Inputs: +- allocator: (default is context.allocator) + +Returns: A new Builder +*/ builder_make_none :: proc(allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, allocator)} } +/* +Produces a Builder with a specified length and cap of max(16,len) byte buffer -// return a builder, with a set length `len` and cap 16 byte buffer +*Allocates Using Provided Allocator* + +Inputs: +- len: The desired length of the Builder's buffer +- allocator: (default is context.allocator) + +Returns: A new Builder +*/ builder_make_len :: proc(len: int, allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, len, allocator)} } +/* +Produces a Builder with a specified length and cap -// return a builder, with a set length `len` byte buffer and a custom `cap` +*Allocates Using Provided Allocator* + +Inputs: +- len: The desired length of the Builder's buffer +- cap: The desired capacity of the Builder's buffer, cap is max(cap, len) +- allocator: (default is context.allocator) + +Returns: A new Builder +*/ builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, len, cap, allocator)} } - // overload simple `builder_make_*` with or without len / cap parameters builder_make :: proc{ builder_make_none, builder_make_len, builder_make_len_cap, } +/* +Initializes a Builder with a length of 0 and cap of 16 +It replaces the existing `buf` -// initialize a builder, default length 0 / cap 16 are done through make -// replaces the existing `buf` +*Allocates Using Provided Allocator* + +Inputs: +- b: A pointer to the Builder +- allocator: (default is context.allocator) + +Returns: initialized ^Builder +*/ builder_init_none :: proc(b: ^Builder, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, allocator) return b } +/* +Initializes a Builder with a specified length and cap, which is max(len,16) +It replaces the existing `buf` -// initialize a builder, with a set length `len` and cap 16 byte buffer -// replaces the existing `buf` +*Allocates Using Provided Allocator* + +Inputs: +- b: A pointer to the Builder +- len: The desired length of the Builder's buffer +- allocator: (default is context.allocator) + +Returns: Initialized ^Builder +*/ builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, len, allocator) return b } +/* +Initializes a Builder with a specified length and cap +It replaces the existing `buf` -// initialize a builder, with a set length `len` byte buffer and a custom `cap` -// replaces the existing `buf` +Inputs: +- b: A pointer to the Builder +- len: The desired length of the Builder's buffer +- cap: The desired capacity of the Builder's buffer, actual max(len,cap) +- allocator: (default is context.allocator) + +Returns: A pointer to the initialized Builder +*/ builder_init_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, len, cap, allocator) return b } - // overload simple `builder_init_*` with or without len / ap parameters builder_init :: proc{ builder_init_none, builder_init_len, builder_init_len_cap, } - @(private) _builder_stream_vtable_obj := io.Stream_VTable{ impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) { @@ -91,49 +149,80 @@ _builder_stream_vtable_obj := io.Stream_VTable{ impl_destroy = proc(s: io.Stream) -> io.Error { b := (^Builder)(s.stream_data) delete(b.buf) + b.buf=nil return .None }, } - // NOTE(dweiler): Work around a miscompilation bug on Linux still. @(private) _builder_stream_vtable := &_builder_stream_vtable_obj +/* +Returns an io.Stream from a Builder -// return an `io.Stream` from a builder +Inputs: +- b: A pointer to the Builder + +Returns: An io.Stream +*/ to_stream :: proc(b: ^Builder) -> io.Stream { return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b} } +/* +Returns an io.Writer from a Builder -// return an `io.Writer` from a builder +Inputs: +- b: A pointer to the Builder + +Returns: An io.Writer +*/ to_writer :: proc(b: ^Builder) -> io.Writer { return io.to_writer(to_stream(b)) } +/* +Deletes and clears the Builder byte buffer content -// delete and clear the builder byte buffer content +Inputs: +- b: A pointer to the Builder +*/ builder_destroy :: proc(b: ^Builder) { delete(b.buf) - clear(&b.buf) + b.buf = nil } +/* +Reserves the Builder byte buffer to a specific capacity, when it's higher than before -// reserve the builfer byte buffer to a specific cap, when it's higher than before +Inputs: +- b: A pointer to the Builder +- cap: The desired capacity for the Builder's buffer +*/ builder_grow :: proc(b: ^Builder, cap: int) { reserve(&b.buf, cap) } +/* +Clears the Builder byte buffer content (sets len to zero) -// clear the builder byte buffer content +Inputs: +- b: A pointer to the Builder +*/ builder_reset :: proc(b: ^Builder) { clear(&b.buf) } - /* - create an empty builder with the same slice length as its cap - uses the `mem.nil_allocator` to avoid allocation and keep a fixed length - used in `fmt.bprint*` - +Creates a Builder from a slice of bytes with the same slice length as its capacity. Used in fmt.bprint* + +*Uses Nil Allocator - Does NOT allocate* + +Inputs: +- backing: A slice of bytes to be used as the backing buffer + +Example: +```odin bytes: [8]byte // <-- gets filled builder := strings.builder_from_bytes(bytes[:]) - strings.write_byte(&builder, 'a') -> "a" - strings.write_byte(&builder, 'b') -> "ab" + strings.write_byte(&builder, 'a') // -> "a" + strings.write_byte(&builder, 'b') // -> "ab" +``` +Returns: A new Builder */ builder_from_bytes :: proc(backing: []byte) -> Builder { s := transmute(runtime.Raw_Slice)backing @@ -147,36 +236,69 @@ builder_from_bytes :: proc(backing: []byte) -> Builder { buf = transmute([dynamic]byte)d, } } +// Alias to `builder_from_bytes` builder_from_slice :: builder_from_bytes +/* +Casts the Builder byte buffer to a string and returns it -// cast the builder byte buffer to a string and return it +Inputs: +- b: A Builder + +Returns: The contents of the Builder's buffer, as a string +*/ to_string :: proc(b: Builder) -> string { return string(b.buf[:]) } +/* +Returns the length of the Builder's buffer, in bytes -// return the length of the builder byte buffer +Inputs: +- b: A Builder + +Returns: The length of the Builder's buffer +*/ builder_len :: proc(b: Builder) -> int { return len(b.buf) } +/* +Returns the capacity of the Builder's buffer, in bytes -// return the cap of the builder byte buffer +Inputs: +- b: A Builder + +Returns: The capacity of the Builder's buffer +*/ builder_cap :: proc(b: Builder) -> int { return cap(b.buf) } +/* +The free space left in the Builder's buffer, in bytes -// returns the space left in the builder byte buffer to use up +Inputs: +- b: A Builder + +Returns: The available space left in the Builder's buffer +*/ builder_space :: proc(b: Builder) -> int { return cap(b.buf) - len(b.buf) } - /* - appends a byte to the builder, returns the append diff +Appends a byte to the Builder and returns the number of bytes appended +Inputs: +- b: A pointer to the Builder +- x: The byte to be appended + +Example: +```odin builder := strings.builder_make() - strings.write_byte(&builder, 'a') // 1 - strings.write_byte(&builder, 'b') // 1 - strings.write_byte(&builder, 'c') // 1 - fmt.println(strings.to_string(builder)) // -> abc + strings.write_byte(&builder, 'a') // 1 + strings.write_byte(&builder, 'b') // 1 + fmt.println(strings.to_string(builder)) // -> ab +``` +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of bytes appended */ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) { n0 := len(b.buf) @@ -184,14 +306,23 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) { n1 := len(b.buf) return n1-n0 } - /* - appends a slice of bytes to the builder, returns the append diff +Appends a slice of bytes to the Builder and returns the number of bytes appended +Inputs: +- b: A pointer to the Builder +- x: The slice of bytes to be appended + +Example: +```odin builder := strings.builder_make() bytes := [?]byte { 'a', 'b', 'c' } strings.write_bytes(&builder, bytes[:]) // 3 fmt.println(strings.to_string(builder)) // -> abc +``` +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of bytes appended */ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { n0 := len(b.buf) @@ -199,42 +330,66 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { n1 := len(b.buf) return n1-n0 } - /* - appends a single rune into the builder, returns written rune size and an `io.Error` +Appends a single rune to the Builder and returns the number of bytes written and an `io.Error` +Inputs: +- b: A pointer to the Builder +- r: The rune to be appended + +Example: +```odin builder := strings.builder_make() - strings.write_rune(&builder, 'ä') // 2 None - strings.write_rune(&builder, 'b') // 1 None - strings.write_rune(&builder, 'c') // 1 None - fmt.println(strings.to_string(builder)) // -> äbc + strings.write_rune(&builder, 'ä') // 2 None + strings.write_rune(&builder, 'b') // 1 None + fmt.println(strings.to_string(builder)) // -> äb +``` +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of bytes written and an io.Error (if any) */ write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) { return io.write_rune(to_writer(b), r) } - /* - appends a quoted rune into the builder, returns written size +Appends a quoted rune to the Builder and returns the number of bytes written +Inputs: +- b: A pointer to the Builder +- r: The rune to be appended + +Example: +```odin builder := strings.builder_make() - strings.write_string(&builder, "abc") // 3 + strings.write_string(&builder, "abc") // 3 strings.write_quoted_rune(&builder, 'ä') // 4 - strings.write_string(&builder, "abc") // 3 - fmt.println(strings.to_string(builder)) // -> abc'ä'abc + strings.write_string(&builder, "abc") // 3 + fmt.println(strings.to_string(builder)) // -> abc'ä'abc +``` +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of bytes written */ write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) { return io.write_quoted_rune(to_writer(b), r) } - - /* - appends a string to the builder, return the written byte size - +Appends a string to the Builder and returns the number of bytes written + +Inputs: +- b: A pointer to the Builder +- s: The string to be appended + +Example: +```odin builder := strings.builder_make() - strings.write_string(&builder, "a") // 1 - strings.write_string(&builder, "bc") // 2 - strings.write_string(&builder, "xyz") // 3 - fmt.println(strings.to_string(builder)) // -> abcxyz + strings.write_string(&builder, "a") // 1 + strings.write_string(&builder, "bc") // 2 + fmt.println(strings.to_string(builder)) // -> abc +``` +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of bytes written */ write_string :: proc(b: ^Builder, s: string) -> (n: int) { n0 := len(b.buf) @@ -242,10 +397,14 @@ write_string :: proc(b: ^Builder, s: string) -> (n: int) { n1 := len(b.buf) return n1-n0 } +/* +Pops and returns the last byte in the Builder or 0 when the Builder is empty +Inputs: +- b: A pointer to the Builder -// pops and returns the last byte in the builder -// returns 0 when the builder is empty +Returns: The last byte in the Builder or 0 if empty +*/ pop_byte :: proc(b: ^Builder) -> (r: byte) { if len(b.buf) == 0 { return 0 @@ -256,9 +415,14 @@ pop_byte :: proc(b: ^Builder) -> (r: byte) { d.len = max(d.len-1, 0) return } +/* +Pops the last rune in the Builder and returns the popped rune and its rune width or (0, 0) if empty -// pops the last rune in the builder and returns the popped rune and its rune width -// returns 0, 0 when the builder is empty +Inputs: +- b: A pointer to the Builder + +Returns: The popped rune and its rune width or (0, 0) if empty +*/ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) { if len(b.buf) == 0 { return 0, 0 @@ -269,41 +433,84 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) { d.len = max(d.len-width, 0) return } - @(private) DIGITS_LOWER := "0123456789abcdefx" - /* - append a quoted string into the builder, return the written byte size +Inputs: +- b: A pointer to the Builder +- str: The string to be quoted and appended +- quote: The optional quote character (default is double quotes) +Example: +```odin builder := strings.builder_make() - strings.write_quoted_string(&builder, "a") // 3 - strings.write_quoted_string(&builder, "bc", '\'') // 4 - strings.write_quoted_string(&builder, "xyz") // 5 - fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz" + strings.write_quoted_string(&builder, "a") // 3 + strings.write_quoted_string(&builder, "bc", '\'') // 4 + strings.write_quoted_string(&builder, "xyz") // 5 + fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz" +``` +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of bytes written */ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) { n, _ = io.write_quoted_string(to_writer(b), str, quote) return } +/* +Appends an encoded rune to the Builder and returns the number of bytes written +Inputs: +- b: A pointer to the Builder +- r: The rune to be appended +- write_quote: Optional boolean flag to write the quote character (default is true) -// appends a rune to the builder, optional `write_quote` boolean tag, returns the written rune size +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of bytes written +*/ write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) { n, _ = io.write_encoded_rune(to_writer(b), r, write_quote) return } +/* +Appends an escaped rune to the Builder and returns the number of bytes written -// appends a rune to the builder, fully written out in case of escaped runes e.g. '\a' will be written as such -// when `r` and `quote` match and `quote` is `\\` - they will be written as two slashes -// `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026` +Inputs: +- b: A pointer to the Builder +- r: The rune to be appended +- quote: The quote character +- html_safe: Optional boolean flag to encode '<', '>', '&' as digits (default is false) + +Examples: +- '\a' will be written as such +- `r` and `quote` match and `quote` is `\\` - they will be written as two slashes +- `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026` + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of bytes written +*/ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) { n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe) return } +/* +Writes a f64 value to the Builder and returns the number of characters written -// writes a f64 value into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- f: The f64 value to be appended +- fmt: The format byte +- prec: The precision +- bit_size: The bit size +- always_signed: Optional boolean flag to always include the sign (default is false) + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of characters written +*/ write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) { buf: [384]byte s := strconv.append_float(buf[:], f, fmt, prec, bit_size) @@ -314,8 +521,19 @@ write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_ } return write_string(b, s) } +/* +Writes a f16 value to the Builder and returns the number of characters written -// writes a f16 value into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- f: The f16 value to be appended +- fmt: The format byte +- always_signed: Optional boolean flag to always include the sign + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of characters written +*/ write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) @@ -324,8 +542,27 @@ write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: } return write_string(b, s) } +/* +Writes a f32 value to the Builder and returns the number of characters written -// writes a f32 value into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- f: The f32 value to be appended +- fmt: The format byte +- always_signed: Optional boolean flag to always include the sign + +Example: +```odin + builder := strings.builder_make() + strings.write_f32(&builder, 3.14159, 'f') // 6 + strings.write_string(&builder, " - ") // 3 + strings.write_f32(&builder, -0.123, 'e') // 8 + fmt.println(strings.to_string(builder)) // -> 3.14159012 - -1.23000003e-01 +``` +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of characters written +*/ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) @@ -334,8 +571,19 @@ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: } return write_string(b, s) } +/* +Writes a f32 value to the Builder and returns the number of characters written -// writes a f64 value into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- f: The f32 value to be appended +- fmt: The format byte +- always_signed: Optional boolean flag to always include the sign + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of characters written +*/ write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) @@ -344,30 +592,67 @@ write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: } return write_string(b, s) } +/* +Writes a u64 value to the Builder and returns the number of characters written +Inputs: +- b: A pointer to the Builder +- i: The u64 value to be appended +- base: The optional base for the numeric representation +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -// writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters +Returns: The number of characters written +*/ write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) { buf: [32]byte s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil) return write_string(b, s) } +/* +Writes a i64 value to the Builder and returns the number of characters written -// writes a i64 value `i` in `base` = 10 into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- i: The i64 value to be appended +- base: The optional base for the numeric representation + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of characters written +*/ write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) { buf: [32]byte s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) return write_string(b, s) } +/* +Writes a uint value to the Builder and returns the number of characters written -// writes a uint value `i` in `base` = 10 into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- i: The uint value to be appended +- base: The optional base for the numeric representation + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of characters written +*/ write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) { return write_u64(b, u64(i), base) } +/* +Writes a int value to the Builder and returns the number of characters written -// writes a int value `i` in `base` = 10 into the builder, returns the written amount of characters +Inputs: +- b: A pointer to the Builder +- i: The int value to be appended +- base: The optional base for the numeric representation + +NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. + +Returns: The number of characters written +*/ write_int :: proc(b: ^Builder, i: int, base: int = 10) -> (n: int) { return write_i64(b, i64(i), base) } - diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin index df03442c5..c952725fa 100644 --- a/core/strings/conversion.odin +++ b/core/strings/conversion.odin @@ -4,6 +4,20 @@ import "core:io" import "core:unicode" import "core:unicode/utf8" +/* +Converts invalid UTF-8 sequences in the input string `s` to the `replacement` string. + +*Allocates Using Provided Allocator* + +Inputs: +- s: Input string that may contain invalid UTF-8 sequences. +- replacement: String to replace invalid UTF-8 sequences with. +- allocator: (default: context.allocator). + +WARNING: Allocation does not occur when len(s) == 0 + +Returns: A valid UTF-8 string with invalid sequences replaced by `replacement`. +*/ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string { if len(s) == 0 { return "" @@ -33,7 +47,7 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> invalid := false - for i := 0; i < len(s); /**/ { + for i := 0; i < len(s); /**/{ c := s[i] if c < utf8.RUNE_SELF { i += 1 @@ -57,13 +71,20 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> } return to_string(b) } - /* - returns the input string `s` with all runes set to lowered case - always allocates using the `allocator` +Converts the input string `s` to all lowercase characters. - strings.to_lower("test") -> test - strings.to_lower("Test") -> test +*Allocates Using Provided Allocator* + +Inputs: +- s: Input string to be converted. +- allocator: (default: context.allocator). + +Example: +```odin + strings.to_lower("TeST") -> test +``` +Returns: A new string with all characters converted to lowercase. */ to_lower :: proc(s: string, allocator := context.allocator) -> string { b: Builder @@ -73,13 +94,20 @@ to_lower :: proc(s: string, allocator := context.allocator) -> string { } return to_string(b) } - /* - returns the input string `s` with all runes set to upper case - always allocates using the `allocator` +Converts the input string `s` to all uppercase characters. - strings.to_upper("test") -> TEST +*Allocates Using Provided Allocator* + +Inputs: +- s: Input string to be converted. +- allocator: (default: context.allocator). + +Example: +```odin strings.to_upper("Test") -> TEST +``` +Returns: A new string with all characters converted to uppercase. */ to_upper :: proc(s: string, allocator := context.allocator) -> string { b: Builder @@ -89,21 +117,36 @@ to_upper :: proc(s: string, allocator := context.allocator) -> string { } return to_string(b) } +/* +Checks if the rune `c` is a delimiter (' ', '-', or '_'). -// returns true when the `c` rune is a space, '-' or '_' -// useful when treating strings like words in a text editor or html paths +Inputs: +- c: Rune to check for delimiter status. + +Returns: True if `c` is a delimiter, false otherwise. +*/ is_delimiter :: proc(c: rune) -> bool { return c == '-' || c == '_' || is_space(c) } +/* +Checks if the rune `r` is a non-alphanumeric or space character. -// returns true when the `r` rune is a non alpha or `unicode.is_space` rune +Inputs: +- r: Rune to check for separator status. + +Returns: True if `r` is a non-alpha or `unicode.is_space` rune. +*/ is_separator :: proc(r: rune) -> bool { if r <= 0x7f { switch r { - case '0'..='9': return false - case 'a'..='z': return false - case 'A'..='Z': return false - case '_': return false + case '0' ..= '9': + return false + case 'a' ..= 'z': + return false + case 'A' ..= 'Z': + return false + case '_': + return false } return true } @@ -115,12 +158,32 @@ is_separator :: proc(r: rune) -> bool { return unicode.is_space(r) } - /* - iterator that loops through the string and calls the callback with the `prev`, `curr` and `next` rune - on empty string `s` the callback gets called once with empty runes +Iterates over a string, calling a callback for each rune with the previous, current, and next runes as arguments. + +Inputs: +- w: An io.Writer to be used by the callback for writing output. +- s: The input string to be iterated over. +- callback: A procedure to be called for each rune in the string, with arguments (w: io.Writer, prev, curr, next: rune). +The callback can utilize the provided io.Writer to write output during the iteration. + +Example: +```odin + my_callback :: proc(w: io.Writer, prev, curr, next: rune) { + fmt.println("my_callback", curr) // <-- Custom logic here + } + s := "hello world" + b: strings.Builder + strings.builder_init_len(&b, len(s)) + w := strings.to_writer(&b) + strings.string_case_iterator(w, s, my_callback) +``` */ -string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Writer, prev, curr, next: rune)) { +string_case_iterator :: proc( + w: io.Writer, + s: string, + callback: proc(w: io.Writer, prev, curr, next: rune), +) { prev, curr: rune for next in s { if curr == 0 { @@ -139,10 +202,19 @@ string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Write callback(w, prev, curr, 0) } } - +// Alias to `to_camel_case` to_lower_camel_case :: to_camel_case +/* +Converts the input string `s` to "lowerCamelCase". -// converts the `s` string to "lowerCamelCase" +*Allocates Using Provided Allocator* + +Inputs: +- s: Input string to be converted. +- allocator: (default: context.allocator). + +Returns: A "lowerCamelCase" formatted string. +*/ to_camel_case :: proc(s: string, allocator := context.allocator) -> string { s := s s = trim_space(s) @@ -164,10 +236,19 @@ to_camel_case :: proc(s: string, allocator := context.allocator) -> string { return to_string(b) } - +// Alias to `to_pascal_case` to_upper_camel_case :: to_pascal_case +/* +Converts the input string `s` to "UpperCamelCase" (PascalCase). -// converts the `s` string to "PascalCase" +*Allocates Using Provided Allocator* + +Inputs: +- s: Input string to be converted. +- allocator: (default: context.allocator). + +Returns: A "PascalCase" formatted string. +*/ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { s := s s = trim_space(s) @@ -189,17 +270,31 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { return to_string(b) } +/* +Returns a string converted to a delimiter-separated case with configurable casing -/* - returns the `s` string to words seperated by the given `delimiter` rune - all runes will be upper or lowercased based on the `all_uppercase` bool +*Allocates Using Provided Allocator* - strings.to_delimiter_case("Hello World", '_', false) -> hello_world - strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD - strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD - strings.to_delimiter_case("aBC", '_', false) -> a_b_c +Inputs: +- s: The input string to be converted +- delimiter: The rune to be used as the delimiter between words +- all_upper_case: A boolean indicating if the output should be all uppercased (true) or lowercased (false) +- allocator: (default: context.allocator). + +Example: +```odin + strings.to_delimiter_case("Hello World", '_', false) // -> "hello_world" + strings.to_delimiter_case("Hello World", ' ', true) // -> "HELLO WORLD" + strings.to_delimiter_case("aBC", '_', false) // -> "a_b_c" +``` +Returns: The converted string */ -to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string { +to_delimiter_case :: proc( + s: string, + delimiter: rune, + all_upper_case: bool, + allocator := context.allocator, +) -> string { s := s s = trim_space(s) b: Builder @@ -237,35 +332,96 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo return to_string(b) } +/* +Converts a string to "snake_case" with all runes lowercased -/* - converts the `s` string to "snake_case" with all runes lowercased - - strings.to_snake_case("HelloWorld") -> hello_world - strings.to_snake_case("Hello World") -> hello_world +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to be converted +- allocator: (default: context.allocator). + +Example: +```odin + strings.to_snake_case("HelloWorld") // -> "hello_world" + strings.to_snake_case("Hello World") // -> "hello_world" +``` +Returns: The converted string */ to_snake_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '_', false, allocator) } - +// Alias for `to_upper_snake_case` to_screaming_snake_case :: to_upper_snake_case +/* +Converts a string to "SNAKE_CASE" with all runes uppercased -// converts the `s` string to "SNAKE_CASE" with all runes uppercased +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to be converted +- allocator: (default: context.allocator). + +Example: +```odin + strings.to_upper_snake_case("HelloWorld") // -> "HELLO_WORLD" +``` +Returns: The converted string +*/ to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '_', true, allocator) } +/* +Converts a string to "kebab-case" with all runes lowercased -// converts the `s` string to "kebab-case" with all runes lowercased +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to be converted +- allocator: (default: context.allocator). + +Example: +```odin + strings.to_kebab_case("HelloWorld") // -> "hello-world" +``` +Returns: The converted string +*/ to_kebab_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '-', false, allocator) } +/* +Converts a string to "KEBAB-CASE" with all runes uppercased -// converts the `s` string to "KEBAB-CASE" with all runes uppercased +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to be converted +- allocator: (default: context.allocator). + +Example: +```odin + strings.to_upper_kebab_case("HelloWorld") // -> "HELLO-WORLD" +``` +Returns: The converted string +*/ to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '-', true, allocator) } +/* +Converts a string to "Ada_Case" -// converts the `s` string to "Ada_Case" +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to be converted +- allocator: (default: context.allocator). + +Example: +```odin + strings.to_ada_case("HelloWorld") // -> "Hello_World" +``` +Returns: The converted string +*/ to_ada_case :: proc(s: string, allocator := context.allocator) -> string { s := s s = trim_space(s) @@ -275,7 +431,9 @@ to_ada_case :: proc(s: string, allocator := context.allocator) -> string { string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) { if !is_delimiter(curr) { - if is_delimiter(prev) || prev == 0 || (unicode.is_lower(prev) && unicode.is_upper(curr)) { + if is_delimiter(prev) || + prev == 0 || + (unicode.is_lower(prev) && unicode.is_upper(curr)) { if prev != 0 { io.write_rune(w, '_') } diff --git a/core/strings/intern.odin b/core/strings/intern.odin index 5e9193a0d..5f849ed13 100644 --- a/core/strings/intern.odin +++ b/core/strings/intern.odin @@ -2,49 +2,96 @@ package strings import "core:runtime" -// custom string entry struct +// Custom string entry struct Intern_Entry :: struct { len: int, str: [1]byte, // string is allocated inline with the entry to keep allocations simple } +/* +Intern is a more memory efficient string map -// "intern" is a more memory efficient string map -// `allocator` is used to allocate the actual `Intern_Entry` strings +Uses Specified Allocator for `Intern_Entry` strings + +Fields: +- allocator: The allocator used for the Intern_Entry strings +- entries: A map of strings to interned string entries +*/ Intern :: struct { allocator: runtime.Allocator, entries: map[string]^Intern_Entry, } +/* +Initializes the entries map and sets the allocator for the string entries -// initialize the entries map and set the allocator for the string entries +*Allocates Using Provided Allocators* + +Inputs: +- m: A pointer to the Intern struct to be initialized +- allocator: The allocator for the Intern_Entry strings (Default: context.allocator) +- map_allocator: The allocator for the map of entries (Default: context.allocator) +*/ intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator) { m.allocator = allocator m.entries = make(map[string]^Intern_Entry, 16, map_allocator) } +/* +Frees the map and all its content allocated using the `.allocator`. -// free the map and all its content allocated using the `.allocator` +Inputs: +- m: A pointer to the Intern struct to be destroyed +*/ intern_destroy :: proc(m: ^Intern) { for _, value in m.entries { free(value, m.allocator) } delete(m.entries) } +/* +Returns the interned string for the given text, is set in the map if it didnt exist yet. -// returns the `text` string from the intern map - gets set if it didnt exist yet -// the returned string lives as long as the map entry lives +*MAY Allocate using the Intern's Allocator* + +Inputs: +- m: A pointer to the Intern struct +- text: The string to be interned + +NOTE: The returned string lives as long as the map entry lives. + +Returns: The interned string and an allocator error if any +*/ intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) { entry := _intern_get_entry(m, text) or_return #no_bounds_check return string(entry.str[:entry.len]), nil } +/* +Returns the interned C-String for the given text, is set in the map if it didnt exist yet. -// returns the `text` cstring from the intern map - gets set if it didnt exist yet -// the returned cstring lives as long as the map entry lives +*MAY Allocate using the Intern's Allocator* + +Inputs: +- m: A pointer to the Intern struct +- text: The string to be interned + +NOTE: The returned C-String lives as long as the map entry lives + +Returns: The interned C-String and an allocator error if any +*/ intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) { entry := _intern_get_entry(m, text) or_return return cstring(&entry.str[0]), nil } +/* +Internal function to lookup whether the text string exists in the map, returns the entry +Sets and allocates the entry if it wasn't set yet -// looks up wether the `text` string exists in the map, returns the entry -// sets & allocates the entry if it wasnt set yet +*MAY Allocate using the Intern's Allocator* + +Inputs: +- m: A pointer to the Intern struct +- text: The string to be looked up or interned + +Returns: The new or existing interned entry and an allocator error if any +*/ _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check { if prev, ok := m.entries[text]; ok { return prev, nil diff --git a/core/strings/reader.odin b/core/strings/reader.odin index 038740526..f4e3b213a 100644 --- a/core/strings/reader.odin +++ b/core/strings/reader.odin @@ -4,59 +4,104 @@ import "core:io" import "core:unicode/utf8" /* - io stream data for a string reader that can read based on bytes or runes - implements the vtable when using the io.Reader variants - "read" calls advance the current reading offset `i` +io stream data for a string reader that can read based on bytes or runes +implements the vtable when using the io.Reader variants +"read" calls advance the current reading offset `i` */ Reader :: struct { s: string, // read-only buffer i: i64, // current reading index prev_rune: int, // previous reading index of rune or < 0 } +/* +Initializes a string Reader with the provided string -// init the reader to the string `s` +Inputs: +- r: A pointer to a Reader struct +- s: The input string to be read +*/ reader_init :: proc(r: ^Reader, s: string) { r.s = s r.i = 0 r.prev_rune = -1 } +/* +Converts a Reader into an io.Stream -// returns a stream from the reader data +Inputs: +- r: A pointer to a Reader struct + +Returns: An io.Stream for the given Reader +*/ reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) { s.stream_data = r s.stream_vtable = &_reader_vtable return } +/* +Initializes a string Reader and returns an io.Reader for the given string -// init a reader to the string `s` and return an io.Reader +Inputs: +- r: A pointer to a Reader struct +- s: The input string to be read + +Returns: An io.Reader for the given string +*/ to_reader :: proc(r: ^Reader, s: string) -> io.Reader { reader_init(r, s) rr, _ := io.to_reader(reader_to_stream(r)) return rr } +/* +Initializes a string Reader and returns an io.Reader_At for the given string -// init a reader to the string `s` and return an io.Reader_At +Inputs: +- r: A pointer to a Reader struct +- s: The input string to be read + +Returns: An io.Reader_At for the given string +*/ to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At { reader_init(r, s) rr, _ := io.to_reader_at(reader_to_stream(r)) return rr } +/* +Returns the remaining length of the Reader -// remaining length of the reader +Inputs: +- r: A pointer to a Reader struct + +Returns: The remaining length of the Reader +*/ reader_length :: proc(r: ^Reader) -> int { if r.i >= i64(len(r.s)) { return 0 } return int(i64(len(r.s)) - r.i) } +/* +Returns the length of the string stored in the Reader -// returns the string length stored by the reader +Inputs: +- r: A pointer to a Reader struct + +Returns: The length of the string stored in the Reader +*/ reader_size :: proc(r: ^Reader) -> i64 { return i64(len(r.s)) } +/* +Reads len(p) bytes from the Reader's string and copies into the provided slice. -// reads len(p) bytes into the slice from the string in the reader -// returns `n` amount of read bytes and an io.Error +Inputs: +- r: A pointer to a Reader struct +- p: A byte slice to copy data into + +Returns: +- n: The number of bytes read +- err: An io.Error if an error occurs while reading, including .EOF, otherwise nil denotes success. +*/ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) { if r.i >= i64(len(r.s)) { return 0, .EOF @@ -66,9 +111,18 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) { r.i += i64(n) return } +/* +Reads len(p) bytes from the Reader's string and copies into the provided slice, at the specified offset from the current index. -// reads len(p) bytes into the slice from the string in the reader at an offset -// returns `n` amount of read bytes and an io.Error +Inputs: +- r: A pointer to a Reader struct +- p: A byte slice to copy data into +- off: The offset from which to read + +Returns: +- n: The number of bytes read +- err: An io.Error if an error occurs while reading, including .EOF, otherwise nil denotes success. +*/ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) { if off < 0 { return 0, .Invalid_Offset @@ -82,8 +136,16 @@ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Erro } return } +/* +Reads and returns a single byte from the Reader's string -// reads and returns a single byte - error when out of bounds +Inputs: +- r: A pointer to a Reader struct + +Returns: +- The byte read from the Reader +- err: An io.Error if an error occurs while reading, including .EOF, otherwise nil denotes success. +*/ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) { r.prev_rune = -1 if r.i >= i64(len(r.s)) { @@ -93,8 +155,14 @@ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) { r.i += 1 return b, nil } +/* +Decrements the Reader's index (i) by 1 -// decreases the reader offset - error when below 0 +Inputs: +- r: A pointer to a Reader struct + +Returns: An io.Error if `r.i <= 0` (.Invalid_Unread), otherwise nil denotes success. +*/ reader_unread_byte :: proc(r: ^Reader) -> io.Error { if r.i <= 0 { return .Invalid_Unread @@ -103,8 +171,17 @@ reader_unread_byte :: proc(r: ^Reader) -> io.Error { r.i -= 1 return nil } +/* +Reads and returns a single rune and its size from the Reader's string -// reads and returns a single rune and the rune size - error when out bounds +Inputs: +- r: A pointer to a Reader struct + +Returns: +- ch: The rune read from the Reader +- size: The size of the rune in bytes +- err: An io.Error if an error occurs while reading +*/ reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) { if r.i >= i64(len(r.s)) { r.prev_rune = -1 @@ -119,9 +196,16 @@ reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) { r.i += i64(size) return } +/* +Decrements the Reader's index (i) by the size of the last read rune -// decreases the reader offset by the last rune -// can only be used once and after a valid read_rune call +Inputs: +- r: A pointer to a Reader struct + +WARNING: May only be used once and after a valid `read_rune` call + +Returns: An io.Error if an error occurs while unreading (.Invalid_Unread), else nil denotes success. +*/ reader_unread_rune :: proc(r: ^Reader) -> io.Error { if r.i <= 0 { return .Invalid_Unread @@ -133,8 +217,18 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error { r.prev_rune = -1 return nil } +/* +Seeks the Reader's index to a new position -// seeks the reader offset to a wanted offset +Inputs: +- r: A pointer to a Reader struct +- offset: The new offset position +- whence: The reference point for the new position (.Start, .Current, or .End) + +Returns: +- The absolute offset after seeking +- err: An io.Error if an error occurs while seeking (.Invalid_Whence, .Invalid_Offset) +*/ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { r.prev_rune = -1 abs: i64 @@ -155,8 +249,19 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E r.i = abs return abs, nil } +/* +Writes the remaining content of the Reader's string into the provided io.Writer -// writes the string content left to read into the io.Writer `w` +Inputs: +- r: A pointer to a Reader struct +- w: The io.Writer to write the remaining content into + +WARNING: Panics if writer writes more bytes than remainig length of string. + +Returns: +- n: The number of bytes written +- err: An io.Error if an error occurs while writing (.Short_Write) +*/ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { r.prev_rune = -1 if r.i >= i64(len(r.s)) { @@ -175,7 +280,12 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { } return } +/* +VTable containing implementations for various io.Stream methods +This VTable is used by the Reader struct to provide its functionality +as an io.Stream. +*/ @(private) _reader_vtable := io.Stream_VTable{ impl_size = proc(s: io.Stream) -> i64 { diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 33cdafef3..bd2dcc004 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -1,4 +1,4 @@ -// simple procedures to manipulate UTF-8 encoded strings +// Procedures to manipulate UTF-8 encoded strings package strings import "core:io" @@ -6,58 +6,128 @@ import "core:mem" import "core:unicode" import "core:unicode/utf8" -// returns a clone of the string `s` allocated using the `allocator` +/* +Clones a string + +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to be cloned +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +Returns: A cloned string +*/ clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> string { c := make([]byte, len(s), allocator, loc) copy(c, s) return string(c[:len(s)]) } +/* +Clones a string safely (returns early with an allocation error on failure) -// returns a clone of the string `s` allocated using the `allocator` +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to be cloned +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +Returns: +- str: A cloned string +- err: A mem.Allocator_Error if an error occurs during allocation +*/ clone_safe :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (str: string, err: mem.Allocator_Error) { c := make([]byte, len(s), allocator, loc) or_return copy(c, s) return string(c[:len(s)]), nil } +/* +Clones a string and appends a nul byte to make it a cstring -// returns a clone of the string `s` allocated using the `allocator` as a cstring -// a nul byte is appended to the clone, to make the cstring safe +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to be cloned +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +Returns: A cloned cstring with an appended nul byte +*/ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> cstring { c := make([]byte, len(s)+1, allocator, loc) copy(c, s) c[len(s)] = 0 return cstring(&c[0]) } +/* +Transmutes a raw pointer into a string. Non-allocating. -// returns a string from a byte pointer `ptr` and byte length `len` -// the string is valid as long as the parameters stay alive +Inputs: +- ptr: A pointer to the start of the byte sequence +- len: The length of the byte sequence + +NOTE: The created string is only valid as long as the pointer and length are valid. + +Returns: A string created from the byte pointer and length +*/ string_from_ptr :: proc(ptr: ^byte, len: int) -> string { return transmute(string)mem.Raw_String{ptr, len} } +/* +Transmutes a raw pointer (nul-terminated) into a string. Non-allocating. Searches for a nul byte from 0.. string { s := transmute(string)mem.Raw_String{ptr, len} s = truncate_to_byte(s, 0) return s } +/* +Gets the raw byte pointer for the start of a string `str` -// returns the raw ^byte start of the string `str` +Inputs: +- str: The input string + +Returns: A pointer to the start of the string's bytes +*/ ptr_from_string :: proc(str: string) -> ^byte { d := transmute(mem.Raw_String)str return d.data } +/* +Converts a string `str` to a cstring -// returns the transmute of string `str` to a cstring -// not safe since the origin string may not contain a nul byte +Inputs: +- str: The input string + +WARNING: This is unsafe because the original string may not contain a nul byte. + +Returns: The converted cstring +*/ unsafe_string_to_cstring :: proc(str: string) -> cstring { d := transmute(mem.Raw_String)str return cstring(d.data) } +/* +Truncates a string `str` at the first occurrence of char/byte `b` -// returns a string truncated to the first time it finds the byte `b` -// uses the `len` of the string `str` when it couldn't find the input +Inputs: +- str: The input string +- b: The byte to truncate the string at + +NOTE: Failure to find the byte results in returning the entire string. + +Returns: The truncated string +*/ truncate_to_byte :: proc(str: string, b: byte) -> string { n := index_byte(str, b) if n < 0 { @@ -65,9 +135,15 @@ truncate_to_byte :: proc(str: string, b: byte) -> string { } return str[:n] } +/* +Truncates a string str at the first occurrence of rune r as a slice of the original, entire string if not found -// returns a string truncated to the first time it finds the rune `r` -// uses the `len` of the string `str` when it couldn't find the input +Inputs: +- str: The input string +- r: The rune to truncate the string at + +Returns: The truncated string +*/ truncate_to_rune :: proc(str: string, r: rune) -> string { n := index_rune(str, r) if n < 0 { @@ -75,51 +151,107 @@ truncate_to_rune :: proc(str: string, r: rune) -> string { } return str[:n] } +/* +Clones a byte array s and appends a nul byte -// returns a cloned string of the byte array `s` using the `allocator` -// appends a leading nul byte +*Allocates Using Provided Allocator* + +Inputs: +- s: The byte array to be cloned +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +Returns: A cloned string from the byte array with a nul byte +*/ clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> string { c := make([]byte, len(s)+1, allocator, loc) copy(c, s) c[len(s)] = 0 return string(c[:len(s)]) } +/* +Clones a cstring s as a string -// returns a clone of the cstring `s` using the `allocator` as a string +*Allocates Using Provided Allocator* + +Inputs: +- s: The cstring to be cloned +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +Returns: A cloned string from the cstring +*/ clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> string { return clone(string(s), allocator, loc) } +/* +Clones a string from a byte pointer ptr and a byte length len -// returns a cloned string from the pointer `ptr` and a byte length `len` using the `allocator` -// same to `string_from_ptr` but allocates +*Allocates Using Provided Allocator* + +Inputs: +- ptr: A pointer to the start of the byte sequence +- len: The length of the byte sequence +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +NOTE: Same as `string_from_ptr`, but perform an additional `clone` operation + +Returns: A cloned string from the byte pointer and length +*/ clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc := #caller_location) -> string { s := string_from_ptr(ptr, len) return clone(s, allocator, loc) } - -// overload to clone from a `string`, `[]byte`, `cstring` or a `^byte + length` to a string +// Overloaded procedure to clone from a string, []byte, cstring or a ^byte + length clone_from :: proc{ clone, clone_from_bytes, clone_from_cstring, clone_from_ptr, } +/* +Clones a string from a nul-terminated cstring ptr and a byte length len -// returns a cloned string from the cstring `ptr` and a byte length `len` using the `allocator` -// truncates till the first nul byte it finds or the byte len +*Allocates Using Provided Allocator* + +Inputs: +- ptr: A pointer to the start of the nul-terminated cstring +- len: The byte length of the cstring +- allocator: (default: context.allocator) +- loc: The caller location for debugging purposes (default: #caller_location) + +NOTE: Truncates at the first nul byte encountered or the byte length. + +Returns: A cloned string from the nul-terminated cstring and byte length +*/ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.allocator, loc := #caller_location) -> string { s := string_from_ptr((^u8)(ptr), len) s = truncate_to_byte(s, 0) return clone(s, allocator, loc) } +/* +Compares two strings, returning a value representing which one comes first lexicographically. +-1 for lhs; 1 for rhs, or 0 if they are equal. -// Compares two strings, returning a value representing which one comes first lexiographically. -// -1 for `lhs`; 1 for `rhs`, or 0 if they are equal. +Inputs: +- lhs: First string for comparison +- rhs: Second string for comparison + +Returns: -1 if lhs comes first, 1 if rhs comes first, or 0 if they are equal +*/ compare :: proc(lhs, rhs: string) -> int { return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs) } +/* +Returns the byte offset of the rune r in the string s, -1 when not found -// returns the byte offset of the rune `r` in the string `s`, -1 when not found +Inputs: +- s: The input string +- r: The rune to search for + +Returns: The byte offset of the rune r in the string s, or -1 if not found +*/ contains_rune :: proc(s: string, r: rune) -> int { for c, offset in s { if c == r { @@ -128,48 +260,79 @@ contains_rune :: proc(s: string, r: rune) -> int { } return -1 } - /* - returns true when the string `substr` is contained inside the string `s` +Returns true when the string substr is contained inside the string s - strings.contains("testing", "test") -> true - strings.contains("testing", "ing") -> true - strings.contains("testing", "text") -> false +Inputs: +- s: The input string +- substr: The substring to search for + +Example: +```odin + strings.contains("testing", "test") // -> true + strings.contains("testing", "ing") // -> true + strings.contains("testing", "text") // -> false +``` + +Returns: true if substr is contained inside the string s, false otherwise */ contains :: proc(s, substr: string) -> bool { return index(s, substr) >= 0 } - /* - returns true when the string `s` contains any of the characters inside the string `chars` - - strings.contains_any("test", "test") -> true - strings.contains_any("test", "ts") -> true - strings.contains_any("test", "et") -> true - strings.contains_any("test", "a") -> false +Returns true when the string s contains any of the characters inside the string chars + +Inputs: +- s: The input string +- chars: The characters to search for + +Example: +```odin + strings.contains_any("test", "test") // -> true + strings.contains_any("test", "ts") // -> true + strings.contains_any("test", "et") // -> true + strings.contains_any("test", "a") // -> false +``` + +Returns: true if the string s contains any of the characters in chars, false otherwise */ contains_any :: proc(s, chars: string) -> bool { return index_any(s, chars) >= 0 } - /* - returns the utf8 rune count of the string `s` +Returns the UTF-8 rune count of the string s - strings.rune_count("test") -> 4 - strings.rune_count("testö") -> 5, where len("testö") -> 6 +Inputs: +- s: The input string + +Example: +```odin + strings.rune_count("test") // -> 4 + strings.rune_count("testö") // -> 5, where len("testö") -> 6 +``` + +Returns: The UTF-8 rune count of the string s */ rune_count :: proc(s: string) -> int { return utf8.rune_count_in_string(s) } - /* - returns wether the strings `u` and `v` are the same alpha characters - works with utf8 string content and ignores different casings +Returns whether the strings u and v are the same alpha characters, ignoring different casings +Works with UTF-8 string content - strings.equal_fold("test", "test") -> true - strings.equal_fold("Test", "test") -> true - strings.equal_fold("Test", "tEsT") -> true - strings.equal_fold("test", "tes") -> false +Inputs: +- u: The first string for comparison +- v: The second string for comparison + +Example: +```odin + strings.equal_fold("test", "test") // -> true + strings.equal_fold("Test", "test") // -> true + strings.equal_fold("Test", "tEsT") // -> true + strings.equal_fold("test", "tes") // -> false +``` + +Returns: True if the strings u and v are the same alpha characters (ignoring case), false */ equal_fold :: proc(u, v: string) -> bool { s, t := u, v @@ -213,14 +376,22 @@ equal_fold :: proc(u, v: string) -> bool { return s == t } - /* - return the prefix length common between strings `a` and `b`. +Returns the prefix length common between strings a and b - strings.prefix_length("testing", "test") -> 4 - strings.prefix_length("testing", "te") -> 2 - strings.prefix_length("telephone", "te") -> 2 - strings.prefix_length("testing", "est") -> 0 +Inputs: +- a: The first input string +- b: The second input string + +Example: +```odin + strings.prefix_length("testing", "test") // -> 4 + strings.prefix_length("testing", "te") // -> 2 + strings.prefix_length("telephone", "te") // -> 2 + strings.prefix_length("testing", "est") // -> 0 +``` + +Returns: The prefix length common between strings a and b */ prefix_length :: proc(a, b: string) -> (n: int) { _len := min(len(a), len(b)) @@ -245,39 +416,61 @@ prefix_length :: proc(a, b: string) -> (n: int) { } return } - /* - return true when the string `prefix` is contained at the start of the string `s` +Determines if a string s starts with a given prefix - strings.has_prefix("testing", "test") -> true - strings.has_prefix("testing", "te") -> true - strings.has_prefix("telephone", "te") -> true - strings.has_prefix("testing", "est") -> false +Example: +```odin + strings.has_prefix("testing", "test") // -> true + strings.has_prefix("testing", "te") // -> true + strings.has_prefix("telephone", "te") // -> true + strings.has_prefix("testing", "est") // -> false +``` +Inputs: +- s: The string to check for the prefix +- prefix: The prefix to look for + +Returns: true if the string s starts with the prefix, otherwise false */ has_prefix :: proc(s, prefix: string) -> bool { return len(s) >= len(prefix) && s[0:len(prefix)] == prefix } - /* - returns true when the string `suffix` is contained at the end of the string `s` - good example to use this is for file extensions +Determines if a string s ends with a given suffix - strings.has_suffix("todo.txt", ".txt") -> true - strings.has_suffix("todo.doc", ".txt") -> false - strings.has_suffix("todo.doc.txt", ".txt") -> true +Example: +```odin + strings.has_suffix("todo.txt", ".txt") // -> true + strings.has_suffix("todo.doc", ".txt") // -> false + strings.has_suffix("todo.doc.txt", ".txt") // -> true +``` +Inputs: +- s: The string to check for the suffix +- suffix: The suffix to look for + +Returns: true if the string s ends with the suffix, otherwise false */ has_suffix :: proc(s, suffix: string) -> bool { return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix } - /* - returns a combined string from the slice of strings `a` seperated with the `sep` string - allocates the string using the `allocator` +Joins a slice of strings a with a sep string +*Allocates Using Provided Allocator* + +Example: +```odin a := [?]string { "a", "b", "c" } - b := strings.join(a[:], " ") -> "a b c" - c := strings.join(a[:], "-") -> "a-b-c" - d := strings.join(a[:], "...") -> "a...b...c" + b := strings.join(a[:], " ") // -> "a b c" + c := strings.join(a[:], "-") // -> "a-b-c" + d := strings.join(a[:], "...") // -> "a...b...c" +``` +Inputs: +- a: A slice of strings to join +- sep: The separator string +- allocator: (default is context.allocator) + +Returns: A combined string from the slice of strings a separated with the sep string */ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string { if len(a) == 0 { @@ -297,7 +490,20 @@ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string } return string(b) } +/* +Joins a slice of strings a with a sep string, returns an error on allocation failure +*Allocates Using Provided Allocator* + +Inputs: +- a: A slice of strings to join +- sep: The separator string +- allocator: (default is context.allocator) + +Returns: +- str: A combined string from the slice of strings a separated with the sep string +- err: An error if allocation failed, otherwise nil +*/ join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> (str: string, err: mem.Allocator_Error) { if len(a) == 0 { return "", nil @@ -316,14 +522,21 @@ join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> ( } return string(b), nil } - /* - returns a combined string from the slice of strings `a` without a seperator - allocates the string using the `allocator` - +Returns a combined string from the slice of strings `a` without a separator +*Allocates Using Provided Allocator* + +Inputs: +- a: A slice of strings to concatenate +- allocator: An optional custom allocator (default is context.allocator) + +Example: +```odin a := [?]string { "a", "b", "c" } - b := strings.concatenate(a[:]) -> "abc" + b := strings.concatenate(a[:]) // -> "abc" +``` +Returns: The concatenated string */ concatenate :: proc(a: []string, allocator := context.allocator) -> string { if len(a) == 0 { @@ -341,7 +554,17 @@ concatenate :: proc(a: []string, allocator := context.allocator) -> string { } return string(b) } +/* +Returns a combined string from the slice of strings `a` without a separator, or an error if allocation fails +*Allocates Using Provided Allocator* + +Inputs: +- a: A slice of strings to concatenate +- allocator: An optional custom allocator (default is context.allocator) + +Returns: The concatenated string, and an error if allocation fails +*/ concatenate_safe :: proc(a: []string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) { if len(a) == 0 { return "", nil @@ -358,14 +581,24 @@ concatenate_safe :: proc(a: []string, allocator := context.allocator) -> (res: s } return string(b), nil } - /* - `rune_offset` and `rune_length` are in runes, not bytes. - If `rune_length` <= 0, then it'll return the remainder of the string starting at `rune_offset`. +Returns a substring of the input string `s` with the specified rune offset and length - strings.cut("some example text", 0, 4) -> "some" - strings.cut("some example text", 2, 2) -> "me" - strings.cut("some example text", 5, 7) -> "example" +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to cut +- rune_offset: The starting rune index (default is 0). In runes, not bytes. +- rune_length: The number of runes to include in the substring (default is 0, which returns the remainder of the string). In runes, not bytes. +- allocator: An optional custom allocator (default is context.allocator) + +Example: +```odin + strings.cut("some example text", 0, 4) // -> "some" + strings.cut("some example text", 2, 2) // -> "me" + strings.cut("some example text", 5, 7) // -> "example" +``` +Returns: The substring */ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator := context.allocator) -> (res: string) { s := s; rune_length := rune_length @@ -418,7 +651,20 @@ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator : } return string(buf[:byte_offset]) } +/* +Splits the input string `s` into a slice of substrings separated by the specified `sep` string +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to split +- sep: The separator string +- sep_save: A flag determining if the separator should be saved in the resulting substrings +- n: The maximum number of substrings to return, returns nil without alloc when n=0 +- allocator: An optional custom allocator (default is context.allocator) + +Returns: A slice of substrings +*/ @private _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator) -> []string { s, n := s_, n_ @@ -466,58 +712,110 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato return res[:i+1] } - /* - Splits a string into parts, based on a separator. - Returned strings are substrings of 's'. - ``` - s := "aaa.bbb.ccc.ddd.eee" // 5 parts - ss := split(s, ".") - fmt.println(ss) // [aaa, bbb, ccc, ddd, eee] - ``` +Splits a string into parts based on a separator. + +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to split. +- sep: The separator string used to split the input string. +- allocator: (default is context.allocator). + +Example: +```odin + s := "aaa.bbb.ccc.ddd.eee" // 5 parts + ss := strings.split(s, ".") + fmt.println(ss) // [aaa, bbb, ccc, ddd, eee] +``` + +Returns: A slice of strings, each representing a part of the split string. */ split :: proc(s, sep: string, allocator := context.allocator) -> []string { return _split(s, sep, 0, -1, allocator) } - /* - Splits a string into a total of 'n' parts, based on a separator. - Returns fewer parts if there wasn't enough occurrences of the separator. - Returned strings are substrings of 's'. - ``` - s := "aaa.bbb.ccc.ddd.eee" // 5 parts present - ss := split_n(s, ".", 3) // total of 3 wanted - fmt.println(ss) // [aaa, bbb, ccc.ddd.eee] - ``` +Splits a string into parts based on a separator. if n < count of seperators, the remainder of the string is returned in the last entry. + +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to split. +- sep: The separator string used to split the input string. +- allocator: (default is context.allocator) + +Example: +```odin + s := "aaa.bbb.ccc.ddd.eee" // 5 parts present + ss := strings.split(s, ".") // total of 3 wanted + fmt.println(ss) // [aaa, bbb, ccc.ddd.eee] +``` + +Returns: A slice of strings, each representing a part of the split string. */ split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string { return _split(s, sep, 0, n, allocator) } - /* - splits the string `s` after the seperator string `sep` appears - returns the slice of split strings allocated using `allocator` +Splits a string into parts after the separator, retaining it in the substrings. - a := "aaa.bbb.ccc.ddd.eee" +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to split. +- sep: The separator string used to split the input string. +- allocator: (Optional) The allocator used for allocation (default is context.allocator). + +Example: +```odin + a := "aaa.bbb.ccc.ddd.eee" // 5 parts aa := strings.split_after(a, ".") - fmt.eprintln(aa) // [aaa., bbb., ccc., ddd., eee] + fmt.println(aa) // [aaa., bbb., ccc., ddd., eee] +``` + +Returns: A slice of strings, each representing a part of the split string after the separator. */ split_after :: proc(s, sep: string, allocator := context.allocator) -> []string { return _split(s, sep, len(sep), -1, allocator) } - /* - splits the string `s` after the seperator string `sep` appears into a total of `n` parts - returns the slice of split strings allocated using `allocator` +Splits a string into a total of 'n' parts after the separator. +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to split. +- sep: The separator string used to split the input string. +- n: The maximum number of parts to split the string into. +- allocator: (default is context.allocator) + +Example: +```odin a := "aaa.bbb.ccc.ddd.eee" - aa := strings.split_after(a, ".") - fmt.eprintln(aa) // [aaa., bbb., ccc., ddd., eee] + aa := strings.split_after_n(a, ".", 3) + fmt.println(aa) // [aaa., bbb., ccc.ddd.eee] +``` + +Returns: A slice of strings with 'n' parts or fewer if there weren't */ split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string { return _split(s, sep, len(sep), n, allocator) } +/* +Searches for the first occurrence of 'sep' in the given string and returns the substring +up to (but not including) the separator, as well as a boolean indicating success. +*Used Internally - Private Function* + +Inputs: +- s: Pointer to the input string, which is modified during the search. +- sep: The separator string to search for. +- sep_save: Number of characters from the separator to include in the result. + +NOTE: Destructively consumes the string + +Returns: A tuple containing the resulting substring and a boolean indicating success. +*/ @private _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) { // stop once the string is empty or nil @@ -545,15 +843,22 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, } return } - /* - split the ^string `s` by the byte seperator `sep` in an iterator fashion - consumes the original string till the end, leaving the string `s` with len == 0 +Splits the input string by the byte separator in an iterator fashion. +Destructively consumes the original string until the end. +Inputs: +- s: Pointer to the input string, which is modified during the search. +- sep: The byte separator to search for. + +Example: +```odin text := "a.b.c.d.e" for str in strings.split_by_byte_iterator(&text, '.') { - fmt.eprintln(str) // every loop -> a b c d e + fmt.println(str) // every loop -> a b c d e } +``` +Returns: A tuple containing the resulting substring and a boolean indicating success. */ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) { m := index_byte(s^, sep) @@ -569,34 +874,56 @@ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) { } return } - /* - split the ^string `s` by the seperator string `sep` in an iterator fashion - consumes the original string till the end +Splits the input string by the separator string in an iterator fashion. +Destructively consumes the original string until the end. +Inputs: +- s: Pointer to the input string, which is modified during the search. +- sep: The separator string to search for. + +Example: +```odin text := "a.b.c.d.e" for str in strings.split_iterator(&text, ".") { - fmt.eprintln(str) // every loop -> a b c d e + fmt.println(str) // every loop -> a b c d e } +``` +Returns: A tuple containing the resulting substring and a boolean indicating success. */ split_iterator :: proc(s: ^string, sep: string) -> (string, bool) { return _split_iterator(s, sep, 0) } - /* - split the ^string `s` after every seperator string `sep` in an iterator fashion - consumes the original string till the end +Splits the input string after every separator string in an iterator fashion. +Destructively consumes the original string until the end. +Inputs: +- s: Pointer to the input string, which is modified during the search. +- sep: The separator string to search for. + +Example: +```odin text := "a.b.c.d.e" for str in strings.split_after_iterator(&text, ".") { - fmt.eprintln(str) // every loop -> a. b. c. d. e + fmt.println(str) // every loop -> a. b. c. d. e } +``` +Returns: A tuple containing the resulting substring and a boolean indicating success. */ split_after_iterator :: proc(s: ^string, sep: string) -> (string, bool) { return _split_iterator(s, sep, len(sep)) } +/* +Trims the carriage return character from the end of the input string. +*Used Internally - Private Function* +Inputs: +- s: The input string to trim. + +Returns: The trimmed string as a slice. +*/ @(private) _trim_cr :: proc(s: string) -> string { n := len(s) @@ -607,14 +934,22 @@ _trim_cr :: proc(s: string) -> string { } return s } - /* - split the string `s` at every line break '\n' - return an allocated slice of strings +Splits the input string at every line break '\n'. +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to split. +- allocator: (default is context.allocator) + +Example: +```odin a := "a\nb\nc\nd\ne" b := strings.split_lines(a) fmt.eprintln(b) // [a, b, c, d, e] +``` +Returns: An allocated slice of strings split by line breaks. */ split_lines :: proc(s: string, allocator := context.allocator) -> []string { sep :: "\n" @@ -624,14 +959,23 @@ split_lines :: proc(s: string, allocator := context.allocator) -> []string { } return lines } - /* - split the string `s` at every line break '\n' for `n` parts - return an allocated slice of strings +Splits the input string at every line break '\n' for n parts. +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to split. +- n: The number of parts to split into. +- allocator: (default is context.allocator) + +Example: +```odin a := "a\nb\nc\nd\ne" b := strings.split_lines_n(a, 3) - fmt.eprintln(b) // [a, b, c, d\ne\n] + fmt.println(b) // [a, b, c, d\ne\n] +``` +Returns: An allocated array of strings split by line breaks. */ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []string { sep :: "\n" @@ -641,14 +985,22 @@ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []st } return lines } - /* - split the string `s` at every line break '\n' leaving the '\n' in the resulting strings - return an allocated slice of strings +Splits the input string at every line break '\n' leaving the '\n' in the resulting strings. +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to split. +- allocator: (default is context.allocator) + +Example: +```odin a := "a\nb\nc\nd\ne" b := strings.split_lines_after(a) - fmt.eprintln(b) // [a\n, b\n, c\n, d\n, e\n] + fmt.println(b) // [a\n, b\n, c\n, d\n, e\n] +``` +Returns: An allocated slice of strings split by line breaks with line breaks included. */ split_lines_after :: proc(s: string, allocator := context.allocator) -> []string { sep :: "\n" @@ -658,15 +1010,24 @@ split_lines_after :: proc(s: string, allocator := context.allocator) -> []string } return lines } - /* - split the string `s` at every line break '\n' leaving the '\n' in the resulting strings - only runs for `n` parts - return an allocated slice of strings +Splits the input string at every line break '\n' leaving the '\n' in the resulting strings. +Only runs for n parts. +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string to split. +- n: The number of parts to split into. +- allocator: (default is context.allocator) + +Example: +```odin a := "a\nb\nc\nd\ne" b := strings.split_lines_after_n(a, 3) - fmt.eprintln(b) // [a\n, b\n, c\n, d\ne\n] + fmt.println(b) // [a\n, b\n, c\n, d\ne\n] +``` +Returns: An allocated slice of strings split by line breaks with line breaks included. */ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -> []string { sep :: "\n" @@ -676,45 +1037,64 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) - } return lines } - /* - split the string `s` at every line break '\n' - returns the current split string every iteration till the string is consumed +Splits the input string at every line break '\n'. +Returns the current split string every iteration until the string is consumed. +Inputs: +- s: Pointer to the input string, which is modified during the search. + +Example: +```odin text := "a\nb\nc\nd\ne" for str in strings.split_lines_iterator(&text) { - fmt.eprintln(text) // every loop -> a b c d e + fmt.println(str) // every loop -> a b c d e } +``` +Returns: A tuple containing the resulting substring and a boolean indicating success. */ split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) { sep :: "\n" line = _split_iterator(s, sep, 0) or_return return _trim_cr(line), true } - /* - split the string `s` at every line break '\n' - returns the current split string every iteration till the string is consumed +Splits the input string at every line break '\n'. +Returns the current split string with line breaks included every iteration until the string is consumed. +Inputs: +- s: Pointer to the input string, which is modified during the search. + +Example: +```odin text := "a\nb\nc\nd\ne" for str in strings.split_lines_after_iterator(&text) { - fmt.eprintln(text) // every loop -> a\n b\n c\n d\n e\n + fmt.println(str) // every loop -> a\n b\n c\n d\n e\n } +``` +Returns: A tuple containing the resulting substring with line breaks included and a boolean indicating success. */ split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) { sep :: "\n" line = _split_iterator(s, sep, len(sep)) or_return return _trim_cr(line), true } - /* - returns the byte offset of the first byte `c` in the string `s` it finds, -1 when not found - can't find utf8 based runes +Returns the byte offset of the first byte c in the string s it finds, -1 when not found. +NOTE: Can't find UTF-8 based runes. - strings.index_byte("test", 't') -> 0 - strings.index_byte("test", 'e') -> 1 - strings.index_byte("test", 'x') -> -1 - strings.index_byte("teäst", 'ä') -> -1 +Inputs: +- s: The input string to search in. +- c: The byte to search for. + +Example: +```odin + strings.index_byte("test", 't') // -> 0 + strings.index_byte("test", 'e') // -> 1 + strings.index_byte("test", 'x') // -> -1 + strings.index_byte("teäst", 'ä') // -> -1 +``` +Returns: The byte offset of the first occurrence of c in s, or -1 if not found. */ index_byte :: proc(s: string, c: byte) -> int { for i := 0; i < len(s); i += 1 { @@ -724,15 +1104,18 @@ index_byte :: proc(s: string, c: byte) -> int { } return -1 } - /* - returns the byte offset of the last byte `c` in the string `s` it finds, -1 when not found - can't find utf8 based runes +Returns the byte offset of the last byte `c` in the string `s`, -1 when not found. +NOTE: Can't find UTF-8 based runes. - strings.index_byte("test", 't') -> 3 - strings.index_byte("test", 'e') -> 1 - strings.index_byte("test", 'x') -> -1 - strings.index_byte("teäst", 'ä') -> -1 +Example: +```odin + strings.last_index_byte("test", 't') // -> 3 + strings.last_index_byte("test", 'e') // -> 1 + strings.last_index_byte("test", 'x') // -> -1 + strings.last_index_byte("teäst", 'ä') // -> -1 +``` +Returns: The byte offset of the last occurrence of `c` in `s`, or -1 if not found. */ last_index_byte :: proc(s: string, c: byte) -> int { for i := len(s)-1; i >= 0; i -= 1 { @@ -742,20 +1125,22 @@ last_index_byte :: proc(s: string, c: byte) -> int { } return -1 } - - /* - returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found - avoids invalid runes +Returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found. +Invalid runes return -1 - strings.index_rune("abcädef", 'x') -> -1 - strings.index_rune("abcädef", 'a') -> 0 - strings.index_rune("abcädef", 'b') -> 1 - strings.index_rune("abcädef", 'c') -> 2 - strings.index_rune("abcädef", 'ä') -> 3 - strings.index_rune("abcädef", 'd') -> 5 - strings.index_rune("abcädef", 'e') -> 6 - strings.index_rune("abcädef", 'f') -> 7 +Example: +```odin + strings.index_rune("abcädef", 'x') // -> -1 + strings.index_rune("abcädef", 'a') // -> 0 + strings.index_rune("abcädef", 'b') // -> 1 + strings.index_rune("abcädef", 'c') // -> 2 + strings.index_rune("abcädef", 'ä') // -> 3 + strings.index_rune("abcädef", 'd') // -> 5 + strings.index_rune("abcädef", 'e') // -> 6 + strings.index_rune("abcädef", 'f') // -> 7 +``` +Returns: The byte offset of the first occurrence of `r` in `s`, or -1 if not found. */ index_rune :: proc(s: string, r: rune) -> int { switch { @@ -779,14 +1164,17 @@ index_rune :: proc(s: string, r: rune) -> int { } @private PRIME_RABIN_KARP :: 16777619 - /* - returns the byte offset of the string `substr` in the string `s`, -1 when not found - - strings.index("test", "t") -> 0 - strings.index("test", "te") -> 0 - strings.index("test", "st") -> 2 - strings.index("test", "tt") -> -1 +Returns the byte offset of the string `substr` in the string `s`, -1 when not found. + +Example: +```odin + strings.index("test", "t") // -> 0 + strings.index("test", "te") // -> 0 + strings.index("test", "st") // -> 2 + strings.index("test", "tt") // -> -1 +``` +Returns: The byte offset of the first occurrence of `substr` in `s`, or -1 if not found. */ index :: proc(s, substr: string) -> int { hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { @@ -837,14 +1225,17 @@ index :: proc(s, substr: string) -> int { } return -1 } - /* - returns the last byte offset of the string `substr` in the string `s`, -1 when not found - - strings.index("test", "t") -> 3 - strings.index("test", "te") -> 0 - strings.index("test", "st") -> 2 - strings.index("test", "tt") -> -1 +Returns the last byte offset of the string `substr` in the string `s`, -1 when not found. + +Example: +```odin + strings.last_index("test", "t") // -> 3 + strings.last_index("test", "te") // -> 0 + strings.last_index("test", "st") // -> 2 + strings.last_index("test", "tt") // -> -1 +``` +Returns: The byte offset of the last occurrence of `substr` in `s`, or -1 if not found. */ last_index :: proc(s, substr: string) -> int { hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { @@ -893,15 +1284,18 @@ last_index :: proc(s, substr: string) -> int { } return -1 } - /* - returns the index of any first char of `chars` found in `s`, -1 if not found - - strings.index_any("test", "s") -> 2 - strings.index_any("test", "se") -> 1 - strings.index_any("test", "et") -> 0 - strings.index_any("test", "set") -> 0 - strings.index_any("test", "x") -> -1 +Returns the index of any first char of `chars` found in `s`, -1 if not found. + +Example: +```odin + strings.index_any("test", "s") // -> 2 + strings.index_any("test", "se") // -> 1 + strings.index_any("test", "et") // -> 0 + strings.index_any("test", "set") // -> 0 + strings.index_any("test", "x") // -> -1 +``` +Returns: The index of the first character of `chars` found in `s`, or -1 if not found. */ index_any :: proc(s, chars: string) -> int { if chars == "" { @@ -934,16 +1328,22 @@ index_any :: proc(s, chars: string) -> int { } return -1 } - /* - returns the last matching index in `s` of any char in `chars` found in `s`, -1 if not found - iterates the string in reverse +Finds the last occurrence of any character in 'chars' within 's'. Iterates in reverse. - strings.last_index_any("test", "s") -> 2 - strings.last_index_any("test", "se") -> 2 - strings.last_index_any("test", "et") -> 3 - strings.last_index_any("test", "set") -> 3 - strings.last_index_any("test", "x") -> -1 +Inputs: +- s: The string to search in +- chars: The characters to look for + +Example: +```odin + strings.last_index_any("test", "s") // -> 2 + strings.last_index_any("test", "se") // -> 2 + strings.last_index_any("test", "et") // -> 3 + strings.last_index_any("test", "set") // -> 3 + strings.last_index_any("test", "x") // -> -1 +``` +Returns: The index of the last matching character, or -1 if not found */ last_index_any :: proc(s, chars: string) -> int { if chars == "" { @@ -993,7 +1393,15 @@ last_index_any :: proc(s, chars: string) -> int { } return -1 } +/* +Finds the first occurrence of any substring in 'substrs' within 's' +Inputs: +- s: The string to search in +- substrs: The substrings to look for + +Returns: A tuple containing the index of the first matching substring, and its length (width) +*/ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) { idx = -1 if s == "" || len(substrs) <= 0 { @@ -1023,16 +1431,22 @@ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) { } return } - /* - returns the count of the string `substr` found in the string `s` - returns the rune_count + 1 of the string `s` on empty `substr` +Counts the number of non-overlapping occurrences of 'substr' in 's' - strings.count("abbccc", "a") -> 1 - strings.count("abbccc", "b") -> 2 - strings.count("abbccc", "c") -> 3 - strings.count("abbccc", "ab") -> 1 - strings.count("abbccc", " ") -> 0 +Inputs: +- s: The string to search in +- substr: The substring to count + +Example: +```odin + strings.count("abbccc", "a") // -> 1 + strings.count("abbccc", "b") // -> 2 + strings.count("abbccc", "c") // -> 3 + strings.count("abbccc", "ab") // -> 1 + strings.count("abbccc", " ") // -> 0 +``` +Returns: The number of occurrences of 'substr' in 's', returns the rune_count + 1 of the string `s` on empty `substr` */ count :: proc(s, substr: string) -> int { if len(substr) == 0 { // special case @@ -1068,12 +1482,23 @@ count :: proc(s, substr: string) -> int { } return n } - /* - repeats the string `s` multiple `count` times and returns the allocated string - panics when `count` is below 0 +Repeats the string 's' 'count' times, concatenating the result - strings.repeat("abc", 2) -> "abcabc" +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to repeat +- count: The number of times to repeat 's' +- allocator: (default is context.allocator) + +WARNING: Panics if count < 0 + +Example: +```odin + strings.repeat("abc", 2) // -> "abcabc" +``` +Returns: The concatenated repeated string */ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string { if count < 0 { @@ -1090,28 +1515,48 @@ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string } return string(b) } - /* - replaces all instances of `old` in the string `s` with the `new` string - returns the `output` string and true when an a allocation through a replace happened +Replaces all occurrences of 'old' in 's' with 'new' - strings.replace_all("xyzxyz", "xyz", "abc") -> "abcabc", true - strings.replace_all("xyzxyz", "abc", "xyz") -> "xyzxyz", false - strings.replace_all("xyzxyz", "xy", "z") -> "zzzz", true +*Allocates Using Provided Allocator* + +Inputs: +- s: The string to modify +- old: The substring to replace +- new: The substring to replace 'old' with +- allocator: The allocator to use for the new string (default is context.allocator) + +Example: +```odin + strings.replace_all("xyzxyz", "xyz", "abc") // -> "abcabc", true + strings.replace_all("xyzxyz", "abc", "xyz") // -> "xyzxyz", false + strings.replace_all("xyzxyz", "xy", "z") // -> "zzzz", true +``` +Returns: A tuple containing the modified string and a boolean indicating if an allocation occurred during the replacement */ replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { return replace(s, old, new, -1, allocator) } - /* - replaces `n` instances of `old` in the string `s` with the `new` string - if n < 0, no limit on the number of replacements - returns the `output` string and true when an a allocation through a replace happened +Replaces n instances of old in the string s with the new string - strings.replace("xyzxyz", "xyz", "abc", 2) -> "abcabc", true - strings.replace("xyzxyz", "xyz", "abc", 1) -> "abcxyz", true - strings.replace("xyzxyz", "abc", "xyz", -1) -> "xyzxyz", false - strings.replace("xyzxyz", "xy", "z", -1) -> "zzzz", true +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- old: The substring to be replaced +- new: The replacement string +- n: The number of instances to replace (if n < 0, no limit on the number of replacements) +- allocator: (default: context.allocator) + +Example: +```odin + strings.replace("xyzxyz", "xyz", "abc", 2) // -> "abcabc", true + strings.replace("xyzxyz", "xyz", "abc", 1) // -> "abcxyz", true + strings.replace("xyzxyz", "abc", "xyz", -1) // -> "xyzxyz", false + strings.replace("xyzxyz", "xy", "z", -1) // -> "zzzz", true +``` +Returns: A tuple containing the modified string and a boolean indicating if an allocation occurred during the replacement */ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) { if old == new || n == 0 { @@ -1152,44 +1597,61 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) -> output = string(t[0:w]) return } - /* - removes the `key` string `n` times from the `s` string - if n < 0, no limit on the number of removes - returns the `output` string and true when an a allocation through a remove happened +Removes the key string n times from the s string - strings.remove("abcabc", "abc", 1) -> "abc", true - strings.remove("abcabc", "abc", -1) -> "", true - strings.remove("abcabc", "a", -1) -> "bcbc", true - strings.remove("abcabc", "x", -1) -> "abcabc", false +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- key: The substring to be removed +- n: The number of instances to remove (if n < 0, no limit on the number of removes) +- allocator: (default: context.allocator) + +Example: +```odin + strings.remove("abcabc", "abc", 1) // -> "abc", true + strings.remove("abcabc", "abc", -1) // -> "", true + strings.remove("abcabc", "a", -1) // -> "bcbc", true + strings.remove("abcabc", "x", -1) // -> "abcabc", false +``` +Returns: A tuple containing the modified string and a boolean indicating if an allocation occurred during the removal */ remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) { return replace(s, key, "", n, allocator) } - /* - removes all the `key` string instanes from the `s` string - returns the `output` string and true when an a allocation through a remove happened +Removes all the key string instances from the s string - strings.remove("abcabc", "abc") -> "", true - strings.remove("abcabc", "a") -> "bcbc", true - strings.remove("abcabc", "x") -> "abcabc", false +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- key: The substring to be removed +- allocator: (default: context.allocator) + +Example: +```odin + strings.remove_all("abcabc", "abc") // -> "", true + strings.remove_all("abcabc", "a") // -> "bcbc", true + strings.remove_all("abcabc", "x") // -> "abcabc", false +``` +Returns: A tuple containing the modified string and a boolean indicating if an allocation occurred during the removal */ remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { return remove(s, key, -1, allocator) } - +// Returns true if the r rune is an ASCII space character ('\t', '\n', '\v', '\f', '\r', ' ') @(private) _ascii_space := [256]bool{'\t' = true, '\n' = true, '\v' = true, '\f' = true, '\r' = true, ' ' = true} -// return true when the `r` rune is '\t', '\n', '\v', '\f', '\r' or ' ' +// Returns true when the `r` rune is '\t', '\n', '\v', '\f', '\r' or ' ' is_ascii_space :: proc(r: rune) -> bool { if r < utf8.RUNE_SELF { return _ascii_space[u8(r)] } return false } - -// returns true when the `r` rune is any asci or utf8 based whitespace +// Returns true if the r rune is any ASCII or UTF-8 based whitespace character is_space :: proc(r: rune) -> bool { if r < 0x2000 { switch r { @@ -1207,24 +1669,30 @@ is_space :: proc(r: rune) -> bool { } return false } - -// returns true when the `r` rune is a nul byte +// Returns true if the `r` rune is a null byte (0x0) is_null :: proc(r: rune) -> bool { return r == 0x0000 } - /* - runs trough the `s` string linearly and watches wether the `p` procedure matches the `truth` bool - returns the rune offset or -1 when no match was found +Finds the index of the first rune in the string s for which the procedure p returns the same value as truth +Inputs: +- s: The input string +- p: A procedure that takes a rune and returns a boolean +- truth: The boolean value to be matched (default: true) + +Example: +```odin call :: proc(r: rune) -> bool { return r == 'a' } - strings.index_proc("abcabc", call) -> 0 - strings.index_proc("cbacba", call) -> 2 - strings.index_proc("cbacba", call, false) -> 0 - strings.index_proc("abcabc", call, false) -> 1 - strings.index_proc("xyz", call) -> -1 + strings.index_proc("abcabc", call) // -> 0 + strings.index_proc("cbacba", call) // -> 2 + strings.index_proc("cbacba", call, false) // -> 0 + strings.index_proc("abcabc", call, false) // -> 1 + strings.index_proc("xyz", call) // -> -1 +``` +Returns: The index of the first matching rune, or -1 if no match was found */ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int { for r, i in s { @@ -1234,8 +1702,7 @@ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int { } return -1 } - -// same as `index_proc` but with a `p` procedure taking a rawptr for state +// Same as `index_proc`, but the procedure p takes a raw pointer for state index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int { for r, i in s { if p(state, r) == truth { @@ -1244,8 +1711,7 @@ index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: r } return -1 } - -// same as `index_proc` but runs through the string in reverse +// Finds the index of the *last* rune in the string s for which the procedure p returns the same value as truth last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int { // TODO(bill): Probably use Rabin-Karp Search for i := len(s); i > 0; { @@ -1257,8 +1723,7 @@ last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int } return -1 } - -// same as `index_proc_with_state` but runs through the string in reverse +// Same as `index_proc_with_state`, runs through the string in reverse last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int { // TODO(bill): Probably use Rabin-Karp Search for i := len(s); i > 0; { @@ -1270,16 +1735,21 @@ last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta } return -1 } - /* - trims the input string `s` until the procedure `p` returns false - does not allocate - only returns a cut variant of the input string - returns an empty string when no match was found at all +Trims the input string s from the left until the procedure p returns false +Inputs: +- s: The input string +- p: A procedure that takes a rune and returns a boolean + +Example: +```odin find :: proc(r: rune) -> bool { return r != 'i' } - strings.trim_left_proc("testing", find) -> "ing" + strings.trim_left_proc("testing", find) // -> "ing" +``` +Returns: The trimmed string as a slice of the original */ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string { i := index_proc(s, p, false) @@ -1288,10 +1758,15 @@ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string { } return s[i:] } - /* - trims the input string `s` until the procedure `p` with state returns false - returns an empty string when no match was found at all +Trims the input string s from the left until the procedure p with state returns false + +Inputs: +- s: The input string +- p: A procedure that takes a raw pointer and a rune and returns a boolean +- state: The raw pointer to be passed to the procedure p + +Returns: The trimmed string as a slice of the original */ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string { i := index_proc_with_state(s, p, state, false) @@ -1300,16 +1775,21 @@ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, stat } return s[i:] } - /* - trims the input string `s` from the right until the procedure `p` returns false - does not allocate - only returns a cut variant of the input string - returns an empty string when no match was found at all +Trims the input string s from the right until the procedure p returns false +Inputs: +- s: The input string +- p: A procedure that takes a rune and returns a boolean + +Example: +```odin find :: proc(r: rune) -> bool { return r != 't' } - strings.trim_left_proc("testing", find) -> "test" + strings.trim_right_proc("testing", find) -> "test" +``` +Returns: The trimmed string as a slice of the original */ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string { i := last_index_proc(s, p, false) @@ -1321,10 +1801,15 @@ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string { } return s[0:i] } - /* - trims the input string `s` from the right until the procedure `p` with state returns false - returns an empty string when no match was found at all +Trims the input string s from the right until the procedure p with state returns false + +Inputs: +- s: The input string +- p: A procedure that takes a raw pointer and a rune and returns a boolean +- state: The raw pointer to be passed to the procedure p + +Returns: The trimmed string as a slice of the original, empty when no match */ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string { i := last_index_proc_with_state(s, p, state, false) @@ -1336,8 +1821,7 @@ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta } return s[0:i] } - -// procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison +// Procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison is_in_cutset :: proc(state: rawptr, r: rune) -> bool { if state == nil { return false @@ -1350,8 +1834,15 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> bool { } return false } +/* +Trims the cutset string from the s string -// trims the `cutset` string from the `s` string +Inputs: +- s: The input string +- cutset: The set of characters to be trimmed from the left of the input string + +Returns: The trimmed string as a slice of the original +*/ trim_left :: proc(s: string, cutset: string) -> string { if s == "" || cutset == "" { return s @@ -1359,8 +1850,15 @@ trim_left :: proc(s: string, cutset: string) -> string { state := cutset return trim_left_proc_with_state(s, is_in_cutset, &state) } +/* +Trims the cutset string from the s string from the right -// trims the `cutset` string from the `s` string from the right +Inputs: +- s: The input string +- cutset: The set of characters to be trimmed from the right of the input string + +Returns: The trimmed string as a slice of the original +*/ trim_right :: proc(s: string, cutset: string) -> string { if s == "" || cutset == "" { return s @@ -1368,48 +1866,96 @@ trim_right :: proc(s: string, cutset: string) -> string { state := cutset return trim_right_proc_with_state(s, is_in_cutset, &state) } +/* +Trims the cutset string from the s string, both from left and right -// trims the `cutset` string from the `s` string, both from left and right +Inputs: +- s: The input string +- cutset: The set of characters to be trimmed from both sides of the input string + +Returns: The trimmed string as a slice of the original +*/ trim :: proc(s: string, cutset: string) -> string { return trim_right(trim_left(s, cutset), cutset) } +/* +Trims until a valid non-space rune from the left, "\t\txyz\t\t" -> "xyz\t\t" -// trims until a valid non space rune: "\t\txyz\t\t" -> "xyz\t\t" +Inputs: +- s: The input string + +Returns: The trimmed string as a slice of the original +*/ trim_left_space :: proc(s: string) -> string { return trim_left_proc(s, is_space) } +/* +Trims from the right until a valid non-space rune, "\t\txyz\t\t" -> "\t\txyz" -// trims from the right until a valid non space rune: "\t\txyz\t\t" -> "\t\txyz" +Inputs: +- s: The input string + +Returns: The trimmed string as a slice of the original +*/ trim_right_space :: proc(s: string) -> string { return trim_right_proc(s, is_space) } +/* +Trims from both sides until a valid non-space rune, "\t\txyz\t\t" -> "xyz" -// trims from both sides until a valid non space rune: "\t\txyz\t\t" -> "xyz" +Inputs: +- s: The input string + +Returns: The trimmed string as a slice of the original +*/ trim_space :: proc(s: string) -> string { return trim_right_space(trim_left_space(s)) } +/* +Trims null runes from the left, "\x00\x00testing\x00\x00" -> "testing\x00\x00" -// trims nul runes from the left: "\x00\x00testing\x00\x00" -> "testing\x00\x00" +Inputs: +- s: The input string + +Returns: The trimmed string as a slice of the original +*/ trim_left_null :: proc(s: string) -> string { return trim_left_proc(s, is_null) } +/* +Trims null runes from the right, "\x00\x00testing\x00\x00" -> "\x00\x00testing" -// trims nul runes from the right: "\x00\x00testing\x00\x00" -> "\x00\x00testing" +Inputs: +- s: The input string + +Returns: The trimmed string as a slice of the original +*/ trim_right_null :: proc(s: string) -> string { return trim_right_proc(s, is_null) } +/* +Trims null runes from both sides, "\x00\x00testing\x00\x00" -> "testing" -// trims nul runes from both sides: "\x00\x00testing\x00\x00" -> "testing" +Inputs: +- s: The input string +Returns: The trimmed string as a slice of the original +*/ trim_null :: proc(s: string) -> string { return trim_right_null(trim_left_null(s)) } - /* - trims a `prefix` string from the start of the `s` string and returns the trimmed string - returns the input string `s` when no prefix was found +Trims a prefix string from the start of the s string and returns the trimmed string - strings.trim_prefix("testing", "test") -> "ing" - strings.trim_prefix("testing", "abc") -> "testing" +Inputs: +- s: The input string +- prefix: The prefix string to be removed + +Example: +```odin + strings.trim_prefix("testing", "test") // -> "ing" + strings.trim_prefix("testing", "abc") // -> "testing" +``` +Returns: The trimmed string as a slice of original, or the input string if no prefix was found */ trim_prefix :: proc(s, prefix: string) -> string { if has_prefix(s, prefix) { @@ -1417,13 +1963,19 @@ trim_prefix :: proc(s, prefix: string) -> string { } return s } - /* - trims a `suffix` string from the end of the `s` string and returns the trimmed string - returns the input string `s` when no suffix was found +Trims a suffix string from the end of the s string and returns the trimmed string - strings.trim_suffix("todo.txt", ".txt") -> "todo" - strings.trim_suffix("todo.doc", ".txt") -> "todo.doc" +Inputs: +- s: The input string +- suffix: The suffix string to be removed + +Example: +```odin + strings.trim_suffix("todo.txt", ".txt") // -> "todo" + strings.trim_suffix("todo.doc", ".txt") // -> "todo.doc" +``` +Returns: The trimmed string as a slice of original, or the input string if no suffix was found */ trim_suffix :: proc(s, suffix: string) -> string { if has_suffix(s, suffix) { @@ -1431,14 +1983,25 @@ trim_suffix :: proc(s, suffix: string) -> string { } return s } - /* - splits the input string `s` by all possible `substrs` []string - returns the allocated []string, nil on any empty substring or no matches +Splits the input string s by all possible substrs and returns an allocated array of strings +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- substrs: An array of substrings used for splitting +- allocator: (default is context.allocator) + +NOTE: Allocation occurs for the array, the splits are all slices of the original string. + +Example: +```odin splits := [?]string { "---", "~~~", ".", "_", "," } res := strings.split_multi("testing,this.out_nice---done~~~last", splits[:]) - fmt.eprintln(res) // -> [testing, this, out, nice, done, last] + fmt.println(res) // -> [testing, this, out, nice, done, last] +``` +Returns: An array of strings, or nil on empty substring or no matches */ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator) -> []string #no_bounds_check { if s == "" || len(substrs) <= 0 { @@ -1480,15 +2043,21 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator assert(len(results) == n) return results[:] } - /* - splits the input string `s` by all possible `substrs` []string in an iterator fashion - returns the split string every iteration, the full string on no match - splits := [?]string { "---", "~~~", ".", "_", "," } +Splits the input string s by all possible substrs in an iterator fashion. The full string is returned if no match. + +Inputs: +- it: A pointer to the input string +- substrs: An array of substrings used for splitting + +Example: +```odin it := "testing,this.out_nice---done~~~last" for str in strings.split_multi_iterate(&it, splits[:]) { - fmt.eprintln(str) // every iteration -> [testing, this, out, nice, done, last] + fmt.println(str) // every iteration // -> [testing, this, out, nice, done, last] } +``` +Returns: A tuple containing the split string and a boolean indicating success or failure */ split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok: bool) #no_bounds_check { if it == nil || len(it) == 0 || len(substrs) <= 0 { @@ -1515,9 +2084,24 @@ split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok: ok = true return } +/* +Replaces invalid UTF-8 characters in the input string with a specified replacement string. Adjacent invalid bytes are only replaced once. -// scrub scruvs invalid utf-8 characters and replaces them with the replacement string -// Adjacent invalid bytes are only replaced once +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- replacement: The string used to replace invalid UTF-8 characters +- allocator: (default is context.allocator) + +Example: +```odin + text := "Hello\xC0\x80World" + result := strings.scrub(text, "?") + fmt.println(result) // -> "Hello?World" +``` +Returns: A new string with invalid UTF-8 characters replaced +*/ scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string { str := s b: Builder @@ -1549,13 +2133,22 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> return to_string(b) } - /* - returns a reversed version of the `s` string +Reverses the input string s +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- allocator: (default is context.allocator) + +Example: +```odin a := "abcxyz" b := strings.reverse(a) - fmt.eprintln(a, b) // abcxyz zyxcba + fmt.println(a, b) // -> abcxyz zyxcba +``` +Returns: A reversed version of the input string */ reverse :: proc(s: string, allocator := context.allocator) -> string { str := s @@ -1571,14 +2164,25 @@ reverse :: proc(s: string, allocator := context.allocator) -> string { } return string(buf) } - /* - expands the string to a grid spaced by `tab_size` whenever a `\t` character appears - returns the tabbed string, panics on tab_size <= 0 +Expands the input string by replacing tab characters with spaces to align to a specified tab size - strings.expand_tabs("abc1\tabc2\tabc3", 4) -> abc1 abc2 abc3 - strings.expand_tabs("abc1\tabc2\tabc3", 5) -> abc1 abc2 abc3 - strings.expand_tabs("abc1\tabc2\tabc3", 6) -> abc1 abc2 abc3 +*Allocates Using Provided Allocator* + +Inputs: +- s: The input string +- tab_size: The number of spaces to use for each tab character +- allocator: (default is context.allocator) + +Example: +```odin + text := "abc1\tabc2\tabc3" + result := strings.expand_tabs(text, 4) + fmt.println(result) // -> "abc1 abc2 abc3" +``` +WARNING: Panics if tab_size <= 0 + +Returns: A new string with tab characters expanded to the specified tab size */ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string { if tab_size <= 0 { @@ -1621,16 +2225,21 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> return to_string(b) } - /* - splits the `str` string by the seperator `sep` string and returns 3 parts - `head`: before the split, `match`: the seperator, `tail`: the end of the split - returns the input string when the `sep` was not found +Splits the input string str by the separator sep string and returns 3 parts. The values are slices of the original string. +Inputs: +- str: The input string +- sep: The separator string + +Example: +```odin text := "testing this out" - strings.partition(text, " this ") -> head: "testing", match: " this ", tail: "out" - strings.partition(text, "hi") -> head: "testing t", match: "hi", tail: "s out" - strings.partition(text, "xyz") -> head: "testing this out", match: "", tail: "" + strings.partition(text, " this ") // -> head: "testing", match: " this ", tail: "out" + strings.partition(text, "hi") // -> head: "testing t", match: "hi", tail: "s out" + strings.partition(text, "xyz") // -> head: "testing this out", match: "", tail: "" +``` +Returns: A tuple with head (before the split), match (the separator), and tail (the end of the split) strings */ partition :: proc(str, sep: string) -> (head, match, tail: string) { i := index(str, sep) @@ -1644,10 +2253,21 @@ partition :: proc(str, sep: string) -> (head, match, tail: string) { tail = str[i+len(sep):] return } - +// Alias for centre_justify center_justify :: centre_justify // NOTE(bill): Because Americans exist +/* +Centers the input string within a field of specified length by adding pad string on both sides, if its length is less than the target length. -// centre_justify returns a string with a pad string at boths sides if the str's rune length is smaller than length +*Allocates Using Provided Allocator* + +Inputs: +- str: The input string +- length: The desired length of the centered string +- pad: The string used for padding on both sides +- allocator: (default is context.allocator) + +Returns: A new string centered within a field of the specified length +*/ centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) if n >= length || pad == "" { @@ -1669,8 +2289,19 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte return to_string(b) } +/* +Left-justifies the input string within a field of specified length by adding pad string on the right side, if its length is less than the target length. -// left_justify returns a string with a pad string at right side if the str's rune length is smaller than length +*Allocates Using Provided Allocator* + +Inputs: +- str: The input string +- length: The desired length of the left-justified string +- pad: The string used for padding on the right side +- allocator: (default is context.allocator) + +Returns: A new string left-justified within a field of the specified length +*/ left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) if n >= length || pad == "" { @@ -1691,8 +2322,19 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context return to_string(b) } +/* +Right-justifies the input string within a field of specified length by adding pad string on the left side, if its length is less than the target length. -// right_justify returns a string with a pad string at left side if the str's rune length is smaller than length +*Allocates Using Provided Allocator* + +Inputs: +- str: The input string +- length: The desired length of the right-justified string +- pad: The string used for padding on the left side +- allocator: (default is context.allocator) + +Returns: A new string right-justified within a field of the specified length +*/ right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) if n >= length || pad == "" { @@ -1713,10 +2355,15 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex return to_string(b) } +/* +Writes a given pad string a specified number of times to an io.Writer - - - +Inputs: +- w: The io.Writer to write the pad string to +- pad: The pad string to be written +- pad_len: The length of the pad string +- remains: The number of times to write the pad string +*/ @private write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { repeats := remains / pad_len @@ -1734,10 +2381,17 @@ write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { p = p[width:] } } +/* +Splits a string into a slice of substrings at each instance of one or more consecutive white space characters, as defined by unicode.is_space +*Allocates Using Provided Allocator* -// fields splits the string s around each instance of one or more consecutive white space character, defined by unicode.is_space -// returning a slice of substrings of s or an empty slice if s only contains white space +Inputs: +- s: The input string +- allocator: (default is context.allocator) + +Returns: A slice of substrings of the input string, or an empty slice if the input string only contains white space +*/ fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds_check { n := 0 was_space := 1 @@ -1786,14 +2440,20 @@ fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds } return a } +/* +Splits a string into a slice of substrings at each run of unicode code points `ch` satisfying the predicate f(ch) +*Allocates Using Provided Allocator* -// fields_proc splits the string s at each run of unicode code points `ch` satisfying f(ch) -// returns a slice of substrings of s -// If all code points in s satisfy f(ch) or string is empty, an empty slice is returned -// -// fields_proc makes no guarantee about the order in which it calls f(ch) -// it assumes that `f` always returns the same value for a given ch +Inputs: +- s: The input string +- f: A predicate function to determine the split points +- allocator: (default is context.allocator) + +NOTE: fields_proc makes no guarantee about the order in which it calls f(ch), it assumes that `f` always returns the same value for a given ch + +Returns: 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 +*/ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> []string #no_bounds_check { substrings := make([dynamic]string, 0, 32, allocator) @@ -1820,10 +2480,16 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc return substrings[:] } +/* +Retrieves the first non-space substring from a mutable string reference and advances the reference. s is advanced from any space after the substring, or be an empty string if the substring was the remaining characters +Inputs: +- s: A mutable string reference to be iterated -// `fields_iterator` returns the first run of characters in `s` that does not contain white space, defined by `unicode.is_space` -// `s` will then start from any space after the substring, or be an empty string if the substring was the remaining characters +Returns: +- field: The first non-space substring found +- ok: A boolean indicating if a non-space substring was found +*/ fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) { start, end := -1, -1 for r, offset in s { @@ -1852,10 +2518,21 @@ fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) { s^ = s[len(s):] return } +/* +Computes the Levenshtein edit distance between two strings -// `levenshtein_distance` returns the Levenshtein edit distance between 2 strings. -// This is a single-row-version of the Wagner–Fischer algorithm, based on C code by Martin Ettl. -// Note: allocator isn't used if the length of string b in runes is smaller than 64. +*Allocates Using Provided Allocator (deletion occurs internal to proc)* + +NOTE: Does not perform internal allocation if Length of String b in Runes is Smaller Than 64 + +Inputs: +- a, b: The two strings to compare +- allocator: (default is context.allocator) + +Returns: The Levenshtein edit distance between the two strings + +NOTE: This implementation is a single-row-version of the Wagner–Fischer algorithm, based on C code by Martin Ettl. +*/ levenshtein_distance :: proc(a, b: string, allocator := context.allocator) -> int { LEVENSHTEIN_DEFAULT_COSTS: []int : { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, From f5d66bcb6fcb44a07dc95c5f7aa15b742e8b6c92 Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Mon, 27 Mar 2023 22:00:53 -0700 Subject: [PATCH 2/8] transform into odin-site parsable format --- core/strings/ascii_set.odin | 8 +- core/strings/builder.odin | 308 +++++--- core/strings/conversion.odin | 214 ++++-- core/strings/intern.odin | 16 +- core/strings/reader.odin | 54 +- core/strings/strings.odin | 1355 +++++++++++++++++++++++----------- 6 files changed, 1314 insertions(+), 641 deletions(-) diff --git a/core/strings/ascii_set.odin b/core/strings/ascii_set.odin index 7b6f46f5b..d597cad6d 100644 --- a/core/strings/ascii_set.odin +++ b/core/strings/ascii_set.odin @@ -12,10 +12,10 @@ Ascii_Set :: distinct [8]u32 /* Creates an Ascii_Set with unique characters from the input string. -Inputs: +**Inputs** - chars: A string containing characters to include in the Ascii_Set. -Returns: +**Returns** - as: An Ascii_Set with unique characters from the input string. - ok: false if any character in the input string is not a valid ASCII character. */ @@ -33,11 +33,11 @@ ascii_set_make :: proc(chars: string) -> (as: Ascii_Set, ok: bool) #no_bounds_ch /* Determines if a given char is contained within an Ascii_Set. -Inputs: +**Inputs** - as: The Ascii_Set to search. - c: The char to check for in the Ascii_Set. -Returns: A boolean indicating if the byte is contained in the Ascii_Set (true) or not (false). +**Returns** A boolean indicating if the byte is contained in the Ascii_Set (true) or not (false). */ ascii_set_contains :: proc(as: Ascii_Set, c: byte) -> bool #no_bounds_check { return as[c>>5] & (1<<(c&31)) != 0 diff --git a/core/strings/builder.odin b/core/strings/builder.odin index f405f285b..db5215dee 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -7,10 +7,10 @@ import "core:io" /* Type definition for a procedure that flushes a Builder -Inputs: +**Inputs** - b: A pointer to the Builder -Returns: A boolean indicating whether the Builder should be reset +**Returns** A boolean indicating whether the Builder should be reset */ Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool) /* @@ -26,10 +26,10 @@ Produces a Builder with a default length of 0 and cap of 16 *Allocates Using Provided Allocator* -Inputs: +**Inputs** - allocator: (default is context.allocator) -Returns: A new Builder +**Returns** A new Builder */ builder_make_none :: proc(allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, allocator)} @@ -39,11 +39,11 @@ Produces a Builder with a specified length and cap of max(16,len) byte buffer *Allocates Using Provided Allocator* -Inputs: +**Inputs** - len: The desired length of the Builder's buffer - allocator: (default is context.allocator) -Returns: A new Builder +**Returns** A new Builder */ builder_make_len :: proc(len: int, allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, len, allocator)} @@ -53,12 +53,12 @@ Produces a Builder with a specified length and cap *Allocates Using Provided Allocator* -Inputs: +**Inputs** - len: The desired length of the Builder's buffer - cap: The desired capacity of the Builder's buffer, cap is max(cap, len) - allocator: (default is context.allocator) -Returns: A new Builder +**Returns** A new Builder */ builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, len, cap, allocator)} @@ -75,11 +75,11 @@ It replaces the existing `buf` *Allocates Using Provided Allocator* -Inputs: +**Inputs** - b: A pointer to the Builder - allocator: (default is context.allocator) -Returns: initialized ^Builder +**Returns** initialized ^Builder */ builder_init_none :: proc(b: ^Builder, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, allocator) @@ -91,12 +91,12 @@ It replaces the existing `buf` *Allocates Using Provided Allocator* -Inputs: +**Inputs** - b: A pointer to the Builder - len: The desired length of the Builder's buffer - allocator: (default is context.allocator) -Returns: Initialized ^Builder +**Returns** Initialized ^Builder */ builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, len, allocator) @@ -106,19 +106,19 @@ builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) Initializes a Builder with a specified length and cap It replaces the existing `buf` -Inputs: +**Inputs** - b: A pointer to the Builder - len: The desired length of the Builder's buffer - cap: The desired capacity of the Builder's buffer, actual max(len,cap) - allocator: (default is context.allocator) -Returns: A pointer to the initialized Builder +**Returns** A pointer to the initialized Builder */ builder_init_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, len, cap, allocator) return b } -// overload simple `builder_init_*` with or without len / ap parameters +// Overload simple `builder_init_*` with or without len / ap parameters builder_init :: proc{ builder_init_none, builder_init_len, @@ -159,10 +159,10 @@ _builder_stream_vtable := &_builder_stream_vtable_obj /* Returns an io.Stream from a Builder -Inputs: +**Inputs** - b: A pointer to the Builder -Returns: An io.Stream +**Returns** An io.Stream */ to_stream :: proc(b: ^Builder) -> io.Stream { return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b} @@ -170,10 +170,10 @@ to_stream :: proc(b: ^Builder) -> io.Stream { /* Returns an io.Writer from a Builder -Inputs: +**Inputs** - b: A pointer to the Builder -Returns: An io.Writer +**Returns** An io.Writer */ to_writer :: proc(b: ^Builder) -> io.Writer { return io.to_writer(to_stream(b)) @@ -181,7 +181,7 @@ to_writer :: proc(b: ^Builder) -> io.Writer { /* Deletes and clears the Builder byte buffer content -Inputs: +**Inputs** - b: A pointer to the Builder */ builder_destroy :: proc(b: ^Builder) { @@ -191,7 +191,7 @@ builder_destroy :: proc(b: ^Builder) { /* Reserves the Builder byte buffer to a specific capacity, when it's higher than before -Inputs: +**Inputs** - b: A pointer to the Builder - cap: The desired capacity for the Builder's buffer */ @@ -201,7 +201,7 @@ builder_grow :: proc(b: ^Builder, cap: int) { /* Clears the Builder byte buffer content (sets len to zero) -Inputs: +**Inputs** - b: A pointer to the Builder */ builder_reset :: proc(b: ^Builder) { @@ -212,17 +212,26 @@ Creates a Builder from a slice of bytes with the same slice length as its capaci *Uses Nil Allocator - Does NOT allocate* -Inputs: +**Inputs** - backing: A slice of bytes to be used as the backing buffer Example: -```odin - bytes: [8]byte // <-- gets filled - builder := strings.builder_from_bytes(bytes[:]) - strings.write_byte(&builder, 'a') // -> "a" - strings.write_byte(&builder, 'b') // -> "ab" -``` -Returns: A new Builder + + import "core:fmt" + import "core:strings" + strings_builder_from_bytes_example :: proc() { + bytes: [8]byte // <-- gets filled + builder := strings.builder_from_bytes(bytes[:]) + fmt.println(strings.write_byte(&builder, 'a')) // -> "a" + fmt.println(strings.write_byte(&builder, 'b')) // -> "ab" + } + +Output: + + a + ab + +**Returns** A new Builder */ builder_from_bytes :: proc(backing: []byte) -> Builder { s := transmute(runtime.Raw_Slice)backing @@ -241,10 +250,10 @@ builder_from_slice :: builder_from_bytes /* Casts the Builder byte buffer to a string and returns it -Inputs: +**Inputs** - b: A Builder -Returns: The contents of the Builder's buffer, as a string +**Returns** The contents of the Builder's buffer, as a string */ to_string :: proc(b: Builder) -> string { return string(b.buf[:]) @@ -252,10 +261,10 @@ to_string :: proc(b: Builder) -> string { /* Returns the length of the Builder's buffer, in bytes -Inputs: +**Inputs** - b: A Builder -Returns: The length of the Builder's buffer +**Returns** The length of the Builder's buffer */ builder_len :: proc(b: Builder) -> int { return len(b.buf) @@ -263,10 +272,10 @@ builder_len :: proc(b: Builder) -> int { /* Returns the capacity of the Builder's buffer, in bytes -Inputs: +**Inputs** - b: A Builder -Returns: The capacity of the Builder's buffer +**Returns** The capacity of the Builder's buffer */ builder_cap :: proc(b: Builder) -> int { return cap(b.buf) @@ -274,10 +283,10 @@ builder_cap :: proc(b: Builder) -> int { /* The free space left in the Builder's buffer, in bytes -Inputs: +**Inputs** - b: A Builder -Returns: The available space left in the Builder's buffer +**Returns** The available space left in the Builder's buffer */ builder_space :: proc(b: Builder) -> int { return cap(b.buf) - len(b.buf) @@ -285,20 +294,29 @@ builder_space :: proc(b: Builder) -> int { /* Appends a byte to the Builder and returns the number of bytes appended -Inputs: +**Inputs** - b: A pointer to the Builder - x: The byte to be appended Example: -```odin - builder := strings.builder_make() - strings.write_byte(&builder, 'a') // 1 - strings.write_byte(&builder, 'b') // 1 - fmt.println(strings.to_string(builder)) // -> ab -``` + + import "core:fmt" + import "core:strings" + + strings_write_byte_example :: proc() { + builder := strings.builder_make() + strings.write_byte(&builder, 'a') // 1 + strings.write_byte(&builder, 'b') // 1 + fmt.println(strings.to_string(builder)) // -> ab + } + +**Output** + + ab + NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of bytes appended +**Returns** The number of bytes appended */ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) { n0 := len(b.buf) @@ -309,20 +327,25 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) { /* Appends a slice of bytes to the Builder and returns the number of bytes appended -Inputs: +**Inputs** - b: A pointer to the Builder - x: The slice of bytes to be appended Example: -```odin - builder := strings.builder_make() - bytes := [?]byte { 'a', 'b', 'c' } - strings.write_bytes(&builder, bytes[:]) // 3 - fmt.println(strings.to_string(builder)) // -> abc -``` + + import "core:fmt" + import "core:strings" + + strings_write_bytes_example :: proc() { + builder := strings.builder_make() + bytes := [?]byte { 'a', 'b', 'c' } + strings.write_bytes(&builder, bytes[:]) // 3 + fmt.println(strings.to_string(builder)) // -> abc + } + NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of bytes appended +**Returns** The number of bytes appended */ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { n0 := len(b.buf) @@ -333,20 +356,29 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { /* Appends a single rune to the Builder and returns the number of bytes written and an `io.Error` -Inputs: +**Inputs** - b: A pointer to the Builder - r: The rune to be appended Example: -```odin - builder := strings.builder_make() - strings.write_rune(&builder, 'ä') // 2 None - strings.write_rune(&builder, 'b') // 1 None - fmt.println(strings.to_string(builder)) // -> äb -``` + + import "core:fmt" + import "core:strings" + + strings_write_rune_example :: proc() { + builder := strings.builder_make() + strings.write_rune(&builder, 'ä') // 2 None + strings.write_rune(&builder, 'b') // 1 None + fmt.println(strings.to_string(builder)) // -> äb + } + +Output: + + äb + NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of bytes written and an io.Error (if any) +**Returns** The number of bytes written and an io.Error (if any) */ write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) { return io.write_rune(to_writer(b), r) @@ -354,21 +386,30 @@ write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) { /* Appends a quoted rune to the Builder and returns the number of bytes written -Inputs: +**Inputs** - b: A pointer to the Builder - r: The rune to be appended Example: -```odin - builder := strings.builder_make() - strings.write_string(&builder, "abc") // 3 - strings.write_quoted_rune(&builder, 'ä') // 4 - strings.write_string(&builder, "abc") // 3 - fmt.println(strings.to_string(builder)) // -> abc'ä'abc -``` + + import "core:fmt" + import "core:strings" + + strings_write_quoted_rune_example :: proc() { + builder := strings.builder_make() + strings.write_string(&builder, "abc") // 3 + strings.write_quoted_rune(&builder, 'ä') // 4 + strings.write_string(&builder, "abc") // 3 + fmt.println(strings.to_string(builder)) // -> abc'ä'abc + } + +Output: + + abc'ä'abc + NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of bytes written +**Returns** The number of bytes written */ write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) { return io.write_quoted_rune(to_writer(b), r) @@ -376,20 +417,29 @@ write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) { /* Appends a string to the Builder and returns the number of bytes written -Inputs: +**Inputs** - b: A pointer to the Builder - s: The string to be appended Example: -```odin - builder := strings.builder_make() - strings.write_string(&builder, "a") // 1 - strings.write_string(&builder, "bc") // 2 - fmt.println(strings.to_string(builder)) // -> abc -``` + + import "core:fmt" + import "core:strings" + + strings_write_string_example :: proc() { + builder := strings.builder_make() + strings.write_string(&builder, "a") // 1 + strings.write_string(&builder, "bc") // 2 + fmt.println(strings.to_string(builder)) // -> abc + } + +Output: + + abc + NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of bytes written +**Returns** The number of bytes written */ write_string :: proc(b: ^Builder, s: string) -> (n: int) { n0 := len(b.buf) @@ -400,10 +450,10 @@ write_string :: proc(b: ^Builder, s: string) -> (n: int) { /* Pops and returns the last byte in the Builder or 0 when the Builder is empty -Inputs: +**Inputs** - b: A pointer to the Builder -Returns: The last byte in the Builder or 0 if empty +**Returns** The last byte in the Builder or 0 if empty */ pop_byte :: proc(b: ^Builder) -> (r: byte) { if len(b.buf) == 0 { @@ -418,10 +468,10 @@ pop_byte :: proc(b: ^Builder) -> (r: byte) { /* Pops the last rune in the Builder and returns the popped rune and its rune width or (0, 0) if empty -Inputs: +**Inputs** - b: A pointer to the Builder -Returns: The popped rune and its rune width or (0, 0) if empty +**Returns** The popped rune and its rune width or (0, 0) if empty */ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) { if len(b.buf) == 0 { @@ -436,22 +486,31 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) { @(private) DIGITS_LOWER := "0123456789abcdefx" /* -Inputs: +**Inputs** - b: A pointer to the Builder - str: The string to be quoted and appended - quote: The optional quote character (default is double quotes) Example: -```odin - builder := strings.builder_make() - strings.write_quoted_string(&builder, "a") // 3 - strings.write_quoted_string(&builder, "bc", '\'') // 4 - strings.write_quoted_string(&builder, "xyz") // 5 - fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz" -``` + + import "core:fmt" + import "core:strings" + + strings_write_quoted_string_example :: proc() { + builder := strings.builder_make() + strings.write_quoted_string(&builder, "a") // 3 + strings.write_quoted_string(&builder, "bc", '\'') // 4 + strings.write_quoted_string(&builder, "xyz") // 5 + fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz" + } + +Output: + + "a"'bc'xyz" + NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of bytes written +**Returns** The number of bytes written */ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) { n, _ = io.write_quoted_string(to_writer(b), str, quote) @@ -460,14 +519,14 @@ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: /* Appends an encoded rune to the Builder and returns the number of bytes written -Inputs: +**Inputs** - b: A pointer to the Builder - r: The rune to be appended - write_quote: Optional boolean flag to write the quote character (default is true) NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of bytes written +**Returns** The number of bytes written */ write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) { n, _ = io.write_encoded_rune(to_writer(b), r, write_quote) @@ -477,20 +536,20 @@ write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int /* Appends an escaped rune to the Builder and returns the number of bytes written -Inputs: +**Inputs** - b: A pointer to the Builder - r: The rune to be appended - quote: The quote character - html_safe: Optional boolean flag to encode '<', '>', '&' as digits (default is false) -Examples: +**Usage** - '\a' will be written as such - `r` and `quote` match and `quote` is `\\` - they will be written as two slashes - `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026` NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of bytes written +**Returns** The number of bytes written */ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) { n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe) @@ -499,7 +558,7 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false /* Writes a f64 value to the Builder and returns the number of characters written -Inputs: +**Inputs** - b: A pointer to the Builder - f: The f64 value to be appended - fmt: The format byte @@ -509,7 +568,7 @@ Inputs: NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of characters written +**Returns** The number of characters written */ write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) { buf: [384]byte @@ -524,7 +583,7 @@ write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_ /* Writes a f16 value to the Builder and returns the number of characters written -Inputs: +**Inputs** - b: A pointer to the Builder - f: The f16 value to be appended - fmt: The format byte @@ -532,7 +591,7 @@ Inputs: NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of characters written +**Returns** The number of characters written */ write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte @@ -545,23 +604,32 @@ write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: /* Writes a f32 value to the Builder and returns the number of characters written -Inputs: +**Inputs** - b: A pointer to the Builder - f: The f32 value to be appended - fmt: The format byte - always_signed: Optional boolean flag to always include the sign Example: -```odin - builder := strings.builder_make() - strings.write_f32(&builder, 3.14159, 'f') // 6 - strings.write_string(&builder, " - ") // 3 - strings.write_f32(&builder, -0.123, 'e') // 8 - fmt.println(strings.to_string(builder)) // -> 3.14159012 - -1.23000003e-01 -``` + + import "core:fmt" + import "core:strings" + + strings_write_f32_example :: proc() { + builder := strings.builder_make() + strings.write_f32(&builder, 3.14159, 'f') // 6 + strings.write_string(&builder, " - ") // 3 + strings.write_f32(&builder, -0.123, 'e') // 8 + fmt.println(strings.to_string(builder)) // -> 3.14159012 - -1.23000003e-01 + } + +Output: + + 3.14159012 - -1.23000003e-01 + NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of characters written +**Returns** The number of characters written */ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte @@ -574,7 +642,7 @@ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: /* Writes a f32 value to the Builder and returns the number of characters written -Inputs: +**Inputs** - b: A pointer to the Builder - f: The f32 value to be appended - fmt: The format byte @@ -582,7 +650,7 @@ Inputs: NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of characters written +**Returns** The number of characters written */ write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte @@ -595,14 +663,14 @@ write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: /* Writes a u64 value to the Builder and returns the number of characters written -Inputs: +**Inputs** - b: A pointer to the Builder - i: The u64 value to be appended - base: The optional base for the numeric representation NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of characters written +**Returns** The number of characters written */ write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) { buf: [32]byte @@ -612,14 +680,14 @@ write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) { /* Writes a i64 value to the Builder and returns the number of characters written -Inputs: +**Inputs** - b: A pointer to the Builder - i: The i64 value to be appended - base: The optional base for the numeric representation NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of characters written +**Returns** The number of characters written */ write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) { buf: [32]byte @@ -629,14 +697,14 @@ write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) { /* Writes a uint value to the Builder and returns the number of characters written -Inputs: +**Inputs** - b: A pointer to the Builder - i: The uint value to be appended - base: The optional base for the numeric representation NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of characters written +**Returns** The number of characters written */ write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) { return write_u64(b, u64(i), base) @@ -644,14 +712,14 @@ write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) { /* Writes a int value to the Builder and returns the number of characters written -Inputs: +**Inputs** - b: A pointer to the Builder - i: The int value to be appended - base: The optional base for the numeric representation NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -Returns: The number of characters written +**Returns** The number of characters written */ write_int :: proc(b: ^Builder, i: int, base: int = 10) -> (n: int) { return write_i64(b, i64(i), base) diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin index c952725fa..ba86058ab 100644 --- a/core/strings/conversion.odin +++ b/core/strings/conversion.odin @@ -9,14 +9,14 @@ Converts invalid UTF-8 sequences in the input string `s` to the `replacement` st *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: Input string that may contain invalid UTF-8 sequences. - replacement: String to replace invalid UTF-8 sequences with. - allocator: (default: context.allocator). WARNING: Allocation does not occur when len(s) == 0 -Returns: A valid UTF-8 string with invalid sequences replaced by `replacement`. +**Returns** A valid UTF-8 string with invalid sequences replaced by `replacement`. */ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string { if len(s) == 0 { @@ -76,15 +76,24 @@ Converts the input string `s` to all lowercase characters. *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: Input string to be converted. - allocator: (default: context.allocator). Example: -```odin - strings.to_lower("TeST") -> test -``` -Returns: A new string with all characters converted to lowercase. + + import "core:fmt" + import "core:strings" + + strings_to_lower_example :: proc() { + fmt.println(strings.to_lower("TeST")) + } + +Output: + + test + +**Returns** A new string with all characters converted to lowercase. */ to_lower :: proc(s: string, allocator := context.allocator) -> string { b: Builder @@ -99,15 +108,24 @@ Converts the input string `s` to all uppercase characters. *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: Input string to be converted. - allocator: (default: context.allocator). Example: -```odin - strings.to_upper("Test") -> TEST -``` -Returns: A new string with all characters converted to uppercase. + + import "core:fmt" + import "core:strings" + + strings_to_upper_example :: proc() { + fmt.println(strings.to_upper("Test")) + } + +Output: + + TEST + +**Returns** A new string with all characters converted to uppercase. */ to_upper :: proc(s: string, allocator := context.allocator) -> string { b: Builder @@ -120,10 +138,10 @@ to_upper :: proc(s: string, allocator := context.allocator) -> string { /* Checks if the rune `c` is a delimiter (' ', '-', or '_'). -Inputs: +**Inputs** - c: Rune to check for delimiter status. -Returns: True if `c` is a delimiter, false otherwise. +**Returns** True if `c` is a delimiter, false otherwise. */ is_delimiter :: proc(c: rune) -> bool { return c == '-' || c == '_' || is_space(c) @@ -131,10 +149,10 @@ is_delimiter :: proc(c: rune) -> bool { /* Checks if the rune `r` is a non-alphanumeric or space character. -Inputs: +**Inputs** - r: Rune to check for separator status. -Returns: True if `r` is a non-alpha or `unicode.is_space` rune. +**Returns** True if `r` is a non-alpha or `unicode.is_space` rune. */ is_separator :: proc(r: rune) -> bool { if r <= 0x7f { @@ -161,23 +179,37 @@ is_separator :: proc(r: rune) -> bool { /* Iterates over a string, calling a callback for each rune with the previous, current, and next runes as arguments. -Inputs: +**Inputs** - w: An io.Writer to be used by the callback for writing output. - s: The input string to be iterated over. - callback: A procedure to be called for each rune in the string, with arguments (w: io.Writer, prev, curr, next: rune). The callback can utilize the provided io.Writer to write output during the iteration. Example: -```odin - my_callback :: proc(w: io.Writer, prev, curr, next: rune) { - fmt.println("my_callback", curr) // <-- Custom logic here + + import "core:fmt" + import "core:strings" + import "core:io" + + strings_string_case_iterator_example :: proc() { + my_callback :: proc(w: io.Writer, prev, curr, next: rune) { + fmt.println("my_callback", curr) // <-- Custom logic here + } + s := "hello world" + b: strings.Builder + strings.builder_init_len(&b, len(s)) + w := strings.to_writer(&b) + strings.string_case_iterator(w, s, my_callback) } - s := "hello world" - b: strings.Builder - strings.builder_init_len(&b, len(s)) - w := strings.to_writer(&b) - strings.string_case_iterator(w, s, my_callback) -``` + +Output: + + my_callback h + my_callback e + my_callback l + my_callback l + my_callback o + */ string_case_iterator :: proc( w: io.Writer, @@ -209,11 +241,11 @@ Converts the input string `s` to "lowerCamelCase". *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: Input string to be converted. - allocator: (default: context.allocator). -Returns: A "lowerCamelCase" formatted string. +**Returns** A "lowerCamelCase" formatted string. */ to_camel_case :: proc(s: string, allocator := context.allocator) -> string { s := s @@ -243,11 +275,11 @@ Converts the input string `s` to "UpperCamelCase" (PascalCase). *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: Input string to be converted. - allocator: (default: context.allocator). -Returns: A "PascalCase" formatted string. +**Returns** A "PascalCase" formatted string. */ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { s := s @@ -275,19 +307,30 @@ Returns a string converted to a delimiter-separated case with configurable casin *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string to be converted - delimiter: The rune to be used as the delimiter between words - all_upper_case: A boolean indicating if the output should be all uppercased (true) or lowercased (false) - allocator: (default: context.allocator). Example: -```odin - strings.to_delimiter_case("Hello World", '_', false) // -> "hello_world" - strings.to_delimiter_case("Hello World", ' ', true) // -> "HELLO WORLD" - strings.to_delimiter_case("aBC", '_', false) // -> "a_b_c" -``` -Returns: The converted string + + import "core:fmt" + import "core:strings" + + strings_to_delimiter_case_example :: proc() { + fmt.println(strings.to_delimiter_case("Hello World", '_', false)) + fmt.println(strings.to_delimiter_case("Hello World", ' ', true)) + fmt.println(strings.to_delimiter_case("aBC", '_', false)) + } + +Output: + + hello_world + HELLO WORLD + a_b_c + +**Returns** The converted string */ to_delimiter_case :: proc( s: string, @@ -337,16 +380,27 @@ Converts a string to "snake_case" with all runes lowercased *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string to be converted - allocator: (default: context.allocator). Example: -```odin - strings.to_snake_case("HelloWorld") // -> "hello_world" - strings.to_snake_case("Hello World") // -> "hello_world" + + import "core:fmt" + import "core:strings" + + strings_to_snake_case_example :: proc() { + fmt.println(strings.to_snake_case("HelloWorld")) + fmt.println(strings.to_snake_case("Hello World")) + } + +Output: + + hello_world + hello_world + ``` -Returns: The converted string +**Returns** The converted string */ to_snake_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '_', false, allocator) @@ -358,15 +412,24 @@ Converts a string to "SNAKE_CASE" with all runes uppercased *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string to be converted - allocator: (default: context.allocator). Example: -```odin - strings.to_upper_snake_case("HelloWorld") // -> "HELLO_WORLD" -``` -Returns: The converted string + + import "core:fmt" + import "core:strings" + + strings_to_upper_snake_case_example :: proc() { + fmt.println(strings.to_upper_snake_case("HelloWorld")) + } + +Output: + + HELLO_WORLD + +**Returns** The converted string */ to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '_', true, allocator) @@ -376,15 +439,24 @@ Converts a string to "kebab-case" with all runes lowercased *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string to be converted - allocator: (default: context.allocator). Example: -```odin - strings.to_kebab_case("HelloWorld") // -> "hello-world" -``` -Returns: The converted string + + import "core:fmt" + import "core:strings" + + strings_to_kebab_case_example :: proc() { + fmt.println(strings.to_kebab_case("HelloWorld")) + } + +Output: + + hello-world + +**Returns** The converted string */ to_kebab_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '-', false, allocator) @@ -394,15 +466,24 @@ Converts a string to "KEBAB-CASE" with all runes uppercased *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string to be converted - allocator: (default: context.allocator). Example: -```odin - strings.to_upper_kebab_case("HelloWorld") // -> "HELLO-WORLD" -``` -Returns: The converted string + + import "core:fmt" + import "core:strings" + + strings_to_upper_kebab_case_example :: proc() { + fmt.println(strings.to_upper_kebab_case("HelloWorld")) + } + +Output: + + HELLO-WORLD + +**Returns** The converted string */ to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '-', true, allocator) @@ -412,15 +493,24 @@ Converts a string to "Ada_Case" *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string to be converted - allocator: (default: context.allocator). Example: -```odin - strings.to_ada_case("HelloWorld") // -> "Hello_World" -``` -Returns: The converted string + + import "core:fmt" + import "core:strings" + + strings_to_upper_kebab_case_example :: proc() { + fmt.println(strings.to_ada_case("HelloWorld")) + } + +Output: + + Hello_World + +**Returns** The converted string */ to_ada_case :: proc(s: string, allocator := context.allocator) -> string { s := s diff --git a/core/strings/intern.odin b/core/strings/intern.odin index 5f849ed13..e05a58478 100644 --- a/core/strings/intern.odin +++ b/core/strings/intern.odin @@ -25,7 +25,7 @@ Initializes the entries map and sets the allocator for the string entries *Allocates Using Provided Allocators* -Inputs: +**Inputs** - m: A pointer to the Intern struct to be initialized - allocator: The allocator for the Intern_Entry strings (Default: context.allocator) - map_allocator: The allocator for the map of entries (Default: context.allocator) @@ -37,7 +37,7 @@ intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := /* Frees the map and all its content allocated using the `.allocator`. -Inputs: +**Inputs** - m: A pointer to the Intern struct to be destroyed */ intern_destroy :: proc(m: ^Intern) { @@ -51,13 +51,13 @@ Returns the interned string for the given text, is set in the map if it didnt ex *MAY Allocate using the Intern's Allocator* -Inputs: +**Inputs** - m: A pointer to the Intern struct - text: The string to be interned NOTE: The returned string lives as long as the map entry lives. -Returns: The interned string and an allocator error if any +**Returns** The interned string and an allocator error if any */ intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) { entry := _intern_get_entry(m, text) or_return @@ -68,13 +68,13 @@ Returns the interned C-String for the given text, is set in the map if it didnt *MAY Allocate using the Intern's Allocator* -Inputs: +**Inputs** - m: A pointer to the Intern struct - text: The string to be interned NOTE: The returned C-String lives as long as the map entry lives -Returns: The interned C-String and an allocator error if any +**Returns** The interned C-String and an allocator error if any */ intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) { entry := _intern_get_entry(m, text) or_return @@ -86,11 +86,11 @@ Sets and allocates the entry if it wasn't set yet *MAY Allocate using the Intern's Allocator* -Inputs: +**Inputs** - m: A pointer to the Intern struct - text: The string to be looked up or interned -Returns: The new or existing interned entry and an allocator error if any +**Returns** The new or existing interned entry and an allocator error if any */ _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check { if prev, ok := m.entries[text]; ok { diff --git a/core/strings/reader.odin b/core/strings/reader.odin index f4e3b213a..9489ab2b9 100644 --- a/core/strings/reader.odin +++ b/core/strings/reader.odin @@ -16,7 +16,7 @@ Reader :: struct { /* Initializes a string Reader with the provided string -Inputs: +**Inputs** - r: A pointer to a Reader struct - s: The input string to be read */ @@ -28,10 +28,10 @@ reader_init :: proc(r: ^Reader, s: string) { /* Converts a Reader into an io.Stream -Inputs: +**Inputs** - r: A pointer to a Reader struct -Returns: An io.Stream for the given Reader +**Returns** An io.Stream for the given Reader */ reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) { s.stream_data = r @@ -41,11 +41,11 @@ reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) { /* Initializes a string Reader and returns an io.Reader for the given string -Inputs: +**Inputs** - r: A pointer to a Reader struct - s: The input string to be read -Returns: An io.Reader for the given string +**Returns** An io.Reader for the given string */ to_reader :: proc(r: ^Reader, s: string) -> io.Reader { reader_init(r, s) @@ -55,11 +55,11 @@ to_reader :: proc(r: ^Reader, s: string) -> io.Reader { /* Initializes a string Reader and returns an io.Reader_At for the given string -Inputs: +**Inputs** - r: A pointer to a Reader struct - s: The input string to be read -Returns: An io.Reader_At for the given string +**Returns** An io.Reader_At for the given string */ to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At { reader_init(r, s) @@ -69,10 +69,10 @@ to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At { /* Returns the remaining length of the Reader -Inputs: +**Inputs** - r: A pointer to a Reader struct -Returns: The remaining length of the Reader +**Returns** The remaining length of the Reader */ reader_length :: proc(r: ^Reader) -> int { if r.i >= i64(len(r.s)) { @@ -83,10 +83,10 @@ reader_length :: proc(r: ^Reader) -> int { /* Returns the length of the string stored in the Reader -Inputs: +**Inputs** - r: A pointer to a Reader struct -Returns: The length of the string stored in the Reader +**Returns** The length of the string stored in the Reader */ reader_size :: proc(r: ^Reader) -> i64 { return i64(len(r.s)) @@ -94,11 +94,11 @@ reader_size :: proc(r: ^Reader) -> i64 { /* Reads len(p) bytes from the Reader's string and copies into the provided slice. -Inputs: +**Inputs** - r: A pointer to a Reader struct - p: A byte slice to copy data into -Returns: +**Returns** - n: The number of bytes read - err: An io.Error if an error occurs while reading, including .EOF, otherwise nil denotes success. */ @@ -114,12 +114,12 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) { /* Reads len(p) bytes from the Reader's string and copies into the provided slice, at the specified offset from the current index. -Inputs: +**Inputs** - r: A pointer to a Reader struct - p: A byte slice to copy data into - off: The offset from which to read -Returns: +**Returns** - n: The number of bytes read - err: An io.Error if an error occurs while reading, including .EOF, otherwise nil denotes success. */ @@ -139,10 +139,10 @@ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Erro /* Reads and returns a single byte from the Reader's string -Inputs: +**Inputs** - r: A pointer to a Reader struct -Returns: +**Returns** - The byte read from the Reader - err: An io.Error if an error occurs while reading, including .EOF, otherwise nil denotes success. */ @@ -158,10 +158,10 @@ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) { /* Decrements the Reader's index (i) by 1 -Inputs: +**Inputs** - r: A pointer to a Reader struct -Returns: An io.Error if `r.i <= 0` (.Invalid_Unread), otherwise nil denotes success. +**Returns** An io.Error if `r.i <= 0` (.Invalid_Unread), otherwise nil denotes success. */ reader_unread_byte :: proc(r: ^Reader) -> io.Error { if r.i <= 0 { @@ -174,10 +174,10 @@ reader_unread_byte :: proc(r: ^Reader) -> io.Error { /* Reads and returns a single rune and its size from the Reader's string -Inputs: +**Inputs** - r: A pointer to a Reader struct -Returns: +**Returns** - ch: The rune read from the Reader - size: The size of the rune in bytes - err: An io.Error if an error occurs while reading @@ -199,12 +199,12 @@ reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) { /* Decrements the Reader's index (i) by the size of the last read rune -Inputs: +**Inputs** - r: A pointer to a Reader struct WARNING: May only be used once and after a valid `read_rune` call -Returns: An io.Error if an error occurs while unreading (.Invalid_Unread), else nil denotes success. +**Returns** An io.Error if an error occurs while unreading (.Invalid_Unread), else nil denotes success. */ reader_unread_rune :: proc(r: ^Reader) -> io.Error { if r.i <= 0 { @@ -220,12 +220,12 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error { /* Seeks the Reader's index to a new position -Inputs: +**Inputs** - r: A pointer to a Reader struct - offset: The new offset position - whence: The reference point for the new position (.Start, .Current, or .End) -Returns: +**Returns** - The absolute offset after seeking - err: An io.Error if an error occurs while seeking (.Invalid_Whence, .Invalid_Offset) */ @@ -252,13 +252,13 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E /* Writes the remaining content of the Reader's string into the provided io.Writer -Inputs: +**Inputs** - r: A pointer to a Reader struct - w: The io.Writer to write the remaining content into WARNING: Panics if writer writes more bytes than remainig length of string. -Returns: +**Returns** - n: The number of bytes written - err: An io.Error if an error occurs while writing (.Short_Write) */ diff --git a/core/strings/strings.odin b/core/strings/strings.odin index bd2dcc004..ca2054b33 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -11,12 +11,12 @@ Clones a string *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The string to be cloned - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: #caller_location) -Returns: A cloned string +**Returns** A cloned string */ clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> string { c := make([]byte, len(s), allocator, loc) @@ -28,12 +28,12 @@ Clones a string safely (returns early with an allocation error on failure) *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The string to be cloned - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: #caller_location) -Returns: +**Returns** - str: A cloned string - err: A mem.Allocator_Error if an error occurs during allocation */ @@ -47,12 +47,12 @@ Clones a string and appends a nul byte to make it a cstring *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The string to be cloned - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: #caller_location) -Returns: A cloned cstring with an appended nul byte +**Returns** A cloned cstring with an appended nul byte */ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> cstring { c := make([]byte, len(s)+1, allocator, loc) @@ -63,13 +63,13 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #call /* Transmutes a raw pointer into a string. Non-allocating. -Inputs: +**Inputs** - ptr: A pointer to the start of the byte sequence - len: The length of the byte sequence NOTE: The created string is only valid as long as the pointer and length are valid. -Returns: A string created from the byte pointer and length +**Returns** A string created from the byte pointer and length */ string_from_ptr :: proc(ptr: ^byte, len: int) -> string { return transmute(string)mem.Raw_String{ptr, len} @@ -80,11 +80,11 @@ Transmutes a raw pointer (nul-terminated) into a string. Non-allocating. Searche NOTE: The created string is only valid as long as the pointer and length are valid. The string is truncated at the first nul byte encountered. -Inputs: +**Inputs** - ptr: A pointer to the start of the nul-terminated byte sequence - len: The length of the byte sequence -Returns: A string created from the nul-terminated byte pointer and length +**Returns** A string created from the nul-terminated byte pointer and length */ string_from_nul_terminated_ptr :: proc(ptr: ^byte, len: int) -> string { s := transmute(string)mem.Raw_String{ptr, len} @@ -94,10 +94,10 @@ string_from_nul_terminated_ptr :: proc(ptr: ^byte, len: int) -> string { /* Gets the raw byte pointer for the start of a string `str` -Inputs: +**Inputs** - str: The input string -Returns: A pointer to the start of the string's bytes +**Returns** A pointer to the start of the string's bytes */ ptr_from_string :: proc(str: string) -> ^byte { d := transmute(mem.Raw_String)str @@ -106,12 +106,12 @@ ptr_from_string :: proc(str: string) -> ^byte { /* Converts a string `str` to a cstring -Inputs: +**Inputs** - str: The input string WARNING: This is unsafe because the original string may not contain a nul byte. -Returns: The converted cstring +**Returns** The converted cstring */ unsafe_string_to_cstring :: proc(str: string) -> cstring { d := transmute(mem.Raw_String)str @@ -120,13 +120,13 @@ unsafe_string_to_cstring :: proc(str: string) -> cstring { /* Truncates a string `str` at the first occurrence of char/byte `b` -Inputs: +**Inputs** - str: The input string - b: The byte to truncate the string at NOTE: Failure to find the byte results in returning the entire string. -Returns: The truncated string +**Returns** The truncated string */ truncate_to_byte :: proc(str: string, b: byte) -> string { n := index_byte(str, b) @@ -138,11 +138,11 @@ truncate_to_byte :: proc(str: string, b: byte) -> string { /* Truncates a string str at the first occurrence of rune r as a slice of the original, entire string if not found -Inputs: +**Inputs** - str: The input string - r: The rune to truncate the string at -Returns: The truncated string +**Returns** The truncated string */ truncate_to_rune :: proc(str: string, r: rune) -> string { n := index_rune(str, r) @@ -156,12 +156,12 @@ Clones a byte array s and appends a nul byte *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The byte array to be cloned - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: #caller_location) -Returns: A cloned string from the byte array with a nul byte +**Returns** A cloned string from the byte array with a nul byte */ clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> string { c := make([]byte, len(s)+1, allocator, loc) @@ -174,12 +174,12 @@ Clones a cstring s as a string *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The cstring to be cloned - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: #caller_location) -Returns: A cloned string from the cstring +**Returns** A cloned string from the cstring */ clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> string { return clone(string(s), allocator, loc) @@ -189,7 +189,7 @@ Clones a string from a byte pointer ptr and a byte length len *Allocates Using Provided Allocator* -Inputs: +**Inputs** - ptr: A pointer to the start of the byte sequence - len: The length of the byte sequence - allocator: (default: context.allocator) @@ -197,7 +197,7 @@ Inputs: NOTE: Same as `string_from_ptr`, but perform an additional `clone` operation -Returns: A cloned string from the byte pointer and length +**Returns** A cloned string from the byte pointer and length */ clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc := #caller_location) -> string { s := string_from_ptr(ptr, len) @@ -215,7 +215,7 @@ Clones a string from a nul-terminated cstring ptr and a byte length len *Allocates Using Provided Allocator* -Inputs: +**Inputs** - ptr: A pointer to the start of the nul-terminated cstring - len: The byte length of the cstring - allocator: (default: context.allocator) @@ -223,7 +223,7 @@ Inputs: NOTE: Truncates at the first nul byte encountered or the byte length. -Returns: A cloned string from the nul-terminated cstring and byte length +**Returns** A cloned string from the nul-terminated cstring and byte length */ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.allocator, loc := #caller_location) -> string { s := string_from_ptr((^u8)(ptr), len) @@ -234,11 +234,11 @@ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context. Compares two strings, returning a value representing which one comes first lexicographically. -1 for lhs; 1 for rhs, or 0 if they are equal. -Inputs: +**Inputs** - lhs: First string for comparison - rhs: Second string for comparison -Returns: -1 if lhs comes first, 1 if rhs comes first, or 0 if they are equal +**Returns** -1 if lhs comes first, 1 if rhs comes first, or 0 if they are equal */ compare :: proc(lhs, rhs: string) -> int { return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs) @@ -246,11 +246,11 @@ compare :: proc(lhs, rhs: string) -> int { /* Returns the byte offset of the rune r in the string s, -1 when not found -Inputs: +**Inputs** - s: The input string - r: The rune to search for -Returns: The byte offset of the rune r in the string s, or -1 if not found +**Returns** The byte offset of the rune r in the string s, or -1 if not found */ contains_rune :: proc(s: string, r: rune) -> int { for c, offset in s { @@ -263,18 +263,40 @@ contains_rune :: proc(s: string, r: rune) -> int { /* Returns true when the string substr is contained inside the string s -Inputs: +**Inputs** - s: The input string - substr: The substring to search for +Example: Example: +```odin + strings.contains("testing", "test") // -> true + strings.contains("testing", "ing") // -> true + strings.contains("testing", "text") // -> false +``` + Example: ```odin strings.contains("testing", "test") // -> true strings.contains("testing", "ing") // -> true strings.contains("testing", "text") // -> false ``` -Returns: true if substr is contained inside the string s, false otherwise + import "core:fmt" + import "core:strings" + + strings_contains_example :: proc() { + fmt.println(strings.contains("testing", "test")) + fmt.println(strings.contains("testing", "ing")) + fmt.println(strings.contains("testing", "text")) + } + +Output: + + true + true + false + +**Returns** true if substr is contained inside the string s, false otherwise */ contains :: proc(s, substr: string) -> bool { return index(s, substr) >= 0 @@ -282,19 +304,30 @@ contains :: proc(s, substr: string) -> bool { /* Returns true when the string s contains any of the characters inside the string chars -Inputs: +**Inputs** - s: The input string - chars: The characters to search for Example: -```odin - strings.contains_any("test", "test") // -> true - strings.contains_any("test", "ts") // -> true - strings.contains_any("test", "et") // -> true - strings.contains_any("test", "a") // -> false -``` -Returns: true if the string s contains any of the characters in chars, false otherwise + import "core:fmt" + import "core:strings" + + strings_contains_any_example :: proc() { + fmt.println(strings.contains_any("test", "test")) + fmt.println(strings.contains_any("test", "ts")) + fmt.println(strings.contains_any("test", "et")) + fmt.println(strings.contains_any("test", "a")) + } + +Output: + + true + true + true + false + +**Returns** true if the string s contains any of the characters in chars, false otherwise */ contains_any :: proc(s, chars: string) -> bool { return index_any(s, chars) >= 0 @@ -302,16 +335,25 @@ contains_any :: proc(s, chars: string) -> bool { /* Returns the UTF-8 rune count of the string s -Inputs: +**Inputs** - s: The input string Example: -```odin - strings.rune_count("test") // -> 4 - strings.rune_count("testö") // -> 5, where len("testö") -> 6 -``` -Returns: The UTF-8 rune count of the string s + import "core:fmt" + import "core:strings" + + strings_rune_count_example :: proc() { + fmt.println(strings.rune_count("test")) + fmt.println(strings.rune_count("testö")) // where len("testö") == 6 + } + +Output: + + 4 + 5 + +**Returns** The UTF-8 rune count of the string s */ rune_count :: proc(s: string) -> int { return utf8.rune_count_in_string(s) @@ -320,19 +362,30 @@ rune_count :: proc(s: string) -> int { Returns whether the strings u and v are the same alpha characters, ignoring different casings Works with UTF-8 string content -Inputs: +**Inputs** - u: The first string for comparison - v: The second string for comparison Example: -```odin - strings.equal_fold("test", "test") // -> true - strings.equal_fold("Test", "test") // -> true - strings.equal_fold("Test", "tEsT") // -> true - strings.equal_fold("test", "tes") // -> false -``` -Returns: True if the strings u and v are the same alpha characters (ignoring case), false + import "core:fmt" + import "core:strings" + + strings_equal_fold_example :: proc() { + fmt.println(strings.equal_fold("test", "test")) + fmt.println(strings.equal_fold("Test", "test")) + fmt.println(strings.equal_fold("Test", "tEsT")) + fmt.println(strings.equal_fold("test", "tes")) + } + +Output: + + true + true + true + false + +**Returns** True if the strings u and v are the same alpha characters (ignoring case), false */ equal_fold :: proc(u, v: string) -> bool { s, t := u, v @@ -379,19 +432,30 @@ equal_fold :: proc(u, v: string) -> bool { /* Returns the prefix length common between strings a and b -Inputs: +**Inputs** - a: The first input string - b: The second input string Example: -```odin - strings.prefix_length("testing", "test") // -> 4 - strings.prefix_length("testing", "te") // -> 2 - strings.prefix_length("telephone", "te") // -> 2 - strings.prefix_length("testing", "est") // -> 0 -``` -Returns: The prefix length common between strings a and b + import "core:fmt" + import "core:strings" + + strings_prefix_length_example :: proc() { + fmt.println(strings.prefix_length("testing", "test")) + fmt.println(strings.prefix_length("testing", "te")) + fmt.println(strings.prefix_length("telephone", "te")) + fmt.println(strings.prefix_length("testing", "est")) + } + +Output: + + 4 + 2 + 2 + 0 + +**Returns** The prefix length common between strings a and b */ prefix_length :: proc(a, b: string) -> (n: int) { _len := min(len(a), len(b)) @@ -419,18 +483,30 @@ prefix_length :: proc(a, b: string) -> (n: int) { /* Determines if a string s starts with a given prefix -Example: -```odin - strings.has_prefix("testing", "test") // -> true - strings.has_prefix("testing", "te") // -> true - strings.has_prefix("telephone", "te") // -> true - strings.has_prefix("testing", "est") // -> false -``` -Inputs: +**Inputs** - s: The string to check for the prefix - prefix: The prefix to look for -Returns: true if the string s starts with the prefix, otherwise false +Example: + + import "core:fmt" + import "core:strings" + + strings_has_prefix_example :: proc() { + fmt.println(strings.has_prefix("testing", "test")) + fmt.println(strings.has_prefix("testing", "te")) + fmt.println(strings.has_prefix("telephone", "te")) + fmt.println(strings.has_prefix("testing", "est")) + } + +Output: + + true + true + true + false + +**Returns** true if the string s starts with the prefix, otherwise false */ has_prefix :: proc(s, prefix: string) -> bool { return len(s) >= len(prefix) && s[0:len(prefix)] == prefix @@ -439,16 +515,27 @@ has_prefix :: proc(s, prefix: string) -> bool { Determines if a string s ends with a given suffix Example: -```odin - strings.has_suffix("todo.txt", ".txt") // -> true - strings.has_suffix("todo.doc", ".txt") // -> false - strings.has_suffix("todo.doc.txt", ".txt") // -> true -``` -Inputs: + + import "core:fmt" + import "core:strings" + + strings_has_suffix_example :: proc() { + fmt.println(strings.has_suffix("todo.txt", ".txt")) + fmt.println(strings.has_suffix("todo.doc", ".txt")) + fmt.println(strings.has_suffix("todo.doc.txt", ".txt")) + } + +Output: + + true + false + true + +**Inputs** - s: The string to check for the suffix - suffix: The suffix to look for -Returns: true if the string s ends with the suffix, otherwise false +**Returns** true if the string s ends with the suffix, otherwise false */ has_suffix :: proc(s, suffix: string) -> bool { return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix @@ -459,18 +546,29 @@ Joins a slice of strings a with a sep string *Allocates Using Provided Allocator* Example: -```odin - a := [?]string { "a", "b", "c" } - b := strings.join(a[:], " ") // -> "a b c" - c := strings.join(a[:], "-") // -> "a-b-c" - d := strings.join(a[:], "...") // -> "a...b...c" -``` -Inputs: + + import "core:fmt" + import "core:strings" + + strings_join_example :: proc() { + a := [?]string { "a", "b", "c" } + fmt.println(strings.join(a[:], " ")) + fmt.println(strings.join(a[:], "-")) + fmt.println(strings.join(a[:], "...")) + } + +Output: + + a b c + a-b-c + a...b...c + +**Inputs** - a: A slice of strings to join - sep: The separator string - allocator: (default is context.allocator) -Returns: A combined string from the slice of strings a separated with the sep string +**Returns** A combined string from the slice of strings a separated with the sep string */ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string { if len(a) == 0 { @@ -495,12 +593,12 @@ Joins a slice of strings a with a sep string, returns an error on allocation fai *Allocates Using Provided Allocator* -Inputs: +**Inputs** - a: A slice of strings to join - sep: The separator string - allocator: (default is context.allocator) -Returns: +**Returns** - str: A combined string from the slice of strings a separated with the sep string - err: An error if allocation failed, otherwise nil */ @@ -527,16 +625,25 @@ Returns a combined string from the slice of strings `a` without a separator *Allocates Using Provided Allocator* -Inputs: +**Inputs** - a: A slice of strings to concatenate - allocator: An optional custom allocator (default is context.allocator) Example: -```odin - a := [?]string { "a", "b", "c" } - b := strings.concatenate(a[:]) // -> "abc" -``` -Returns: The concatenated string + + import "core:fmt" + import "core:strings" + + strings_concatenate_example :: proc() { + a := [?]string { "a", "b", "c" } + fmt.println(strings.concatenate(a[:])) + } + +Output: + + abc + +**Returns** The concatenated string */ concatenate :: proc(a: []string, allocator := context.allocator) -> string { if len(a) == 0 { @@ -559,11 +666,11 @@ Returns a combined string from the slice of strings `a` without a separator, or *Allocates Using Provided Allocator* -Inputs: +**Inputs** - a: A slice of strings to concatenate - allocator: An optional custom allocator (default is context.allocator) -Returns: The concatenated string, and an error if allocation fails +**Returns** The concatenated string, and an error if allocation fails */ concatenate_safe :: proc(a: []string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) { if len(a) == 0 { @@ -586,19 +693,30 @@ Returns a substring of the input string `s` with the specified rune offset and l *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string to cut - rune_offset: The starting rune index (default is 0). In runes, not bytes. - rune_length: The number of runes to include in the substring (default is 0, which returns the remainder of the string). In runes, not bytes. - allocator: An optional custom allocator (default is context.allocator) Example: -```odin - strings.cut("some example text", 0, 4) // -> "some" - strings.cut("some example text", 2, 2) // -> "me" - strings.cut("some example text", 5, 7) // -> "example" -``` -Returns: The substring + + import "core:fmt" + import "core:strings" + + strings_cut_example :: proc() { + strings.cut("some example text", 0, 4) // -> "some" + strings.cut("some example text", 2, 2) // -> "me" + strings.cut("some example text", 5, 7) // -> "example" + } + +Output: + + some + me + example + +**Returns** The substring */ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator := context.allocator) -> (res: string) { s := s; rune_length := rune_length @@ -656,14 +774,14 @@ Splits the input string `s` into a slice of substrings separated by the specifie *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string to split - sep: The separator string - sep_save: A flag determining if the separator should be saved in the resulting substrings - n: The maximum number of substrings to return, returns nil without alloc when n=0 - allocator: An optional custom allocator (default is context.allocator) -Returns: A slice of substrings +**Returns** A slice of substrings */ @private _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator) -> []string { @@ -717,19 +835,27 @@ Splits a string into parts based on a separator. *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The string to split. - sep: The separator string used to split the input string. - allocator: (default is context.allocator). Example: -```odin - s := "aaa.bbb.ccc.ddd.eee" // 5 parts - ss := strings.split(s, ".") - fmt.println(ss) // [aaa, bbb, ccc, ddd, eee] -``` -Returns: A slice of strings, each representing a part of the split string. + import "core:fmt" + import "core:strings" + + strings_split_example :: proc() { + s := "aaa.bbb.ccc.ddd.eee" // 5 parts + ss := strings.split(s, ".") + fmt.println(ss) + } + +Output: + + ["aaa", "bbb", "ccc", "ddd", "eee"] + +**Returns** A slice of strings, each representing a part of the split string. */ split :: proc(s, sep: string, allocator := context.allocator) -> []string { return _split(s, sep, 0, -1, allocator) @@ -739,19 +865,27 @@ Splits a string into parts based on a separator. if n < count of seperators, the *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The string to split. - sep: The separator string used to split the input string. - allocator: (default is context.allocator) Example: -```odin - s := "aaa.bbb.ccc.ddd.eee" // 5 parts present - ss := strings.split(s, ".") // total of 3 wanted - fmt.println(ss) // [aaa, bbb, ccc.ddd.eee] -``` -Returns: A slice of strings, each representing a part of the split string. + import "core:fmt" + import "core:strings" + + strings_split_n_example :: proc() { + s := "aaa.bbb.ccc.ddd.eee" // 5 parts present + ss := strings.split_n(s, ".",3) // total of 3 wanted + fmt.println(ss) + } + +Output: + + ["aaa", "bbb", "ccc.ddd.eee"] + +**Returns** A slice of strings, each representing a part of the split string. */ split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string { return _split(s, sep, 0, n, allocator) @@ -761,19 +895,27 @@ Splits a string into parts after the separator, retaining it in the substrings. *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The string to split. - sep: The separator string used to split the input string. - allocator: (Optional) The allocator used for allocation (default is context.allocator). Example: -```odin - a := "aaa.bbb.ccc.ddd.eee" // 5 parts - aa := strings.split_after(a, ".") - fmt.println(aa) // [aaa., bbb., ccc., ddd., eee] -``` -Returns: A slice of strings, each representing a part of the split string after the separator. + import "core:fmt" + import "core:strings" + + strings_split_after_example :: proc() { + a := "aaa.bbb.ccc.ddd.eee" // 5 parts + aa := strings.split_after(a, ".") + fmt.println(aa) + } + +Output: + + ["aaa.", "bbb.", "ccc.", "ddd.", "eee"] + +**Returns** A slice of strings, each representing a part of the split string after the separator. */ split_after :: proc(s, sep: string, allocator := context.allocator) -> []string { return _split(s, sep, len(sep), -1, allocator) @@ -783,20 +925,28 @@ Splits a string into a total of 'n' parts after the separator. *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The string to split. - sep: The separator string used to split the input string. - n: The maximum number of parts to split the string into. - allocator: (default is context.allocator) Example: -```odin - a := "aaa.bbb.ccc.ddd.eee" - aa := strings.split_after_n(a, ".", 3) - fmt.println(aa) // [aaa., bbb., ccc.ddd.eee] -``` -Returns: A slice of strings with 'n' parts or fewer if there weren't + import "core:fmt" + import "core:strings" + + strings_split_after_n_example :: proc() { + a := "aaa.bbb.ccc.ddd.eee" + aa := strings.split_after_n(a, ".", 3) + fmt.println(aa) + } + +Output: + + ["aaa.", "bbb.", "ccc.ddd.eee"] + +**Returns** A slice of strings with 'n' parts or fewer if there weren't */ split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string { return _split(s, sep, len(sep), n, allocator) @@ -807,14 +957,14 @@ up to (but not including) the separator, as well as a boolean indicating success *Used Internally - Private Function* -Inputs: +**Inputs** - s: Pointer to the input string, which is modified during the search. - sep: The separator string to search for. - sep_save: Number of characters from the separator to include in the result. NOTE: Destructively consumes the string -Returns: A tuple containing the resulting substring and a boolean indicating success. +**Returns** A tuple containing the resulting substring and a boolean indicating success. */ @private _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) { @@ -847,18 +997,31 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, Splits the input string by the byte separator in an iterator fashion. Destructively consumes the original string until the end. -Inputs: +**Inputs** - s: Pointer to the input string, which is modified during the search. - sep: The byte separator to search for. Example: -```odin - text := "a.b.c.d.e" - for str in strings.split_by_byte_iterator(&text, '.') { - fmt.println(str) // every loop -> a b c d e + + import "core:fmt" + import "core:strings" + + strings_split_by_byte_iterator_example :: proc() { + text := "a.b.c.d.e" + for str in strings.split_by_byte_iterator(&text, '.') { + fmt.println(str) // every loop -> a b c d e + } } -``` -Returns: A tuple containing the resulting substring and a boolean indicating success. + +Output: + + a + b + c + d + e + +**Returns** A tuple containing the resulting substring and a boolean indicating success. */ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) { m := index_byte(s^, sep) @@ -878,18 +1041,31 @@ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) { Splits the input string by the separator string in an iterator fashion. Destructively consumes the original string until the end. -Inputs: +**Inputs** - s: Pointer to the input string, which is modified during the search. - sep: The separator string to search for. Example: -```odin - text := "a.b.c.d.e" - for str in strings.split_iterator(&text, ".") { - fmt.println(str) // every loop -> a b c d e + + import "core:fmt" + import "core:strings" + + strings_split_iterator_example :: proc() { + text := "a.b.c.d.e" + for str in strings.split_iterator(&text, ".") { + fmt.println(str) + } } -``` -Returns: A tuple containing the resulting substring and a boolean indicating success. + +Output: + + a + b + c + d + e + +**Returns** A tuple containing the resulting substring and a boolean indicating success. */ split_iterator :: proc(s: ^string, sep: string) -> (string, bool) { return _split_iterator(s, sep, 0) @@ -898,18 +1074,31 @@ split_iterator :: proc(s: ^string, sep: string) -> (string, bool) { Splits the input string after every separator string in an iterator fashion. Destructively consumes the original string until the end. -Inputs: +**Inputs** - s: Pointer to the input string, which is modified during the search. - sep: The separator string to search for. Example: -```odin - text := "a.b.c.d.e" - for str in strings.split_after_iterator(&text, ".") { - fmt.println(str) // every loop -> a. b. c. d. e + + import "core:fmt" + import "core:strings" + + strings_split_after_iterator_example :: proc() { + text := "a.b.c.d.e" + for str in strings.split_after_iterator(&text, ".") { + fmt.println(str) + } } -``` -Returns: A tuple containing the resulting substring and a boolean indicating success. + +Output: + + a. + b. + c. + d. + e + +**Returns** A tuple containing the resulting substring and a boolean indicating success. */ split_after_iterator :: proc(s: ^string, sep: string) -> (string, bool) { return _split_iterator(s, sep, len(sep)) @@ -919,10 +1108,10 @@ Trims the carriage return character from the end of the input string. *Used Internally - Private Function* -Inputs: +**Inputs** - s: The input string to trim. -Returns: The trimmed string as a slice. +**Returns** The trimmed string as a slice. */ @(private) _trim_cr :: proc(s: string) -> string { @@ -939,17 +1128,26 @@ Splits the input string at every line break '\n'. *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string to split. - allocator: (default is context.allocator) Example: -```odin - a := "a\nb\nc\nd\ne" - b := strings.split_lines(a) - fmt.eprintln(b) // [a, b, c, d, e] -``` -Returns: An allocated slice of strings split by line breaks. + + import "core:fmt" + import "core:strings" + + strings_split_lines_example :: proc() { + a := "a\nb\nc\nd\ne" + b := strings.split_lines(a) + fmt.println(b) + } + +Output: + + ["a", "b", "c", "d", "e"] + +**Returns** An allocated slice of strings split by line breaks. */ split_lines :: proc(s: string, allocator := context.allocator) -> []string { sep :: "\n" @@ -964,18 +1162,27 @@ Splits the input string at every line break '\n' for n parts. *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string to split. - n: The number of parts to split into. - allocator: (default is context.allocator) Example: -```odin - a := "a\nb\nc\nd\ne" - b := strings.split_lines_n(a, 3) - fmt.println(b) // [a, b, c, d\ne\n] -``` -Returns: An allocated array of strings split by line breaks. + + import "core:fmt" + import "core:strings" + + strings_split_lines_n_example :: proc() { + a := "a\nb\nc\nd\ne" + b := strings.split_lines_n(a, 3) + fmt.println(b) + } + +Output: + + ["a", "b", "c\nd\ne"] + +**Returns** An allocated array of strings split by line breaks. */ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []string { sep :: "\n" @@ -990,17 +1197,26 @@ Splits the input string at every line break '\n' leaving the '\n' in the resulti *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string to split. - allocator: (default is context.allocator) Example: -```odin - a := "a\nb\nc\nd\ne" - b := strings.split_lines_after(a) - fmt.println(b) // [a\n, b\n, c\n, d\n, e\n] -``` -Returns: An allocated slice of strings split by line breaks with line breaks included. + + import "core:fmt" + import "core:strings" + + strings_split_lines_after_example :: proc() { + a := "a\nb\nc\nd\ne" + b := strings.split_lines_after(a) + fmt.println(b) + } + +Output: + + ["a\n", "b\n", "c\n", "d\n", "e"] + +**Returns** An allocated slice of strings split by line breaks with line breaks included. */ split_lines_after :: proc(s: string, allocator := context.allocator) -> []string { sep :: "\n" @@ -1016,18 +1232,27 @@ Only runs for n parts. *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string to split. - n: The number of parts to split into. - allocator: (default is context.allocator) Example: -```odin - a := "a\nb\nc\nd\ne" - b := strings.split_lines_after_n(a, 3) - fmt.println(b) // [a\n, b\n, c\n, d\ne\n] -``` -Returns: An allocated slice of strings split by line breaks with line breaks included. + + import "core:fmt" + import "core:strings" + + strings_split_lines_after_n_example :: proc() { + a := "a\nb\nc\nd\ne" + b := strings.split_lines_after_n(a, 3) + fmt.println(b) + } + +Output: + + ["a\n", "b\n", "c\nd\ne"] + +**Returns** An allocated slice of strings split by line breaks with line breaks included. */ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -> []string { sep :: "\n" @@ -1041,17 +1266,26 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) - Splits the input string at every line break '\n'. Returns the current split string every iteration until the string is consumed. -Inputs: +**Inputs** - s: Pointer to the input string, which is modified during the search. Example: -```odin - text := "a\nb\nc\nd\ne" - for str in strings.split_lines_iterator(&text) { - fmt.println(str) // every loop -> a b c d e + + import "core:fmt" + import "core:strings" + + strings_split_lines_iterator_example :: proc() { + text := "a\nb\nc\nd\ne" + for str in strings.split_lines_iterator(&text) { + fmt.print(str) // every loop -> a b c d e + } } -``` -Returns: A tuple containing the resulting substring and a boolean indicating success. + +Output: + + abcde + +**Returns** A tuple containing the resulting substring and a boolean indicating success. */ split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) { sep :: "\n" @@ -1062,17 +1296,30 @@ split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) { Splits the input string at every line break '\n'. Returns the current split string with line breaks included every iteration until the string is consumed. -Inputs: +**Inputs** - s: Pointer to the input string, which is modified during the search. Example: -```odin - text := "a\nb\nc\nd\ne" - for str in strings.split_lines_after_iterator(&text) { - fmt.println(str) // every loop -> a\n b\n c\n d\n e\n + + import "core:fmt" + import "core:strings" + + strings_split_lines_after_iterator_example :: proc() { + text := "a\nb\nc\nd\ne" + for str in strings.split_lines_after_iterator(&text) { + fmt.print(str) // every loop -> a\n b\n c\n d\n e\n + } } -``` -Returns: A tuple containing the resulting substring with line breaks included and a boolean indicating success. + +Output: + + a + b + c + d + e + +**Returns** A tuple containing the resulting substring with line breaks included and a boolean indicating success. */ split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) { sep :: "\n" @@ -1083,18 +1330,30 @@ split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) { Returns the byte offset of the first byte c in the string s it finds, -1 when not found. NOTE: Can't find UTF-8 based runes. -Inputs: +**Inputs** - s: The input string to search in. - c: The byte to search for. Example: -```odin - strings.index_byte("test", 't') // -> 0 - strings.index_byte("test", 'e') // -> 1 - strings.index_byte("test", 'x') // -> -1 - strings.index_byte("teäst", 'ä') // -> -1 -``` -Returns: The byte offset of the first occurrence of c in s, or -1 if not found. + + import "core:fmt" + import "core:strings" + + strings_index_byte_example :: proc() { + fmt.println(strings.index_byte("test", 't')) + fmt.println(strings.index_byte("test", 'e')) + fmt.println(strings.index_byte("test", 'x')) + fmt.println(strings.index_byte("teäst", 'ä')) + } + +Output: + + 0 + 1 + -1 + -1 + +**Returns** The byte offset of the first occurrence of c in s, or -1 if not found. */ index_byte :: proc(s: string, c: byte) -> int { for i := 0; i < len(s); i += 1 { @@ -1109,13 +1368,25 @@ Returns the byte offset of the last byte `c` in the string `s`, -1 when not foun NOTE: Can't find UTF-8 based runes. Example: -```odin - strings.last_index_byte("test", 't') // -> 3 - strings.last_index_byte("test", 'e') // -> 1 - strings.last_index_byte("test", 'x') // -> -1 - strings.last_index_byte("teäst", 'ä') // -> -1 -``` -Returns: The byte offset of the last occurrence of `c` in `s`, or -1 if not found. + + import "core:fmt" + import "core:strings" + + strings_last_index_byte_example :: proc() { + fmt.println(strings.last_index_byte("test", 't')) + fmt.println(strings.last_index_byte("test", 'e')) + fmt.println(strings.last_index_byte("test", 'x')) + fmt.println(strings.last_index_byte("teäst", 'ä')) + } + +Output: + + 3 + 1 + -1 + -1 + +**Returns** The byte offset of the last occurrence of `c` in `s`, or -1 if not found. */ last_index_byte :: proc(s: string, c: byte) -> int { for i := len(s)-1; i >= 0; i -= 1 { @@ -1130,17 +1401,32 @@ Returns the byte offset of the first rune `r` in the string `s` it finds, -1 whe Invalid runes return -1 Example: -```odin - strings.index_rune("abcädef", 'x') // -> -1 - strings.index_rune("abcädef", 'a') // -> 0 - strings.index_rune("abcädef", 'b') // -> 1 - strings.index_rune("abcädef", 'c') // -> 2 - strings.index_rune("abcädef", 'ä') // -> 3 - strings.index_rune("abcädef", 'd') // -> 5 - strings.index_rune("abcädef", 'e') // -> 6 - strings.index_rune("abcädef", 'f') // -> 7 -``` -Returns: The byte offset of the first occurrence of `r` in `s`, or -1 if not found. + + import "core:fmt" + import "core:strings" + + strings_index_rune_example :: proc() { + fmt.println(strings.index_rune("abcädef", 'x')) + fmt.println(strings.index_rune("abcädef", 'a')) + fmt.println(strings.index_rune("abcädef", 'b')) + fmt.println(strings.index_rune("abcädef", 'c')) + fmt.println(strings.index_rune("abcädef", 'ä')) + fmt.println(strings.index_rune("abcädef", 'd')) + fmt.println(strings.index_rune("abcädef", 'e')) + fmt.println(strings.index_rune("abcädef", 'f')) + } + +Output: + + -1 + 0 + 1 + 2 + 5 + 6 + 7 + +**Returns** The byte offset of the first occurrence of `r` in `s`, or -1 if not found. */ index_rune :: proc(s: string, r: rune) -> int { switch { @@ -1168,13 +1454,25 @@ index_rune :: proc(s: string, r: rune) -> int { Returns the byte offset of the string `substr` in the string `s`, -1 when not found. Example: -```odin - strings.index("test", "t") // -> 0 - strings.index("test", "te") // -> 0 - strings.index("test", "st") // -> 2 - strings.index("test", "tt") // -> -1 -``` -Returns: The byte offset of the first occurrence of `substr` in `s`, or -1 if not found. + + import "core:fmt" + import "core:strings" + + strings_index_example :: proc() { + fmt.println(strings.index("test", "t")) + fmt.println(strings.index("test", "te")) + fmt.println(strings.index("test", "st")) + fmt.println(strings.index("test", "tt")) + } + +Output: + + 0 + 0 + 2 + -1 + +**Returns** The byte offset of the first occurrence of `substr` in `s`, or -1 if not found. */ index :: proc(s, substr: string) -> int { hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { @@ -1229,13 +1527,25 @@ index :: proc(s, substr: string) -> int { Returns the last byte offset of the string `substr` in the string `s`, -1 when not found. Example: -```odin - strings.last_index("test", "t") // -> 3 - strings.last_index("test", "te") // -> 0 - strings.last_index("test", "st") // -> 2 - strings.last_index("test", "tt") // -> -1 -``` -Returns: The byte offset of the last occurrence of `substr` in `s`, or -1 if not found. + + import "core:fmt" + import "core:strings" + + strings_last_index_example :: proc() { + fmt.println(strings.last_index("test", "t")) + fmt.println(strings.last_index("test", "te")) + fmt.println(strings.last_index("test", "st")) + fmt.println(strings.last_index("test", "tt")) + } + +Output: + + 3 + 0 + 2 + -1 + +**Returns** The byte offset of the last occurrence of `substr` in `s`, or -1 if not found. */ last_index :: proc(s, substr: string) -> int { hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { @@ -1288,14 +1598,27 @@ last_index :: proc(s, substr: string) -> int { Returns the index of any first char of `chars` found in `s`, -1 if not found. Example: -```odin - strings.index_any("test", "s") // -> 2 - strings.index_any("test", "se") // -> 1 - strings.index_any("test", "et") // -> 0 - strings.index_any("test", "set") // -> 0 - strings.index_any("test", "x") // -> -1 -``` -Returns: The index of the first character of `chars` found in `s`, or -1 if not found. + + import "core:fmt" + import "core:strings" + + strings_index_any_example :: proc() { + fmt.println(strings.index_any("test", "s")) + fmt.println(strings.index_any("test", "se")) + fmt.println(strings.index_any("test", "et")) + fmt.println(strings.index_any("test", "set")) + fmt.println(strings.index_any("test", "x")) + } + +Output: + + 2 + 1 + 0 + 0 + -1 + +**Returns** The index of the first character of `chars` found in `s`, or -1 if not found. */ index_any :: proc(s, chars: string) -> int { if chars == "" { @@ -1331,19 +1654,32 @@ index_any :: proc(s, chars: string) -> int { /* Finds the last occurrence of any character in 'chars' within 's'. Iterates in reverse. -Inputs: +**Inputs** - s: The string to search in - chars: The characters to look for Example: -```odin - strings.last_index_any("test", "s") // -> 2 - strings.last_index_any("test", "se") // -> 2 - strings.last_index_any("test", "et") // -> 3 - strings.last_index_any("test", "set") // -> 3 - strings.last_index_any("test", "x") // -> -1 -``` -Returns: The index of the last matching character, or -1 if not found + + import "core:fmt" + import "core:strings" + + strings_last_index_any_example :: proc() { + fmt.println(strings.last_index_any("test", "s")) + fmt.println(strings.last_index_any("test", "se")) + fmt.println(strings.last_index_any("test", "et")) + fmt.println(strings.last_index_any("test", "set")) + fmt.println(strings.last_index_any("test", "x")) + } + +Output: + + 2 + 2 + 3 + 3 + -1 + +**Returns** The index of the last matching character, or -1 if not found */ last_index_any :: proc(s, chars: string) -> int { if chars == "" { @@ -1396,11 +1732,11 @@ last_index_any :: proc(s, chars: string) -> int { /* Finds the first occurrence of any substring in 'substrs' within 's' -Inputs: +**Inputs** - s: The string to search in - substrs: The substrings to look for -Returns: A tuple containing the index of the first matching substring, and its length (width) +**Returns** A tuple containing the index of the first matching substring, and its length (width) */ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) { idx = -1 @@ -1434,19 +1770,32 @@ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) { /* Counts the number of non-overlapping occurrences of 'substr' in 's' -Inputs: +**Inputs** - s: The string to search in - substr: The substring to count Example: -```odin - strings.count("abbccc", "a") // -> 1 - strings.count("abbccc", "b") // -> 2 - strings.count("abbccc", "c") // -> 3 - strings.count("abbccc", "ab") // -> 1 - strings.count("abbccc", " ") // -> 0 -``` -Returns: The number of occurrences of 'substr' in 's', returns the rune_count + 1 of the string `s` on empty `substr` + + import "core:fmt" + import "core:strings" + + strings_count_example :: proc() { + fmt.println(strings.count("abbccc", "a")) + fmt.println(strings.count("abbccc", "b")) + fmt.println(strings.count("abbccc", "c")) + fmt.println(strings.count("abbccc", "ab")) + fmt.println(strings.count("abbccc", " ")) + } + +Output: + + 1 + 2 + 3 + 1 + 0 + +**Returns** The number of occurrences of 'substr' in 's', returns the rune_count + 1 of the string `s` on empty `substr` */ count :: proc(s, substr: string) -> int { if len(substr) == 0 { // special case @@ -1487,7 +1836,7 @@ Repeats the string 's' 'count' times, concatenating the result *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The string to repeat - count: The number of times to repeat 's' - allocator: (default is context.allocator) @@ -1495,10 +1844,19 @@ Inputs: WARNING: Panics if count < 0 Example: -```odin - strings.repeat("abc", 2) // -> "abcabc" -``` -Returns: The concatenated repeated string + + import "core:fmt" + import "core:strings" + + strings_repeat_example :: proc() { + fmt.println(strings.repeat("abc", 2)) + } + +Output: + + abcabc + +**Returns** The concatenated repeated string */ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string { if count < 0 { @@ -1520,19 +1878,30 @@ Replaces all occurrences of 'old' in 's' with 'new' *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The string to modify - old: The substring to replace - new: The substring to replace 'old' with - allocator: The allocator to use for the new string (default is context.allocator) Example: -```odin - strings.replace_all("xyzxyz", "xyz", "abc") // -> "abcabc", true - strings.replace_all("xyzxyz", "abc", "xyz") // -> "xyzxyz", false - strings.replace_all("xyzxyz", "xy", "z") // -> "zzzz", true -``` -Returns: A tuple containing the modified string and a boolean indicating if an allocation occurred during the replacement + + import "core:fmt" + import "core:strings" + + strings_replace_all_example :: proc() { + fmt.println(strings.replace_all("xyzxyz", "xyz", "abc")) + fmt.println(strings.replace_all("xyzxyz", "abc", "xyz")) + fmt.println(strings.replace_all("xyzxyz", "xy", "z")) + } + +Output: + + abcabc true + xyzxyz false + zzzz true + +**Returns** A tuple containing the modified string and a boolean indicating if an allocation occurred during the replacement */ replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { return replace(s, old, new, -1, allocator) @@ -1542,7 +1911,7 @@ Replaces n instances of old in the string s with the new string *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string - old: The substring to be replaced - new: The replacement string @@ -1550,13 +1919,25 @@ Inputs: - allocator: (default: context.allocator) Example: -```odin - strings.replace("xyzxyz", "xyz", "abc", 2) // -> "abcabc", true - strings.replace("xyzxyz", "xyz", "abc", 1) // -> "abcxyz", true - strings.replace("xyzxyz", "abc", "xyz", -1) // -> "xyzxyz", false - strings.replace("xyzxyz", "xy", "z", -1) // -> "zzzz", true -``` -Returns: A tuple containing the modified string and a boolean indicating if an allocation occurred during the replacement + + import "core:fmt" + import "core:strings" + + strings_replace_example :: proc() { + fmt.println(strings.replace("xyzxyz", "xyz", "abc", 2)) + fmt.println(strings.replace("xyzxyz", "xyz", "abc", 1)) + fmt.println(strings.replace("xyzxyz", "abc", "xyz", -1)) + fmt.println(strings.replace("xyzxyz", "xy", "z", -1)) + } + +Output: + + abcabc true + abcxyz true + xyzxyz false + zzzz true + +**Returns** A tuple containing the modified string and a boolean indicating if an allocation occurred during the replacement */ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) { if old == new || n == 0 { @@ -1602,20 +1983,32 @@ Removes the key string n times from the s string *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string - key: The substring to be removed - n: The number of instances to remove (if n < 0, no limit on the number of removes) - allocator: (default: context.allocator) Example: -```odin - strings.remove("abcabc", "abc", 1) // -> "abc", true - strings.remove("abcabc", "abc", -1) // -> "", true - strings.remove("abcabc", "a", -1) // -> "bcbc", true - strings.remove("abcabc", "x", -1) // -> "abcabc", false -``` -Returns: A tuple containing the modified string and a boolean indicating if an allocation occurred during the removal + + import "core:fmt" + import "core:strings" + + strings_remove_example :: proc() { + fmt.println(strings.remove("abcabc", "abc", 1)) + fmt.println(strings.remove("abcabc", "abc", -1)) + fmt.println(strings.remove("abcabc", "a", -1)) + fmt.println(strings.remove("abcabc", "x", -1)) + } + +Output: + + abc true + true + bcbc true + abcabc false + +**Returns** A tuple containing the modified string and a boolean indicating if an allocation occurred during the removal */ remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) { return replace(s, key, "", n, allocator) @@ -1625,18 +2018,29 @@ Removes all the key string instances from the s string *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string - key: The substring to be removed - allocator: (default: context.allocator) Example: -```odin - strings.remove_all("abcabc", "abc") // -> "", true - strings.remove_all("abcabc", "a") // -> "bcbc", true - strings.remove_all("abcabc", "x") // -> "abcabc", false -``` -Returns: A tuple containing the modified string and a boolean indicating if an allocation occurred during the removal + + import "core:fmt" + import "core:strings" + + strings_remove_all_example :: proc() { + fmt.println(strings.remove_all("abcabc", "abc")) + fmt.println(strings.remove_all("abcabc", "a")) + fmt.println(strings.remove_all("abcabc", "x")) + } + +Output: + + true + bcbc true + abcabc false + +**Returns** A tuple containing the modified string and a boolean indicating if an allocation occurred during the removal */ remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { return remove(s, key, -1, allocator) @@ -1676,23 +2080,36 @@ is_null :: proc(r: rune) -> bool { /* Finds the index of the first rune in the string s for which the procedure p returns the same value as truth -Inputs: +**Inputs** - s: The input string - p: A procedure that takes a rune and returns a boolean - truth: The boolean value to be matched (default: true) Example: -```odin - call :: proc(r: rune) -> bool { - return r == 'a' + + import "core:fmt" + import "core:strings" + + strings_index_proc_example :: proc() { + call :: proc(r: rune) -> bool { + return r == 'a' + } + fmt.println(strings.index_proc("abcabc", call)) + fmt.println(strings.index_proc("cbacba", call)) + fmt.println(strings.index_proc("cbacba", call, false)) + fmt.println(strings.index_proc("abcabc", call, false)) + fmt.println(strings.index_proc("xyz", call)) } - strings.index_proc("abcabc", call) // -> 0 - strings.index_proc("cbacba", call) // -> 2 - strings.index_proc("cbacba", call, false) // -> 0 - strings.index_proc("abcabc", call, false) // -> 1 - strings.index_proc("xyz", call) // -> -1 -``` -Returns: The index of the first matching rune, or -1 if no match was found + +Output: + + 0 + 2 + 0 + 1 + -1 + +**Returns** The index of the first matching rune, or -1 if no match was found */ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int { for r, i in s { @@ -1738,18 +2155,27 @@ last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta /* Trims the input string s from the left until the procedure p returns false -Inputs: +**Inputs** - s: The input string - p: A procedure that takes a rune and returns a boolean Example: -```odin - find :: proc(r: rune) -> bool { - return r != 'i' + + import "core:fmt" + import "core:strings" + + strings_trim_left_proc_example :: proc() { + find :: proc(r: rune) -> bool { + return r != 'i' + } + strings.trim_left_proc("testing", find) } - strings.trim_left_proc("testing", find) // -> "ing" -``` -Returns: The trimmed string as a slice of the original + +Output: + + ing + +**Returns** The trimmed string as a slice of the original */ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string { i := index_proc(s, p, false) @@ -1761,12 +2187,12 @@ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string { /* Trims the input string s from the left until the procedure p with state returns false -Inputs: +**Inputs** - s: The input string - p: A procedure that takes a raw pointer and a rune and returns a boolean - state: The raw pointer to be passed to the procedure p -Returns: The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string { i := index_proc_with_state(s, p, state, false) @@ -1778,18 +2204,27 @@ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, stat /* Trims the input string s from the right until the procedure p returns false -Inputs: +**Inputs** - s: The input string - p: A procedure that takes a rune and returns a boolean Example: -```odin - find :: proc(r: rune) -> bool { - return r != 't' + + import "core:fmt" + import "core:strings" + + strings_trim_right_proc_example :: proc() { + find :: proc(r: rune) -> bool { + return r != 't' + } + fmt.println(strings.trim_right_proc("testing", find)) } - strings.trim_right_proc("testing", find) -> "test" -``` -Returns: The trimmed string as a slice of the original + +Output: + + test + +**Returns** The trimmed string as a slice of the original */ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string { i := last_index_proc(s, p, false) @@ -1804,12 +2239,12 @@ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string { /* Trims the input string s from the right until the procedure p with state returns false -Inputs: +**Inputs** - s: The input string - p: A procedure that takes a raw pointer and a rune and returns a boolean - state: The raw pointer to be passed to the procedure p -Returns: The trimmed string as a slice of the original, empty when no match +**Returns** The trimmed string as a slice of the original, empty when no match */ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string { i := last_index_proc_with_state(s, p, state, false) @@ -1837,11 +2272,11 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> bool { /* Trims the cutset string from the s string -Inputs: +**Inputs** - s: The input string - cutset: The set of characters to be trimmed from the left of the input string -Returns: The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_left :: proc(s: string, cutset: string) -> string { if s == "" || cutset == "" { @@ -1853,11 +2288,11 @@ trim_left :: proc(s: string, cutset: string) -> string { /* Trims the cutset string from the s string from the right -Inputs: +**Inputs** - s: The input string - cutset: The set of characters to be trimmed from the right of the input string -Returns: The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_right :: proc(s: string, cutset: string) -> string { if s == "" || cutset == "" { @@ -1869,11 +2304,11 @@ trim_right :: proc(s: string, cutset: string) -> string { /* Trims the cutset string from the s string, both from left and right -Inputs: +**Inputs** - s: The input string - cutset: The set of characters to be trimmed from both sides of the input string -Returns: The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim :: proc(s: string, cutset: string) -> string { return trim_right(trim_left(s, cutset), cutset) @@ -1881,10 +2316,10 @@ trim :: proc(s: string, cutset: string) -> string { /* Trims until a valid non-space rune from the left, "\t\txyz\t\t" -> "xyz\t\t" -Inputs: +**Inputs** - s: The input string -Returns: The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_left_space :: proc(s: string) -> string { return trim_left_proc(s, is_space) @@ -1892,10 +2327,10 @@ trim_left_space :: proc(s: string) -> string { /* Trims from the right until a valid non-space rune, "\t\txyz\t\t" -> "\t\txyz" -Inputs: +**Inputs** - s: The input string -Returns: The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_right_space :: proc(s: string) -> string { return trim_right_proc(s, is_space) @@ -1903,10 +2338,10 @@ trim_right_space :: proc(s: string) -> string { /* Trims from both sides until a valid non-space rune, "\t\txyz\t\t" -> "xyz" -Inputs: +**Inputs** - s: The input string -Returns: The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_space :: proc(s: string) -> string { return trim_right_space(trim_left_space(s)) @@ -1914,10 +2349,10 @@ trim_space :: proc(s: string) -> string { /* Trims null runes from the left, "\x00\x00testing\x00\x00" -> "testing\x00\x00" -Inputs: +**Inputs** - s: The input string -Returns: The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_left_null :: proc(s: string) -> string { return trim_left_proc(s, is_null) @@ -1925,10 +2360,10 @@ trim_left_null :: proc(s: string) -> string { /* Trims null runes from the right, "\x00\x00testing\x00\x00" -> "\x00\x00testing" -Inputs: +**Inputs** - s: The input string -Returns: The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_right_null :: proc(s: string) -> string { return trim_right_proc(s, is_null) @@ -1936,9 +2371,9 @@ trim_right_null :: proc(s: string) -> string { /* Trims null runes from both sides, "\x00\x00testing\x00\x00" -> "testing" -Inputs: +**Inputs** - s: The input string -Returns: The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_null :: proc(s: string) -> string { return trim_right_null(trim_left_null(s)) @@ -1946,16 +2381,26 @@ trim_null :: proc(s: string) -> string { /* Trims a prefix string from the start of the s string and returns the trimmed string -Inputs: +**Inputs** - s: The input string - prefix: The prefix string to be removed Example: -```odin - strings.trim_prefix("testing", "test") // -> "ing" - strings.trim_prefix("testing", "abc") // -> "testing" -``` -Returns: The trimmed string as a slice of original, or the input string if no prefix was found + + import "core:fmt" + import "core:strings" + + strings_trim_prefix_example :: proc() { + fmt.println(strings.trim_prefix("testing", "test")) + fmt.println(strings.trim_prefix("testing", "abc")) + } + +Output: + + ing + testing + +**Returns** The trimmed string as a slice of original, or the input string if no prefix was found */ trim_prefix :: proc(s, prefix: string) -> string { if has_prefix(s, prefix) { @@ -1966,16 +2411,26 @@ trim_prefix :: proc(s, prefix: string) -> string { /* Trims a suffix string from the end of the s string and returns the trimmed string -Inputs: +**Inputs** - s: The input string - suffix: The suffix string to be removed Example: -```odin - strings.trim_suffix("todo.txt", ".txt") // -> "todo" - strings.trim_suffix("todo.doc", ".txt") // -> "todo.doc" -``` -Returns: The trimmed string as a slice of original, or the input string if no suffix was found + + import "core:fmt" + import "core:strings" + + strings_trim_suffix_example :: proc() { + fmt.println(strings.trim_suffix("todo.txt", ".txt")) + fmt.println(strings.trim_suffix("todo.doc", ".txt")) + } + +Output: + + todo + todo.doc + +**Returns** The trimmed string as a slice of original, or the input string if no suffix was found */ trim_suffix :: proc(s, suffix: string) -> string { if has_suffix(s, suffix) { @@ -1988,7 +2443,7 @@ Splits the input string s by all possible substrs and returns an allocated array *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string - substrs: An array of substrings used for splitting - allocator: (default is context.allocator) @@ -1996,12 +2451,21 @@ Inputs: NOTE: Allocation occurs for the array, the splits are all slices of the original string. Example: -```odin - splits := [?]string { "---", "~~~", ".", "_", "," } - res := strings.split_multi("testing,this.out_nice---done~~~last", splits[:]) - fmt.println(res) // -> [testing, this, out, nice, done, last] -``` -Returns: An array of strings, or nil on empty substring or no matches + + import "core:fmt" + import "core:strings" + + strings_split_multi_example :: proc() { + splits := [?]string { "---", "~~~", ".", "_", "," } + res := strings.split_multi("testing,this.out_nice---done~~~last", splits[:]) + fmt.println(res) // -> [testing, this, out, nice, done, last] + } + +Output: + + ["testing", "this", "out", "nice", "done", "last"] + +**Returns** An array of strings, or nil on empty substring or no matches */ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator) -> []string #no_bounds_check { if s == "" || len(substrs) <= 0 { @@ -2046,18 +2510,33 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator /* Splits the input string s by all possible substrs in an iterator fashion. The full string is returned if no match. -Inputs: +**Inputs** - it: A pointer to the input string - substrs: An array of substrings used for splitting Example: -```odin - it := "testing,this.out_nice---done~~~last" - for str in strings.split_multi_iterate(&it, splits[:]) { - fmt.println(str) // every iteration // -> [testing, this, out, nice, done, last] + + import "core:fmt" + import "core:strings" + + strings_split_multi_iterate_example :: proc() { + it := "testing,this.out_nice---done~~~last" + splits := [?]string { "---", "~~~", ".", "_", "," } + for str in strings.split_multi_iterate(&it, splits[:]) { + fmt.println(str) + } } -``` -Returns: A tuple containing the split string and a boolean indicating success or failure + +Output: + + testing + this + out + nice + done + last + +**Returns** A tuple containing the split string and a boolean indicating success or failure */ split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok: bool) #no_bounds_check { if it == nil || len(it) == 0 || len(substrs) <= 0 { @@ -2089,18 +2568,26 @@ Replaces invalid UTF-8 characters in the input string with a specified replaceme *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string - replacement: The string used to replace invalid UTF-8 characters - allocator: (default is context.allocator) Example: -```odin - text := "Hello\xC0\x80World" - result := strings.scrub(text, "?") - fmt.println(result) // -> "Hello?World" -``` -Returns: A new string with invalid UTF-8 characters replaced + + import "core:fmt" + import "core:strings" + + strings_scrub_example :: proc() { + text := "Hello\xC0\x80World" + fmt.println(strings.scrub(text, "?")) // -> "Hello?World" + } + +Output: + + Hello? + +**Returns** A new string with invalid UTF-8 characters replaced */ scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string { str := s @@ -2138,17 +2625,26 @@ Reverses the input string s *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string - allocator: (default is context.allocator) Example: -```odin - a := "abcxyz" - b := strings.reverse(a) - fmt.println(a, b) // -> abcxyz zyxcba -``` -Returns: A reversed version of the input string + + import "core:fmt" + import "core:strings" + + strings_reverse_example :: proc() { + a := "abcxyz" + b := strings.reverse(a) + fmt.println(a, b) + } + +Output: + + abcxyz zyxcba + +**Returns** A reversed version of the input string */ reverse :: proc(s: string, allocator := context.allocator) -> string { str := s @@ -2169,20 +2665,28 @@ Expands the input string by replacing tab characters with spaces to align to a s *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string - tab_size: The number of spaces to use for each tab character - allocator: (default is context.allocator) Example: -```odin - text := "abc1\tabc2\tabc3" - result := strings.expand_tabs(text, 4) - fmt.println(result) // -> "abc1 abc2 abc3" -``` + + import "core:fmt" + import "core:strings" + + strings_expand_tabs_example :: proc() { + text := "abc1\tabc2\tabc3" + fmt.println(strings.expand_tabs(text, 4)) + } + +Output: + + abc1 abc2 abc3 + WARNING: Panics if tab_size <= 0 -Returns: A new string with tab characters expanded to the specified tab size +**Returns** A new string with tab characters expanded to the specified tab size */ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string { if tab_size <= 0 { @@ -2228,18 +2732,29 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> /* Splits the input string str by the separator sep string and returns 3 parts. The values are slices of the original string. -Inputs: +**Inputs** - str: The input string - sep: The separator string Example: -```odin - text := "testing this out" - strings.partition(text, " this ") // -> head: "testing", match: " this ", tail: "out" - strings.partition(text, "hi") // -> head: "testing t", match: "hi", tail: "s out" - strings.partition(text, "xyz") // -> head: "testing this out", match: "", tail: "" -``` -Returns: A tuple with head (before the split), match (the separator), and tail (the end of the split) strings + + import "core:fmt" + import "core:strings" + + strings_partition_example :: proc() { + text := "testing this out" + strings.partition(text, " this ") // -> head: "testing", match: " this ", tail: "out" + strings.partition(text, "hi") // -> head: "testing t", match: "hi", tail: "s out" + strings.partition(text, "xyz") // -> head: "testing this out", match: "", tail: "" + } + +Output: + + testing this out + testing t hi s out + testing this out + +**Returns** A tuple with head (before the split), match (the separator), and tail (the end of the split) strings */ partition :: proc(str, sep: string) -> (head, match, tail: string) { i := index(str, sep) @@ -2260,13 +2775,13 @@ Centers the input string within a field of specified length by adding pad string *Allocates Using Provided Allocator* -Inputs: +**Inputs** - str: The input string - length: The desired length of the centered string - pad: The string used for padding on both sides - allocator: (default is context.allocator) -Returns: A new string centered within a field of the specified length +**Returns** A new string centered within a field of the specified length */ centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) @@ -2294,13 +2809,13 @@ Left-justifies the input string within a field of specified length by adding pad *Allocates Using Provided Allocator* -Inputs: +**Inputs** - str: The input string - length: The desired length of the left-justified string - pad: The string used for padding on the right side - allocator: (default is context.allocator) -Returns: A new string left-justified within a field of the specified length +**Returns** A new string left-justified within a field of the specified length */ left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) @@ -2327,13 +2842,13 @@ Right-justifies the input string within a field of specified length by adding pa *Allocates Using Provided Allocator* -Inputs: +**Inputs** - str: The input string - length: The desired length of the right-justified string - pad: The string used for padding on the left side - allocator: (default is context.allocator) -Returns: A new string right-justified within a field of the specified length +**Returns** A new string right-justified within a field of the specified length */ right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) @@ -2358,7 +2873,7 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex /* Writes a given pad string a specified number of times to an io.Writer -Inputs: +**Inputs** - w: The io.Writer to write the pad string to - pad: The pad string to be written - pad_len: The length of the pad string @@ -2386,11 +2901,11 @@ Splits a string into a slice of substrings at each instance of one or more conse *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string - allocator: (default is context.allocator) -Returns: A slice of substrings of the input string, or an empty slice if the input string only contains white space +**Returns** A slice of substrings of the input string, or an empty slice if the input string only contains white space */ fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds_check { n := 0 @@ -2445,14 +2960,14 @@ Splits a string into a slice of substrings at each run of unicode code points `c *Allocates Using Provided Allocator* -Inputs: +**Inputs** - s: The input string - f: A predicate function to determine the split points - allocator: (default is context.allocator) NOTE: fields_proc makes no guarantee about the order in which it calls f(ch), it assumes that `f` always returns the same value for a given ch -Returns: 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 +**Returns** 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 */ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> []string #no_bounds_check { substrings := make([dynamic]string, 0, 32, allocator) @@ -2483,10 +2998,10 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc /* Retrieves the first non-space substring from a mutable string reference and advances the reference. s is advanced from any space after the substring, or be an empty string if the substring was the remaining characters -Inputs: +**Inputs** - s: A mutable string reference to be iterated -Returns: +**Returns** - field: The first non-space substring found - ok: A boolean indicating if a non-space substring was found */ @@ -2525,11 +3040,11 @@ Computes the Levenshtein edit distance between two strings NOTE: Does not perform internal allocation if Length of String b in Runes is Smaller Than 64 -Inputs: +**Inputs** - a, b: The two strings to compare - allocator: (default is context.allocator) -Returns: The Levenshtein edit distance between the two strings +**Returns** The Levenshtein edit distance between the two strings NOTE: This implementation is a single-row-version of the Wagner–Fischer algorithm, based on C code by Martin Ettl. */ From 7de67f8c1b51296d849f2f95bcc7f33ca3a1b9d5 Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Mon, 27 Mar 2023 22:20:24 -0700 Subject: [PATCH 3/8] markdown compliant spaces --- core/strings/ascii_set.odin | 8 +- core/strings/builder.odin | 136 +++++++------- core/strings/conversion.odin | 54 +++--- core/strings/intern.odin | 16 +- core/strings/reader.odin | 54 +++--- core/strings/strings.odin | 336 +++++++++++++++++------------------ 6 files changed, 302 insertions(+), 302 deletions(-) diff --git a/core/strings/ascii_set.odin b/core/strings/ascii_set.odin index d597cad6d..7e7cec642 100644 --- a/core/strings/ascii_set.odin +++ b/core/strings/ascii_set.odin @@ -12,10 +12,10 @@ Ascii_Set :: distinct [8]u32 /* Creates an Ascii_Set with unique characters from the input string. -**Inputs** +**Inputs** - chars: A string containing characters to include in the Ascii_Set. -**Returns** +**Returns** - as: An Ascii_Set with unique characters from the input string. - ok: false if any character in the input string is not a valid ASCII character. */ @@ -33,11 +33,11 @@ ascii_set_make :: proc(chars: string) -> (as: Ascii_Set, ok: bool) #no_bounds_ch /* Determines if a given char is contained within an Ascii_Set. -**Inputs** +**Inputs** - as: The Ascii_Set to search. - c: The char to check for in the Ascii_Set. -**Returns** A boolean indicating if the byte is contained in the Ascii_Set (true) or not (false). +**Returns** A boolean indicating if the byte is contained in the Ascii_Set (true) or not (false). */ ascii_set_contains :: proc(as: Ascii_Set, c: byte) -> bool #no_bounds_check { return as[c>>5] & (1<<(c&31)) != 0 diff --git a/core/strings/builder.odin b/core/strings/builder.odin index db5215dee..eb78551bd 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -7,10 +7,10 @@ import "core:io" /* Type definition for a procedure that flushes a Builder -**Inputs** +**Inputs** - b: A pointer to the Builder -**Returns** A boolean indicating whether the Builder should be reset +**Returns** A boolean indicating whether the Builder should be reset */ Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool) /* @@ -26,10 +26,10 @@ Produces a Builder with a default length of 0 and cap of 16 *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - allocator: (default is context.allocator) -**Returns** A new Builder +**Returns** A new Builder */ builder_make_none :: proc(allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, allocator)} @@ -39,11 +39,11 @@ Produces a Builder with a specified length and cap of max(16,len) byte buffer *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - len: The desired length of the Builder's buffer - allocator: (default is context.allocator) -**Returns** A new Builder +**Returns** A new Builder */ builder_make_len :: proc(len: int, allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, len, allocator)} @@ -53,12 +53,12 @@ Produces a Builder with a specified length and cap *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - len: The desired length of the Builder's buffer - cap: The desired capacity of the Builder's buffer, cap is max(cap, len) - allocator: (default is context.allocator) -**Returns** A new Builder +**Returns** A new Builder */ builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder { return Builder{buf=make([dynamic]byte, len, cap, allocator)} @@ -75,11 +75,11 @@ It replaces the existing `buf` *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - b: A pointer to the Builder - allocator: (default is context.allocator) -**Returns** initialized ^Builder +**Returns** initialized ^Builder */ builder_init_none :: proc(b: ^Builder, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, allocator) @@ -91,12 +91,12 @@ It replaces the existing `buf` *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - b: A pointer to the Builder - len: The desired length of the Builder's buffer - allocator: (default is context.allocator) -**Returns** Initialized ^Builder +**Returns** Initialized ^Builder */ builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, len, allocator) @@ -106,13 +106,13 @@ builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) Initializes a Builder with a specified length and cap It replaces the existing `buf` -**Inputs** +**Inputs** - b: A pointer to the Builder - len: The desired length of the Builder's buffer - cap: The desired capacity of the Builder's buffer, actual max(len,cap) - allocator: (default is context.allocator) -**Returns** A pointer to the initialized Builder +**Returns** A pointer to the initialized Builder */ builder_init_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) -> ^Builder { b.buf = make([dynamic]byte, len, cap, allocator) @@ -159,10 +159,10 @@ _builder_stream_vtable := &_builder_stream_vtable_obj /* Returns an io.Stream from a Builder -**Inputs** +**Inputs** - b: A pointer to the Builder -**Returns** An io.Stream +**Returns** An io.Stream */ to_stream :: proc(b: ^Builder) -> io.Stream { return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b} @@ -170,10 +170,10 @@ to_stream :: proc(b: ^Builder) -> io.Stream { /* Returns an io.Writer from a Builder -**Inputs** +**Inputs** - b: A pointer to the Builder -**Returns** An io.Writer +**Returns** An io.Writer */ to_writer :: proc(b: ^Builder) -> io.Writer { return io.to_writer(to_stream(b)) @@ -181,7 +181,7 @@ to_writer :: proc(b: ^Builder) -> io.Writer { /* Deletes and clears the Builder byte buffer content -**Inputs** +**Inputs** - b: A pointer to the Builder */ builder_destroy :: proc(b: ^Builder) { @@ -191,7 +191,7 @@ builder_destroy :: proc(b: ^Builder) { /* Reserves the Builder byte buffer to a specific capacity, when it's higher than before -**Inputs** +**Inputs** - b: A pointer to the Builder - cap: The desired capacity for the Builder's buffer */ @@ -201,7 +201,7 @@ builder_grow :: proc(b: ^Builder, cap: int) { /* Clears the Builder byte buffer content (sets len to zero) -**Inputs** +**Inputs** - b: A pointer to the Builder */ builder_reset :: proc(b: ^Builder) { @@ -212,7 +212,7 @@ Creates a Builder from a slice of bytes with the same slice length as its capaci *Uses Nil Allocator - Does NOT allocate* -**Inputs** +**Inputs** - backing: A slice of bytes to be used as the backing buffer Example: @@ -231,7 +231,7 @@ Output: a ab -**Returns** A new Builder +**Returns** A new Builder */ builder_from_bytes :: proc(backing: []byte) -> Builder { s := transmute(runtime.Raw_Slice)backing @@ -250,10 +250,10 @@ builder_from_slice :: builder_from_bytes /* Casts the Builder byte buffer to a string and returns it -**Inputs** +**Inputs** - b: A Builder -**Returns** The contents of the Builder's buffer, as a string +**Returns** The contents of the Builder's buffer, as a string */ to_string :: proc(b: Builder) -> string { return string(b.buf[:]) @@ -261,10 +261,10 @@ to_string :: proc(b: Builder) -> string { /* Returns the length of the Builder's buffer, in bytes -**Inputs** +**Inputs** - b: A Builder -**Returns** The length of the Builder's buffer +**Returns** The length of the Builder's buffer */ builder_len :: proc(b: Builder) -> int { return len(b.buf) @@ -272,10 +272,10 @@ builder_len :: proc(b: Builder) -> int { /* Returns the capacity of the Builder's buffer, in bytes -**Inputs** +**Inputs** - b: A Builder -**Returns** The capacity of the Builder's buffer +**Returns** The capacity of the Builder's buffer */ builder_cap :: proc(b: Builder) -> int { return cap(b.buf) @@ -283,10 +283,10 @@ builder_cap :: proc(b: Builder) -> int { /* The free space left in the Builder's buffer, in bytes -**Inputs** +**Inputs** - b: A Builder -**Returns** The available space left in the Builder's buffer +**Returns** The available space left in the Builder's buffer */ builder_space :: proc(b: Builder) -> int { return cap(b.buf) - len(b.buf) @@ -294,7 +294,7 @@ builder_space :: proc(b: Builder) -> int { /* Appends a byte to the Builder and returns the number of bytes appended -**Inputs** +**Inputs** - b: A pointer to the Builder - x: The byte to be appended @@ -310,13 +310,13 @@ Example: fmt.println(strings.to_string(builder)) // -> ab } -**Output** +Output: ab NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of bytes appended +**Returns** The number of bytes appended */ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) { n0 := len(b.buf) @@ -327,7 +327,7 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) { /* Appends a slice of bytes to the Builder and returns the number of bytes appended -**Inputs** +**Inputs** - b: A pointer to the Builder - x: The slice of bytes to be appended @@ -345,7 +345,7 @@ Example: NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of bytes appended +**Returns** The number of bytes appended */ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { n0 := len(b.buf) @@ -356,7 +356,7 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) { /* Appends a single rune to the Builder and returns the number of bytes written and an `io.Error` -**Inputs** +**Inputs** - b: A pointer to the Builder - r: The rune to be appended @@ -378,7 +378,7 @@ Output: NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of bytes written and an io.Error (if any) +**Returns** The number of bytes written and an io.Error (if any) */ write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) { return io.write_rune(to_writer(b), r) @@ -386,7 +386,7 @@ write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) { /* Appends a quoted rune to the Builder and returns the number of bytes written -**Inputs** +**Inputs** - b: A pointer to the Builder - r: The rune to be appended @@ -409,7 +409,7 @@ Output: NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of bytes written +**Returns** The number of bytes written */ write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) { return io.write_quoted_rune(to_writer(b), r) @@ -417,7 +417,7 @@ write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) { /* Appends a string to the Builder and returns the number of bytes written -**Inputs** +**Inputs** - b: A pointer to the Builder - s: The string to be appended @@ -439,7 +439,7 @@ Output: NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of bytes written +**Returns** The number of bytes written */ write_string :: proc(b: ^Builder, s: string) -> (n: int) { n0 := len(b.buf) @@ -450,10 +450,10 @@ write_string :: proc(b: ^Builder, s: string) -> (n: int) { /* Pops and returns the last byte in the Builder or 0 when the Builder is empty -**Inputs** +**Inputs** - b: A pointer to the Builder -**Returns** The last byte in the Builder or 0 if empty +**Returns** The last byte in the Builder or 0 if empty */ pop_byte :: proc(b: ^Builder) -> (r: byte) { if len(b.buf) == 0 { @@ -468,10 +468,10 @@ pop_byte :: proc(b: ^Builder) -> (r: byte) { /* Pops the last rune in the Builder and returns the popped rune and its rune width or (0, 0) if empty -**Inputs** +**Inputs** - b: A pointer to the Builder -**Returns** The popped rune and its rune width or (0, 0) if empty +**Returns** The popped rune and its rune width or (0, 0) if empty */ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) { if len(b.buf) == 0 { @@ -486,7 +486,7 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) { @(private) DIGITS_LOWER := "0123456789abcdefx" /* -**Inputs** +**Inputs** - b: A pointer to the Builder - str: The string to be quoted and appended - quote: The optional quote character (default is double quotes) @@ -510,7 +510,7 @@ Output: NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of bytes written +**Returns** The number of bytes written */ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) { n, _ = io.write_quoted_string(to_writer(b), str, quote) @@ -519,14 +519,14 @@ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: /* Appends an encoded rune to the Builder and returns the number of bytes written -**Inputs** +**Inputs** - b: A pointer to the Builder - r: The rune to be appended - write_quote: Optional boolean flag to write the quote character (default is true) NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of bytes written +**Returns** The number of bytes written */ write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) { n, _ = io.write_encoded_rune(to_writer(b), r, write_quote) @@ -536,7 +536,7 @@ write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int /* Appends an escaped rune to the Builder and returns the number of bytes written -**Inputs** +**Inputs** - b: A pointer to the Builder - r: The rune to be appended - quote: The quote character @@ -549,7 +549,7 @@ Appends an escaped rune to the Builder and returns the number of bytes written NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of bytes written +**Returns** The number of bytes written */ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) { n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe) @@ -558,7 +558,7 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false /* Writes a f64 value to the Builder and returns the number of characters written -**Inputs** +**Inputs** - b: A pointer to the Builder - f: The f64 value to be appended - fmt: The format byte @@ -568,7 +568,7 @@ Writes a f64 value to the Builder and returns the number of characters written NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of characters written +**Returns** The number of characters written */ write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) { buf: [384]byte @@ -583,7 +583,7 @@ write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_ /* Writes a f16 value to the Builder and returns the number of characters written -**Inputs** +**Inputs** - b: A pointer to the Builder - f: The f16 value to be appended - fmt: The format byte @@ -591,7 +591,7 @@ Writes a f16 value to the Builder and returns the number of characters written NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of characters written +**Returns** The number of characters written */ write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte @@ -604,7 +604,7 @@ write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: /* Writes a f32 value to the Builder and returns the number of characters written -**Inputs** +**Inputs** - b: A pointer to the Builder - f: The f32 value to be appended - fmt: The format byte @@ -629,7 +629,7 @@ Output: NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of characters written +**Returns** The number of characters written */ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte @@ -642,7 +642,7 @@ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: /* Writes a f32 value to the Builder and returns the number of characters written -**Inputs** +**Inputs** - b: A pointer to the Builder - f: The f32 value to be appended - fmt: The format byte @@ -650,7 +650,7 @@ Writes a f32 value to the Builder and returns the number of characters written NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of characters written +**Returns** The number of characters written */ write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte @@ -663,14 +663,14 @@ write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: /* Writes a u64 value to the Builder and returns the number of characters written -**Inputs** +**Inputs** - b: A pointer to the Builder - i: The u64 value to be appended - base: The optional base for the numeric representation NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of characters written +**Returns** The number of characters written */ write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) { buf: [32]byte @@ -680,14 +680,14 @@ write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) { /* Writes a i64 value to the Builder and returns the number of characters written -**Inputs** +**Inputs** - b: A pointer to the Builder - i: The i64 value to be appended - base: The optional base for the numeric representation NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of characters written +**Returns** The number of characters written */ write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) { buf: [32]byte @@ -697,14 +697,14 @@ write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) { /* Writes a uint value to the Builder and returns the number of characters written -**Inputs** +**Inputs** - b: A pointer to the Builder - i: The uint value to be appended - base: The optional base for the numeric representation NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of characters written +**Returns** The number of characters written */ write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) { return write_u64(b, u64(i), base) @@ -712,14 +712,14 @@ write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) { /* Writes a int value to the Builder and returns the number of characters written -**Inputs** +**Inputs** - b: A pointer to the Builder - i: The int value to be appended - base: The optional base for the numeric representation NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. -**Returns** The number of characters written +**Returns** The number of characters written */ write_int :: proc(b: ^Builder, i: int, base: int = 10) -> (n: int) { return write_i64(b, i64(i), base) diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin index ba86058ab..b617db94b 100644 --- a/core/strings/conversion.odin +++ b/core/strings/conversion.odin @@ -9,14 +9,14 @@ Converts invalid UTF-8 sequences in the input string `s` to the `replacement` st *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: Input string that may contain invalid UTF-8 sequences. - replacement: String to replace invalid UTF-8 sequences with. - allocator: (default: context.allocator). WARNING: Allocation does not occur when len(s) == 0 -**Returns** A valid UTF-8 string with invalid sequences replaced by `replacement`. +**Returns** A valid UTF-8 string with invalid sequences replaced by `replacement`. */ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string { if len(s) == 0 { @@ -76,7 +76,7 @@ Converts the input string `s` to all lowercase characters. *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: Input string to be converted. - allocator: (default: context.allocator). @@ -93,7 +93,7 @@ Output: test -**Returns** A new string with all characters converted to lowercase. +**Returns** A new string with all characters converted to lowercase. */ to_lower :: proc(s: string, allocator := context.allocator) -> string { b: Builder @@ -108,7 +108,7 @@ Converts the input string `s` to all uppercase characters. *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: Input string to be converted. - allocator: (default: context.allocator). @@ -125,7 +125,7 @@ Output: TEST -**Returns** A new string with all characters converted to uppercase. +**Returns** A new string with all characters converted to uppercase. */ to_upper :: proc(s: string, allocator := context.allocator) -> string { b: Builder @@ -138,10 +138,10 @@ to_upper :: proc(s: string, allocator := context.allocator) -> string { /* Checks if the rune `c` is a delimiter (' ', '-', or '_'). -**Inputs** +**Inputs** - c: Rune to check for delimiter status. -**Returns** True if `c` is a delimiter, false otherwise. +**Returns** True if `c` is a delimiter, false otherwise. */ is_delimiter :: proc(c: rune) -> bool { return c == '-' || c == '_' || is_space(c) @@ -149,10 +149,10 @@ is_delimiter :: proc(c: rune) -> bool { /* Checks if the rune `r` is a non-alphanumeric or space character. -**Inputs** +**Inputs** - r: Rune to check for separator status. -**Returns** True if `r` is a non-alpha or `unicode.is_space` rune. +**Returns** True if `r` is a non-alpha or `unicode.is_space` rune. */ is_separator :: proc(r: rune) -> bool { if r <= 0x7f { @@ -179,7 +179,7 @@ is_separator :: proc(r: rune) -> bool { /* Iterates over a string, calling a callback for each rune with the previous, current, and next runes as arguments. -**Inputs** +**Inputs** - w: An io.Writer to be used by the callback for writing output. - s: The input string to be iterated over. - callback: A procedure to be called for each rune in the string, with arguments (w: io.Writer, prev, curr, next: rune). @@ -241,11 +241,11 @@ Converts the input string `s` to "lowerCamelCase". *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: Input string to be converted. - allocator: (default: context.allocator). -**Returns** A "lowerCamelCase" formatted string. +**Returns** A "lowerCamelCase" formatted string. */ to_camel_case :: proc(s: string, allocator := context.allocator) -> string { s := s @@ -275,11 +275,11 @@ Converts the input string `s` to "UpperCamelCase" (PascalCase). *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: Input string to be converted. - allocator: (default: context.allocator). -**Returns** A "PascalCase" formatted string. +**Returns** A "PascalCase" formatted string. */ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string { s := s @@ -307,7 +307,7 @@ Returns a string converted to a delimiter-separated case with configurable casin *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string to be converted - delimiter: The rune to be used as the delimiter between words - all_upper_case: A boolean indicating if the output should be all uppercased (true) or lowercased (false) @@ -330,7 +330,7 @@ Output: HELLO WORLD a_b_c -**Returns** The converted string +**Returns** The converted string */ to_delimiter_case :: proc( s: string, @@ -380,7 +380,7 @@ Converts a string to "snake_case" with all runes lowercased *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string to be converted - allocator: (default: context.allocator). @@ -400,7 +400,7 @@ Output: hello_world ``` -**Returns** The converted string +**Returns** The converted string */ to_snake_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '_', false, allocator) @@ -412,7 +412,7 @@ Converts a string to "SNAKE_CASE" with all runes uppercased *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string to be converted - allocator: (default: context.allocator). @@ -429,7 +429,7 @@ Output: HELLO_WORLD -**Returns** The converted string +**Returns** The converted string */ to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '_', true, allocator) @@ -439,7 +439,7 @@ Converts a string to "kebab-case" with all runes lowercased *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string to be converted - allocator: (default: context.allocator). @@ -456,7 +456,7 @@ Output: hello-world -**Returns** The converted string +**Returns** The converted string */ to_kebab_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '-', false, allocator) @@ -466,7 +466,7 @@ Converts a string to "KEBAB-CASE" with all runes uppercased *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string to be converted - allocator: (default: context.allocator). @@ -483,7 +483,7 @@ Output: HELLO-WORLD -**Returns** The converted string +**Returns** The converted string */ to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> string { return to_delimiter_case(s, '-', true, allocator) @@ -493,7 +493,7 @@ Converts a string to "Ada_Case" *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string to be converted - allocator: (default: context.allocator). @@ -510,7 +510,7 @@ Output: Hello_World -**Returns** The converted string +**Returns** The converted string */ to_ada_case :: proc(s: string, allocator := context.allocator) -> string { s := s diff --git a/core/strings/intern.odin b/core/strings/intern.odin index e05a58478..e4577d14f 100644 --- a/core/strings/intern.odin +++ b/core/strings/intern.odin @@ -25,7 +25,7 @@ Initializes the entries map and sets the allocator for the string entries *Allocates Using Provided Allocators* -**Inputs** +**Inputs** - m: A pointer to the Intern struct to be initialized - allocator: The allocator for the Intern_Entry strings (Default: context.allocator) - map_allocator: The allocator for the map of entries (Default: context.allocator) @@ -37,7 +37,7 @@ intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := /* Frees the map and all its content allocated using the `.allocator`. -**Inputs** +**Inputs** - m: A pointer to the Intern struct to be destroyed */ intern_destroy :: proc(m: ^Intern) { @@ -51,13 +51,13 @@ Returns the interned string for the given text, is set in the map if it didnt ex *MAY Allocate using the Intern's Allocator* -**Inputs** +**Inputs** - m: A pointer to the Intern struct - text: The string to be interned NOTE: The returned string lives as long as the map entry lives. -**Returns** The interned string and an allocator error if any +**Returns** The interned string and an allocator error if any */ intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) { entry := _intern_get_entry(m, text) or_return @@ -68,13 +68,13 @@ Returns the interned C-String for the given text, is set in the map if it didnt *MAY Allocate using the Intern's Allocator* -**Inputs** +**Inputs** - m: A pointer to the Intern struct - text: The string to be interned NOTE: The returned C-String lives as long as the map entry lives -**Returns** The interned C-String and an allocator error if any +**Returns** The interned C-String and an allocator error if any */ intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) { entry := _intern_get_entry(m, text) or_return @@ -86,11 +86,11 @@ Sets and allocates the entry if it wasn't set yet *MAY Allocate using the Intern's Allocator* -**Inputs** +**Inputs** - m: A pointer to the Intern struct - text: The string to be looked up or interned -**Returns** The new or existing interned entry and an allocator error if any +**Returns** The new or existing interned entry and an allocator error if any */ _intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check { if prev, ok := m.entries[text]; ok { diff --git a/core/strings/reader.odin b/core/strings/reader.odin index 9489ab2b9..82a31c92c 100644 --- a/core/strings/reader.odin +++ b/core/strings/reader.odin @@ -16,7 +16,7 @@ Reader :: struct { /* Initializes a string Reader with the provided string -**Inputs** +**Inputs** - r: A pointer to a Reader struct - s: The input string to be read */ @@ -28,10 +28,10 @@ reader_init :: proc(r: ^Reader, s: string) { /* Converts a Reader into an io.Stream -**Inputs** +**Inputs** - r: A pointer to a Reader struct -**Returns** An io.Stream for the given Reader +**Returns** An io.Stream for the given Reader */ reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) { s.stream_data = r @@ -41,11 +41,11 @@ reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) { /* Initializes a string Reader and returns an io.Reader for the given string -**Inputs** +**Inputs** - r: A pointer to a Reader struct - s: The input string to be read -**Returns** An io.Reader for the given string +**Returns** An io.Reader for the given string */ to_reader :: proc(r: ^Reader, s: string) -> io.Reader { reader_init(r, s) @@ -55,11 +55,11 @@ to_reader :: proc(r: ^Reader, s: string) -> io.Reader { /* Initializes a string Reader and returns an io.Reader_At for the given string -**Inputs** +**Inputs** - r: A pointer to a Reader struct - s: The input string to be read -**Returns** An io.Reader_At for the given string +**Returns** An io.Reader_At for the given string */ to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At { reader_init(r, s) @@ -69,10 +69,10 @@ to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At { /* Returns the remaining length of the Reader -**Inputs** +**Inputs** - r: A pointer to a Reader struct -**Returns** The remaining length of the Reader +**Returns** The remaining length of the Reader */ reader_length :: proc(r: ^Reader) -> int { if r.i >= i64(len(r.s)) { @@ -83,10 +83,10 @@ reader_length :: proc(r: ^Reader) -> int { /* Returns the length of the string stored in the Reader -**Inputs** +**Inputs** - r: A pointer to a Reader struct -**Returns** The length of the string stored in the Reader +**Returns** The length of the string stored in the Reader */ reader_size :: proc(r: ^Reader) -> i64 { return i64(len(r.s)) @@ -94,11 +94,11 @@ reader_size :: proc(r: ^Reader) -> i64 { /* Reads len(p) bytes from the Reader's string and copies into the provided slice. -**Inputs** +**Inputs** - r: A pointer to a Reader struct - p: A byte slice to copy data into -**Returns** +**Returns** - n: The number of bytes read - err: An io.Error if an error occurs while reading, including .EOF, otherwise nil denotes success. */ @@ -114,12 +114,12 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) { /* Reads len(p) bytes from the Reader's string and copies into the provided slice, at the specified offset from the current index. -**Inputs** +**Inputs** - r: A pointer to a Reader struct - p: A byte slice to copy data into - off: The offset from which to read -**Returns** +**Returns** - n: The number of bytes read - err: An io.Error if an error occurs while reading, including .EOF, otherwise nil denotes success. */ @@ -139,10 +139,10 @@ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Erro /* Reads and returns a single byte from the Reader's string -**Inputs** +**Inputs** - r: A pointer to a Reader struct -**Returns** +**Returns** - The byte read from the Reader - err: An io.Error if an error occurs while reading, including .EOF, otherwise nil denotes success. */ @@ -158,10 +158,10 @@ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) { /* Decrements the Reader's index (i) by 1 -**Inputs** +**Inputs** - r: A pointer to a Reader struct -**Returns** An io.Error if `r.i <= 0` (.Invalid_Unread), otherwise nil denotes success. +**Returns** An io.Error if `r.i <= 0` (.Invalid_Unread), otherwise nil denotes success. */ reader_unread_byte :: proc(r: ^Reader) -> io.Error { if r.i <= 0 { @@ -174,10 +174,10 @@ reader_unread_byte :: proc(r: ^Reader) -> io.Error { /* Reads and returns a single rune and its size from the Reader's string -**Inputs** +**Inputs** - r: A pointer to a Reader struct -**Returns** +**Returns** - ch: The rune read from the Reader - size: The size of the rune in bytes - err: An io.Error if an error occurs while reading @@ -199,12 +199,12 @@ reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) { /* Decrements the Reader's index (i) by the size of the last read rune -**Inputs** +**Inputs** - r: A pointer to a Reader struct WARNING: May only be used once and after a valid `read_rune` call -**Returns** An io.Error if an error occurs while unreading (.Invalid_Unread), else nil denotes success. +**Returns** An io.Error if an error occurs while unreading (.Invalid_Unread), else nil denotes success. */ reader_unread_rune :: proc(r: ^Reader) -> io.Error { if r.i <= 0 { @@ -220,12 +220,12 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error { /* Seeks the Reader's index to a new position -**Inputs** +**Inputs** - r: A pointer to a Reader struct - offset: The new offset position - whence: The reference point for the new position (.Start, .Current, or .End) -**Returns** +**Returns** - The absolute offset after seeking - err: An io.Error if an error occurs while seeking (.Invalid_Whence, .Invalid_Offset) */ @@ -252,13 +252,13 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E /* Writes the remaining content of the Reader's string into the provided io.Writer -**Inputs** +**Inputs** - r: A pointer to a Reader struct - w: The io.Writer to write the remaining content into WARNING: Panics if writer writes more bytes than remainig length of string. -**Returns** +**Returns** - n: The number of bytes written - err: An io.Error if an error occurs while writing (.Short_Write) */ diff --git a/core/strings/strings.odin b/core/strings/strings.odin index ca2054b33..36b244d27 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -11,12 +11,12 @@ Clones a string *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The string to be cloned - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: #caller_location) -**Returns** A cloned string +**Returns** A cloned string */ clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> string { c := make([]byte, len(s), allocator, loc) @@ -28,12 +28,12 @@ Clones a string safely (returns early with an allocation error on failure) *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The string to be cloned - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: #caller_location) -**Returns** +**Returns** - str: A cloned string - err: A mem.Allocator_Error if an error occurs during allocation */ @@ -47,12 +47,12 @@ Clones a string and appends a nul byte to make it a cstring *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The string to be cloned - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: #caller_location) -**Returns** A cloned cstring with an appended nul byte +**Returns** A cloned cstring with an appended nul byte */ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> cstring { c := make([]byte, len(s)+1, allocator, loc) @@ -63,13 +63,13 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #call /* Transmutes a raw pointer into a string. Non-allocating. -**Inputs** +**Inputs** - ptr: A pointer to the start of the byte sequence - len: The length of the byte sequence NOTE: The created string is only valid as long as the pointer and length are valid. -**Returns** A string created from the byte pointer and length +**Returns** A string created from the byte pointer and length */ string_from_ptr :: proc(ptr: ^byte, len: int) -> string { return transmute(string)mem.Raw_String{ptr, len} @@ -80,11 +80,11 @@ Transmutes a raw pointer (nul-terminated) into a string. Non-allocating. Searche NOTE: The created string is only valid as long as the pointer and length are valid. The string is truncated at the first nul byte encountered. -**Inputs** +**Inputs** - ptr: A pointer to the start of the nul-terminated byte sequence - len: The length of the byte sequence -**Returns** A string created from the nul-terminated byte pointer and length +**Returns** A string created from the nul-terminated byte pointer and length */ string_from_nul_terminated_ptr :: proc(ptr: ^byte, len: int) -> string { s := transmute(string)mem.Raw_String{ptr, len} @@ -94,10 +94,10 @@ string_from_nul_terminated_ptr :: proc(ptr: ^byte, len: int) -> string { /* Gets the raw byte pointer for the start of a string `str` -**Inputs** +**Inputs** - str: The input string -**Returns** A pointer to the start of the string's bytes +**Returns** A pointer to the start of the string's bytes */ ptr_from_string :: proc(str: string) -> ^byte { d := transmute(mem.Raw_String)str @@ -106,12 +106,12 @@ ptr_from_string :: proc(str: string) -> ^byte { /* Converts a string `str` to a cstring -**Inputs** +**Inputs** - str: The input string WARNING: This is unsafe because the original string may not contain a nul byte. -**Returns** The converted cstring +**Returns** The converted cstring */ unsafe_string_to_cstring :: proc(str: string) -> cstring { d := transmute(mem.Raw_String)str @@ -120,13 +120,13 @@ unsafe_string_to_cstring :: proc(str: string) -> cstring { /* Truncates a string `str` at the first occurrence of char/byte `b` -**Inputs** +**Inputs** - str: The input string - b: The byte to truncate the string at NOTE: Failure to find the byte results in returning the entire string. -**Returns** The truncated string +**Returns** The truncated string */ truncate_to_byte :: proc(str: string, b: byte) -> string { n := index_byte(str, b) @@ -138,11 +138,11 @@ truncate_to_byte :: proc(str: string, b: byte) -> string { /* Truncates a string str at the first occurrence of rune r as a slice of the original, entire string if not found -**Inputs** +**Inputs** - str: The input string - r: The rune to truncate the string at -**Returns** The truncated string +**Returns** The truncated string */ truncate_to_rune :: proc(str: string, r: rune) -> string { n := index_rune(str, r) @@ -156,12 +156,12 @@ Clones a byte array s and appends a nul byte *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The byte array to be cloned - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: #caller_location) -**Returns** A cloned string from the byte array with a nul byte +**Returns** A cloned string from the byte array with a nul byte */ clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> string { c := make([]byte, len(s)+1, allocator, loc) @@ -174,12 +174,12 @@ Clones a cstring s as a string *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The cstring to be cloned - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: #caller_location) -**Returns** A cloned string from the cstring +**Returns** A cloned string from the cstring */ clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> string { return clone(string(s), allocator, loc) @@ -189,7 +189,7 @@ Clones a string from a byte pointer ptr and a byte length len *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - ptr: A pointer to the start of the byte sequence - len: The length of the byte sequence - allocator: (default: context.allocator) @@ -197,7 +197,7 @@ Clones a string from a byte pointer ptr and a byte length len NOTE: Same as `string_from_ptr`, but perform an additional `clone` operation -**Returns** A cloned string from the byte pointer and length +**Returns** A cloned string from the byte pointer and length */ clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc := #caller_location) -> string { s := string_from_ptr(ptr, len) @@ -215,7 +215,7 @@ Clones a string from a nul-terminated cstring ptr and a byte length len *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - ptr: A pointer to the start of the nul-terminated cstring - len: The byte length of the cstring - allocator: (default: context.allocator) @@ -223,7 +223,7 @@ Clones a string from a nul-terminated cstring ptr and a byte length len NOTE: Truncates at the first nul byte encountered or the byte length. -**Returns** A cloned string from the nul-terminated cstring and byte length +**Returns** A cloned string from the nul-terminated cstring and byte length */ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.allocator, loc := #caller_location) -> string { s := string_from_ptr((^u8)(ptr), len) @@ -234,11 +234,11 @@ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context. Compares two strings, returning a value representing which one comes first lexicographically. -1 for lhs; 1 for rhs, or 0 if they are equal. -**Inputs** +**Inputs** - lhs: First string for comparison - rhs: Second string for comparison -**Returns** -1 if lhs comes first, 1 if rhs comes first, or 0 if they are equal +**Returns** -1 if lhs comes first, 1 if rhs comes first, or 0 if they are equal */ compare :: proc(lhs, rhs: string) -> int { return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs) @@ -246,11 +246,11 @@ compare :: proc(lhs, rhs: string) -> int { /* Returns the byte offset of the rune r in the string s, -1 when not found -**Inputs** +**Inputs** - s: The input string - r: The rune to search for -**Returns** The byte offset of the rune r in the string s, or -1 if not found +**Returns** The byte offset of the rune r in the string s, or -1 if not found */ contains_rune :: proc(s: string, r: rune) -> int { for c, offset in s { @@ -263,7 +263,7 @@ contains_rune :: proc(s: string, r: rune) -> int { /* Returns true when the string substr is contained inside the string s -**Inputs** +**Inputs** - s: The input string - substr: The substring to search for @@ -296,7 +296,7 @@ Output: true false -**Returns** true if substr is contained inside the string s, false otherwise +**Returns** true if substr is contained inside the string s, false otherwise */ contains :: proc(s, substr: string) -> bool { return index(s, substr) >= 0 @@ -304,7 +304,7 @@ contains :: proc(s, substr: string) -> bool { /* Returns true when the string s contains any of the characters inside the string chars -**Inputs** +**Inputs** - s: The input string - chars: The characters to search for @@ -327,7 +327,7 @@ Output: true false -**Returns** true if the string s contains any of the characters in chars, false otherwise +**Returns** true if the string s contains any of the characters in chars, false otherwise */ contains_any :: proc(s, chars: string) -> bool { return index_any(s, chars) >= 0 @@ -335,7 +335,7 @@ contains_any :: proc(s, chars: string) -> bool { /* Returns the UTF-8 rune count of the string s -**Inputs** +**Inputs** - s: The input string Example: @@ -353,7 +353,7 @@ Output: 4 5 -**Returns** The UTF-8 rune count of the string s +**Returns** The UTF-8 rune count of the string s */ rune_count :: proc(s: string) -> int { return utf8.rune_count_in_string(s) @@ -362,7 +362,7 @@ rune_count :: proc(s: string) -> int { Returns whether the strings u and v are the same alpha characters, ignoring different casings Works with UTF-8 string content -**Inputs** +**Inputs** - u: The first string for comparison - v: The second string for comparison @@ -385,7 +385,7 @@ Output: true false -**Returns** True if the strings u and v are the same alpha characters (ignoring case), false +**Returns** True if the strings u and v are the same alpha characters (ignoring case), false */ equal_fold :: proc(u, v: string) -> bool { s, t := u, v @@ -432,7 +432,7 @@ equal_fold :: proc(u, v: string) -> bool { /* Returns the prefix length common between strings a and b -**Inputs** +**Inputs** - a: The first input string - b: The second input string @@ -455,7 +455,7 @@ Output: 2 0 -**Returns** The prefix length common between strings a and b +**Returns** The prefix length common between strings a and b */ prefix_length :: proc(a, b: string) -> (n: int) { _len := min(len(a), len(b)) @@ -483,7 +483,7 @@ prefix_length :: proc(a, b: string) -> (n: int) { /* Determines if a string s starts with a given prefix -**Inputs** +**Inputs** - s: The string to check for the prefix - prefix: The prefix to look for @@ -506,7 +506,7 @@ Output: true false -**Returns** true if the string s starts with the prefix, otherwise false +**Returns** true if the string s starts with the prefix, otherwise false */ has_prefix :: proc(s, prefix: string) -> bool { return len(s) >= len(prefix) && s[0:len(prefix)] == prefix @@ -531,11 +531,11 @@ Output: false true -**Inputs** +**Inputs** - s: The string to check for the suffix - suffix: The suffix to look for -**Returns** true if the string s ends with the suffix, otherwise false +**Returns** true if the string s ends with the suffix, otherwise false */ has_suffix :: proc(s, suffix: string) -> bool { return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix @@ -563,12 +563,12 @@ Output: a-b-c a...b...c -**Inputs** +**Inputs** - a: A slice of strings to join - sep: The separator string - allocator: (default is context.allocator) -**Returns** A combined string from the slice of strings a separated with the sep string +**Returns** A combined string from the slice of strings a separated with the sep string */ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string { if len(a) == 0 { @@ -593,12 +593,12 @@ Joins a slice of strings a with a sep string, returns an error on allocation fai *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - a: A slice of strings to join - sep: The separator string - allocator: (default is context.allocator) -**Returns** +**Returns** - str: A combined string from the slice of strings a separated with the sep string - err: An error if allocation failed, otherwise nil */ @@ -625,7 +625,7 @@ Returns a combined string from the slice of strings `a` without a separator *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - a: A slice of strings to concatenate - allocator: An optional custom allocator (default is context.allocator) @@ -643,7 +643,7 @@ Output: abc -**Returns** The concatenated string +**Returns** The concatenated string */ concatenate :: proc(a: []string, allocator := context.allocator) -> string { if len(a) == 0 { @@ -666,11 +666,11 @@ Returns a combined string from the slice of strings `a` without a separator, or *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - a: A slice of strings to concatenate - allocator: An optional custom allocator (default is context.allocator) -**Returns** The concatenated string, and an error if allocation fails +**Returns** The concatenated string, and an error if allocation fails */ concatenate_safe :: proc(a: []string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) { if len(a) == 0 { @@ -693,7 +693,7 @@ Returns a substring of the input string `s` with the specified rune offset and l *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string to cut - rune_offset: The starting rune index (default is 0). In runes, not bytes. - rune_length: The number of runes to include in the substring (default is 0, which returns the remainder of the string). In runes, not bytes. @@ -716,7 +716,7 @@ Output: me example -**Returns** The substring +**Returns** The substring */ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator := context.allocator) -> (res: string) { s := s; rune_length := rune_length @@ -774,14 +774,14 @@ Splits the input string `s` into a slice of substrings separated by the specifie *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string to split - sep: The separator string - sep_save: A flag determining if the separator should be saved in the resulting substrings - n: The maximum number of substrings to return, returns nil without alloc when n=0 - allocator: An optional custom allocator (default is context.allocator) -**Returns** A slice of substrings +**Returns** A slice of substrings */ @private _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator) -> []string { @@ -835,7 +835,7 @@ Splits a string into parts based on a separator. *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The string to split. - sep: The separator string used to split the input string. - allocator: (default is context.allocator). @@ -855,7 +855,7 @@ Output: ["aaa", "bbb", "ccc", "ddd", "eee"] -**Returns** A slice of strings, each representing a part of the split string. +**Returns** A slice of strings, each representing a part of the split string. */ split :: proc(s, sep: string, allocator := context.allocator) -> []string { return _split(s, sep, 0, -1, allocator) @@ -865,7 +865,7 @@ Splits a string into parts based on a separator. if n < count of seperators, the *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The string to split. - sep: The separator string used to split the input string. - allocator: (default is context.allocator) @@ -885,7 +885,7 @@ Output: ["aaa", "bbb", "ccc.ddd.eee"] -**Returns** A slice of strings, each representing a part of the split string. +**Returns** A slice of strings, each representing a part of the split string. */ split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string { return _split(s, sep, 0, n, allocator) @@ -895,7 +895,7 @@ Splits a string into parts after the separator, retaining it in the substrings. *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The string to split. - sep: The separator string used to split the input string. - allocator: (Optional) The allocator used for allocation (default is context.allocator). @@ -915,7 +915,7 @@ Output: ["aaa.", "bbb.", "ccc.", "ddd.", "eee"] -**Returns** A slice of strings, each representing a part of the split string after the separator. +**Returns** A slice of strings, each representing a part of the split string after the separator. */ split_after :: proc(s, sep: string, allocator := context.allocator) -> []string { return _split(s, sep, len(sep), -1, allocator) @@ -925,7 +925,7 @@ Splits a string into a total of 'n' parts after the separator. *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The string to split. - sep: The separator string used to split the input string. - n: The maximum number of parts to split the string into. @@ -946,7 +946,7 @@ Output: ["aaa.", "bbb.", "ccc.ddd.eee"] -**Returns** A slice of strings with 'n' parts or fewer if there weren't +**Returns** A slice of strings with 'n' parts or fewer if there weren't */ split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string { return _split(s, sep, len(sep), n, allocator) @@ -957,14 +957,14 @@ up to (but not including) the separator, as well as a boolean indicating success *Used Internally - Private Function* -**Inputs** +**Inputs** - s: Pointer to the input string, which is modified during the search. - sep: The separator string to search for. - sep_save: Number of characters from the separator to include in the result. NOTE: Destructively consumes the string -**Returns** A tuple containing the resulting substring and a boolean indicating success. +**Returns** A tuple containing the resulting substring and a boolean indicating success. */ @private _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) { @@ -997,7 +997,7 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, Splits the input string by the byte separator in an iterator fashion. Destructively consumes the original string until the end. -**Inputs** +**Inputs** - s: Pointer to the input string, which is modified during the search. - sep: The byte separator to search for. @@ -1021,7 +1021,7 @@ Output: d e -**Returns** A tuple containing the resulting substring and a boolean indicating success. +**Returns** A tuple containing the resulting substring and a boolean indicating success. */ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) { m := index_byte(s^, sep) @@ -1041,7 +1041,7 @@ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) { Splits the input string by the separator string in an iterator fashion. Destructively consumes the original string until the end. -**Inputs** +**Inputs** - s: Pointer to the input string, which is modified during the search. - sep: The separator string to search for. @@ -1065,7 +1065,7 @@ Output: d e -**Returns** A tuple containing the resulting substring and a boolean indicating success. +**Returns** A tuple containing the resulting substring and a boolean indicating success. */ split_iterator :: proc(s: ^string, sep: string) -> (string, bool) { return _split_iterator(s, sep, 0) @@ -1074,7 +1074,7 @@ split_iterator :: proc(s: ^string, sep: string) -> (string, bool) { Splits the input string after every separator string in an iterator fashion. Destructively consumes the original string until the end. -**Inputs** +**Inputs** - s: Pointer to the input string, which is modified during the search. - sep: The separator string to search for. @@ -1098,7 +1098,7 @@ Output: d. e -**Returns** A tuple containing the resulting substring and a boolean indicating success. +**Returns** A tuple containing the resulting substring and a boolean indicating success. */ split_after_iterator :: proc(s: ^string, sep: string) -> (string, bool) { return _split_iterator(s, sep, len(sep)) @@ -1108,10 +1108,10 @@ Trims the carriage return character from the end of the input string. *Used Internally - Private Function* -**Inputs** +**Inputs** - s: The input string to trim. -**Returns** The trimmed string as a slice. +**Returns** The trimmed string as a slice. */ @(private) _trim_cr :: proc(s: string) -> string { @@ -1128,7 +1128,7 @@ Splits the input string at every line break '\n'. *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string to split. - allocator: (default is context.allocator) @@ -1147,7 +1147,7 @@ Output: ["a", "b", "c", "d", "e"] -**Returns** An allocated slice of strings split by line breaks. +**Returns** An allocated slice of strings split by line breaks. */ split_lines :: proc(s: string, allocator := context.allocator) -> []string { sep :: "\n" @@ -1162,7 +1162,7 @@ Splits the input string at every line break '\n' for n parts. *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string to split. - n: The number of parts to split into. - allocator: (default is context.allocator) @@ -1182,7 +1182,7 @@ Output: ["a", "b", "c\nd\ne"] -**Returns** An allocated array of strings split by line breaks. +**Returns** An allocated array of strings split by line breaks. */ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []string { sep :: "\n" @@ -1197,7 +1197,7 @@ Splits the input string at every line break '\n' leaving the '\n' in the resulti *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string to split. - allocator: (default is context.allocator) @@ -1216,7 +1216,7 @@ Output: ["a\n", "b\n", "c\n", "d\n", "e"] -**Returns** An allocated slice of strings split by line breaks with line breaks included. +**Returns** An allocated slice of strings split by line breaks with line breaks included. */ split_lines_after :: proc(s: string, allocator := context.allocator) -> []string { sep :: "\n" @@ -1232,7 +1232,7 @@ Only runs for n parts. *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string to split. - n: The number of parts to split into. - allocator: (default is context.allocator) @@ -1252,7 +1252,7 @@ Output: ["a\n", "b\n", "c\nd\ne"] -**Returns** An allocated slice of strings split by line breaks with line breaks included. +**Returns** An allocated slice of strings split by line breaks with line breaks included. */ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -> []string { sep :: "\n" @@ -1266,7 +1266,7 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) - Splits the input string at every line break '\n'. Returns the current split string every iteration until the string is consumed. -**Inputs** +**Inputs** - s: Pointer to the input string, which is modified during the search. Example: @@ -1285,7 +1285,7 @@ Output: abcde -**Returns** A tuple containing the resulting substring and a boolean indicating success. +**Returns** A tuple containing the resulting substring and a boolean indicating success. */ split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) { sep :: "\n" @@ -1296,7 +1296,7 @@ split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) { Splits the input string at every line break '\n'. Returns the current split string with line breaks included every iteration until the string is consumed. -**Inputs** +**Inputs** - s: Pointer to the input string, which is modified during the search. Example: @@ -1319,7 +1319,7 @@ Output: d e -**Returns** A tuple containing the resulting substring with line breaks included and a boolean indicating success. +**Returns** A tuple containing the resulting substring with line breaks included and a boolean indicating success. */ split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) { sep :: "\n" @@ -1330,7 +1330,7 @@ split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) { Returns the byte offset of the first byte c in the string s it finds, -1 when not found. NOTE: Can't find UTF-8 based runes. -**Inputs** +**Inputs** - s: The input string to search in. - c: The byte to search for. @@ -1353,7 +1353,7 @@ Output: -1 -1 -**Returns** The byte offset of the first occurrence of c in s, or -1 if not found. +**Returns** The byte offset of the first occurrence of c in s, or -1 if not found. */ index_byte :: proc(s: string, c: byte) -> int { for i := 0; i < len(s); i += 1 { @@ -1386,7 +1386,7 @@ Output: -1 -1 -**Returns** The byte offset of the last occurrence of `c` in `s`, or -1 if not found. +**Returns** The byte offset of the last occurrence of `c` in `s`, or -1 if not found. */ last_index_byte :: proc(s: string, c: byte) -> int { for i := len(s)-1; i >= 0; i -= 1 { @@ -1426,7 +1426,7 @@ Output: 6 7 -**Returns** The byte offset of the first occurrence of `r` in `s`, or -1 if not found. +**Returns** The byte offset of the first occurrence of `r` in `s`, or -1 if not found. */ index_rune :: proc(s: string, r: rune) -> int { switch { @@ -1472,7 +1472,7 @@ Output: 2 -1 -**Returns** The byte offset of the first occurrence of `substr` in `s`, or -1 if not found. +**Returns** The byte offset of the first occurrence of `substr` in `s`, or -1 if not found. */ index :: proc(s, substr: string) -> int { hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { @@ -1545,7 +1545,7 @@ Output: 2 -1 -**Returns** The byte offset of the last occurrence of `substr` in `s`, or -1 if not found. +**Returns** The byte offset of the last occurrence of `substr` in `s`, or -1 if not found. */ last_index :: proc(s, substr: string) -> int { hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { @@ -1618,7 +1618,7 @@ Output: 0 -1 -**Returns** The index of the first character of `chars` found in `s`, or -1 if not found. +**Returns** The index of the first character of `chars` found in `s`, or -1 if not found. */ index_any :: proc(s, chars: string) -> int { if chars == "" { @@ -1654,7 +1654,7 @@ index_any :: proc(s, chars: string) -> int { /* Finds the last occurrence of any character in 'chars' within 's'. Iterates in reverse. -**Inputs** +**Inputs** - s: The string to search in - chars: The characters to look for @@ -1679,7 +1679,7 @@ Output: 3 -1 -**Returns** The index of the last matching character, or -1 if not found +**Returns** The index of the last matching character, or -1 if not found */ last_index_any :: proc(s, chars: string) -> int { if chars == "" { @@ -1732,11 +1732,11 @@ last_index_any :: proc(s, chars: string) -> int { /* Finds the first occurrence of any substring in 'substrs' within 's' -**Inputs** +**Inputs** - s: The string to search in - substrs: The substrings to look for -**Returns** A tuple containing the index of the first matching substring, and its length (width) +**Returns** A tuple containing the index of the first matching substring, and its length (width) */ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) { idx = -1 @@ -1770,7 +1770,7 @@ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) { /* Counts the number of non-overlapping occurrences of 'substr' in 's' -**Inputs** +**Inputs** - s: The string to search in - substr: The substring to count @@ -1795,7 +1795,7 @@ Output: 1 0 -**Returns** The number of occurrences of 'substr' in 's', returns the rune_count + 1 of the string `s` on empty `substr` +**Returns** The number of occurrences of 'substr' in 's', returns the rune_count + 1 of the string `s` on empty `substr` */ count :: proc(s, substr: string) -> int { if len(substr) == 0 { // special case @@ -1836,7 +1836,7 @@ Repeats the string 's' 'count' times, concatenating the result *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The string to repeat - count: The number of times to repeat 's' - allocator: (default is context.allocator) @@ -1856,7 +1856,7 @@ Output: abcabc -**Returns** The concatenated repeated string +**Returns** The concatenated repeated string */ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string { if count < 0 { @@ -1878,7 +1878,7 @@ Replaces all occurrences of 'old' in 's' with 'new' *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The string to modify - old: The substring to replace - new: The substring to replace 'old' with @@ -1901,7 +1901,7 @@ Output: xyzxyz false zzzz true -**Returns** A tuple containing the modified string and a boolean indicating if an allocation occurred during the replacement +**Returns** A tuple containing the modified string and a boolean indicating if an allocation occurred during the replacement */ replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { return replace(s, old, new, -1, allocator) @@ -1911,7 +1911,7 @@ Replaces n instances of old in the string s with the new string *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string - old: The substring to be replaced - new: The replacement string @@ -1937,7 +1937,7 @@ Output: xyzxyz false zzzz true -**Returns** A tuple containing the modified string and a boolean indicating if an allocation occurred during the replacement +**Returns** A tuple containing the modified string and a boolean indicating if an allocation occurred during the replacement */ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) { if old == new || n == 0 { @@ -1983,7 +1983,7 @@ Removes the key string n times from the s string *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string - key: The substring to be removed - n: The number of instances to remove (if n < 0, no limit on the number of removes) @@ -2008,7 +2008,7 @@ Output: bcbc true abcabc false -**Returns** A tuple containing the modified string and a boolean indicating if an allocation occurred during the removal +**Returns** A tuple containing the modified string and a boolean indicating if an allocation occurred during the removal */ remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) { return replace(s, key, "", n, allocator) @@ -2018,7 +2018,7 @@ Removes all the key string instances from the s string *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string - key: The substring to be removed - allocator: (default: context.allocator) @@ -2040,7 +2040,7 @@ Output: bcbc true abcabc false -**Returns** A tuple containing the modified string and a boolean indicating if an allocation occurred during the removal +**Returns** A tuple containing the modified string and a boolean indicating if an allocation occurred during the removal */ remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { return remove(s, key, -1, allocator) @@ -2080,7 +2080,7 @@ is_null :: proc(r: rune) -> bool { /* Finds the index of the first rune in the string s for which the procedure p returns the same value as truth -**Inputs** +**Inputs** - s: The input string - p: A procedure that takes a rune and returns a boolean - truth: The boolean value to be matched (default: true) @@ -2109,7 +2109,7 @@ Output: 1 -1 -**Returns** The index of the first matching rune, or -1 if no match was found +**Returns** The index of the first matching rune, or -1 if no match was found */ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int { for r, i in s { @@ -2155,7 +2155,7 @@ last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta /* Trims the input string s from the left until the procedure p returns false -**Inputs** +**Inputs** - s: The input string - p: A procedure that takes a rune and returns a boolean @@ -2175,7 +2175,7 @@ Output: ing -**Returns** The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string { i := index_proc(s, p, false) @@ -2187,12 +2187,12 @@ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string { /* Trims the input string s from the left until the procedure p with state returns false -**Inputs** +**Inputs** - s: The input string - p: A procedure that takes a raw pointer and a rune and returns a boolean - state: The raw pointer to be passed to the procedure p -**Returns** The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string { i := index_proc_with_state(s, p, state, false) @@ -2204,7 +2204,7 @@ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, stat /* Trims the input string s from the right until the procedure p returns false -**Inputs** +**Inputs** - s: The input string - p: A procedure that takes a rune and returns a boolean @@ -2224,7 +2224,7 @@ Output: test -**Returns** The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string { i := last_index_proc(s, p, false) @@ -2239,12 +2239,12 @@ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string { /* Trims the input string s from the right until the procedure p with state returns false -**Inputs** +**Inputs** - s: The input string - p: A procedure that takes a raw pointer and a rune and returns a boolean - state: The raw pointer to be passed to the procedure p -**Returns** The trimmed string as a slice of the original, empty when no match +**Returns** The trimmed string as a slice of the original, empty when no match */ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string { i := last_index_proc_with_state(s, p, state, false) @@ -2272,11 +2272,11 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> bool { /* Trims the cutset string from the s string -**Inputs** +**Inputs** - s: The input string - cutset: The set of characters to be trimmed from the left of the input string -**Returns** The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_left :: proc(s: string, cutset: string) -> string { if s == "" || cutset == "" { @@ -2288,11 +2288,11 @@ trim_left :: proc(s: string, cutset: string) -> string { /* Trims the cutset string from the s string from the right -**Inputs** +**Inputs** - s: The input string - cutset: The set of characters to be trimmed from the right of the input string -**Returns** The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_right :: proc(s: string, cutset: string) -> string { if s == "" || cutset == "" { @@ -2304,11 +2304,11 @@ trim_right :: proc(s: string, cutset: string) -> string { /* Trims the cutset string from the s string, both from left and right -**Inputs** +**Inputs** - s: The input string - cutset: The set of characters to be trimmed from both sides of the input string -**Returns** The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim :: proc(s: string, cutset: string) -> string { return trim_right(trim_left(s, cutset), cutset) @@ -2316,10 +2316,10 @@ trim :: proc(s: string, cutset: string) -> string { /* Trims until a valid non-space rune from the left, "\t\txyz\t\t" -> "xyz\t\t" -**Inputs** +**Inputs** - s: The input string -**Returns** The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_left_space :: proc(s: string) -> string { return trim_left_proc(s, is_space) @@ -2327,10 +2327,10 @@ trim_left_space :: proc(s: string) -> string { /* Trims from the right until a valid non-space rune, "\t\txyz\t\t" -> "\t\txyz" -**Inputs** +**Inputs** - s: The input string -**Returns** The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_right_space :: proc(s: string) -> string { return trim_right_proc(s, is_space) @@ -2338,10 +2338,10 @@ trim_right_space :: proc(s: string) -> string { /* Trims from both sides until a valid non-space rune, "\t\txyz\t\t" -> "xyz" -**Inputs** +**Inputs** - s: The input string -**Returns** The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_space :: proc(s: string) -> string { return trim_right_space(trim_left_space(s)) @@ -2349,10 +2349,10 @@ trim_space :: proc(s: string) -> string { /* Trims null runes from the left, "\x00\x00testing\x00\x00" -> "testing\x00\x00" -**Inputs** +**Inputs** - s: The input string -**Returns** The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_left_null :: proc(s: string) -> string { return trim_left_proc(s, is_null) @@ -2360,10 +2360,10 @@ trim_left_null :: proc(s: string) -> string { /* Trims null runes from the right, "\x00\x00testing\x00\x00" -> "\x00\x00testing" -**Inputs** +**Inputs** - s: The input string -**Returns** The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_right_null :: proc(s: string) -> string { return trim_right_proc(s, is_null) @@ -2371,9 +2371,9 @@ trim_right_null :: proc(s: string) -> string { /* Trims null runes from both sides, "\x00\x00testing\x00\x00" -> "testing" -**Inputs** +**Inputs** - s: The input string -**Returns** The trimmed string as a slice of the original +**Returns** The trimmed string as a slice of the original */ trim_null :: proc(s: string) -> string { return trim_right_null(trim_left_null(s)) @@ -2381,7 +2381,7 @@ trim_null :: proc(s: string) -> string { /* Trims a prefix string from the start of the s string and returns the trimmed string -**Inputs** +**Inputs** - s: The input string - prefix: The prefix string to be removed @@ -2400,7 +2400,7 @@ Output: ing testing -**Returns** The trimmed string as a slice of original, or the input string if no prefix was found +**Returns** The trimmed string as a slice of original, or the input string if no prefix was found */ trim_prefix :: proc(s, prefix: string) -> string { if has_prefix(s, prefix) { @@ -2411,7 +2411,7 @@ trim_prefix :: proc(s, prefix: string) -> string { /* Trims a suffix string from the end of the s string and returns the trimmed string -**Inputs** +**Inputs** - s: The input string - suffix: The suffix string to be removed @@ -2430,7 +2430,7 @@ Output: todo todo.doc -**Returns** The trimmed string as a slice of original, or the input string if no suffix was found +**Returns** The trimmed string as a slice of original, or the input string if no suffix was found */ trim_suffix :: proc(s, suffix: string) -> string { if has_suffix(s, suffix) { @@ -2443,7 +2443,7 @@ Splits the input string s by all possible substrs and returns an allocated array *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string - substrs: An array of substrings used for splitting - allocator: (default is context.allocator) @@ -2465,7 +2465,7 @@ Output: ["testing", "this", "out", "nice", "done", "last"] -**Returns** An array of strings, or nil on empty substring or no matches +**Returns** An array of strings, or nil on empty substring or no matches */ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator) -> []string #no_bounds_check { if s == "" || len(substrs) <= 0 { @@ -2510,7 +2510,7 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator /* Splits the input string s by all possible substrs in an iterator fashion. The full string is returned if no match. -**Inputs** +**Inputs** - it: A pointer to the input string - substrs: An array of substrings used for splitting @@ -2536,7 +2536,7 @@ Output: done last -**Returns** A tuple containing the split string and a boolean indicating success or failure +**Returns** A tuple containing the split string and a boolean indicating success or failure */ split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok: bool) #no_bounds_check { if it == nil || len(it) == 0 || len(substrs) <= 0 { @@ -2568,7 +2568,7 @@ Replaces invalid UTF-8 characters in the input string with a specified replaceme *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string - replacement: The string used to replace invalid UTF-8 characters - allocator: (default is context.allocator) @@ -2587,7 +2587,7 @@ Output: Hello? -**Returns** A new string with invalid UTF-8 characters replaced +**Returns** A new string with invalid UTF-8 characters replaced */ scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string { str := s @@ -2625,7 +2625,7 @@ Reverses the input string s *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string - allocator: (default is context.allocator) @@ -2644,7 +2644,7 @@ Output: abcxyz zyxcba -**Returns** A reversed version of the input string +**Returns** A reversed version of the input string */ reverse :: proc(s: string, allocator := context.allocator) -> string { str := s @@ -2665,7 +2665,7 @@ Expands the input string by replacing tab characters with spaces to align to a s *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string - tab_size: The number of spaces to use for each tab character - allocator: (default is context.allocator) @@ -2686,7 +2686,7 @@ Output: WARNING: Panics if tab_size <= 0 -**Returns** A new string with tab characters expanded to the specified tab size +**Returns** A new string with tab characters expanded to the specified tab size */ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string { if tab_size <= 0 { @@ -2732,7 +2732,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> /* Splits the input string str by the separator sep string and returns 3 parts. The values are slices of the original string. -**Inputs** +**Inputs** - str: The input string - sep: The separator string @@ -2754,7 +2754,7 @@ Output: testing t hi s out testing this out -**Returns** A tuple with head (before the split), match (the separator), and tail (the end of the split) strings +**Returns** A tuple with head (before the split), match (the separator), and tail (the end of the split) strings */ partition :: proc(str, sep: string) -> (head, match, tail: string) { i := index(str, sep) @@ -2775,13 +2775,13 @@ Centers the input string within a field of specified length by adding pad string *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - str: The input string - length: The desired length of the centered string - pad: The string used for padding on both sides - allocator: (default is context.allocator) -**Returns** A new string centered within a field of the specified length +**Returns** A new string centered within a field of the specified length */ centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) @@ -2809,13 +2809,13 @@ Left-justifies the input string within a field of specified length by adding pad *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - str: The input string - length: The desired length of the left-justified string - pad: The string used for padding on the right side - allocator: (default is context.allocator) -**Returns** A new string left-justified within a field of the specified length +**Returns** A new string left-justified within a field of the specified length */ left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) @@ -2842,13 +2842,13 @@ Right-justifies the input string within a field of specified length by adding pa *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - str: The input string - length: The desired length of the right-justified string - pad: The string used for padding on the left side - allocator: (default is context.allocator) -**Returns** A new string right-justified within a field of the specified length +**Returns** A new string right-justified within a field of the specified length */ right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { n := rune_count(str) @@ -2873,7 +2873,7 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex /* Writes a given pad string a specified number of times to an io.Writer -**Inputs** +**Inputs** - w: The io.Writer to write the pad string to - pad: The pad string to be written - pad_len: The length of the pad string @@ -2901,11 +2901,11 @@ Splits a string into a slice of substrings at each instance of one or more conse *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string - allocator: (default is context.allocator) -**Returns** A slice of substrings of the input string, or an empty slice if the input string only contains white space +**Returns** A slice of substrings of the input string, or an empty slice if the input string only contains white space */ fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds_check { n := 0 @@ -2960,14 +2960,14 @@ Splits a string into a slice of substrings at each run of unicode code points `c *Allocates Using Provided Allocator* -**Inputs** +**Inputs** - s: The input string - f: A predicate function to determine the split points - allocator: (default is context.allocator) NOTE: fields_proc makes no guarantee about the order in which it calls f(ch), it assumes that `f` always returns the same value for a given ch -**Returns** 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 +**Returns** 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 */ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> []string #no_bounds_check { substrings := make([dynamic]string, 0, 32, allocator) @@ -2998,10 +2998,10 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc /* Retrieves the first non-space substring from a mutable string reference and advances the reference. s is advanced from any space after the substring, or be an empty string if the substring was the remaining characters -**Inputs** +**Inputs** - s: A mutable string reference to be iterated -**Returns** +**Returns** - field: The first non-space substring found - ok: A boolean indicating if a non-space substring was found */ @@ -3040,11 +3040,11 @@ Computes the Levenshtein edit distance between two strings NOTE: Does not perform internal allocation if Length of String b in Runes is Smaller Than 64 -**Inputs** +**Inputs** - a, b: The two strings to compare - allocator: (default is context.allocator) -**Returns** The Levenshtein edit distance between the two strings +**Returns** The Levenshtein edit distance between the two strings NOTE: This implementation is a single-row-version of the Wagner–Fischer algorithm, based on C code by Martin Ettl. */ From 937e5de1d8676c8ae25a8e4b52d2272277aad396 Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Mon, 27 Mar 2023 22:23:13 -0700 Subject: [PATCH 4/8] add missing eof newline --- core/strings/ascii_set.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/strings/ascii_set.odin b/core/strings/ascii_set.odin index 7e7cec642..11ad8b947 100644 --- a/core/strings/ascii_set.odin +++ b/core/strings/ascii_set.odin @@ -41,4 +41,4 @@ Determines if a given char is contained within an Ascii_Set. */ ascii_set_contains :: proc(as: Ascii_Set, c: byte) -> bool #no_bounds_check { return as[c>>5] & (1<<(c&31)) != 0 -} \ No newline at end of file +} From 203ae32b797b758cf98c49db18752cee02c1c7e7 Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Tue, 28 Mar 2023 10:24:41 -0700 Subject: [PATCH 5/8] pr pickups --- core/strings/builder.odin | 30 +++++++++++++++++++++++------- core/strings/strings.odin | 10 +++++----- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/core/strings/builder.odin b/core/strings/builder.odin index eb78551bd..15bda1b2d 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -148,8 +148,7 @@ _builder_stream_vtable_obj := io.Stream_VTable{ }, impl_destroy = proc(s: io.Stream) -> io.Error { b := (^Builder)(s.stream_data) - delete(b.buf) - b.buf=nil + builder_destroy(b) return .None }, } @@ -179,7 +178,7 @@ to_writer :: proc(b: ^Builder) -> io.Writer { return io.to_writer(to_stream(b)) } /* -Deletes and clears the Builder byte buffer content +Deletes the Builder byte buffer content **Inputs** - b: A pointer to the Builder @@ -501,12 +500,12 @@ Example: strings.write_quoted_string(&builder, "a") // 3 strings.write_quoted_string(&builder, "bc", '\'') // 4 strings.write_quoted_string(&builder, "xyz") // 5 - fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz" + fmt.println(strings.to_string(builder)) } Output: - "a"'bc'xyz" + "a"'bc'"xyz" NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. @@ -517,12 +516,29 @@ write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: return } /* -Appends an encoded rune to the Builder and returns the number of bytes written +Appends a rune to the Builder and returns the number of bytes written **Inputs** - b: A pointer to the Builder - r: The rune to be appended -- write_quote: Optional boolean flag to write the quote character (default is true) +- write_quote: Optional boolean flag to wrap in single-quotes (') (default is true) + +Example: + + import "core:fmt" + import "core:strings" + + write_encoded_rune_example :: proc() { + builder := strings.builder_make() + strings.write_encoded_rune(&builder, 'a', false) // 1 + strings.write_encoded_rune(&builder, '\"', true) // 3 + strings.write_encoded_rune(&builder, 'x', false) // 1 + fmt.println(strings.to_string(builder)) + } + +Output: + + a'"'x NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written. diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 36b244d27..118faadab 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -2078,7 +2078,7 @@ is_null :: proc(r: rune) -> bool { return r == 0x0000 } /* -Finds the index of the first rune in the string s for which the procedure p returns the same value as truth +Find the index of the first rune r in string s for which procedure p returns the same as truth, or -1 if no such rune appears. **Inputs** - s: The input string @@ -2777,7 +2777,7 @@ Centers the input string within a field of specified length by adding pad string **Inputs** - str: The input string -- length: The desired length of the centered string +- length: The desired length of the centered string, in runes - pad: The string used for padding on both sides - allocator: (default is context.allocator) @@ -2876,8 +2876,8 @@ Writes a given pad string a specified number of times to an io.Writer **Inputs** - w: The io.Writer to write the pad string to - pad: The pad string to be written -- pad_len: The length of the pad string -- remains: The number of times to write the pad string +- pad_len: The length of the pad string, in runes +- remains: The number of times to write the pad string, in runes */ @private write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { @@ -2956,7 +2956,7 @@ fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds return a } /* -Splits a string into a slice of substrings at each run of unicode code points `ch` satisfying the predicate f(ch) +Splits a string into a slice of substrings at each run of unicode code points `r` satisfying the predicate f(r) *Allocates Using Provided Allocator* From 6dce07790a9311f87356ba5a07b6051b0e17ebe8 Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Tue, 28 Mar 2023 11:07:33 -0700 Subject: [PATCH 6/8] add backticks on variables, code review comments --- core/strings/conversion.odin | 16 ++- core/strings/intern.odin | 14 +-- core/strings/reader.odin | 42 ++++---- core/strings/strings.odin | 184 +++++++++++++++++------------------ 4 files changed, 127 insertions(+), 129 deletions(-) diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin index b617db94b..d71dc4724 100644 --- a/core/strings/conversion.odin +++ b/core/strings/conversion.odin @@ -136,15 +136,15 @@ to_upper :: proc(s: string, allocator := context.allocator) -> string { return to_string(b) } /* -Checks if the rune `c` is a delimiter (' ', '-', or '_'). +Checks if the rune `r` is a delimiter (' ', '-', or '_'). **Inputs** -- c: Rune to check for delimiter status. +- r: Rune to check for delimiter status. -**Returns** True if `c` is a delimiter, false otherwise. +**Returns** True if `r` is a delimiter, false otherwise. */ -is_delimiter :: proc(c: rune) -> bool { - return c == '-' || c == '_' || is_space(c) +is_delimiter :: proc(r: rune) -> bool { + return r == '-' || r == '_' || is_space(r) } /* Checks if the rune `r` is a non-alphanumeric or space character. @@ -195,7 +195,7 @@ Example: my_callback :: proc(w: io.Writer, prev, curr, next: rune) { fmt.println("my_callback", curr) // <-- Custom logic here } - s := "hello world" + s := "hello" b: strings.Builder strings.builder_init_len(&b, len(s)) w := strings.to_writer(&b) @@ -521,9 +521,7 @@ to_ada_case :: proc(s: string, allocator := context.allocator) -> string { string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) { if !is_delimiter(curr) { - if is_delimiter(prev) || - prev == 0 || - (unicode.is_lower(prev) && unicode.is_upper(curr)) { + if is_delimiter(prev) || prev == 0 || (unicode.is_lower(prev) && unicode.is_upper(curr)) { if prev != 0 { io.write_rune(w, '_') } diff --git a/core/strings/intern.odin b/core/strings/intern.odin index e4577d14f..59395824a 100644 --- a/core/strings/intern.odin +++ b/core/strings/intern.odin @@ -47,9 +47,9 @@ intern_destroy :: proc(m: ^Intern) { delete(m.entries) } /* -Returns the interned string for the given text, is set in the map if it didnt exist yet. +Returns an interned copy of the given text, adding it to the map if not already present. -*MAY Allocate using the Intern's Allocator* +*Allocate using the Intern's Allocator (First time string is seen only)* **Inputs** - m: A pointer to the Intern struct @@ -64,17 +64,17 @@ intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Alloc #no_bounds_check return string(entry.str[:entry.len]), nil } /* -Returns the interned C-String for the given text, is set in the map if it didnt exist yet. +Returns an interned copy of the given text as a cstring, adding it to the map if not already present. -*MAY Allocate using the Intern's Allocator* +*Allocate using the Intern's Allocator (First time string is seen only)* **Inputs** - m: A pointer to the Intern struct - text: The string to be interned -NOTE: The returned C-String lives as long as the map entry lives +NOTE: The returned cstring lives as long as the map entry lives -**Returns** The interned C-String and an allocator error if any +**Returns** The interned cstring and an allocator error if any */ intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) { entry := _intern_get_entry(m, text) or_return @@ -84,7 +84,7 @@ intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runt Internal function to lookup whether the text string exists in the map, returns the entry Sets and allocates the entry if it wasn't set yet -*MAY Allocate using the Intern's Allocator* +*Allocate using the Intern's Allocator (First time string is seen only)* **Inputs** - m: A pointer to the Intern struct diff --git a/core/strings/reader.odin b/core/strings/reader.odin index 82a31c92c..fcd8ea223 100644 --- a/core/strings/reader.odin +++ b/core/strings/reader.odin @@ -5,7 +5,7 @@ import "core:unicode/utf8" /* io stream data for a string reader that can read based on bytes or runes -implements the vtable when using the io.Reader variants +implements the vtable when using the `io.Reader` variants "read" calls advance the current reading offset `i` */ Reader :: struct { @@ -26,7 +26,7 @@ reader_init :: proc(r: ^Reader, s: string) { r.prev_rune = -1 } /* -Converts a Reader into an io.Stream +Converts a Reader into an `io.Stream` **Inputs** - r: A pointer to a Reader struct @@ -39,7 +39,7 @@ reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) { return } /* -Initializes a string Reader and returns an io.Reader for the given string +Initializes a string Reader and returns an `io.Reader` for the given string **Inputs** - r: A pointer to a Reader struct @@ -53,13 +53,13 @@ to_reader :: proc(r: ^Reader, s: string) -> io.Reader { return rr } /* -Initializes a string Reader and returns an io.Reader_At for the given string +Initializes a string Reader and returns an `io.Reader_At` for the given string **Inputs** - r: A pointer to a Reader struct - s: The input string to be read -**Returns** An io.Reader_At for the given string +**Returns** An `io.Reader_At` for the given string */ to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At { reader_init(r, s) @@ -100,7 +100,7 @@ Reads len(p) bytes from the Reader's string and copies into the provided slice. **Returns** - n: The number of bytes read -- err: An io.Error if an error occurs while reading, including .EOF, otherwise nil denotes success. +- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success. */ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) { if r.i >= i64(len(r.s)) { @@ -121,7 +121,7 @@ Reads len(p) bytes from the Reader's string and copies into the provided slice, **Returns** - n: The number of bytes read -- err: An io.Error if an error occurs while reading, including .EOF, otherwise nil denotes success. +- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success. */ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) { if off < 0 { @@ -144,7 +144,7 @@ Reads and returns a single byte from the Reader's string **Returns** - The byte read from the Reader -- err: An io.Error if an error occurs while reading, including .EOF, otherwise nil denotes success. +- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success. */ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) { r.prev_rune = -1 @@ -161,7 +161,7 @@ Decrements the Reader's index (i) by 1 **Inputs** - r: A pointer to a Reader struct -**Returns** An io.Error if `r.i <= 0` (.Invalid_Unread), otherwise nil denotes success. +**Returns** An `io.Error` if `r.i <= 0` (`.Invalid_Unread`), otherwise `nil` denotes success. */ reader_unread_byte :: proc(r: ^Reader) -> io.Error { if r.i <= 0 { @@ -172,17 +172,17 @@ reader_unread_byte :: proc(r: ^Reader) -> io.Error { return nil } /* -Reads and returns a single rune and its size from the Reader's string +Reads and returns a single rune and its `size` from the Reader's string **Inputs** - r: A pointer to a Reader struct **Returns** -- ch: The rune read from the Reader +- r: The rune read from the Reader - size: The size of the rune in bytes -- err: An io.Error if an error occurs while reading +- err: An `io.Error` if an error occurs while reading */ -reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) { +reader_read_rune :: proc(r: ^Reader) -> (rn: rune, size: int, err: io.Error) { if r.i >= i64(len(r.s)) { r.prev_rune = -1 return 0, 0, .EOF @@ -192,7 +192,7 @@ reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) { r.i += 1 return rune(c), 1, nil } - ch, size = utf8.decode_rune_in_string(r.s[r.i:]) + rn, size = utf8.decode_rune_in_string(r.s[r.i:]) r.i += i64(size) return } @@ -204,7 +204,7 @@ Decrements the Reader's index (i) by the size of the last read rune WARNING: May only be used once and after a valid `read_rune` call -**Returns** An io.Error if an error occurs while unreading (.Invalid_Unread), else nil denotes success. +**Returns** An `io.Error` if an error occurs while unreading (`.Invalid_Unread`), else `nil` denotes success. */ reader_unread_rune :: proc(r: ^Reader) -> io.Error { if r.i <= 0 { @@ -223,11 +223,11 @@ Seeks the Reader's index to a new position **Inputs** - r: A pointer to a Reader struct - offset: The new offset position -- whence: The reference point for the new position (.Start, .Current, or .End) +- whence: The reference point for the new position (`.Start`, `.Current`, or `.End`) **Returns** - The absolute offset after seeking -- err: An io.Error if an error occurs while seeking (.Invalid_Whence, .Invalid_Offset) +- err: An `io.Error` if an error occurs while seeking (`.Invalid_Whence`, `.Invalid_Offset`) */ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) { r.prev_rune = -1 @@ -250,7 +250,7 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E return abs, nil } /* -Writes the remaining content of the Reader's string into the provided io.Writer +Writes the remaining content of the Reader's string into the provided `io.Writer` **Inputs** - r: A pointer to a Reader struct @@ -260,7 +260,7 @@ WARNING: Panics if writer writes more bytes than remainig length of string. **Returns** - n: The number of bytes written -- err: An io.Error if an error occurs while writing (.Short_Write) +- err: An io.Error if an error occurs while writing (`.Short_Write`) */ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { r.prev_rune = -1 @@ -281,10 +281,10 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) { return } /* -VTable containing implementations for various io.Stream methods +VTable containing implementations for various `io.Stream` methods This VTable is used by the Reader struct to provide its functionality -as an io.Stream. +as an `io.Stream`. */ @(private) _reader_vtable := io.Stream_VTable{ diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 118faadab..96acb9ba4 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -43,7 +43,7 @@ clone_safe :: proc(s: string, allocator := context.allocator, loc := #caller_loc return string(c[:len(s)]), nil } /* -Clones a string and appends a nul byte to make it a cstring +Clones a string and appends a `nul` byte to make it a cstring *Allocates Using Provided Allocator* @@ -52,7 +52,7 @@ Clones a string and appends a nul byte to make it a cstring - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: #caller_location) -**Returns** A cloned cstring with an appended nul byte +**Returns** A cloned cstring with an appended `nul` byte */ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> cstring { c := make([]byte, len(s)+1, allocator, loc) @@ -75,7 +75,7 @@ string_from_ptr :: proc(ptr: ^byte, len: int) -> string { return transmute(string)mem.Raw_String{ptr, len} } /* -Transmutes a raw pointer (nul-terminated) into a string. Non-allocating. Searches for a nul byte from 0.. string { return str[:n] } /* -Truncates a string str at the first occurrence of rune r as a slice of the original, entire string if not found +Truncates a string `str` at the first occurrence of rune `r` as a slice of the original, entire string if not found **Inputs** - str: The input string @@ -152,16 +152,16 @@ truncate_to_rune :: proc(str: string, r: rune) -> string { return str[:n] } /* -Clones a byte array s and appends a nul byte +Clones a byte array `s` and appends a `nul` byte *Allocates Using Provided Allocator* **Inputs** - s: The byte array to be cloned - allocator: (default: context.allocator) -- loc: The caller location for debugging purposes (default: #caller_location) +- loc: The caller location for debugging purposes (default: `#caller_location`) -**Returns** A cloned string from the byte array with a nul byte +**Returns** A cloned string from the byte array with a `nul` byte */ clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> string { c := make([]byte, len(s)+1, allocator, loc) @@ -170,14 +170,14 @@ clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #call return string(c[:len(s)]) } /* -Clones a cstring s as a string +Clones a cstring `s` as a string *Allocates Using Provided Allocator* **Inputs** - s: The cstring to be cloned - allocator: (default: context.allocator) -- loc: The caller location for debugging purposes (default: #caller_location) +- loc: The caller location for debugging purposes (default: `#caller_location`) **Returns** A cloned string from the cstring */ @@ -185,7 +185,7 @@ clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #c return clone(string(s), allocator, loc) } /* -Clones a string from a byte pointer ptr and a byte length len +Clones a string from a byte pointer `ptr` and a byte length `len` *Allocates Using Provided Allocator* @@ -193,7 +193,7 @@ Clones a string from a byte pointer ptr and a byte length len - ptr: A pointer to the start of the byte sequence - len: The length of the byte sequence - allocator: (default: context.allocator) -- loc: The caller location for debugging purposes (default: #caller_location) +- loc: The caller location for debugging purposes (default: `#caller_location`) NOTE: Same as `string_from_ptr`, but perform an additional `clone` operation @@ -203,7 +203,7 @@ clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc s := string_from_ptr(ptr, len) return clone(s, allocator, loc) } -// Overloaded procedure to clone from a string, []byte, cstring or a ^byte + length +// Overloaded procedure to clone from a string, `[]byte`, `cstring` or a `^byte` + length clone_from :: proc{ clone, clone_from_bytes, @@ -211,7 +211,7 @@ clone_from :: proc{ clone_from_ptr, } /* -Clones a string from a nul-terminated cstring ptr and a byte length len +Clones a string from a nul-terminated cstring `ptr` and a byte length `len` *Allocates Using Provided Allocator* @@ -219,7 +219,7 @@ Clones a string from a nul-terminated cstring ptr and a byte length len - ptr: A pointer to the start of the nul-terminated cstring - len: The byte length of the cstring - allocator: (default: context.allocator) -- loc: The caller location for debugging purposes (default: #caller_location) +- loc: The caller location for debugging purposes (default: `#caller_location`) NOTE: Truncates at the first nul byte encountered or the byte length. @@ -232,25 +232,25 @@ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context. } /* Compares two strings, returning a value representing which one comes first lexicographically. --1 for lhs; 1 for rhs, or 0 if they are equal. +-1 for `lhs`; 1 for `rhs`, or 0 if they are equal. **Inputs** - lhs: First string for comparison - rhs: Second string for comparison -**Returns** -1 if lhs comes first, 1 if rhs comes first, or 0 if they are equal +**Returns** -1 if `lhs` comes first, 1 if `rhs` comes first, or 0 if they are equal */ compare :: proc(lhs, rhs: string) -> int { return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs) } /* -Returns the byte offset of the rune r in the string s, -1 when not found +Returns the byte offset of the rune `r` in the string `s`, -1 when not found **Inputs** - s: The input string - r: The rune to search for -**Returns** The byte offset of the rune r in the string s, or -1 if not found +**Returns** The byte offset of the rune `r` in the string `s`, or -1 if not found */ contains_rune :: proc(s: string, r: rune) -> int { for c, offset in s { @@ -261,7 +261,7 @@ contains_rune :: proc(s: string, r: rune) -> int { return -1 } /* -Returns true when the string substr is contained inside the string s +Returns true when the string `substr` is contained inside the string `s` **Inputs** - s: The input string @@ -296,13 +296,13 @@ Output: true false -**Returns** true if substr is contained inside the string s, false otherwise +**Returns** `true` if `substr` is contained inside the string `s`, `false` otherwise */ contains :: proc(s, substr: string) -> bool { return index(s, substr) >= 0 } /* -Returns true when the string s contains any of the characters inside the string chars +Returns `true` when the string `s` contains any of the characters inside the string `chars` **Inputs** - s: The input string @@ -327,13 +327,13 @@ Output: true false -**Returns** true if the string s contains any of the characters in chars, false otherwise +**Returns** `true` if the string `s` contains any of the characters in `chars`, `false` otherwise */ contains_any :: proc(s, chars: string) -> bool { return index_any(s, chars) >= 0 } /* -Returns the UTF-8 rune count of the string s +Returns the UTF-8 rune count of the string `s` **Inputs** - s: The input string @@ -353,13 +353,13 @@ Output: 4 5 -**Returns** The UTF-8 rune count of the string s +**Returns** The UTF-8 rune count of the string `s` */ rune_count :: proc(s: string) -> int { return utf8.rune_count_in_string(s) } /* -Returns whether the strings u and v are the same alpha characters, ignoring different casings +Returns whether the strings `u` and `v` are the same alpha characters, ignoring different casings Works with UTF-8 string content **Inputs** @@ -385,7 +385,7 @@ Output: true false -**Returns** True if the strings u and v are the same alpha characters (ignoring case), false +**Returns** `true` if the strings `u` and `v` are the same alpha characters (ignoring case) */ equal_fold :: proc(u, v: string) -> bool { s, t := u, v @@ -430,7 +430,7 @@ equal_fold :: proc(u, v: string) -> bool { return s == t } /* -Returns the prefix length common between strings a and b +Returns the prefix length common between strings `a` and `b` **Inputs** - a: The first input string @@ -455,7 +455,7 @@ Output: 2 0 -**Returns** The prefix length common between strings a and b +**Returns** The prefix length common between strings `a` and `b` */ prefix_length :: proc(a, b: string) -> (n: int) { _len := min(len(a), len(b)) @@ -481,10 +481,10 @@ prefix_length :: proc(a, b: string) -> (n: int) { return } /* -Determines if a string s starts with a given prefix +Determines if a string `s` starts with a given `prefix` **Inputs** -- s: The string to check for the prefix +- s: The string to check for the `prefix` - prefix: The prefix to look for Example: @@ -506,13 +506,13 @@ Output: true false -**Returns** true if the string s starts with the prefix, otherwise false +**Returns** `true` if the string `s` starts with the `prefix`, otherwise `false` */ has_prefix :: proc(s, prefix: string) -> bool { return len(s) >= len(prefix) && s[0:len(prefix)] == prefix } /* -Determines if a string s ends with a given suffix +Determines if a string `s` ends with a given `suffix` Example: @@ -532,16 +532,16 @@ Output: true **Inputs** -- s: The string to check for the suffix +- s: The string to check for the `suffix` - suffix: The suffix to look for -**Returns** true if the string s ends with the suffix, otherwise false +**Returns** `true` if the string `s` ends with the `suffix`, otherwise `false` */ has_suffix :: proc(s, suffix: string) -> bool { return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix } /* -Joins a slice of strings a with a sep string +Joins a slice of strings `a` with a `sep` string *Allocates Using Provided Allocator* @@ -568,7 +568,7 @@ Output: - sep: The separator string - allocator: (default is context.allocator) -**Returns** A combined string from the slice of strings a separated with the sep string +**Returns** A combined string from the slice of strings `a` separated with the `sep` string */ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string { if len(a) == 0 { @@ -589,7 +589,7 @@ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string return string(b) } /* -Joins a slice of strings a with a sep string, returns an error on allocation failure +Joins a slice of strings `a` with a `sep` string, returns an error on allocation failure *Allocates Using Provided Allocator* @@ -599,8 +599,8 @@ Joins a slice of strings a with a sep string, returns an error on allocation fai - allocator: (default is context.allocator) **Returns** -- str: A combined string from the slice of strings a separated with the sep string -- err: An error if allocation failed, otherwise nil +- str: A combined string from the slice of strings `a` separated with the `sep` string +- err: An error if allocation failed, otherwise `nil` */ join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> (str: string, err: mem.Allocator_Error) { if len(a) == 0 { @@ -778,7 +778,7 @@ Splits the input string `s` into a slice of substrings separated by the specifie - s: The input string to split - sep: The separator string - sep_save: A flag determining if the separator should be saved in the resulting substrings -- n: The maximum number of substrings to return, returns nil without alloc when n=0 +- n: The maximum number of substrings to return, returns `nil` without alloc when `n=0` - allocator: An optional custom allocator (default is context.allocator) **Returns** A slice of substrings @@ -861,7 +861,7 @@ split :: proc(s, sep: string, allocator := context.allocator) -> []string { return _split(s, sep, 0, -1, allocator) } /* -Splits a string into parts based on a separator. if n < count of seperators, the remainder of the string is returned in the last entry. +Splits a string into parts based on a separator. If n < count of seperators, the remainder of the string is returned in the last entry. *Allocates Using Provided Allocator* @@ -921,7 +921,7 @@ split_after :: proc(s, sep: string, allocator := context.allocator) -> []string return _split(s, sep, len(sep), -1, allocator) } /* -Splits a string into a total of 'n' parts after the separator. +Splits a string into a total of `n` parts after the separator. *Allocates Using Provided Allocator* @@ -946,13 +946,13 @@ Output: ["aaa.", "bbb.", "ccc.ddd.eee"] -**Returns** A slice of strings with 'n' parts or fewer if there weren't +**Returns** A slice of strings with `n` parts or fewer if there weren't */ split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string { return _split(s, sep, len(sep), n, allocator) } /* -Searches for the first occurrence of 'sep' in the given string and returns the substring +Searches for the first occurrence of `sep` in the given string and returns the substring up to (but not including) the separator, as well as a boolean indicating success. *Used Internally - Private Function* @@ -1124,7 +1124,7 @@ _trim_cr :: proc(s: string) -> string { return s } /* -Splits the input string at every line break '\n'. +Splits the input string at every line break `\n`. *Allocates Using Provided Allocator* @@ -1158,7 +1158,7 @@ split_lines :: proc(s: string, allocator := context.allocator) -> []string { return lines } /* -Splits the input string at every line break '\n' for n parts. +Splits the input string at every line break `\n` for `n` parts. *Allocates Using Provided Allocator* @@ -1193,7 +1193,7 @@ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []st return lines } /* -Splits the input string at every line break '\n' leaving the '\n' in the resulting strings. +Splits the input string at every line break `\n` leaving the `\n` in the resulting strings. *Allocates Using Provided Allocator* @@ -1227,7 +1227,7 @@ split_lines_after :: proc(s: string, allocator := context.allocator) -> []string return lines } /* -Splits the input string at every line break '\n' leaving the '\n' in the resulting strings. +Splits the input string at every line break `\n` leaving the `\n` in the resulting strings. Only runs for n parts. *Allocates Using Provided Allocator* @@ -1263,7 +1263,7 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) - return lines } /* -Splits the input string at every line break '\n'. +Splits the input string at every line break `\n`. Returns the current split string every iteration until the string is consumed. **Inputs** @@ -1293,7 +1293,7 @@ split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) { return _trim_cr(line), true } /* -Splits the input string at every line break '\n'. +Splits the input string at every line break `\n`. Returns the current split string with line breaks included every iteration until the string is consumed. **Inputs** @@ -1327,7 +1327,7 @@ split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) { return _trim_cr(line), true } /* -Returns the byte offset of the first byte c in the string s it finds, -1 when not found. +Returns the byte offset of the first byte `c` in the string s it finds, -1 when not found. NOTE: Can't find UTF-8 based runes. **Inputs** @@ -1353,7 +1353,7 @@ Output: -1 -1 -**Returns** The byte offset of the first occurrence of c in s, or -1 if not found. +**Returns** The byte offset of the first occurrence of `c` in `s`, or -1 if not found. */ index_byte :: proc(s: string, c: byte) -> int { for i := 0; i < len(s); i += 1 { @@ -1652,7 +1652,7 @@ index_any :: proc(s, chars: string) -> int { return -1 } /* -Finds the last occurrence of any character in 'chars' within 's'. Iterates in reverse. +Finds the last occurrence of any character in `chars` within `s`. Iterates in reverse. **Inputs** - s: The string to search in @@ -1730,7 +1730,7 @@ last_index_any :: proc(s, chars: string) -> int { return -1 } /* -Finds the first occurrence of any substring in 'substrs' within 's' +Finds the first occurrence of any substring in `substrs` within `s` **Inputs** - s: The string to search in @@ -1768,7 +1768,7 @@ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) { return } /* -Counts the number of non-overlapping occurrences of 'substr' in 's' +Counts the number of non-overlapping occurrences of `substr` in `s` **Inputs** - s: The string to search in @@ -1795,7 +1795,7 @@ Output: 1 0 -**Returns** The number of occurrences of 'substr' in 's', returns the rune_count + 1 of the string `s` on empty `substr` +**Returns** The number of occurrences of `substr` in `s`, returns the rune_count + 1 of the string `s` on empty `substr` */ count :: proc(s, substr: string) -> int { if len(substr) == 0 { // special case @@ -1832,13 +1832,13 @@ count :: proc(s, substr: string) -> int { return n } /* -Repeats the string 's' 'count' times, concatenating the result +Repeats the string `s` `count` times, concatenating the result *Allocates Using Provided Allocator* **Inputs** - s: The string to repeat -- count: The number of times to repeat 's' +- count: The number of times to repeat `s` - allocator: (default is context.allocator) WARNING: Panics if count < 0 @@ -1874,14 +1874,14 @@ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string return string(b) } /* -Replaces all occurrences of 'old' in 's' with 'new' +Replaces all occurrences of `old` in `s` with `new` *Allocates Using Provided Allocator* **Inputs** - s: The string to modify - old: The substring to replace -- new: The substring to replace 'old' with +- new: The substring to replace `old` with - allocator: The allocator to use for the new string (default is context.allocator) Example: @@ -1915,7 +1915,7 @@ Replaces n instances of old in the string s with the new string - s: The input string - old: The substring to be replaced - new: The replacement string -- n: The number of instances to replace (if n < 0, no limit on the number of replacements) +- n: The number of instances to replace (if `n < 0`, no limit on the number of replacements) - allocator: (default: context.allocator) Example: @@ -1979,14 +1979,14 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) -> return } /* -Removes the key string n times from the s string +Removes the key string `n` times from the `s` string *Allocates Using Provided Allocator* **Inputs** - s: The input string - key: The substring to be removed -- n: The number of instances to remove (if n < 0, no limit on the number of removes) +- n: The number of instances to remove (if `n < 0`, no limit on the number of removes) - allocator: (default: context.allocator) Example: @@ -2014,7 +2014,7 @@ remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (outpu return replace(s, key, "", n, allocator) } /* -Removes all the key string instances from the s string +Removes all the `key` string instances from the `s` string *Allocates Using Provided Allocator* @@ -2045,7 +2045,7 @@ Output: remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { return remove(s, key, -1, allocator) } -// Returns true if the r rune is an ASCII space character ('\t', '\n', '\v', '\f', '\r', ' ') +// Returns true if is an ASCII space character ('\t', '\n', '\v', '\f', '\r', ' ') @(private) _ascii_space := [256]bool{'\t' = true, '\n' = true, '\v' = true, '\f' = true, '\r' = true, ' ' = true} // Returns true when the `r` rune is '\t', '\n', '\v', '\f', '\r' or ' ' @@ -2055,7 +2055,7 @@ is_ascii_space :: proc(r: rune) -> bool { } return false } -// Returns true if the r rune is any ASCII or UTF-8 based whitespace character +// Returns true if the `r` rune is any ASCII or UTF-8 based whitespace character is_space :: proc(r: rune) -> bool { if r < 0x2000 { switch r { @@ -2073,17 +2073,17 @@ is_space :: proc(r: rune) -> bool { } return false } -// Returns true if the `r` rune is a null byte (0x0) +// Returns true if the `r` rune is a null byte (`0x0`) is_null :: proc(r: rune) -> bool { return r == 0x0000 } /* -Find the index of the first rune r in string s for which procedure p returns the same as truth, or -1 if no such rune appears. +Find the index of the first rune `r` in string `s` for which procedure `p` returns the same as truth, or -1 if no such rune appears. **Inputs** - s: The input string - p: A procedure that takes a rune and returns a boolean -- truth: The boolean value to be matched (default: true) +- truth: The boolean value to be matched (default: `true`) Example: @@ -2153,7 +2153,7 @@ last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta return -1 } /* -Trims the input string s from the left until the procedure p returns false +Trims the input string `s` from the left until the procedure `p` returns false **Inputs** - s: The input string @@ -2185,12 +2185,12 @@ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string { return s[i:] } /* -Trims the input string s from the left until the procedure p with state returns false +Trims the input string `s` from the left until the procedure `p` with state returns false **Inputs** - s: The input string - p: A procedure that takes a raw pointer and a rune and returns a boolean -- state: The raw pointer to be passed to the procedure p +- state: The raw pointer to be passed to the procedure `p` **Returns** The trimmed string as a slice of the original */ @@ -2202,7 +2202,7 @@ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, stat return s[i:] } /* -Trims the input string s from the right until the procedure p returns false +Trims the input string `s` from the right until the procedure `p` returns `false` **Inputs** - s: The input string @@ -2237,12 +2237,12 @@ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string { return s[0:i] } /* -Trims the input string s from the right until the procedure p with state returns false +Trims the input string `s` from the right until the procedure `p` with state returns `false` **Inputs** - s: The input string - p: A procedure that takes a raw pointer and a rune and returns a boolean -- state: The raw pointer to be passed to the procedure p +- state: The raw pointer to be passed to the procedure `p` **Returns** The trimmed string as a slice of the original, empty when no match */ @@ -2270,7 +2270,7 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> bool { return false } /* -Trims the cutset string from the s string +Trims the cutset string from the `s` string **Inputs** - s: The input string @@ -2286,7 +2286,7 @@ trim_left :: proc(s: string, cutset: string) -> string { return trim_left_proc_with_state(s, is_in_cutset, &state) } /* -Trims the cutset string from the s string from the right +Trims the cutset string from the `s` string from the right **Inputs** - s: The input string @@ -2302,7 +2302,7 @@ trim_right :: proc(s: string, cutset: string) -> string { return trim_right_proc_with_state(s, is_in_cutset, &state) } /* -Trims the cutset string from the s string, both from left and right +Trims the cutset string from the `s` string, both from left and right **Inputs** - s: The input string @@ -2379,7 +2379,7 @@ trim_null :: proc(s: string) -> string { return trim_right_null(trim_left_null(s)) } /* -Trims a prefix string from the start of the s string and returns the trimmed string +Trims a `prefix` string from the start of the `s` string and returns the trimmed string **Inputs** - s: The input string @@ -2409,7 +2409,7 @@ trim_prefix :: proc(s, prefix: string) -> string { return s } /* -Trims a suffix string from the end of the s string and returns the trimmed string +Trims a `suffix` string from the end of the `s` string and returns the trimmed string **Inputs** - s: The input string @@ -2439,7 +2439,7 @@ trim_suffix :: proc(s, suffix: string) -> string { return s } /* -Splits the input string s by all possible substrs and returns an allocated array of strings +Splits the input string `s` by all possible `substrs` and returns an allocated array of strings *Allocates Using Provided Allocator* @@ -2508,7 +2508,7 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator return results[:] } /* -Splits the input string s by all possible substrs in an iterator fashion. The full string is returned if no match. +Splits the input string `s` by all possible `substrs` in an iterator fashion. The full string is returned if no match. **Inputs** - it: A pointer to the input string @@ -2621,7 +2621,7 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> return to_string(b) } /* -Reverses the input string s +Reverses the input string `s` *Allocates Using Provided Allocator* @@ -2730,7 +2730,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> return to_string(b) } /* -Splits the input string str by the separator sep string and returns 3 parts. The values are slices of the original string. +Splits the input string `str` by the separator `sep` string and returns 3 parts. The values are slices of the original string. **Inputs** - str: The input string @@ -2754,7 +2754,7 @@ Output: testing t hi s out testing this out -**Returns** A tuple with head (before the split), match (the separator), and tail (the end of the split) strings +**Returns** A tuple with `head` (before the split), `match` (the separator), and `tail` (the end of the split) strings */ partition :: proc(str, sep: string) -> (head, match, tail: string) { i := index(str, sep) @@ -2871,7 +2871,7 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex return to_string(b) } /* -Writes a given pad string a specified number of times to an io.Writer +Writes a given pad string a specified number of times to an `io.Writer` **Inputs** - w: The io.Writer to write the pad string to @@ -2897,7 +2897,7 @@ write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { } } /* -Splits a string into a slice of substrings at each instance of one or more consecutive white space characters, as defined by unicode.is_space +Splits a string into a slice of substrings at each instance of one or more consecutive white space characters, as defined by `unicode.is_space` *Allocates Using Provided Allocator* @@ -2956,7 +2956,7 @@ fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds return a } /* -Splits a string into a slice of substrings at each run of unicode code points `r` satisfying the predicate f(r) +Splits a string into a slice of substrings at each run of unicode code points `r` satisfying the predicate `f(r)` *Allocates Using Provided Allocator* @@ -2965,7 +2965,7 @@ Splits a string into a slice of substrings at each run of unicode code points `r - f: A predicate function to determine the split points - allocator: (default is context.allocator) -NOTE: fields_proc makes no guarantee about the order in which it calls f(ch), it assumes that `f` always returns the same value for a given ch +NOTE: fields_proc makes no guarantee about the order in which it calls `f(r)`, it assumes that `f` always returns the same value for a given `r` **Returns** 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 */ @@ -2996,7 +2996,7 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc return substrings[:] } /* -Retrieves the first non-space substring from a mutable string reference and advances the reference. s is advanced from any space after the substring, or be an empty string if the substring was the remaining characters +Retrieves the first non-space substring from a mutable string reference and advances the reference. `s` is advanced from any space after the substring, or be an empty string if the substring was the remaining characters **Inputs** - s: A mutable string reference to be iterated @@ -3038,7 +3038,7 @@ Computes the Levenshtein edit distance between two strings *Allocates Using Provided Allocator (deletion occurs internal to proc)* -NOTE: Does not perform internal allocation if Length of String b in Runes is Smaller Than 64 +NOTE: Does not perform internal allocation if length of string `b`, in runes, is smaller than 64 **Inputs** - a, b: The two strings to compare From 194fa7cd983596e788997948271b4bc836ffaaa4 Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Tue, 28 Mar 2023 11:51:39 -0700 Subject: [PATCH 7/8] rename nul to null, allocation clarifications --- core/os/os2/path_linux.odin | 2 +- core/os/os_linux.odin | 2 +- core/strings/reader.odin | 6 +-- core/strings/strings.odin | 85 +++++++++++++++++++------------------ 4 files changed, 49 insertions(+), 46 deletions(-) diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index 60161f496..e4c92a871 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -211,7 +211,7 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) { #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf))) if res >= 0 { - return strings.string_from_nul_terminated_ptr(&buf[0], len(buf)), nil + return strings.string_from_zero_terminated_ptr(&buf[0], len(buf)), nil } if res != -ERANGE { return "", _get_platform_error(res) diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index d001d5ec7..0e2af6609 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -913,7 +913,7 @@ get_current_directory :: proc() -> string { #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf))) if res >= 0 { - return strings.string_from_nul_terminated_ptr(&buf[0], len(buf)) + return strings.string_from_zero_terminated_ptr(&buf[0], len(buf)) } if _get_errno(res) != ERANGE { delete(buf) diff --git a/core/strings/reader.odin b/core/strings/reader.odin index fcd8ea223..3e543cb9d 100644 --- a/core/strings/reader.odin +++ b/core/strings/reader.odin @@ -178,11 +178,11 @@ Reads and returns a single rune and its `size` from the Reader's string - r: A pointer to a Reader struct **Returns** -- r: The rune read from the Reader +- rr: The rune read from the Reader - size: The size of the rune in bytes - err: An `io.Error` if an error occurs while reading */ -reader_read_rune :: proc(r: ^Reader) -> (rn: rune, size: int, err: io.Error) { +reader_read_rune :: proc(r: ^Reader) -> (rr: rune, size: int, err: io.Error) { if r.i >= i64(len(r.s)) { r.prev_rune = -1 return 0, 0, .EOF @@ -192,7 +192,7 @@ reader_read_rune :: proc(r: ^Reader) -> (rn: rune, size: int, err: io.Error) { r.i += 1 return rune(c), 1, nil } - rn, size = utf8.decode_rune_in_string(r.s[r.i:]) + rr, size = utf8.decode_rune_in_string(r.s[r.i:]) r.i += i64(size) return } diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 96acb9ba4..b9cecae9d 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -43,7 +43,7 @@ clone_safe :: proc(s: string, allocator := context.allocator, loc := #caller_loc return string(c[:len(s)]), nil } /* -Clones a string and appends a `nul` byte to make it a cstring +Clones a string and appends a null-byte to make it a cstring *Allocates Using Provided Allocator* @@ -52,7 +52,7 @@ Clones a string and appends a `nul` byte to make it a cstring - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: #caller_location) -**Returns** A cloned cstring with an appended `nul` byte +**Returns** A cloned cstring with an appended null-byte */ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> cstring { c := make([]byte, len(s)+1, allocator, loc) @@ -75,18 +75,18 @@ string_from_ptr :: proc(ptr: ^byte, len: int) -> string { return transmute(string)mem.Raw_String{ptr, len} } /* -Transmutes a raw pointer (nul-terminated) into a string. Non-allocating. Searches for a nul byte from `0.. string { +string_from_zero_terminated_ptr :: proc(ptr: ^byte, len: int) -> string { s := transmute(string)mem.Raw_String{ptr, len} s = truncate_to_byte(s, 0) return s @@ -109,7 +109,7 @@ Converts a string `str` to a cstring **Inputs** - str: The input string -WARNING: This is unsafe because the original string may not contain a `nul` byte. +WARNING: This is unsafe because the original string may not contain a null-byte. **Returns** The converted cstring */ @@ -152,7 +152,7 @@ truncate_to_rune :: proc(str: string, r: rune) -> string { return str[:n] } /* -Clones a byte array `s` and appends a `nul` byte +Clones a byte array `s` and appends a null-byte *Allocates Using Provided Allocator* @@ -161,7 +161,7 @@ Clones a byte array `s` and appends a `nul` byte - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: `#caller_location`) -**Returns** A cloned string from the byte array with a `nul` byte +**Returns** A cloned string from the byte array with a null-byte */ clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> string { c := make([]byte, len(s)+1, allocator, loc) @@ -211,19 +211,19 @@ clone_from :: proc{ clone_from_ptr, } /* -Clones a string from a nul-terminated cstring `ptr` and a byte length `len` +Clones a string from a null-terminated cstring `ptr` and a byte length `len` *Allocates Using Provided Allocator* **Inputs** -- ptr: A pointer to the start of the nul-terminated cstring +- ptr: A pointer to the start of the null-terminated cstring - len: The byte length of the cstring - allocator: (default: context.allocator) - loc: The caller location for debugging purposes (default: `#caller_location`) -NOTE: Truncates at the first nul byte encountered or the byte length. +NOTE: Truncates at the first null-byte encountered or the byte length. -**Returns** A cloned string from the nul-terminated cstring and byte length +**Returns** A cloned string from the null-terminated cstring and byte length */ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.allocator, loc := #caller_location) -> string { s := string_from_ptr((^u8)(ptr), len) @@ -268,18 +268,6 @@ Returns true when the string `substr` is contained inside the string `s` - substr: The substring to search for Example: -Example: -```odin - strings.contains("testing", "test") // -> true - strings.contains("testing", "ing") // -> true - strings.contains("testing", "text") // -> false -``` - Example: -```odin - strings.contains("testing", "test") // -> true - strings.contains("testing", "ing") // -> true - strings.contains("testing", "text") // -> false -``` import "core:fmt" import "core:strings" @@ -627,7 +615,7 @@ Returns a combined string from the slice of strings `a` without a separator **Inputs** - a: A slice of strings to concatenate -- allocator: An optional custom allocator (default is context.allocator) +- allocator: (default is context.allocator) Example: @@ -668,7 +656,7 @@ Returns a combined string from the slice of strings `a` without a separator, or **Inputs** - a: A slice of strings to concatenate -- allocator: An optional custom allocator (default is context.allocator) +- allocator: (default is context.allocator) **Returns** The concatenated string, and an error if allocation fails */ @@ -697,7 +685,7 @@ Returns a substring of the input string `s` with the specified rune offset and l - s: The input string to cut - rune_offset: The starting rune index (default is 0). In runes, not bytes. - rune_length: The number of runes to include in the substring (default is 0, which returns the remainder of the string). In runes, not bytes. -- allocator: An optional custom allocator (default is context.allocator) +- allocator: (default is context.allocator) Example: @@ -774,12 +762,16 @@ Splits the input string `s` into a slice of substrings separated by the specifie *Allocates Using Provided Allocator* +*Used Internally - Private Function* + **Inputs** - s: The input string to split - sep: The separator string - sep_save: A flag determining if the separator should be saved in the resulting substrings - n: The maximum number of substrings to return, returns `nil` without alloc when `n=0` -- allocator: An optional custom allocator (default is context.allocator) +- allocator: (default is context.allocator) + +NOTE: Allocation occurs for the array, the splits are all views of the original string. **Returns** A slice of substrings */ @@ -855,6 +847,8 @@ Output: ["aaa", "bbb", "ccc", "ddd", "eee"] +NOTE: Allocation occurs for the array, the splits are all views of the original string. + **Returns** A slice of strings, each representing a part of the split string. */ split :: proc(s, sep: string, allocator := context.allocator) -> []string { @@ -885,6 +879,8 @@ Output: ["aaa", "bbb", "ccc.ddd.eee"] +NOTE: Allocation occurs for the array, the splits are all views of the original string. + **Returns** A slice of strings, each representing a part of the split string. */ split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string { @@ -898,7 +894,7 @@ Splits a string into parts after the separator, retaining it in the substrings. **Inputs** - s: The string to split. - sep: The separator string used to split the input string. -- allocator: (Optional) The allocator used for allocation (default is context.allocator). +- allocator: (default is context.allocator). Example: @@ -915,6 +911,8 @@ Output: ["aaa.", "bbb.", "ccc.", "ddd.", "eee"] +NOTE: Allocation occurs for the array, the splits are all views of the original string. + **Returns** A slice of strings, each representing a part of the split string after the separator. */ split_after :: proc(s, sep: string, allocator := context.allocator) -> []string { @@ -946,6 +944,8 @@ Output: ["aaa.", "bbb.", "ccc.ddd.eee"] +NOTE: Allocation occurs for the array, the splits are all views of the original string. + **Returns** A slice of strings with `n` parts or fewer if there weren't */ split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string { @@ -962,8 +962,6 @@ up to (but not including) the separator, as well as a boolean indicating success - sep: The separator string to search for. - sep_save: Number of characters from the separator to include in the result. -NOTE: Destructively consumes the string - **Returns** A tuple containing the resulting substring and a boolean indicating success. */ @private @@ -995,7 +993,6 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, } /* Splits the input string by the byte separator in an iterator fashion. -Destructively consumes the original string until the end. **Inputs** - s: Pointer to the input string, which is modified during the search. @@ -1111,7 +1108,7 @@ Trims the carriage return character from the end of the input string. **Inputs** - s: The input string to trim. -**Returns** The trimmed string as a slice. +**Returns** The trimmed string as a slice of the original. */ @(private) _trim_cr :: proc(s: string) -> string { @@ -1147,7 +1144,7 @@ Output: ["a", "b", "c", "d", "e"] -**Returns** An allocated slice of strings split by line breaks. +**Returns** A slice (allocated) of the split string (slices into original string) */ split_lines :: proc(s: string, allocator := context.allocator) -> []string { sep :: "\n" @@ -1182,7 +1179,9 @@ Output: ["a", "b", "c\nd\ne"] -**Returns** An allocated array of strings split by line breaks. +NOTE: Allocation occurs for the array, the splits are all views of the original string. + +**Returns** A slice (allocated) of the split string (slices into original string) */ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []string { sep :: "\n" @@ -1216,7 +1215,9 @@ Output: ["a\n", "b\n", "c\n", "d\n", "e"] -**Returns** An allocated slice of strings split by line breaks with line breaks included. +NOTE: Allocation occurs for the array, the splits are all views of the original string. + +**Returns** A slice (allocated) of the split string (slices into original string), with `\n` included. */ split_lines_after :: proc(s: string, allocator := context.allocator) -> []string { sep :: "\n" @@ -1252,7 +1253,9 @@ Output: ["a\n", "b\n", "c\nd\ne"] -**Returns** An allocated slice of strings split by line breaks with line breaks included. +NOTE: Allocation occurs for the array, the splits are all views of the original string. + +**Returns** A slice (allocated) of the split string (slices into original string), with `\n` included. */ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -> []string { sep :: "\n" @@ -2073,7 +2076,7 @@ is_space :: proc(r: rune) -> bool { } return false } -// Returns true if the `r` rune is a null byte (`0x0`) +// Returns true if the `r` rune is a null-byte (`0x0`) is_null :: proc(r: rune) -> bool { return r == 0x0000 } @@ -2448,7 +2451,7 @@ Splits the input string `s` by all possible `substrs` and returns an allocated a - substrs: An array of substrings used for splitting - allocator: (default is context.allocator) -NOTE: Allocation occurs for the array, the splits are all slices of the original string. +NOTE: Allocation occurs for the array, the splits are all views of the original string. Example: From bbafc3fbd62124b4d6602424025f2226a99f408b Mon Sep 17 00:00:00 2001 From: Jon Lipstate Date: Tue, 28 Mar 2023 11:57:12 -0700 Subject: [PATCH 8/8] harmonize to use null for c-string endings --- core/os/os2/path_linux.odin | 2 +- core/os/os_linux.odin | 2 +- core/strings/strings.odin | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin index e4c92a871..3120fcda3 100644 --- a/core/os/os2/path_linux.odin +++ b/core/os/os2/path_linux.odin @@ -211,7 +211,7 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) { #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf))) if res >= 0 { - return strings.string_from_zero_terminated_ptr(&buf[0], len(buf)), nil + return strings.string_from_null_terminated_ptr(&buf[0], len(buf)), nil } if res != -ERANGE { return "", _get_platform_error(res) diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 0e2af6609..3dc48087a 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -913,7 +913,7 @@ get_current_directory :: proc() -> string { #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf))) if res >= 0 { - return strings.string_from_zero_terminated_ptr(&buf[0], len(buf)) + return strings.string_from_null_terminated_ptr(&buf[0], len(buf)) } if _get_errno(res) != ERANGE { delete(buf) diff --git a/core/strings/strings.odin b/core/strings/strings.odin index b9cecae9d..8193f4de1 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -86,7 +86,7 @@ NOTE: The created string is only valid as long as the pointer and length are val **Returns** A string created from the null-terminated byte pointer and length */ -string_from_zero_terminated_ptr :: proc(ptr: ^byte, len: int) -> string { +string_from_null_terminated_ptr :: proc(ptr: ^byte, len: int) -> string { s := transmute(string)mem.Raw_String{ptr, len} s = truncate_to_byte(s, 0) return s