Modules

Basic

moduleSource
macOSLinuxWindowswasm

#import "Basic";

Procedures#

Builder#

(builder: *String_Builder, buffer_size := -1)

Prepares builder for use. Called by the other procedures when needed, so you rarely call it. buffer_size sets the size of the buffers allocated after the first (the default is 4 KiB); the builder takes the current context allocator. Does nothing when already prepared.

append3 overloads
(builder: *String_Builder, s: *u8, length: s64)

Appends length bytes at s to builder. A negative length is an error and sets the builder to failed.

(builder: *String_Builder)

Frees the memory builder took from its allocator and returns it to its empty state, also clearing the failed state. The builder can be used again afterwards.

(builder: *String_Builder)

Empties builder, freeing its extra memory, so it can be used again.

(builder: *String_Builder, extra_bytes_to_prepend := 0, do_reset := true) -> string

The text in builder, in a new string; the string is allocated with the context allocator, not the builder's, and the caller frees it. With do_reset (the default) the builder is emptied afterwards, otherwise it keeps its text. extra_bytes_to_prepend leaves that many unwritten bytes in front of the text. A builder that has failed gives an empty string.

(builder: *String_Builder, do_reset := true, to_standard_error := false) -> s64

Writes the text in builder to standard output, or standard error with to_standard_error, and returns the number of bytes. With do_reset (the default) the builder is emptied afterwards.

Scalar#

(message := "", args: ..Any, loc := #caller_location) -> bool
assert2 overloads
(arg: bool, message := "", args: ..Any, loc := #caller_location)
(format_string: string, args: ..Any, to_standard_error := false) -> bytes_printed: s64

Writes the formatted text to standard output, or standard error when requested. Returns the number of bytes written. Formatting uses the same argument grammar and active print style as the string-builder interface.

min2 overloads
(a: $T, b: T) -> T
max2 overloads
(a: $T, b: T) -> T
(x: $T, a: T, b: T) -> T
(a: *$T, b: *T)
(c: u8) -> u8

The capital for an ASCII lower case letter c; any other byte is returned as it is, so non-ASCII text is not changed.

assert(to_upper(#char "a") == #char "A");
assert(to_upper(#char "1") == #char "1");
↗
(c: u8) -> u8

The lower case letter for an ASCII capital c; any other byte is returned as it is.

assert(to_lower(#char "Q") == #char "q");
↗
(c: u8) -> bool

Whether c is an ASCII letter. Bytes of UTF-8 text above 127 are not letters.

assert(is_alpha(#char "x") && !is_alpha(#char "7") && !is_alpha(#char "_"));
↗
(c: u8) -> bool

Whether c is an ASCII digit, 0 to 9.

(c: u8) -> bool

Whether c is an ASCII letter or digit, or an underscore: whether it can appear in an identifier.

assert(is_alnum(#char "a") && is_alnum(#char "9") && is_alnum(#char "_"));
assert(!is_alnum(#char "-"));
↗
(c: u8) -> bool

Whether c is white space: a space, tab, line feed, vertical tab, form feed or carriage return.

(c: u8) -> bool

Whether c is a printable ASCII character, from the space to ~.

($T: Type) -> (is_integer: bool, is_signed: bool, bits: u8)
advance2 overloads
(s: *string, amount: s64 = 1)

Removes the first amount bytes (one by default) from the string s points to. amount must not be negative or more than the length of the string, or an assert fails.

text := "hello";
advance(*text, 2);
assert(text == "llo");
↗
Min2 overloads
(a: $T, b: T) -> T
Max2 overloads
(a: $T, b: T) -> T

Reflection#

(info: *Type_Info_Struct, name: string) -> (*Type_Info_Struct_Member, offset_in_bytes: s64)

Parsing#

(t: string, base := 10, $T := int) -> (result: T, success: bool, remainder: string)

Reads a whole number of type T (int by default) in the given base (2 to 36) from the start of t.

value, ok, rest := string_to_int("  -42 apples");
assert(ok && value == -42 && rest == " apples");

hex, hex_ok, _ := string_to_int("ff", 16);
assert(hex_ok && hex == 255);

small, small_ok, _ := string_to_int("300", 10, u8);
assert(!small_ok);
↗
(s: string) -> (result: int, success: bool, remainder: string)

Reads a decimal int from the start of s, as string_to_int does.

(_s: string) -> string

_s without its leading white space.

assert(eat_spaces(" \t word") == "word");
↗
(_s: string) -> (result: float64, success: bool, remainder: string)

Reads a number into a float64 from the start of _s: leading white space, an optional sign, then a decimal number with an optional fraction and exponent (-1.5e3), a hexadecimal one (0x1.8p1), inf, infinity or nan, in either case. The result is the value, whether a number was read and the text after it.

value, ok, rest := string_to_float64("2.5e2 units");
assert(ok && value == 250 && rest == " units");
↗
(arg: string) -> (result: float, success: bool, remainder: string)

Like string_to_float64, but the value is rounded to a float.

value, ok, rest := string_to_float("1.25");
assert(ok && value == 1.25 && rest.count == 0);
↗
(_s: string) -> (success: bool, result: float64, remainder: string)

Like string_to_float64, with the success flag first.

(arg: string) -> (success: bool, result: float, remainder: string)

Like string_to_float, with the success flag first.

Allocation#

(size: s64) -> *void
free2 overloads
(memory: *void)

Gives memory back to the context allocator. memory must come from alloc, New, realloc or another procedure that allocates with the context allocator, and must not be freed twice. Freeing a null pointer is up to the allocator; do not rely on it.

(memory: *void, size: s64, old_size: s64) -> *void
(count: int) -> string

A string of count bytes in newly allocated memory, with the context allocator. The bytes are not cleared, so write all of them before reading. Free it with free. With a count of 0 the string is empty and may have no data.

s := alloc_string(3);
defer free(s);
memcpy(s.data, "abc".data, 3);
assert(s == "abc");
↗
(s: string) -> string

A copy of s in newly allocated memory, with the context allocator. Free it with free. Use copy_temporary_string for a copy that needs no freeing.

original := "text";
copy := copy_string(original);
defer free(copy);
assert(copy == original && copy.data != original.data);
↗
to_string2 overloads
(s: *u8, length: s64) -> string

A string that views length bytes at s, without copying them. Nothing is allocated, so the string is valid as long as the bytes are.

bytes := "hello".data;
view := to_string(bytes, 4);
assert(view == "hell");
↗
(s: string) -> *u8

A copy of s in newly allocated memory, followed by a zero byte, for passing to C. It is allocated with the context allocator; free it with free. A zero byte inside s ends the string for the C side.

c := to_c_string("abc");
defer free(c);
assert(c[3] == 0 && to_string(c, 3) == "abc");
↗
($T: Type, $initialized := true) -> *T

Allocates a T with context.allocator and returns a pointer to it.

(count: s64, $T: Type, $initialized := true, alignment: s32 = -1) -> (result: [] T, unaligned_base: *void)

Allocates count elements of T with context.allocator.

Preamble#

Leak reports#

Memory debugger#

(allocator: Allocator, memory: *void, size: s64)

Visualization#

Format#

(s: string, cursor: s64) -> (end_cursor: s64, sum: s64)
(format_string: string, args: ..Any) -> string

The text format_string makes with args, in a new string. See print_to_builder for the format. When the formatting fails the result is an empty string and an error has been logged. It is allocated with the context allocator and the caller frees it with free. Use tprint to avoid freeing.

s := sprint("% is %", "x", 5);
defer free(s);
assert(s == "x is 5");
↗
(format_string: string, args: ..Any) -> string

Like sprint, but the string is in temporary storage. It must not be freed; it lasts until the temporary storage is reset.

assert(tprint("%-%", 1, 2) == "1-2");
reset_temporary_storage();
↗

Format types#

(value: Any, width := -1, trailing_width := -1, mode := FormatFloat.Mode.DECIMAL, zero_removal := FormatFloat.Zero_Removal.YES) -> FormatFloat

Wraps a float for print so it comes out with a chosen width, number of decimals or notation.

assert(tprint("%", formatFloat(3.14159, trailing_width = 2)) == "3.14");
↗
(value: Any, use_long_form_if_more_than_this_many_members := 5, use_newlines_if_long_form := false) -> FormatStruct

Wraps a struct for print with its own layout instead of the default one.

Format integer#

(value: Any, base := 10, minimum_digits := 1, padding: u8 = #char "0", digits_per_comma: u16 = 0, comma_string := "") -> FormatInt

Wraps an integer for print so it comes out in another base, padded, or grouped.

assert(tprint("%", formatInt(255, base = 16)) == "ff");
assert(tprint("%", formatInt(7, minimum_digits = 3)) == "007");
assert(tprint("%", formatInt(1234567, digits_per_comma = 3, comma_string = ",")) == "1,234,567");
↗

Format float#

Format aggregate#

(info: *Type_Info_Struct) -> *Type_Info_Struct_Member

Format type names#

Stream helpers#

Logging#

log2 overloads
(format_string: string, args: ..Any, loc := #caller_location, flags := Log_Flags.NONE, user_flags: u32 = 0, section: *Log_Section = null)

Formats a message as print would and hands it to context.logger.

log_error2 overloads
(format_string: string, args: ..Any, loc := #caller_location, flags := Log_Flags.NONE, user_flags: u32 = 0, section: *Log_Section = null)

Stack traces#

(node: *Stack_Trace_Node = null, log_flags := Log_Flags.ERROR, prefix := "")

Integer128#

Apollo#

(seconds: float64) -> Apollo_Time

Scale the binary significand exactly before truncating sub-femtosecond bits. This avoids overflowing an s64 intermediate for large finite durations.

Calendar text#

Elapsed time#

Clock calendar#

Filetime#

(ft: Filetime_Native.FILETIME) -> Apollo_Time

Converts a Windows FILETIME (100 ns ticks since 1601) to an Apollo_Time. Windows only. Every FILETIME is representable.

(at: Apollo_Time) -> (ft: Filetime_Native.FILETIME, success: bool)

Converts an Apollo_Time to a Windows FILETIME, with false and a zero value when the time lies before 1601 or beyond what a FILETIME can hold. Windows only. Times are cut to a multiple of 100 ns.

Temporary#

(count: int) -> string

A string of count bytes in temporary storage. It must not be freed; it lasts until the temporary storage is reset. The bytes are not cleared.

(s: string) -> *u8

A copy of s in temporary storage followed by a zero byte, for passing to C. It must not be freed; it lasts until the temporary storage is reset.

(mode: Allocator_Mode, requested_size: s64, old_size: s64, old_memory: *void, allocator_data: *void) -> *void

Filesystem#

(s: string) -> bool

Makes s the working directory of the whole program, for every thread, and returns whether it worked. A missing path, a file, a directory without permission to enter, an empty path and a path with a NUL byte give false and change nothing.

assert(!set_working_directory("jai-doc-no-such-directory/inner"));
↗
(status: s32)

Arguments#

Arrays#

array_reserve2 overloads
(array: *[..] $T, desired_items: s64)

Makes room for at least desired_items elements without changing the count.

values: [..] int;
array_reserve(*values, 100);
assert(values.count == 0 && values.allocated >= 100);

data := values.data;
for 0..99 array_add(*values, it);
assert(values.data == data); // no growth, so nothing moved
array_free(values);
↗
(array: *Resizable_Array, element_size: s64)
array_add3 overloads
(array: *[..] $T) -> *T

Appends an element to a dynamic array and returns a pointer to it.

values: [..] int;
array_add(*values, 10);
array_add(*values, 20, 30);

slot := array_add(*values); // a zeroed element, here at the end
slot.* = 40;

assert(values.count == 4 && values[0] == 10 && values[3] == 40);
array_free(values);
↗
(array: [] $T, item: T, $compare: (T, T) -> bool = null) -> (bool, s64)

Searches for item; returns whether it was found and the index of its first occurrence, or -1.

values := int.[5, 8, 5, 13];
found, index := array_find(values, 5);
assert(found && index == 0);

found, index = array_find(values, 6);
assert(!found && index == -1);

same_half :: (a: int, b: int) -> bool { return a / 2 == b / 2; }
found, index = array_find(values, 9, same_half); // 8 and 9 share a half
assert(found && index == 1);
↗
(array: *[..] $T, item: T, $compare: (T, T) -> bool = null) -> (bool, s64)

Appends item unless the array already holds an equal element.

values: [..] string;
added, index := array_add_if_unique(*values, "red");
assert(added && index == 0);
added, index = array_add_if_unique(*values, "blue");
assert(added && index == 1);

added, index = array_add_if_unique(*values, "red");
assert(!added && index == 0 && values.count == 2);
array_free(values);
↗
array_view2 overloads
(array: [] $T, index: int) -> [] T

A view of the elements of array from index on, or of count of them from index.

values := int.[1, 2, 3, 4, 5];
middle := array_view(values, 1, 3);
assert(middle.count == 3 && middle[0] == 2);

tail := array_view(values, 3);
assert(tail.count == 2 && tail[1] == 5);

middle[0] = 20; // writes into `values`
assert(values[1] == 20);
assert(array_view(values, 9).count == 0);
↗
(array: *[..] $T, item: T, index: int)

Inserts item so that it ends up at index, shifting the elements from there on up by one.

list: [..] int;
array_add(*list, 1, 2, 4);
array_insert_at(*list, 3, 2);
assert(list.count == 4 && list[2] == 3 && list[3] == 4);
array_free(list);
↗
(array: *[] $T, item: T, $stop_after_first := false, $compare: (T, T) -> bool = null) -> s64

Removes every element equal to item, each by moving the last element into its place; returns how many were removed.

values: [..] int;
array_add(*values, 1, 2, 1, 3, 1);
removed := array_unordered_remove_by_value(*values, 1);
assert(removed == 3 && values.count == 2);

once := array_unordered_remove_by_value(*values, 2, stop_after_first = true);
assert(once == 1 && values.count == 1 && values[0] == 3);
array_free(values);
↗
(array: [] $T) -> *T

A pointer to the last element, which stays valid until the array is changed. Fails an assert when the array is empty.

(array: [] $T) -> T

The last element of the array, without removing it. Fails an assert when the array is empty.

values := int.[3, 6, 9];
assert(peek(values) == 9);
peek_pointer(values).* = 10;
assert(values[2] == 10);
↗
(array: *[] $T) -> T

Removes the last element and returns it. Fails an assert when the array is empty.

values: [..] int;
array_add(*values, 1, 2, 3);
assert(pop(*values) == 3 && pop(*values) == 2);
assert(values.count == 1);
array_free(values);
↗
array_free2 overloads
(array: [] $T)

Frees the storage of an array. A view is freed with free, i.e. through context.allocator; a dynamic array goes back to the allocator it grew with.

values: [..] int;
array_add(*values, 1, 2, 3);
array_free(values);
values = .{};
↗
array_reset2 overloads
(array: *[] $T)

Frees the storage of an array (as array_free does) and sets the array to empty, so it can be reused.

values: [..] int;
array_add(*values, 1, 2, 3);
array_reset(*values);
assert(values.count == 0 && values.data == null);
↗
array_resize2 overloads
(array: *[..] $T, new_count: s64, $initialize := true)

Sets the count of an array to new_count, growing the storage if needed.

values: [..] int;
array_resize(*values, 4);
assert(values.count == 4 && values[3] == 0);

array_resize(*values, 2);
assert(values.count == 2);
array_free(values);
↗
(a: [] $T, set_allocators := true) -> [..] T
array_copy2 overloads
(array: [] $T) -> [] T

A new array holding a copy of the elements, in memory from context.allocator.

original := int.[1, 2, 3];
copy := array_copy(original);
copy[0] = 100;
assert(original[0] == 1 && copy.count == 3);
array_free(copy);

list: [..] int;
array_copy(*list, original);
assert(list.count == 3 && list[2] == 3);
array_free(list);
↗

Types#

S128struct
:: struct
U128struct
:: struct

Constants#

:: 4096 - size_of(String_Builder.Buffer)

The room, in bytes, a String_Builder has inside it and the default size of each buffer it adds later.

:: U128.{ 0xffffffffffffffff, 0xffffffffffffffff }
:: Allocator.{ temporary_allocator_proc, null }