Modules

Basic

moduleSource

Available on macOS, Linux, Windows, Wasm

#import "Basic";

The everyday toolbox: printing, strings and string builders, dynamic arrays, allocation and temporary storage, assert, logging, time, and a few reflection helpers.

  • Print with print, build text with tprint (temporary storage) or sprint (allocated), and write by hand with a String_Builder.
  • Grow arrays with array_add; allocate with New, NewArray and alloc; scratch memory comes from temp.
  • Time is an Apollo_Time; current_time_monotonic is the clock for timing.
Example↗
numbers: [..] int;
defer array_free(numbers);
for 1..3 array_add(*numbers, it * it);
assert(tprint("%", numbers) == "[1, 4, 9]");

Module parameters#

MEMORY_DEBUGGER := false
Tracks every allocation, so leaks can be reported.
ENABLE_ASSERT := true
Compiles assert out when false.
REPLACEMENT_INTERFACE: $I/interface Memory_Debugger_Interface = Memory_Debugger_Interface
The memory debugger's implementation, to replace it with your own.
VISUALIZE_MEMORY_DEBUGGER := true
Lets the memory debugger draw its live view.
TEMP_ALLOCATOR_OVERWRITE_MEMORY := false
Overwrites temporary storage when it is reset, to expose stale pointers.

Procedures#

Builder#

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

Prepares builder for use. Every other builder procedure does this for you, so the only reason to call it is to pick buffer_size, the size of the buffers added after the first (4 KiB by default). The builder takes the current context allocator. Calling it on a prepared builder does nothing.

builder: String_Builder;
init_string_builder(*builder, 64 * 1024);
defer free_buffers(*builder);
append(*builder, "ready");
assert(builder_string_length(*builder) == 5);
↗
(builder: *String_Builder, bytes: s64) -> pointer: *void

Returns a pointer to bytes of free space at the end of builder, or null if the builder failed. Write there, then call advance_through_ensured_space. See ensure_contiguous_space_and_return_available_range for the slice form.

(builder: *String_Builder, minimum_bytes := 1) -> [] u8

Makes sure builder has at least minimum_bytes of free space in one piece and returns all of it as a slice. Write into the slice, then call advance_through_ensured_space with the number of bytes you used. Use it to format straight into the builder instead of building a temporary string first. An empty slice means the builder failed.

builder: String_Builder;
defer free_buffers(*builder);
space := ensure_contiguous_space_and_return_available_range(*builder, 8);
space[0] = #char "h";
space[1] = #char "i";
advance_through_ensured_space(*builder, 2);
text := builder_to_string(*builder);
defer free(text);
assert(text == "hi");
↗
(builder: *String_Builder, steal_from: *String_Builder)

Appends everything in steal_from to builder and leaves steal_from empty. When both use the same allocator the buffers are moved instead of copied. Handy when threads fill their own builders and one of them joins the results.

a: String_Builder;
b: String_Builder;
defer free_buffers(*a);
defer free_buffers(*b);
append(*a, "one ");
append(*b, "two");
append_and_steal_buffers(*a, *b);
assert(builder_string_length(*b) == 0);
text := builder_to_string(*a);
defer free(text);
assert(text == "one two");
↗
(builder: *String_Builder)

Frees the memory builder took from its allocator and puts it back in its empty state, clearing a failure too. The builder can be used again afterwards. builder_to_string and write_builder already do this, so you only need it for a builder you abandon.

builder: String_Builder;
append(*builder, "scratch");
free_buffers(*builder);
assert(builder_string_length(*builder) == 0);
↗
(builder: *String_Builder, extra_bytes_to_prepend := 0, do_reset := true) -> string

The text in builder as a new string, allocated with the context allocator (not the builder's). You free it. The builder is emptied afterwards unless do_reset is false. extra_bytes_to_prepend leaves that many unwritten bytes in front of the text. A failed builder gives an empty string.

builder: String_Builder;
append(*builder, "total: ");
print_to_builder(*builder, "%", 42);
text := builder_to_string(*builder);
defer free(text);
assert(text == "total: 42");
↗
(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 byte count. The builder is emptied afterwards unless do_reset is false.

builder: String_Builder;
append(*builder, "to the console\n");
written := write_builder(*builder);
assert(written == 15);
↗

Scalar#

(message := "", args: ..Any, loc := #caller_location) -> bool

Calls context.assertion_failed with the formatted message and returns what it returns. assert calls this; call it yourself only to report a failed check of your own.

assert2 overloadsSource
(arg: bool, message := "", args: ..Any, loc := #caller_location)
(arg: bool, message := "", args: ..Any, loc := #caller_location)

Stops the program with a message when arg is false. message can hold % markers filled from args, the same way print does. The failure goes through context.assertion_failed, which prints the location and a stack trace and exits by default.

count := 3;
assert(count > 0, "count must be positive, got %", count);
↗

This one is only checked to compile, not run.

(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 overloadsSource
(a: $T, b: T) -> T
(a: $T, x: ..T) -> T

The smaller of a and b. Works on any type with <.

assert(min(3, 7) == 3);
assert(min(2.5, -1.0) == -1.0);
↗
max2 overloadsSource
(a: $T, b: T) -> T
(a: $T, x: ..T) -> T

The larger of a and b. Works on any type with >.

assert(max(3, 7) == 7);
↗
(x: $T, a: T, b: T) -> T

x limited to the range from a to b. Bring a and b in the right order yourself: with a > b the result is a.

assert(clamp(15, 0, 10) == 10);
assert(clamp(-5, 0, 10) == 0);
assert(clamp(4, 0, 10) == 4);
↗
(a: *$T, b: *T)

Exchanges the values a and b point to.

x := 1;
y := 2;
swap(*x, *y);
assert(x == 2 && y == 1);
↗
(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 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.

assert(is_space(#char " ") && is_space(#char "\n") && !is_space(#char "a"));
↗
($T: Type) -> (is_integer: bool, is_signed: bool, bits: u8)

Tells whether T is an integer type, and if so whether it is signed and how many bits wide.

is_integer, is_signed, bits := is_integer_type(s16);
assert(is_integer && is_signed && bits == 16);
assert(!is_integer_type(float32));
↗
advance2 overloadsSource
(s: *string, amount: s64 = 1)
(s: string, amount: s64 = 1) -> string

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 overloadsSource
(a: $T, b: T) -> T
(a: $T, x: ..T) -> T

The smaller of a and b. Works on any type with <.

assert(min(3, 7) == 3);
assert(min(2.5, -1.0) == -1.0);
↗
Max2 overloadsSource
(a: $T, b: T) -> T
(a: $T, x: ..T) -> T

The larger of a and b. Works on any type with >.

assert(max(3, 7) == 7);
↗

Reflection#

($t: Type) -> [] string

The names of the members of the enum T, in declaration order.

Color :: enum { RED; GREEN; BLUE; }
names := enum_names(Color);
assert(names.count == 3 && names[1] == "GREEN");
↗
($T: Type) -> [] s64

The values of the members of the enum T, in declaration order, as s64.

Level :: enum { LOW :: 1; MID :: 5; HIGH :: 9; }
values := enum_values_as_s64(Level);
assert(values.count == 3 && values[2] == 9);
↗
($T: Type) -> [] T

The members of the enum T, in declaration order. The slice lives in temporary storage.

Color :: enum { RED; GREEN; BLUE; }
members := enum_values_as_enum(Color);
assert(members.count == 3 && members[2] == .BLUE);
↗
(info: *Type_Info_Struct, name: string) -> (*Type_Info_Struct_Member, offset_in_bytes: s64)

Finds the member called name in a struct, looking through using members too. Returns the member and its byte offset from the start of the struct, or null and 0 when there is none.

Point :: struct { x: int; y: int; }
info := cast(*Type_Info_Struct) type_info(Point);
member, offset := get_field(info, "y");
assert(member != null && offset == 8);
↗
($t: Type) -> (low: s64, high: s64)

The lowest and highest member value of the enum t.

Level :: enum { LOW :: 1; MID :: 5; HIGH :: 9; }
low, high := enum_range(Level);
assert(low == 1 && high == 9);
↗
(info: *Type_Info_Enum) -> (low: s64, high: s64)

Like enum_range, for an enum whose Type_Info_Enum you already hold. An enum with no members gives 0, 0.

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.

value, ok, rest := to_integer("42px");
assert(ok && value == 42 && rest == "px");
↗
(_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.

ok, value, rest := string_to_float64_new("0.5!");
assert(ok && value == 0.5 && rest == "!");
↗
(arg: string) -> (success: bool, result: float, remainder: string)

Like string_to_float, with the success flag first.

ok, value, rest := string_to_float_new("2.5");
assert(ok && value == 2.5 && rest.count == 0);
↗

Allocation#

(size: s64) -> *void

Allocates size bytes with the context allocator and returns a pointer to them. The bytes are not cleared. Give them back with free. Prefer New or NewArray when you know the type.

memory := cast(*u8) alloc(16);
defer free(memory);
memory[0] = 7;
assert(memory[0] == 7);
↗
(b: Allocator) -> (caps: Allocator_Caps, name: string)

What the allocator b can do (its Allocator_Caps) and its name.

free2 overloadsSource
(memory: *void)
(s: string)

Gives memory back to the context allocator. It must come from alloc, New, realloc or another procedure that allocates with the context allocator, and must not be freed twice. Don't rely on freeing a null pointer.

number := New(int);
free(number);
↗
(memory: *void, size: s64, old_size: s64) -> *void

Resizes a block from alloc to size bytes and returns the block, which may have moved. old_size is the size it has now. The first min(size, old_size) bytes are kept.

memory := cast(*u8) alloc(4);
memory[0] = 42;
memory = cast(*u8) realloc(memory, 64, 4);
defer free(memory);
assert(memory[0] == 42);
↗
(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 overloadsSource
(s: *u8, length: s64) -> string
(c_string: [] u8) -> 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");
↗
(memory: *$T)

Sets the value memory points to back to the default for its type.

Point :: struct { x := 5; y := 6; }
p: Point;
p.x = 100;
Initialize(*p);
assert(p.x == 5 && p.y == 6);
↗
($T: Type, $initialized := true) -> *T

Allocates a T with context.allocator and returns a pointer to it. The value starts as the default for T. Free it with free.

Point :: struct { x := 1; y := 2; }
p := New(Point);
defer free(p);
assert(p.x == 1 && p.y == 2);
↗
(allocator: Allocator)
(proc: Allocator_Proc, data: *void)

Makes allocator the context.allocator until the end of the enclosing scope, then puts the old one back.

{
    push_allocator(temp);
    text := sprint("%", 123); // lives in temporary storage
    assert(text == "123");
}
reset_temporary_storage();
↗
(count: s64, $T: Type, $initialized := true, alignment: s32 = -1) -> (result: [] T, unaligned_base: *void)

Allocates count elements of T with context.allocator. They start as the default for T.

numbers, base := NewArray(4, int);
defer free(base);
numbers[3] = 9;
assert(numbers.count == 4 && numbers[0] == 0);
↗

Preamble#

Leak reports#

Memory debugger#

Visualization#

Format#

(pointer: *void, size_in_bytes: int) -> s64

Reads a signed integer of size_in_bytes (1, 2, 4 or 8) from pointer, sign-extended.

(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.

Pair :: struct { a: int; b: int; }
pair := Pair.{ 1, 2 };
text := tprint("%", formatStruct(pair, use_long_form_if_more_than_this_many_members = 1));
assert(text == "{a = 1; b = 2; }");
↗

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#

Format type names#

Stream helpers#

Logging#

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

Formats a message like print and hands it to context.logger. By default that writes to standard error; swap the logger to send messages somewhere else.

log("loaded % files", 3);
↗
log_error2 overloadsSource
(format_string: string, args: ..Any, loc := #caller_location, flags := Log_Flags.NONE, user_flags: u32 = 0, section: *Log_Section = null)
(section: *Log_Section, format_string: string, args: ..Any, loc := #caller_location, flags := Log_Flags.NONE, user_flags: u32 = 0)

Like log, with the .ERROR flag added.

log_error("could not open %", "config.txt");
↗

Stack traces#

() -> [] Stack_Trace_Node
(node: *Stack_Trace_Node) -> [] Stack_Trace_Node

Copies the current stack trace into a slice that stays valid after the call returns. Empty when the program keeps no stack trace.

(node: *Stack_Trace_Node = null) -> [..] string

The frames of a stack trace (the current one by default) as strings, innermost first, in a dynamic array.

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

Sends each frame of a stack trace (the current one by default) to the logger, one message per frame.

log_stack_trace();
↗

This one is only checked to compile, not run.

Integer128#

(a: $T) -> U128

Widens an unsigned integer (u8 to u64) to a U128.

x := to_u128(cast(u64) 7);
assert(x.low == 7 && x.high == 0);
↗
(a: $T) -> S128

Widens any integer to an S128, with sign extension.

x := to_s128(-1);
assert(x.high == -1);
↗

Apollo#

(m: s64, FACTOR: s64)

Scales m by FACTOR femtoseconds. The building block of the *_to_apollo procedures.

(t: Apollo_Time, divisor: u64, $DO_ROUNDING: bool) -> (result: s64, success: bool)

Divides t by divisor femtoseconds. The building block of the to_seconds family.

Calendar text#

Elapsed time#

Clock calendar#

Filetime#

to_filetimeWindows onlySource
(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#

(size: s64) -> *void

Allocates size bytes in temporary storage. They are not cleared, must not be freed, and last until the temporary storage is reset.

bytes := cast(*u8) talloc(32);
bytes[0] = 1;
reset_temporary_storage();
↗
(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 := talloc_string(3);
memcpy(s.data, "abc".data, 3);
assert(s == "abc");
reset_temporary_storage();
↗
(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.

c := temp_c_string("hi");
assert(c[0] == #char "h" && c[2] == 0);
reset_temporary_storage();
↗
(loc := #caller_location)

Rewinds temporary storage to where it is now when the enclosing scope ends. Put it at the top of a procedure that makes temporary strings so its callers don't pile them up.

shout :: (s: string) -> int {
    auto_release_temp();
    upper := tprint("%!", s);
    return upper.count;
}
assert(shout("hey") == 4);
↗

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)

Ends the program now with status as its exit code. Deferred statements and cleanup do not run.

exit(1);
↗

This one is only checked to compile, not run.

Arguments#

() -> [] string

The program's command-line arguments, starting with its own path. The slice is allocated; free it with free when done with it. For parsing options, use Command_Line.

args := get_command_line_arguments();
defer free(args.data);
for args print("%\n", it);
↗

This one is only checked to compile, not run.

Arrays#

(array: *[..] $T, desired_items: s64)
(array: *Resizable_Array, desired_items: s64, element_size: 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_add3 overloadsSource
(array: *[..] $T) -> *T
(array: *[..] $T, item: T)
(array: *[..] $T, to_append: ..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 overloadsSource
(array: [] $T, index: int) -> [] T
(array: [] $T, index: int, count: 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.

values := int.[3, 6, 9];
peek_pointer(values).* += 1;
assert(values[2] == 10);
↗
(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 overloadsSource
(array: [] $T)
(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: *[] $T)
(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: *[..] $T, new_count: s64, $initialize := true)
(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);
↗
array_copy2 overloadsSource
(array: [] $T) -> [] T
(dest: *[..] $T, source: [] 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#

:: struct
count: s64
bytes: s64
allocations_since_last_visualizer_update: s64
bytes_allocated_since_last_visualizer_update: s64
alloc_site_trace: *Packed_Stack_Trace
allocator_proc: Allocator_Proc
group_info: *Group_Info
child_count: s64
child_bytes: s64
:: struct

The base of the FormatInt, FormatFloat, FormatStruct and FormatArray wrappers. value is what gets printed.

value: Any
:: struct

Print settings for one float. Build one with formatFloat and pass it to print or tprint in place of the number.

formatter: Formatter
width: := -1
trailing_width: := -1
zero_removal: := Zero_Removal.YES
mode: := Mode.DECIMAL
thousand_separators: := false
thousand_separator_character: u8
decimal_separator_character: u8
exponent_character: u8
positive_number_prefix: := Positive_Number_Prefix.NONE
:: struct

Print settings for one struct: the brackets, the separators, when to switch to the long form. Build one with formatStruct.

formatter: Formatter
draw_type_name: := false
use_long_form_if_more_than_this_many_members: := 5
separator_between_name_and_value: := " = "
short_form_separator_between_fields: := ", "
long_form_separator_between_fields: := "; "
begin_string: := "{"
end_string: := "}"
indentation_width: s32
use_newlines_if_long_form: := false
:: struct

Print settings for one array: the separator and brackets, and how many elements to show before the output is cut with ....

formatter: Formatter
separator: := ", "
begin_string: := "["
end_string: := "]"
printing_stopped_early_string: := "..."
draw_separator_after_last_element: := false
stop_printing_after_this_many_elements: := 100
numbers := int.[1, 2, 3];
format := FormatArray.{ value = numbers, separator = " | ", begin_string = "<", end_string = ">" };
assert(tprint("%", format) == "<1 | 2 | 3>");
↗
:: struct

A signed 128-bit integer as two 64-bit halves. There is no arithmetic operator on it; Apollo_Time is the main user.

low: u64
high: s64
:: struct

An unsigned 128-bit integer as two 64-bit halves.

low: u64
high: u64
:: #type,isa S128

A point or span of time, stored as a 128-bit count of femtoseconds. A time of day counts from 20 July 1969, 20:17 UTC, not from 1970; use APOLLO_TIME_FOR_JAN_1_1970 for the Unix zero. It does not overflow in practice and keeps sub-nanosecond precision. Make one with seconds_to_apollo and its siblings or current_time_consensus, and read it back with to_seconds and the like. APOLLO_TIME_INVALID marks a failure.

a := milliseconds_to_apollo(1500);
b := seconds_to_apollo(2);
assert(compare_apollo_times(a, b) < 0);
seconds, ok := to_seconds(b);
assert(ok && seconds == 2);
↗
:: enum

Which clock to_calendar uses: .UTC, or .LOCAL for the computer's own time zone.

UTC
LOCAL
:: struct

A time broken into year, month, day and time of day. Months, days of the month and days of the week count from 0, so January is 0 and Sunday is 0. Make one with to_calendar.

year: s32
month_starting_at_0: s8
day_of_month_starting_at_0: s8
day_of_week_starting_at_0: s8
hour: s8
minute: s8
second: s8
millisecond: s16
time_zone: Time_Zone
ct := to_calendar(APOLLO_TIME_FOR_JAN_1_1970);
assert(ct.year == 1970 && ct.day_of_week_starting_at_0 == 4); // a Thursday
↗

Constants#

:: Apollo_Time.{ low = 0xdbd87f0edb7c8000, high = 0x300 }

The Apollo_Time of 1970-01-01 00:00 UTC, the zero of the Unix clock.

ct := to_calendar(APOLLO_TIME_FOR_JAN_1_1970);
assert(ct.year == 1970 && ct.month_starting_at_0 == 0 && ct.day_of_month_starting_at_0 == 0);
↗
:: Allocator.{ temporary_allocator_proc, null }

The temporary allocator: a scratch heap that is never freed piece by piece. Everything in it goes away together when reset_temporary_storage runs, which a program typically does once per frame or request. Use it for short-lived results so there is nothing to track or free. Set context.allocator = temp or use push_allocator(temp) to send allocations there.

text := tprint("%, %", 1, 2); // allocated in temporary storage
assert(text == "1, 2");
reset_temporary_storage();
↗
:: Allocator.{ temporary_allocator_proc, null }

The temporary allocator: a scratch heap that is never freed piece by piece. Everything in it goes away together when reset_temporary_storage runs, which a program typically does once per frame or request. Use it for short-lived results so there is nothing to track or free. Set context.allocator = temp or use push_allocator(temp) to send allocations there.

text := tprint("%, %", 1, 2); // allocated in temporary storage
assert(text == "1, 2");
reset_temporary_storage();
↗