String

#import "std/string"

Builtin UTF-8 encoded automatically growing string container. Essentially wrapper over dynamic array with some additional functionality and semantics.

Memory Layout

string :: struct {
    // Count of characters in the string.
    len:             s64;
    // Pointer to the first string character.
    ptr:            *u8;
    // Number of bytes allocated to hold string data (grows automatically).
    allocated_elems: usize;
    // Memory allocator being used to allocate string storage.
    allocator:      *Allocator;
}

String vs String View

As you may notice, there is string and string_view type in the language. Where string represents dynamically allocated sequence of characters, and string_view is lightweight "view" into the string. While string can be appended, shrinked and modified in general, the string_view is supposed to provide only read/write access to memory managed by dynamic strings.

string_view :: []u8;

As you can see the string_view is just a slice of u8 characters, thus we have only pointer to the string data and length of the string (Read more about slice).

You might ask why we need string views:

One important thing about string views is that they are in fact just pointers with length, thus there is no explicit ownership of "viewed" data. It's not a good idea to store them, unless you're sure that the "view" lifetime does not exceeds lifetime of the "viewed" string.

String and String View Conversions

Strings can be implicitly converted to string views, but not other way around. This makes the string API more flexible, any function taking string_view can directly operate on strings too.

Zero Termination

BL strings are NOT zero terminated. Key point behind this decision is possible inconsistency where string_view naturally might or might not be zero terminated. Another issues comes from the rules of initialization, where zero initialized structures should be valid by default; so in case of strings (allocated on stack and initialized to 0) we cannot guarantee the string buffer to be zero-terminated since such a string does not hold any allocation.

In case of interaction with C API or any other API requiring zero-terminated strings, use strtoc function to do the conversion.

str_init

str_init :: fn { 
    fn (str: *string, allocator: *Allocator) ; 
    fn (str: *string, capacity: s64, allocator : *Allocator: null) ; 
    fn (str: *string, text: string_view, allocator : *Allocator: null) ; 
    fn (str: *string, cstr: *C.char, allocator : *Allocator: null) ; 
} #inline

Set of functions for string initialization.

Note that no memory is preallocated unless required capacity is greater than zero or string is initialized with some data.

By default, zero-initialized string is valid.

In case no allocator is specified, string will use current application_context.allocator by default.

File: string.bl

str_make

str_make :: fn { 
    fn (allocator: *Allocator) string; 
    fn (size: s64, allocator : *Allocator: null) string; 
    fn (v: string_view, allocator : *Allocator: null) string; 
    fn (cstr: *C.char, allocator : *Allocator: null) string; 
} #inline

Just wrapper around str_init.

File: string.bl

str_terminate

str_terminate :: fn (str: *string)  #inline

Release all memory allocated by string using internally stored allocator if any, and resets the string to default state.

File: string.bl

str_append

str_append :: fn { 
    _str_append_str; 
    _str_append_any; 
}

Append string with a new data. Underlying preallocated memory block might grow in case there is not enough space (See Preallocation Rules).

It's possible to append values convertible to Any type:

str: string;

str_append(&str, "Hello");
str_append(&str, 10);
str_append(&str, s32);

Does nothing in case the input string is empty.

Returns number of bytes appended to the string.

File: string.bl

str_reserve

str_reserve :: fn (str: *string, capacity: s64)  #inline

Reserve at least required capacity bytes in the string, new allocation is done in case te underlying buffer is not large enough.

Function does nothing in case required capacity is zero, and issues panic in case required capacity is negative.

The capacity specifies total size in bytes required to be available for later use.

Already existing string content is preserved.

String length is not changed.

File: string.bl

str_resize

str_resize :: fn (str: *string, len: s64) s64 #inline

Resize string to required len. Behaviour of this function is similar to str_reserve except the string length is changed.

Buffer reallocation occours in case the current allocated buffer it not capable to handle requested length.

Setting the length to smaller values then length of current string content will shrink the string content, minimal possible length value is zero; setting negative value will cause panic. On the other hand, when string content is smaller then required length, the string is extended, but newly occupied buffer range remains uninitialized.

Returns original length.

File: string.bl

str_concat

str_concat :: fn (str: *string, args: ...) string_view

Append string with multiple values passed as args and returns str. New allocation is done in case there is not enough space left in currently allocated string buffer to hold append args.

File: string.bl

str_clear_concat

str_clear_concat :: fn (str: *string, args: ...) string_view

Clears and append string with multiple values passed as args and returns str. New allocation is done in case there is not enough space left in currently allocated string buffer to hold append args.

File: string.bl

str_insert

str_insert :: fn { 
    _string_insert; 
    _char_insert; 
}

Inserts single character or whole string at desired position. The position is defined as before_index pointing to the character in the input string before which the new content will be inserted, or simply defining position of the first character of the inserted string in the original string after the modification is done.

Valid range for the before_index is <0, str.len>, values out of the range cause panic.

#import "std/print"
#import "std/string"

main :: fn () s32 {
	s: string;
	// Initialize string with "Hello".
	str_init(&s, "name");

	// Insert at the end of the string.
	str_insert(&s, s.len, " is Alois");

	// Insert at the beginning of the string.
	str_insert(&s, 0, "My ");
	print("%\n", s);

	return 0;
}

Function does nothing in case empty string is inserted.

Returns index of the first character of inserted string.

File: string.bl

str_erase

str_erase :: fn (str: *string, index: s32) 

Erase character at index position in the string. Provided index must be in range <0, str.len) otherwise panic is issued.

File: string.bl

str_replace_all

str_replace_all :: fn (str: *string, c: u8, with :: ) s32

Replace all found occurrences of character c in the input string with with character and return count of replacements made. This function cannot be used with constant string literals as input.

If with replacement is 0 character, all c occurrences will be erased from the string.

Function return count of replaced characters or zero.

File: string.bl

str_lower

str_lower :: fn (str: *string) s32 #inline

Converts ascii input string to lower case and returns count of changed characters.

File: string.bl

str_lower_single_character

str_lower_single_character :: fn (str: *string, index: s64)  #inline

Converts single character from the str at index index to lower case. String str must be valid string pointer and index must be in range <0, str.len).

File: string.bl

str_upper

str_upper :: fn (str: *string) s32 #inline

Converts ascii input string to upper case and returns count of changed characters.

File: string.bl

str_upper_single_character

str_upper_single_character :: fn (str: *string, index: s64)  #inline

Converts single character from the str at index index to upper case. String str must be valid string pointer and index must be in range <0, str.len).

File: string.bl

str_new

str_new :: fn { 
    new_empty; 
    new_allocator; 
    new_size; 
    new_dup; 
    new_dup_c_str; 
}

Overloaded function creating new dynamic string instance.

Overloads:

fn () string;
fn (allocator: *Allocator) string
fn (size: s64, allocator: *Allocator = null) string
fn (v: string_view, allocator: *Allocator = null) string
fn (cstr: *u8, allocator: *Allocator = null) string

File: string.bl

str_delete

Warning: Function is marked as obsolete. Since 0.13.0; Use 'str_terminate' instead'.

str_delete :: fn (v: *string) 

Delete dynamic string.

File: string.bl

str_clear

Warning: Function is marked as obsolete. Since 0.13.0; Use 'str.len = 0' instead.

str_clear :: fn (str: *string)  #inline

Clear dynamic string but keep allocated storage.

File: string.bl

str_match

str_match :: fn (first: string_view, second: string_view, n :: -1) bool

Compare first and second strings in specified range n and return true if they are the same otherwise return false.

Range value n is optional and ignored when it's less than 0.

File: string.bl

str_compare

str_compare :: fn (first: string_view, second: string_view) s32

File: string.bl

str_match_one_of

str_match_one_of :: fn (str: string_view, list: []string_view) bool

Returns true in case the input string str is matching exactly one of strings in the list.

File: string.bl

str_first_match

str_first_match :: fn (str: string_view, list: []string_view) s32

Returns index of the first matching string in the input list or -1 if there is no match.

File: string.bl

strtos64

strtos64 :: fn (str: string_view, base :: FmtIntBase.DEC, count :: -1) (_0: s64, _1: Error)

Converts the first count of characters from str to s64 number and return OK on success. Whole string will be used in case the count is less than zero. The base of expected number can be specified as base argument. Note that in case of binary, octal and hex encoding, we do not expect any prefixes as 0b, 0 and 0x.

Negative values can be converted too.

Returns error when:

- The input string is empty and `count` greater than zero.
- Converted number cause overflow of s64.
- The input string contains invalid characters.

File: string.bl

strtof64

strtof64 :: fn (str: string_view, count :: -1) (_0: f64, _1: Error)

Converts the first count of characters from str to f64 and return OK on success. Use the whole string in case the count is not specified.

Returns error when:

- The input string is empty and `count` greater than zero.
- The input string contains invalid characters.

File: string.bl

str_split_by_last

str_split_by_last :: fn (str: string_view, delimiter: u8, lhs: *string_view, rhs : *string_view: null, di : *s32: null) bool

Split input string str into two tokens based on the last occurrence of delimiter. Delimiter is not included in resulting tokens. Result tokens only points into original memory of the str, they are not supposed to be freed.

When delimiter is not present in the input string function return false, lhs is set to the original str string, rhs value is unchanged.

Token destination pointers lhs and rhs are optional. The di output variable is set to index of the split position when it's not null.

Example

#import "std/string"
#import "std/print"

main :: fn () s32 {
    lhs: string_view;
    rhs: string_view;
    if str_split_by_last("this/is/my/epic/path", '/', &lhs, &rhs) {
        print("lhs = %\n", lhs);
        print("rhs = %\n", rhs);
    }

    return 0;
}

File: string.bl

str_split_at_index

str_split_at_index :: fn (str: string_view, index: s32, lhs : *string_view: null, rhs : *string_view: null) bool

Split input string str at index position and return true when split was done. Result tokens only points into original memory of the str, they are not supposed to be freed. When index is out of str range function return false, lhs and rhs buffers are not modified.

Token destination pointers lhs and rhs are optional.

Example

#import "std/string"
#import "std/print"

main :: fn () s32 {
    lhs: string_view;
    rhs: string_view;
    if str_split_at_index("foobar", 3, &lhs, &rhs) {
        print("lhs = %\n", lhs);
        print("rhs = %\n", rhs);
    }

    return 0;
}

File: string.bl

str_split_by_first

str_split_by_first :: fn (str: string_view, delimiter: u8, lhs: *string_view, rhs : *string_view: null, di : *s32: null) bool

Split input string str into two tokens based on the first occurrence of delimiter. Delimiter is not included in resulting tokens. Result tokens only points into original memory of the str, they are not supposed to be freed.

When delimiter is not present in the input string function return false, lhs is set to the original str string, rhs value is unchanged.

Token destination pointers lhs and rhs are optional.

Example

#import "std/string"
#import "std/print"

main :: fn () s32 {
    lhs: string_view;
    rhs: string_view;
    if str_split_by_first("this/is/my/epic/path", '/', &lhs, &rhs) {
        print("lhs = %\n", lhs);
        print("rhs = %\n", rhs);
    }

    return 0;
}

File: string.bl

str_split_by

str_split_by :: fn (str: string_view, delimiter: u8, allocator : *Allocator: null) [..]string_view

Split the str input string by delimiter and return new array allocated using allocator containing all found sub-strings. In case input string is empty, returs empty slice.

Warning: String array should be terminated by array_terminate call.

File: string.bl

str_tokenize

str_tokenize :: fn { 
    fn (str: string_view, delimiter: u8, ctx: *?T, visitor: *fn (token: string_view, ctx: *T) bool) ; 
    fn (str: string_view, delimiter: u8, visitor: *fn (token: string_view) bool) ; 
}

Call the visitor callback for each token in the str input split by delimiter. When the visitor callback returns false parsing is stopped.

Overloads:

fn (str: string_view, delimiter: u8, ctx: *?T, visitor: *fn(token: string_view, ctx: *T) bool)
fn (str: string_view, delimiter: u8, visitor: *fn(token: string_view) bool)

Example

#import "std/string"
#import "std/print"

main :: fn () s32 {
    path := "/home/travis/develop/foo";
    str_tokenize(path, '/', &fn (token: string_view) bool {
        print("TOKEN: %\n", token);
        return true;
    });

    return 0;
}

Note: Callback is not called for empty tokens.

File: string.bl

str_count_of

str_count_of :: fn (str: string_view, c: u8) s32 #inline

Returns number of c characters in the string.

File: string.bl

str_hash

str_hash :: fn (str: string_view) u32

Calculates string u32 hash.

File: string.bl

str_is_null

Warning: Function is marked as obsolete. Since 0.13.0; Use 'str.ptr == null' instead.

str_is_null :: fn (str: string_view) bool #inline

Helper inline function returning true when string is null. In such case string len could be any value.

File: string.bl

str_is_empty

Warning: Function is marked as obsolete. Since 0.13.0; Use 'str.len < 1' instead.

str_is_empty :: fn (str: string_view) bool #inline

Helper inline function returning true when string is empty. In such case string ptr could be any pointer.

File: string.bl

str_empty

str_empty :: 

File: string.bl

str_view_empty

str_view_empty :: 

File: string.bl

str_is_null_or_empty

Warning: Function is marked as obsolete. Since 0.13.0; Use 'str.len < 1 || str.ptr' instead.

str_is_null_or_empty :: fn (s: string_view) bool #inline

Helper inline function returning true when string is empty and null.

File: string.bl

str_sub

str_sub :: fn (str: string_view, start: s64, len : s64: -1) string_view #inline

Creates substring from passed string starting at start index of input string and ending at start + len index.

Starting index start must be greater than 0 and less than str.len. len specifies optional length of substring. When not specified, length from start to the end of the str is used.

Warning: Result sub-string is not guaranteed to be zero terminated.

File: string.bl

str_is_zero_terminated

Warning: Function is marked as obsolete. Since 0.13.0; Will be removed, BL strings are no longer zero-terminated.

str_is_zero_terminated :: fn (str: string_view) bool #inline

Checks whether string view is zero terminated. Returns false In case the string view does not point to any valid data (str.ptr == null).

File: string.bl

strtoc

strtoc :: fn (str: string_view, _allocator : *Allocator: null) *C.char #inline

Converts BL string to C zero-terminated string. This function internally allocates memory in order to put the zero terminator at the end of the string duplicate. In case the _allocator is not specified, application_context.temporary_allocator is used.

Empty strings are valid input and are converted to C string as "\0".

File: string.bl

tstrtoc

Warning: Function is marked as obsolete. Since 0.13.0; Use 'strtoc' instead.

tstrtoc :: fn (str: string_view) *C.char #inline

Converts string view into C string representation. This function first checks if the input string is zero terminated, in case it's not, the temporary allocator is used to create zero terminated copy which is returned. Otherwise returns pointer to data of original string without any allocations.

File: string.bl

ctostr

ctostr :: fn (cstr: *C.char, len : s64: -1) string_view #inline

Converts C string into string view. The len argument is optional and is used as string length if it's greater than -1, otherwise the C.strlen is used.

Empty string view is returned when the input C string is null or contains just null terminator.

File: string.bl

is_alpha

is_alpha :: fn (c: u8) bool #inline

Check whether the input ascii character is an alphabet.

File: string.bl

is_digit

is_digit :: fn (c: u8, base :: FmtIntBase.DEC) bool #inline

Check wheter the input character represents a digit of number of base.

File: string.bl