#import "std/string"
Builtin UTF-8 encoded automatically growing string container. Essentially wrapper over dynamic array with some additional functionality and semantics.
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;
}
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:
While string needs to hold some additional data it's not suitable for passing by value
into functions, on the other hand string_view is just 16 bytes. It's preferred to pass
string_views by value.
The string_view might help to reduce amount of allocations and copying, consider following
function doing split of the input string by delimiter:
split :: fn (str: string, delim: u8) (left: string, right: string) {...}
When delimiter is found in the input string str we have to allocate two new strings for
the left and right substring and populate them with data comming from the original string.
Since left and right substrings are allocated on the heap, we need to call str_terminate
on each one after use.
Same function with string_view:
split :: fn (str: string_view, delim: u8) (left: string_view, right: string_view) {...}
In this case no allocation and string copying is needed, left and right substrings are just a "view" of the original string memory.
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.
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.
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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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 :: 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
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
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 :: 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 :: fn (first: string_view, second: string_view) s32
File: string.bl
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 :: 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 :: 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 :: 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 :: 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.
#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 :: 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.
#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 :: 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.
#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 :: 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 :: 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)
#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 :: fn (str: string_view, c: u8) s32 #inline
Returns number of c characters in the string.
File: string.bl
str_hash :: fn (str: string_view) u32
Calculates string u32 hash.
File: string.bl
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
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 ::
File: string.bl
str_view_empty ::
File: string.bl
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 :: 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
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 :: 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
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 :: 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 :: fn (c: u8) bool #inline
Check whether the input ascii character is an alphabet.
File: string.bl
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