#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 withtprint(temporary storage) orsprint(allocated), and write by hand with aString_Builder. - Grow arrays with
array_add; allocate withNew,NewArrayandalloc; scratch memory comes fromtemp. - Time is an
Apollo_Time;current_time_monotonicis the clock for timing.
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
assertout whenfalse. - 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#
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);↗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.
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");↗Counts the first bytes of the space from ensure_contiguous_space as written. bytes must not be more than the space
that was ensured.
Appends length bytes at s to builder. A negative length marks the builder as failed.
builder: String_Builder;
defer free_buffers(*builder);
data := u8.[1, 2, 3];
append(*builder, data.data, 3);
assert(builder_string_length(*builder) == 3);↗Appends the raw bytes of the value pointer points to, as they sit in memory.
builder: String_Builder;
defer free_buffers(*builder);
value: u32 = 0x01020304;
append_by_pointer(*builder, *value);
assert(builder_string_length(*builder) == 4);↗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");↗The number of bytes in builder.
builder: String_Builder;
defer free_buffers(*builder);
assert(builder_string_length(*builder) == 0);
append(*builder, "abc");
assert(builder_string_length(*builder) == 3);↗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);↗Empties builder and frees its extra buffers so it can be used again.
builder: String_Builder;
defer free_buffers(*builder);
append(*builder, "old");
reset(*builder);
append(*builder, "new");
text := builder_to_string(*builder);
defer free(text);
assert(text == "new");↗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");↗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#
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.
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.
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.
The smaller of a and b. Works on any type with <.
The larger of a and b. Works on any type with >.
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.
Exchanges the values a and b point to.
The capital for an ASCII lower case letter c; any other byte is returned as it is, so non-ASCII text is
not changed.
The lower case letter for an ASCII capital c; any other byte is returned as it is.
Whether c is an ASCII letter. Bytes of UTF-8 text above 127 are not letters.
Whether c is an ASCII digit, 0 to 9.
Whether c is an ASCII letter or digit, or an underscore: whether it can appear in an identifier.
Whether c is white space: a space, tab, line feed, vertical tab, form feed or carriage return.
Whether c is a printable ASCII character, from the space to ~.
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));↗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.
The smaller of a and b. Works on any type with <.
The larger of a and b. Works on any type with >.
Reflection#
Whether T is a number, pointer or enum, the types min, max and clamp accept.
The lowest and highest value of the integer type T.
low, high := get_integer_range(u8);
assert(low == 0 && high == 255);
low_s, high_s := get_integer_range(s8);
assert(low_s == -128 && high_s == 127);↗Converts value to TargetType, using the nearest value that fits when it is out of range, where a plain cast would wrap.
big := 1000;
assert(clamp_to_another_integer_type(big, u8) == 255);
assert(clamp_to_another_integer_type(-5, u8) == 0);
assert(clamp_to_another_integer_type(100, u8) == 100);↗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");↗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);↗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);↗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.
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);↗Like enum_range, for an enum whose Type_Info_Enum you already hold. An enum with no members gives 0, 0.
The highest member value of the enum t.
Level :: enum { LOW :: 1; MID :: 5; HIGH :: 9; }
assert(enum_highest_value(Level) == 9);↗Parsing#
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);↗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 without its leading white space.
assert(eat_spaces(" \t word") == "word");↗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");↗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);↗Like string_to_float64, with the success flag first.
ok, value, rest := string_to_float64_new("0.5!");
assert(ok && value == 0.5 && rest == "!");↗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#
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.
What the allocator b can do (its Allocator_Caps) and its name.
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.
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.
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");↗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);↗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.
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");↗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);↗Allocates a T with context.allocator and returns a pointer to it. The value starts as the default for T.
Free it with free.
Rounds orig_size up to a multiple of alignment.
assert(align_forward(13, 8) == 16);
assert(align_forward(16, 8) == 16);↗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();↗Allocates count elements of T with context.allocator. They start as the default for T.
Preamble#
Leak reports#
Memory debugger#
Visualization#
Format#
Reads an unsigned integer of size_in_bytes (1, 2, 4 or 8) from pointer.
Reads a signed integer of size_in_bytes (1, 2, 4 or 8) from pointer, sign-extended.
Appends one value to builder the way print writes a %. Useful in a procedure that takes an Any.
builder: String_Builder;
defer free_buffers(*builder);
n := 12;
print_item_to_builder(*builder, n);
text := builder_to_string(*builder);
defer free(text);
assert(text == "12");↗Appends text to builder as format_string describes, and returns whether it worked. Each % takes
the next of args and writes it in its default form; %1, %2, ... pick an argument by position.
A format with too few arguments logs an error. Returns false if the builder ran out of memory.
builder: String_Builder;
print_to_builder(*builder, "% + % = %", 1, 2, 3);
result := builder_to_string(*builder);
defer free(result);
assert(result == "1 + 2 = 3");↗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.
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();↗Writes the byte _c to standard output.
print_character(#char "!");
print_character(#char "\n");↗Format types#
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");↗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#
Wraps an integer for print so it comes out in another base, padded, or grouped.
Format float#
Format aggregate#
Format type names#
Appends the name of info to builder, as type_to_string writes it.
The name of a type as source code spells it, in temporary storage.
assert(type_to_string(type_info(int)) == "s64");
assert(type_to_string(type_info(*string)) == "*string");↗Stream helpers#
Appends each of args to builder with nothing between them, written the way values print inside an array, so strings come out in quotes.
Prints length bytes at a in hex, each written with format_string, then a newline. Handy for a quick look at a buffer.
data := u8.[1, 171, 255];
print_bytes(data.data, 3);↗Logging#
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.
Like log, with the .ERROR flag added.
Stack traces#
Copies the current stack trace into a slice that stays valid after the call returns. Empty when the program keeps no stack trace.
The frames of a stack trace (the current one by default) as strings, innermost first, in a dynamic array.
Prints each frame of the trace at node on its own line, after prefix, to standard error unless to_standard_error
is false.
print_stack_trace(context.stack_trace, " at ");↗This one is only checked to compile, not run.
Sends each frame of a stack trace (the current one by default) to the logger, one message per frame.
This one is only checked to compile, not run.
Integer128#
Multiplies two 64-bit numbers without losing the top half: the 128-bit product comes back as high and low.
high, low := multiply_64bits_with_overflow(0xffffffffffffffff, 2);
assert(high == 1 && low == 0xfffffffffffffffe);↗Widens an unsigned integer (u8 to u64) to a U128.
Widens any integer to an S128, with sign extension.
Divides u by v. The third result is true when v is zero, and the others are then U128_ERROR.
q, r, error := unsigned_divide_with_remainder(to_u128(cast(u64) 100), to_u128(cast(u64) 7));
assert(!error && q.low == 14 && r.low == 2);↗Like unsigned_divide_with_remainder, for S128. The quotient rounds toward zero.
Apollo#
An Apollo_Time of m seconds.
t := seconds_to_apollo(90);
minutes, ok := to_milliseconds(t);
assert(ok && minutes == 90000);↗Scales m by FACTOR femtoseconds. The building block of the *_to_apollo procedures.
An Apollo_Time of m milliseconds.
assert(to_microseconds(milliseconds_to_apollo(3)) == 3000);↗An Apollo_Time of m microseconds.
assert(to_nanoseconds(microseconds_to_apollo(5)) == 5000);↗An Apollo_Time of m nanoseconds.
assert(to_nanoseconds(nanoseconds_to_apollo(42)) == 42);↗Divides t by divisor femtoseconds. The building block of the to_seconds family.
t in whole seconds. The second result is false when the value does not fit an s64. Truncates toward zero, or rounds
to nearest with DO_ROUNDING.
seconds, ok := to_seconds(milliseconds_to_apollo(2500));
assert(ok && seconds == 2);
rounded, ok2 := to_seconds(milliseconds_to_apollo(2500), true);
assert(ok2 && rounded == 3);↗t in whole milliseconds; see to_seconds for the rounding and the success flag.
ms, ok := to_milliseconds(seconds_to_apollo(3));
assert(ok && ms == 3000);↗t in whole microseconds; see to_seconds for the rounding and the success flag.
us, ok := to_microseconds(milliseconds_to_apollo(1));
assert(ok && us == 1000);↗t in whole nanoseconds; see to_seconds for the rounding and the success flag.
ns, ok := to_nanoseconds(microseconds_to_apollo(1));
assert(ok && ns == 1000);↗Returns -1, 0 or 1 when a is before, equal to or after b.
assert(compare_apollo_times(seconds_to_apollo(1), seconds_to_apollo(2)) == -1);
assert(compare_apollo_times(seconds_to_apollo(2), seconds_to_apollo(2)) == 0);↗input as seconds in a float64. The second result is false for APOLLO_TIME_INVALID or a value too big.
seconds, ok := to_float64_seconds(milliseconds_to_apollo(1500));
assert(ok && seconds == 1.5);↗An Apollo_Time of seconds, a float64. Infinity and NaN give APOLLO_TIME_INVALID.
t := to_apollo(0.25);
assert(to_milliseconds(t) == 250);↗Calendar text#
Spells ct as text such as 1 January 1970, 00:00:00, in temporary storage.
text := calendar_to_string(to_calendar(APOLLO_TIME_FOR_JAN_1_1970));
assert(text == "1 January 1970, 00:00:00");↗Spells ct in ISO 8601 form such as 1970-01-01T00:00:00.000Z, in temporary storage.
text := calendar_to_iso_string(to_calendar(APOLLO_TIME_FOR_JAN_1_1970));
assert(text == "1970-01-01T00:00:00.000Z");↗Elapsed time#
Seconds since the first call to this procedure, from the monotonic clock. A quick stopwatch: call it once to start and again to read.
start := seconds_since_init();
now := seconds_since_init();
assert(now >= start);↗Clock calendar#
Breaks at into year, month, day and time of day.
ct := to_calendar(APOLLO_TIME_FOR_JAN_1_1970);
assert(ct.year == 1970 && ct.hour == 0);↗The Apollo_Time for a Calendar_Time; the reverse of to_calendar.
ct := to_calendar(seconds_to_apollo(1000000));
back := calendar_to_apollo(ct);
assert(to_seconds(back) == 1000000);↗The current wall-clock time. This is the clock the computer's owner can set, so it can jump; use current_time_monotonic
to measure how long something took. Returns APOLLO_TIME_INVALID if the system cannot say.
now := current_time_consensus();
assert(compare_apollo_times(now, APOLLO_TIME_INVALID) != 0);↗A clock that only moves forward and is not affected by the user changing the date. Its zero is arbitrary, so only differences mean anything. Use it for timing.
start := current_time_monotonic();
stop := current_time_monotonic();
assert(compare_apollo_times(stop, start) >= 0);↗Converts a C timespec to an Apollo_Time.
Converts an Apollo_Time to a C timespec. The second result is false when it does not fit.
Filetime#
Converts a Windows FILETIME (100 ns ticks since 1601) to an Apollo_Time. Windows only. Every FILETIME is
representable.
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#
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();↗Another name for talloc.
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();↗A copy of s in temporary storage. It must not be freed; it lasts until the temporary storage is
reset.
copy := copy_temporary_string("scratch");
assert(copy == "scratch");
reset_temporary_storage();↗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();↗The current position in temporary storage. Pass it to set_temporary_storage_mark to free everything allocated after
this point while keeping what came before.
Rewinds temporary storage to state, a value from get_temporary_storage_mark. Everything allocated since then is gone.
mark := get_temporary_storage_mark();
scratch := tprint("%", 12345);
assert(scratch == "12345");
set_temporary_storage_mark(mark);↗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#
The current working directory of the program, as an absolute path in a new string from context.allocator that the caller frees.
original := get_working_directory();
defer free(original);
assert(original.count > 0);
assert(set_working_directory("."));
same := get_working_directory();
assert(same == original);
free(same);↗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"));↗Ends the program now with status as its exit code. Deferred statements and cleanup do not run.
This one is only checked to compile, not run.
Pauses the calling thread for at least milliseconds.
Arguments#
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#
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);↗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);↗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);↗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);↗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);↗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);↗Removes the element at index and shifts the later ones down, keeping their order.
values: [..] int;
array_add(*values, 1, 2, 3, 4);
array_ordered_remove_by_index(*values, 1);
assert(values.count == 3 && values[1] == 3 && values[2] == 4);
array_free(values);↗Removes the element at index by moving the last element into its place, in constant time.
values: [..] int;
array_add(*values, 1, 2, 3, 4);
array_unordered_remove_by_index(*values, 0);
assert(values.count == 3 && values[0] == 4 && values[1] == 2);
array_free(values);↗Removes the first element equal to item (by ==), keeping the order of the rest.
values: [..] int;
array_add(*values, 7, 8, 7);
assert(array_ordered_remove_by_value(*values, 7));
assert(values.count == 2 && values[0] == 8 && values[1] == 7);
assert(!array_ordered_remove_by_value(*values, 99));
array_free(values);↗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);↗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);↗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);↗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);↗Empties the array but keeps its storage, so refilling it up to the old capacity allocates nothing.
values: [..] int;
array_add(*values, 1, 2, 3);
capacity := values.allocated;
array_reset_keeping_memory(*values);
assert(values.count == 0 && values.allocated == capacity);
array_free(values);↗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 = .{};↗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);↗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 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#
- initialized: := false
- failed: := false
- subsequent_buffer_size: := -1
- allocator: Allocator
- current_buffer: *Buffer
- initial_bytes: [STRING_BUILDER_BUFFER_SIZE] u8
- procedure_name: string
- module_name: string
- procedure: Procedure_To_Elide
- sub_summaries: [] *Leak_Summary
- root_nodes_in_common: s64
- leaf_nodes_in_common: s64
- 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
- known_allocation_leaves: [] Procedure_To_Elide
- roots_to_skip: [] Procedure_To_Elide
- group_by_these_calls: [] Procedure_To_Elide
- scan_global_memory_to_remove_leaks: := true
- scan_leaks_to_combine: := true
- marked_as_non_leak_allocations: s64
- marked_as_non_leak_bytes: s64
- sorted_summaries: [] *Leak_Summary
- leak_combiner_table: Leak_Table.Table(*void, Leak_Combiner_Record)
- pool: Leak_Pool.Pool
The base of the FormatInt, FormatFloat, FormatStruct and FormatArray wrappers. value is what gets printed.
- value: Any
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
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
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
The defaults print uses when a value has no wrapper: one FormatInt, FormatFloat, FormatStruct and FormatArray,
and how pointers are shown. Set a field of context.print_style to change how every later print writes that kind of
value.
- default_format_int: FormatInt
- default_format_float: FormatFloat
- default_format_struct: FormatStruct
- default_format_array: FormatArray
- default_format_absolute_pointer: FormatInt
- struct_printer: (builder: *String_Builder, any: Any, struct_printer_data: *void) -> bool
- struct_printer_data: *void
- indentation_depth: s32
- state_flags: enum u32 { INSIDE_DATA_STRUCTURE_PRINTING :: 0x1; }
- log_runtime_errors: := true
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
An unsigned 128-bit integer as two 64-bit halves.
- low: u64
- high: u64
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);↗Which clock to_calendar uses: .UTC, or .LOCAL for the computer's own time zone.
- UTC
- LOCAL
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↗- base: Temporary_Storage
- derived: struct { overflow_allocator: Allocator; overflow_pages: *Overflow_Page; original_data: *u8; original_size: s64; current_page_bytes_occupied: s64; }
- top_overflow_page: *Overflow_Page
- current_page_bytes_occupied: s64
- total_bytes_occupied: s64
- next: *Overflow_Page
- allocator: Allocator
- size: s64
Constants#
The room, in bytes, a String_Builder has inside it and the default size of each buffer it adds later.
The Apollo_Time that functions return when they fail. Compare with compare_apollo_times.
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);↗Femtoseconds in a second.
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();↗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();↗