various changes
This commit is contained in:
parent
49973e65ed
commit
cd4ad810a0
17 changed files with 569 additions and 339 deletions
|
|
@ -1,76 +0,0 @@
|
||||||
AllocatorMode :: enum {
|
|
||||||
using Allocator_Mode;
|
|
||||||
|
|
||||||
// Compatability
|
|
||||||
Allocate :: ALLOCATE;
|
|
||||||
Resize :: RESIZE;
|
|
||||||
Free :: FREE;
|
|
||||||
Startup :: STARTUP;
|
|
||||||
Shutdown :: SHUTDOWN;
|
|
||||||
ThreadStart :: THREAD_START;
|
|
||||||
ThreadStop :: THREAD_STOP;
|
|
||||||
CreateHeap :: CREATE_HEAP;
|
|
||||||
DestroyHeap :: DESTROY_HEAP;
|
|
||||||
IsThisYours :: IS_THIS_YOURS;
|
|
||||||
Capabilities :: CAPS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// PanicAllocator causes the program to panic when used.
|
|
||||||
PanicAllocator :: () -> Allocator {
|
|
||||||
return .{ proc = xx PanicAllocatorProc };
|
|
||||||
}
|
|
||||||
|
|
||||||
TrySetAllocator :: (thing: *$T, allocator := context.allocator) #modify {
|
|
||||||
info := T.(*Type_Info_Struct);
|
|
||||||
|
|
||||||
ok := false;
|
|
||||||
if info.type == .STRUCT
|
|
||||||
{ ok = true; }
|
|
||||||
|
|
||||||
if ok for info.members if it.name == "allocator" && it.type == Allocator.(*Type_Info) {
|
|
||||||
ok = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ok, "can only set allocator on struct with an allocator field or dynamic array";
|
|
||||||
} #expand {
|
|
||||||
if thing.allocator.proc == null {
|
|
||||||
thing.allocator = allocator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TrySetAllocator :: (array: *[..]$T, allocator := context.allocator) #expand {
|
|
||||||
if array.allocator.proc == null {
|
|
||||||
array.allocator = allocator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#scope_module
|
|
||||||
|
|
||||||
PanicAllocatorProc :: (mode: AllocatorMode, new_size: int, old_size: int, ptr: *void, allocator_data: *void) -> *void {
|
|
||||||
if mode == {
|
|
||||||
case .Allocate;
|
|
||||||
if new_size > 0 {
|
|
||||||
Panic("jc: allocation using the PanicAllocator");
|
|
||||||
}
|
|
||||||
case .Resize;
|
|
||||||
if new_size > 0 {
|
|
||||||
Panic("jc: reallocation using the PanicAllocator");
|
|
||||||
}
|
|
||||||
case .Free;
|
|
||||||
if ptr != null {
|
|
||||||
Panic("jc: free using the PanicAllocator");
|
|
||||||
}
|
|
||||||
|
|
||||||
case .ThreadStart; #through;
|
|
||||||
case .ThreadStop;
|
|
||||||
Panic("jc: start/stop thread using the PanicAllocator");
|
|
||||||
|
|
||||||
case .CreateHeap; #through;
|
|
||||||
case .DestroyHeap;
|
|
||||||
Panic("jc: create/destroy heap using the PanicAllocator");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
} @jc.nodocs
|
|
||||||
259
+internal/arenas.jai
Normal file
259
+internal/arenas.jai
Normal file
|
|
@ -0,0 +1,259 @@
|
||||||
|
// @todo(judah): Conditionally added to the context because it causes some weirdness in tests
|
||||||
|
#if !RunTests {
|
||||||
|
#add_context arena := Arena.{ proc = PagingArenaProc };
|
||||||
|
#add_context temp_arena := Arena.{ proc = TempArenaProc };
|
||||||
|
}
|
||||||
|
|
||||||
|
Arena :: struct {
|
||||||
|
proc: ArenaProc;
|
||||||
|
data: *void;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaProc :: #type (
|
||||||
|
mode: ArenaMode,
|
||||||
|
arena_data: *void,
|
||||||
|
|
||||||
|
new_size: int, old_size: int, align: int,
|
||||||
|
new_ptr: *void, old_ptr: *void,
|
||||||
|
|
||||||
|
caller: Source_Code_Location
|
||||||
|
) -> *void;
|
||||||
|
|
||||||
|
ArenaMode :: enum {
|
||||||
|
Setup;
|
||||||
|
Teardown;
|
||||||
|
|
||||||
|
Acquire;
|
||||||
|
Reset;
|
||||||
|
|
||||||
|
Save;
|
||||||
|
Restore;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ArenaPush :: ($T: Type, loc := #caller_location) -> *T {
|
||||||
|
return ArenaPushSize(size_of(T), loc = loc).(*T);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaPushSize :: (size: int, align := DefaultAlign, loc := #caller_location) -> *void {
|
||||||
|
if size < 0
|
||||||
|
{ Panic("jc: size cannot be negative", loc = loc); }
|
||||||
|
TrySetupArena(*context.arena);
|
||||||
|
return context.arena.proc(.Acquire, context.arena.data, size, 0, align, null, null, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaSaveResult :: #type,distinct *void;
|
||||||
|
|
||||||
|
ArenaSave :: (loc := #caller_location) -> ArenaSaveResult {
|
||||||
|
TrySetupArena(*context.arena, loc);
|
||||||
|
return context.arena.proc(.Save, context.arena.data, 0, 0, 0, null, null, loc).(ArenaSaveResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaRestore :: (savepoint: ArenaSaveResult, loc := #caller_location) {
|
||||||
|
context.arena.proc(.Restore, context.arena.data, 0, 0, 0, null, savepoint.(*void), loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaAutoRestore :: () #expand {
|
||||||
|
wm := ArenaSave();
|
||||||
|
`defer ArenaRestore(wm);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaReset :: (loc := #caller_location) {
|
||||||
|
TrySetupArena(*context.arena);
|
||||||
|
context.arena.proc(.Reset, context.arena.data, 0, 0, 0, null, null, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
ArenaRelease :: (loc := #caller_location) {
|
||||||
|
TrySetupArena(*context.arena, loc);
|
||||||
|
context.arena.proc(.Teardown, context.arena.data, 0, 0, 0, null, null, loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ArenaToAllocator wraps an arena, allowing it to be used with Jai's builtin Allocator system.
|
||||||
|
ArenaToAllocator :: (arena: *Arena) -> Allocator {
|
||||||
|
return .{ proc = JaiAllocatorProc, data = arena };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PanicArena :: () -> Arena {
|
||||||
|
return .{ proc = PanicArenaProc };
|
||||||
|
}
|
||||||
|
|
||||||
|
BumpData :: struct {
|
||||||
|
memory: []u8;
|
||||||
|
offset: int;
|
||||||
|
}
|
||||||
|
|
||||||
|
BumpArena :: (bump: *BumpData) -> Arena {
|
||||||
|
return .{ proc = BumpArenaProc, data = bump };
|
||||||
|
}
|
||||||
|
|
||||||
|
PagingArena :: () -> Arena {
|
||||||
|
return .{ proc = PagingArenaProc };
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#scope_module
|
||||||
|
|
||||||
|
TrySetupArena :: (arena: *Arena, loc := #caller_location) #expand {
|
||||||
|
if arena.data != null
|
||||||
|
{ return; }
|
||||||
|
|
||||||
|
data := arena.proc(.Setup, null, 0, 0, 0, null, null, loc);
|
||||||
|
if data != null
|
||||||
|
{ arena.data = data; }
|
||||||
|
}
|
||||||
|
|
||||||
|
PanicArenaProc :: (
|
||||||
|
mode: ArenaMode,
|
||||||
|
arena_data: *void,
|
||||||
|
|
||||||
|
new_size: int, old_size: int, align: int,
|
||||||
|
new_ptr: *void, old_ptr: *void,
|
||||||
|
|
||||||
|
caller: Source_Code_Location
|
||||||
|
) -> *void {
|
||||||
|
if mode == {
|
||||||
|
case .Acquire;
|
||||||
|
if new_size > 0
|
||||||
|
{ Panic("jc: cannot acquire memory using the PanicArena", loc = caller); }
|
||||||
|
|
||||||
|
case .Save;
|
||||||
|
Panic("jc: cannot save using the PanicArean", loc = caller);
|
||||||
|
case .Restore;
|
||||||
|
Panic("jc: cannot restore using the PanicArean", loc = caller);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} @jc.nodocs
|
||||||
|
|
||||||
|
BumpArenaProc :: (
|
||||||
|
mode: ArenaMode,
|
||||||
|
arena_data: *void,
|
||||||
|
|
||||||
|
new_size: int, old_size: int, align: int,
|
||||||
|
new_ptr: *void, old_ptr: *void,
|
||||||
|
|
||||||
|
caller: Source_Code_Location
|
||||||
|
) -> *void #no_abc {
|
||||||
|
bump := arena_data.(*BumpData);
|
||||||
|
if mode == {
|
||||||
|
case .Setup;
|
||||||
|
if bump.memory.data == null
|
||||||
|
{ Panic("jc: BumpArena has no memory", loc = caller); }
|
||||||
|
return arena_data;
|
||||||
|
|
||||||
|
case .Teardown;
|
||||||
|
// Do nothing, we don't own this memory
|
||||||
|
|
||||||
|
case .Acquire;
|
||||||
|
end := bump.memory.data + bump.offset;
|
||||||
|
if new_size == 0
|
||||||
|
{ return end; }
|
||||||
|
|
||||||
|
ptr := MemAlignForward(end, align.(uint));
|
||||||
|
size := new_size + (ptr - end);
|
||||||
|
if bump.offset + size > bump.memory.count
|
||||||
|
{ Panic("jc: BumpArena out of memory", loc = caller); }
|
||||||
|
|
||||||
|
bump.offset += size;
|
||||||
|
return ptr;
|
||||||
|
case .Reset;
|
||||||
|
bump.offset = 0;
|
||||||
|
|
||||||
|
case .Save;
|
||||||
|
return bump.offset.(*void);
|
||||||
|
case .Restore;
|
||||||
|
wm := old_ptr.(int);
|
||||||
|
if wm < 0 || wm >= bump.memory.count
|
||||||
|
{ Panic("jc: BumpArena restored invalid savepoint", loc = caller); }
|
||||||
|
bump.offset = wm;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} @jc.nodocs
|
||||||
|
|
||||||
|
PagingArenaProc :: (
|
||||||
|
mode: ArenaMode,
|
||||||
|
arena_data: *void,
|
||||||
|
|
||||||
|
new_size: int, old_size: int, align: int,
|
||||||
|
new_ptr: *void, old_ptr: *void,
|
||||||
|
|
||||||
|
caller: Source_Code_Location
|
||||||
|
) -> *void {
|
||||||
|
Panic("not implemented for PagingArena", loc = caller);
|
||||||
|
return null;
|
||||||
|
} @jc.nodocs
|
||||||
|
|
||||||
|
// @note(judah): TempArenaProc just wraps BumpArenaProc, but provides its own backing memory.
|
||||||
|
// This is needed so we can use TempArenaProc without forcing the user to set everything up.
|
||||||
|
TempArenaProc :: (
|
||||||
|
mode: ArenaMode,
|
||||||
|
arena_data: *void,
|
||||||
|
|
||||||
|
new_size: int, old_size: int, align: int,
|
||||||
|
new_ptr: *void, old_ptr: *void,
|
||||||
|
|
||||||
|
caller: Source_Code_Location
|
||||||
|
) -> *void {
|
||||||
|
// @temp(judah)
|
||||||
|
libc :: #library,system "libc";
|
||||||
|
malloc :: (size: int) -> *void #c_call #foreign libc;
|
||||||
|
free :: (ptr: *void) #c_call #foreign libc;
|
||||||
|
|
||||||
|
if mode == {
|
||||||
|
// @note(judah): allows the temp arena to initialize itself without relying on another arena to provide its memory.
|
||||||
|
case .Setup;
|
||||||
|
mcount := AlignForward(size_of(BumpData) + 32768).(int);
|
||||||
|
memory := malloc(mcount).(*u8);
|
||||||
|
MemZero(memory, mcount);
|
||||||
|
|
||||||
|
bump := memory.(*BumpData);
|
||||||
|
bump.memory.data = memory + size_of(BumpData);
|
||||||
|
bump.memory.count = mcount - size_of(BumpData);
|
||||||
|
|
||||||
|
return bump;
|
||||||
|
|
||||||
|
case .Teardown;
|
||||||
|
free(arena_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
return BumpArenaProc(mode, arena_data, new_size, old_size, align, new_ptr, old_ptr, caller);
|
||||||
|
} @jc.nodocs
|
||||||
|
|
||||||
|
|
||||||
|
#scope_file
|
||||||
|
|
||||||
|
JaiAllocatorProc :: (mode: Allocator_Mode, requested_size: s64, old_size: s64, old_memory: *void, allocator_data: *void) -> *void {
|
||||||
|
CallerFromStackTrace :: () -> Source_Code_Location #expand {
|
||||||
|
node := context.stack_trace;
|
||||||
|
if node.next != null {
|
||||||
|
node = node.next;
|
||||||
|
}
|
||||||
|
return node.info.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
arena := allocator_data.(*Arena);
|
||||||
|
if mode == {
|
||||||
|
case .STARTUP;
|
||||||
|
TrySetupArena(arena);
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case .ALLOCATE;
|
||||||
|
loc := CallerFromStackTrace();
|
||||||
|
return ArenaPushSize(requested_size, loc = loc,, arena = arena);
|
||||||
|
|
||||||
|
case .RESIZE;
|
||||||
|
loc := CallerFromStackTrace();
|
||||||
|
new := ArenaPushSize(requested_size, loc = loc,, arena = arena);
|
||||||
|
MemCopy(new, old_memory, old_size);
|
||||||
|
return new;
|
||||||
|
|
||||||
|
case .FREE;
|
||||||
|
// Arenas don't free single pointers so give this a value that's obviously incorrect
|
||||||
|
MemOverwrite(old_memory, old_size, 0xAA);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} @jc.nodocs
|
||||||
|
|
||||||
|
|
@ -1,28 +1,25 @@
|
||||||
/// Append pushes value to the end of an array, resizing if necessary.
|
/// ArrayAppend pushes values to the end of an array, resizing if necessary.
|
||||||
|
/// A pointer to the first value appended will be returned.
|
||||||
///
|
///
|
||||||
/// Note: If no allocator has been set, Append will use the current context allocator.
|
/// Note: If no allocator has been set, ArrayAppend will use the current context allocator.
|
||||||
/// Note: Calls to Append may invalidate pre-existing pointers.
|
/// Note: Calls to Append may invalidate pre-existing pointers.
|
||||||
Append :: inline (arr: *[..]$T, value: T) -> *T {
|
ArrayAppend :: inline (arr: *[..]$T, values: ..T) -> *T {
|
||||||
TrySetAllocator(arr);
|
TrySetAllocator(arr);
|
||||||
ptr := basic.array_add(arr);
|
|
||||||
ptr.* = value;
|
if values.count == 0 {
|
||||||
return ptr;
|
return basic.array_add(arr);
|
||||||
|
}
|
||||||
|
|
||||||
|
count := arr.count;
|
||||||
|
basic.array_add(arr, ..values);
|
||||||
|
return *arr.data[count];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Append pushes a default initialized value to the end of an array,
|
/// ArrayGrow resizes the memory associated with an array to hold new_count values.
|
||||||
/// returning a pointer to the newly pushed value.
|
|
||||||
///
|
///
|
||||||
/// Note: If no allocator has been set, Append will use the current context allocator.
|
/// Note: If the array has enough allocated memory to accomodate new_count, ArrayGrow does nothing.
|
||||||
/// Note: Calls to Append may invalidate pre-existing pointers.
|
/// Note: ArrayGrow does not guarantee pointer stability.
|
||||||
Append :: inline (arr: *[..]$T) -> *T {
|
ArrayGrow :: inline (arr: *[..]$T, new_count: int) {
|
||||||
return Append(arr, default_of(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resize grows the memory associated with an array to hold new_count values.
|
|
||||||
///
|
|
||||||
/// Note: If the array has enough allocated memory to accomodate new_count, Resize does nothing.
|
|
||||||
/// Note: Resize does not guarantee pointer stability.
|
|
||||||
Resize :: inline (arr: *[..]$T, new_count: int) {
|
|
||||||
if new_count <= arr.allocated
|
if new_count <= arr.allocated
|
||||||
{ return; }
|
{ return; }
|
||||||
|
|
||||||
|
|
@ -30,32 +27,32 @@ Resize :: inline (arr: *[..]$T, new_count: int) {
|
||||||
basic.array_reserve(arr, new_count,, allocator = arr.allocator);
|
basic.array_reserve(arr, new_count,, allocator = arr.allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Slice returns a subsection of an array.
|
/// ArraySlice returns a subsection of an array.
|
||||||
Slice :: (view: []$T, start_idx: int, count := -1, loc := #caller_location) -> []T {
|
ArraySlice :: (arr: []$T, start_idx: int, count := -1, loc := #caller_location) -> []T {
|
||||||
AssertCallsite(start_idx >= +0 && start_idx < view.count, "jc: incorrect slice bounds");
|
AssertCallsite(start_idx >= +0 && start_idx < arr.count, "jc: incorrect slice bounds");
|
||||||
AssertCallsite(count >= -1 && count < view.count, "jc: incorrect slice length");
|
AssertCallsite(count >= -1 && count < arr.count, "jc: incorrect slice length");
|
||||||
|
|
||||||
if count == -1
|
if count == -1
|
||||||
{ count = view.count - start_idx; }
|
{ count = arr.count - start_idx; }
|
||||||
|
|
||||||
return .{ data = view.data + start_idx, count = count };
|
return .{ data = arr.data + start_idx, count = count };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset sets an array's length to 0, but leaves its memory intact.
|
/// ArrayReset sets an array's length to 0, but leaves its memory intact.
|
||||||
/// Note: To reset the associated memory as well, see Clear.
|
/// Note: To reset the associated memory as well, see ArrayClear.
|
||||||
Reset :: (view: *[]$T) {
|
ArrayReset :: (arr: *[]$T) {
|
||||||
view.count = 0;
|
arr.count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear zeroes an array's memory and sets its length to 0.
|
/// ArrayClear zeroes an array's memory and sets its length to 0.
|
||||||
/// Note: To leave the associated memory intact, see Reset.
|
/// Note: To leave the associated memory intact, see ArrayReset.
|
||||||
Clear :: (view: *[]$T) {
|
ArrayClear :: (arr: *[]$T) {
|
||||||
MemZero(view.data, view.count * size_of(T));
|
MemZero(arr.data, arr.count * size_of(T));
|
||||||
view.count = 0;
|
arr.count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Equal checks the equality of two arrays.
|
/// ArrayEquals checks equality between two arrays.
|
||||||
Equal :: (lhs: []$T, rhs: []T) -> bool {
|
ArrayEquals :: (lhs: []$T, rhs: []T) -> bool {
|
||||||
if lhs.count != rhs.count
|
if lhs.count != rhs.count
|
||||||
{ return false; }
|
{ return false; }
|
||||||
return MemEqual(lhs.data, rhs.data, lhs.count * size_of(T));
|
return MemEqual(lhs.data, rhs.data, lhs.count * size_of(T));
|
||||||
|
|
@ -73,23 +70,21 @@ CheckBounds :: ($$index: $T, $$count: T, loc := #caller_location) #expand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayIndex :: struct(T: Type) {
|
||||||
FindFlags :: enum_flags {
|
|
||||||
Last; // Return the last matching element.
|
|
||||||
FromEnd; // Search in reverse.
|
|
||||||
}
|
|
||||||
|
|
||||||
FindResult :: struct(T: Type) {
|
|
||||||
value: T = ---;
|
value: T = ---;
|
||||||
index: int;
|
index: int;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find searches through an array, returning the first element
|
ArrayFindFlags :: enum_flags {
|
||||||
/// that matches the given value.
|
Last; // Return the last matching element.
|
||||||
Find :: (view: []$T, value: T, $flags: FindFlags = 0) -> (bool, FindResult(T)) {
|
FromEnd; // Search in reverse.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// ArrayFind searches through an array, returning the first element that matches the given value.
|
||||||
|
ArrayFind :: (view: []$T, value: T, $flags: ArrayFindFlags = 0) -> (bool, ArrayIndex(T)) {
|
||||||
found: bool;
|
found: bool;
|
||||||
|
|
||||||
result: FindResult(T);
|
result: ArrayIndex(T);
|
||||||
result.index = -1;
|
result.index = -1;
|
||||||
|
|
||||||
REVERSE :: #run (flags & .FromEnd).(bool);
|
REVERSE :: #run (flags & .FromEnd).(bool);
|
||||||
|
|
@ -103,21 +98,21 @@ Find :: (view: []$T, value: T, $flags: FindFlags = 0) -> (bool, FindResult(T)) {
|
||||||
return found, result;
|
return found, result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains checks if the given value exists in an array.
|
/// ArrayContains checks if the given value exists in an array.
|
||||||
Contains :: (view: []$T, value: T) -> bool {
|
ArrayContains :: (view: []$T, value: T) -> bool {
|
||||||
return Find(view, value);
|
return ArrayFind(view, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
TrimFlags :: enum_flags {
|
ArrayTrimFlags :: enum_flags {
|
||||||
FromStart; // Trim the start of the array.
|
FromStart; // ArrayTrim the start of the array.
|
||||||
FromEnd; // Trim the end of the array.
|
FromEnd; // ArrayTrim the end of the array.
|
||||||
MatchInFull; // Only trim when the cutset matches exactly.
|
MatchInFull; // Only trim when the cutset matches exactly.
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trim returns a subsection of an array with all leading/trailing values
|
/// ArrayTrim returns a subsection of an array with all leading/trailing values
|
||||||
/// from cutset removed.
|
/// from cutset removed.
|
||||||
Trim :: (view: []$T, cutset: []T, $flags: TrimFlags = .FromStart) -> []T {
|
ArrayTrim :: (view: []$T, cutset: []T, $flags: ArrayTrimFlags = .FromStart) -> []T {
|
||||||
result := view;
|
result := view;
|
||||||
if cutset.count == 0 || cutset.count > view.count {
|
if cutset.count == 0 || cutset.count > view.count {
|
||||||
return result;
|
return result;
|
||||||
|
|
@ -125,13 +120,13 @@ Trim :: (view: []$T, cutset: []T, $flags: TrimFlags = .FromStart) -> []T {
|
||||||
|
|
||||||
#if flags & .FromStart {
|
#if flags & .FromStart {
|
||||||
#if flags & .MatchInFull {
|
#if flags & .MatchInFull {
|
||||||
if Equal(Slice(view, 0, cutset.count), cutset) {
|
if ArrayEquals(ArraySlice(view, 0, cutset.count), cutset) {
|
||||||
result = Slice(view, cutset.count, -1);
|
result = ArraySlice(view, cutset.count, -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
while result.count > 0 {
|
while result.count > 0 {
|
||||||
if !Contains(cutset, result[0]) {
|
if !ArrayContains(cutset, result[0]) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,13 +138,13 @@ Trim :: (view: []$T, cutset: []T, $flags: TrimFlags = .FromStart) -> []T {
|
||||||
|
|
||||||
#if flags & .FromEnd {
|
#if flags & .FromEnd {
|
||||||
#if flags & .MatchInFull {
|
#if flags & .MatchInFull {
|
||||||
if Equal(Slice(view, view.count - cutset.count), cutset) {
|
if ArrayEquals(ArraySlice(view, view.count - cutset.count), cutset) {
|
||||||
result.count -= cutset.count;
|
result.count -= cutset.count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
while result.count > 0 {
|
while result.count > 0 {
|
||||||
if !Contains(cutset, result[result.count - 1]) {
|
if !ArrayContains(cutset, result[result.count - 1]) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -169,82 +164,82 @@ basic :: #import "Basic"; // @future
|
||||||
#if RunTests #run,stallable {
|
#if RunTests #run,stallable {
|
||||||
Test("slice", t => {
|
Test("slice", t => {
|
||||||
a1 := int.[ 1, 2, 3, 4, 5 ];
|
a1 := int.[ 1, 2, 3, 4, 5 ];
|
||||||
a2 := Slice(a1, 2);
|
a2 := ArraySlice(a1, 2);
|
||||||
Expect(a2.count == 3);
|
Expect(a2.count == 3);
|
||||||
Expect(Equal(a2, int.[ 3, 4, 5 ]));
|
Expect(ArrayEquals(a2, int.[ 3, 4, 5 ]));
|
||||||
|
|
||||||
b1 := int.[ 1, 2, 3, 4, 5 ];
|
b1 := int.[ 1, 2, 3, 4, 5 ];
|
||||||
b2 := Slice(b1, 2, 0);
|
b2 := ArraySlice(b1, 2, 0);
|
||||||
Expect(b2.count == 0);
|
Expect(b2.count == 0);
|
||||||
Expect(b2.data == b1.data + 2);
|
Expect(b2.data == b1.data + 2);
|
||||||
|
|
||||||
c1 := int.[ 1, 2, 3, 4, 5 ];
|
c1 := int.[ 1, 2, 3, 4, 5 ];
|
||||||
c2 := Slice(c1, 3, 1);
|
c2 := ArraySlice(c1, 3, 1);
|
||||||
Expect(c2.count == 1);
|
Expect(c2.count == 1);
|
||||||
Expect(Equal(c2, int.[ 4 ]));
|
Expect(ArrayEquals(c2, int.[ 4 ]));
|
||||||
|
|
||||||
d1 := int.[ 1, 2, 3 ];
|
d1 := int.[ 1, 2, 3 ];
|
||||||
d2 := Slice(d1, 2);
|
d2 := ArraySlice(d1, 2);
|
||||||
Expect(d2.count == 1);
|
Expect(d2.count == 1);
|
||||||
Expect(Equal(d2, int.[ 3 ]));
|
Expect(ArrayEquals(d2, int.[ 3 ]));
|
||||||
});
|
});
|
||||||
|
|
||||||
Test("find", t => {
|
Test("find", t => {
|
||||||
a := int.[ 1, 2, 3, 4, 5 ];
|
a := int.[ 1, 2, 3, 4, 5 ];
|
||||||
|
|
||||||
ok, res := Find(a, 3);
|
ok, res := ArrayFind(a, 3);
|
||||||
Expect(ok && res.index == 2);
|
Expect(ok && res.index == 2);
|
||||||
Expect(res.value == 3);
|
Expect(res.value == 3);
|
||||||
|
|
||||||
ok, res = Find(a, -1);
|
ok, res = ArrayFind(a, -1);
|
||||||
Expect(!ok && res.index == -1);
|
Expect(!ok && res.index == -1);
|
||||||
|
|
||||||
b := int.[ 1, 2, 2, 3, 4, 5, 2 ];
|
b := int.[ 1, 2, 2, 3, 4, 5, 2 ];
|
||||||
|
|
||||||
ok, res = Find(b, 2);
|
ok, res = ArrayFind(b, 2);
|
||||||
Expect(ok && res.index == 1);
|
Expect(ok && res.index == 1);
|
||||||
|
|
||||||
ok, res = Find(b, 2, .FromEnd);
|
ok, res = ArrayFind(b, 2, .FromEnd);
|
||||||
Expect(ok && res.index == 6);
|
Expect(ok && res.index == 6);
|
||||||
|
|
||||||
c := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
c := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
||||||
|
|
||||||
ok, res = Find(c, 0, .Last);
|
ok, res = ArrayFind(c, 0, .Last);
|
||||||
Expect(ok && res.index == 8);
|
Expect(ok && res.index == 8);
|
||||||
|
|
||||||
ok, res = Find(c, 0, .FromEnd | .Last);
|
ok, res = ArrayFind(c, 0, .FromEnd | .Last);
|
||||||
Expect(ok && res.index == 0);
|
Expect(ok && res.index == 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
Test("contains", t => {
|
Test("contains", t => {
|
||||||
a := int.[ 1, 2, 3, 4, 5 ];
|
a := int.[ 1, 2, 3, 4, 5 ];
|
||||||
Expect(Contains(a, 3));
|
Expect(ArrayContains(a, 3));
|
||||||
Expect(!Contains(a, -1));
|
Expect(!ArrayContains(a, -1));
|
||||||
});
|
});
|
||||||
|
|
||||||
Test("trim", t => {
|
Test("trim", t => {
|
||||||
a1 := int.[ 0, 0, 0, 1, 2, 3 ];
|
a1 := int.[ 0, 0, 0, 1, 2, 3 ];
|
||||||
a2 := Trim(a1, .[ 0 ]);
|
a2 := ArrayTrim(a1, .[ 0 ]);
|
||||||
Expect(Equal(a1, .[ 0, 0, 0, 1, 2, 3 ]));
|
Expect(ArrayEquals(a1, .[ 0, 0, 0, 1, 2, 3 ]));
|
||||||
Expect(Equal(a2, .[ 1, 2, 3 ]));
|
Expect(ArrayEquals(a2, .[ 1, 2, 3 ]));
|
||||||
|
|
||||||
b1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
b1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
||||||
b2 := Trim(b1, .[ 0 ], .FromEnd);
|
b2 := ArrayTrim(b1, .[ 0 ], .FromEnd);
|
||||||
Expect(Equal(b1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
|
Expect(ArrayEquals(b1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
|
||||||
Expect(Equal(b2, .[ 0, 0, 0, 1, 2, 3 ]));
|
Expect(ArrayEquals(b2, .[ 0, 0, 0, 1, 2, 3 ]));
|
||||||
|
|
||||||
c1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
c1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
||||||
c2 := Trim(c1, .[ 0 ], .FromStart | .FromEnd);
|
c2 := ArrayTrim(c1, .[ 0 ], .FromStart | .FromEnd);
|
||||||
Expect(Equal(c1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
|
Expect(ArrayEquals(c1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
|
||||||
Expect(Equal(c2, .[ 1, 2, 3 ]));
|
Expect(ArrayEquals(c2, .[ 1, 2, 3 ]));
|
||||||
|
|
||||||
d1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
d1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
||||||
d2 := Trim(d1, .[ 0, 0, 0 ], .FromStart | .MatchInFull);
|
d2 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromStart | .MatchInFull);
|
||||||
d3 := Trim(d1, .[ 0, 0, 0 ], .FromEnd | .MatchInFull);
|
d3 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromEnd | .MatchInFull);
|
||||||
d4 := Trim(d1, .[ 0, 0, 0 ], .FromStart | .FromEnd | .MatchInFull);
|
d4 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromStart | .FromEnd | .MatchInFull);
|
||||||
Expect(Equal(d1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
|
Expect(ArrayEquals(d1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
|
||||||
Expect(Equal(d2, .[ 1, 2, 3, 0, 0, 0 ]));
|
Expect(ArrayEquals(d2, .[ 1, 2, 3, 0, 0, 0 ]));
|
||||||
Expect(Equal(d3, .[ 0, 0, 0, 1, 2, 3 ]));
|
Expect(ArrayEquals(d3, .[ 0, 0, 0, 1, 2, 3 ]));
|
||||||
Expect(Equal(d4, .[ 1, 2, 3 ]));
|
Expect(ArrayEquals(d4, .[ 1, 2, 3 ]));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,6 +64,13 @@ b16 :: enum u16 { false_ :: (0 != 0).(u16); true_ :: (0 == 0).(u16); }; /// b16
|
||||||
b32 :: enum u32 { false_ :: (0 != 0).(u32); true_ :: (0 == 0).(u32); }; /// b32 is a 32-bit boolean.
|
b32 :: enum u32 { false_ :: (0 != 0).(u32); true_ :: (0 == 0).(u32); }; /// b32 is a 32-bit boolean.
|
||||||
b64 :: enum u64 { false_ :: (0 != 0).(u64); true_ :: (0 == 0).(u64); }; /// b64 is a 64-bit boolean.
|
b64 :: enum u64 { false_ :: (0 != 0).(u64); true_ :: (0 == 0).(u64); }; /// b64 is a 64-bit boolean.
|
||||||
|
|
||||||
|
#if size_of(int) == {
|
||||||
|
case 8; uint :: u64;
|
||||||
|
case 4; uint :: u32;
|
||||||
|
case 2; uint :: u16;
|
||||||
|
case 1; uint :: u8;
|
||||||
|
}
|
||||||
|
|
||||||
/// Panic displays the given message and crashes the program.
|
/// Panic displays the given message and crashes the program.
|
||||||
///
|
///
|
||||||
/// Note: Defers will not run when Panic is called.
|
/// Note: Defers will not run when Panic is called.
|
||||||
|
|
@ -74,6 +81,11 @@ Panic :: (message := "jc: runtime panic", loc := #caller_location) #expand #no_d
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteStderrString(message, "\n");
|
WriteStderrString(message, "\n");
|
||||||
|
|
||||||
|
#if DebugBuild {
|
||||||
|
PrintStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
Trap();
|
Trap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,7 +101,12 @@ Unreachable :: (message := "jc: unreachable code hit", loc := #caller_location)
|
||||||
}
|
}
|
||||||
|
|
||||||
WriteStderrString(message, "\n");
|
WriteStderrString(message, "\n");
|
||||||
trap();
|
|
||||||
|
#if DebugBuild {
|
||||||
|
PrintStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
Trap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// CompileError displays the given message and stops compilation.
|
/// CompileError displays the given message and stops compilation.
|
||||||
|
|
@ -179,3 +196,22 @@ DebugBuild :: #run -> bool {
|
||||||
opts := get_build_options();
|
opts := get_build_options();
|
||||||
return opts.emit_debug_info != .NONE;
|
return opts.emit_debug_info != .NONE;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PrintStackTrace :: () {
|
||||||
|
WriteStderrString("\n----- Stack Trace -----\n");
|
||||||
|
|
||||||
|
node := context.stack_trace.next;
|
||||||
|
while node != null {
|
||||||
|
loc := node.info.location;
|
||||||
|
loc.line_number = node.line_number;
|
||||||
|
WriteStderrLocation(loc);
|
||||||
|
|
||||||
|
name := node.info.name;
|
||||||
|
if name.count <= 0
|
||||||
|
{ name = "(unknown)"; }
|
||||||
|
WriteStderrString(": in ", name, "\n");
|
||||||
|
|
||||||
|
if node.next != null
|
||||||
|
{ node = node.next; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,7 @@
|
||||||
///
|
///
|
||||||
offset_of :: ($T: Type, ident: Code, loc := #caller_location) -> int #expand {
|
offset_of :: ($T: Type, ident: Code, loc := #caller_location) -> int #expand {
|
||||||
#run (loc: Source_Code_Location) {
|
#run (loc: Source_Code_Location) {
|
||||||
info := type_info(T);
|
if !TypeIsStruct(T) {
|
||||||
if info.type != .STRUCT {
|
|
||||||
CompileError("jc: offset_of can only be used on struct types", loc = loc);
|
CompileError("jc: offset_of can only be used on struct types", loc = loc);
|
||||||
}
|
}
|
||||||
}(loc);
|
}(loc);
|
||||||
|
|
@ -29,14 +28,17 @@ offset_of :: ($T: Type, ident: Code, loc := #caller_location) -> int #expand {
|
||||||
offset_of :: (#discard value: $T, ident: Code, loc := #caller_location) -> int #expand {
|
offset_of :: (#discard value: $T, ident: Code, loc := #caller_location) -> int #expand {
|
||||||
type :: #run -> Type {
|
type :: #run -> Type {
|
||||||
info := T.(*Type_Info);
|
info := T.(*Type_Info);
|
||||||
if info.type == .POINTER {
|
|
||||||
info = info.(*Type_Info_Pointer).pointer_to;
|
ok, pinfo := TypeIsPointer(T);
|
||||||
|
if ok {
|
||||||
// @question(judah): do we want it to traverse all the way up to a non-pointer type?
|
// @question(judah): do we want it to traverse all the way up to a non-pointer type?
|
||||||
// I opted against because if you have a *T, you only want offset_of to get an offset
|
// I opted against because if you have a *T, you only want offset_of to get an offset
|
||||||
// from that pointer. What would you do with a field offset from **T?
|
// from that pointer. What would you do with a field offset from **T?
|
||||||
if info.type == .POINTER {
|
if TypeIsPointer(pinfo.pointer_to) {
|
||||||
CompileError("jc: offset_of only allows one level of pointer indirection.", loc = loc);
|
CompileError("jc: offset_of only allows one level of pointer indirection.", loc = loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info = pinfo.pointer_to;
|
||||||
}
|
}
|
||||||
|
|
||||||
return get_type(info);
|
return get_type(info);
|
||||||
|
|
@ -50,9 +52,7 @@ align_of :: ($T: Type) -> int #expand {
|
||||||
return #run -> int {
|
return #run -> int {
|
||||||
if size_of(T) == 0
|
if size_of(T) == 0
|
||||||
{ return 0; }
|
{ return 0; }
|
||||||
|
return offset_of(struct{ _: u8; t: T; }, #code t);
|
||||||
info := type_info(struct{ p: u8; t: T; });
|
|
||||||
return info.members[1].offset_in_bytes.(int);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,46 +192,9 @@ range_of :: ($T: Type, loc := #caller_location) -> (T, T) #expand {
|
||||||
return min_of(T, loc = loc), max_of(T, loc = loc);
|
return min_of(T, loc = loc), max_of(T, loc = loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// sector creates a named block that can exit early via the 'break' keyword.
|
|
||||||
///
|
|
||||||
/// Note: The block created by sector is called 'early' by default.
|
|
||||||
///
|
|
||||||
/// for sector() {
|
|
||||||
/// break;
|
|
||||||
/// break early; // automatically created
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// for sector("render_player") {
|
|
||||||
/// break render_player;
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
sector :: ($name := Sector().Name) -> Sector(name) #expand { return .{}; }
|
|
||||||
|
|
||||||
// @note(judah): there seems to be a weird race condition in the compiler
|
|
||||||
// that causes this to hit a null reference check error if running at compile-time.
|
|
||||||
for_expansion :: (v: Sector, code: Code, _: For_Flags) #expand {
|
|
||||||
// @todo(judah): fix this case?
|
|
||||||
// 'for this: sector() { break early; break this; }'
|
|
||||||
// both names valid here!
|
|
||||||
#insert #run basic.tprint(#string END
|
|
||||||
for `%1: 0..0 {
|
|
||||||
`it :: #run zero_of(void);
|
|
||||||
`it_index :: #run zero_of(void);
|
|
||||||
#insert,scope(code) code;
|
|
||||||
}
|
|
||||||
END,
|
|
||||||
// @note(judah): guards against calling this_block with
|
|
||||||
// an empty string which results in weird error messages.
|
|
||||||
ifx v.Name.count != 0 v.Name else Sector().Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#scope_file
|
#scope_file
|
||||||
|
|
||||||
Sector :: struct(Name: string = "early") {}
|
|
||||||
|
|
||||||
basic :: #import "Basic"; // @future
|
|
||||||
|
|
||||||
#if RunTests #run {
|
#if RunTests #run {
|
||||||
Test("min_of/max_of:enums", t => {
|
Test("min_of/max_of:enums", t => {
|
||||||
U8Enum :: enum u8 { lo :: -1; hi :: +1; }
|
U8Enum :: enum u8 { lo :: -1; hi :: +1; }
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// Dead simple key-value pair type (aka. hash table or hash map)
|
// Dead simple key-value pair type (aka. hash table or hash map)
|
||||||
Record :: struct(Key: Type, Value: Type) {
|
Map :: struct(Key: Type, Value: Type) {
|
||||||
allocator: Allocator;
|
allocator: Allocator;
|
||||||
slots: [..]Slot;
|
slots: [..]Slot;
|
||||||
free_slots: [..]int;
|
free_slots: [..]int;
|
||||||
|
|
@ -16,12 +16,7 @@ Record :: struct(Key: Type, Value: Type) {
|
||||||
AllocatedItemsAtStart :: 16;
|
AllocatedItemsAtStart :: 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @note(judah): Not sure if I like these names, but I want to give them a try
|
MapGet :: (r: *Map, key: r.Key) -> r.Value, bool {
|
||||||
Fetch :: Get;
|
|
||||||
Update :: Set;
|
|
||||||
Exists :: Has;
|
|
||||||
|
|
||||||
Get :: (r: *Record, key: r.Key) -> r.Value, bool {
|
|
||||||
slot, ok := FindSlot(r, GetHash(r, key));
|
slot, ok := FindSlot(r, GetHash(r, key));
|
||||||
if !ok
|
if !ok
|
||||||
{ return zero_of(r.Value), false; }
|
{ return zero_of(r.Value), false; }
|
||||||
|
|
@ -29,7 +24,7 @@ Get :: (r: *Record, key: r.Key) -> r.Value, bool {
|
||||||
return slot.value, true;
|
return slot.value, true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Set :: (r: *Record, key: r.Key, value: r.Value) {
|
MapSet :: (r: *Map, key: r.Key, value: r.Value) {
|
||||||
hash := GetHash(r, key);
|
hash := GetHash(r, key);
|
||||||
slot, exists := FindSlot(r, hash);
|
slot, exists := FindSlot(r, hash);
|
||||||
if !exists {
|
if !exists {
|
||||||
|
|
@ -41,12 +36,12 @@ Set :: (r: *Record, key: r.Key, value: r.Value) {
|
||||||
slot.value = value;
|
slot.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Has :: (r: *Record, key: r.Key) -> bool {
|
MapHas :: (r: *Map, key: r.Key) -> bool {
|
||||||
_, exists := FindSlot(r, GetHash(r, key));
|
_, exists := FindSlot(r, GetHash(r, key));
|
||||||
return exists;
|
return exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
Remove :: (r: *Record, key: r.Key) -> bool, r.Value {
|
MapRemove :: (r: *Map, key: r.Key) -> bool, r.Value {
|
||||||
slot, ok, idx := FindSlot(r, GetHash(r, key));
|
slot, ok, idx := FindSlot(r, GetHash(r, key));
|
||||||
if !ok
|
if !ok
|
||||||
{ return false, zero_of(r.Value); }
|
{ return false, zero_of(r.Value); }
|
||||||
|
|
@ -57,13 +52,13 @@ Remove :: (r: *Record, key: r.Key) -> bool, r.Value {
|
||||||
return true, last_value;
|
return true, last_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Reset :: (t: *Record) {
|
MapReset :: (t: *Map) {
|
||||||
t.count = 0;
|
t.count = 0;
|
||||||
t.slots.count = 0;
|
t.slots.count = 0;
|
||||||
t.free_slots.count = 0;
|
t.free_slots.count = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for_expansion :: (r: *Record, body: Code, flags: For_Flags) #expand {
|
for_expansion :: (r: *Map, body: Code, flags: For_Flags) #expand {
|
||||||
#assert (flags & .POINTER == 0) "cannot iterate by pointer";
|
#assert (flags & .POINTER == 0) "cannot iterate by pointer";
|
||||||
for <=(flags & .REVERSE == .REVERSE) slot: r.slots if slot.hash != r.InvalidHash {
|
for <=(flags & .REVERSE == .REVERSE) slot: r.slots if slot.hash != r.InvalidHash {
|
||||||
`it := slot.value;
|
`it := slot.value;
|
||||||
|
|
@ -73,15 +68,15 @@ for_expansion :: (r: *Record, body: Code, flags: For_Flags) #expand {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#scope_file;
|
#scope_file
|
||||||
|
|
||||||
GetHash :: inline (r: *Record, key: r.Key) -> u32 {
|
GetHash :: inline (r: *Map, key: r.Key) -> u32 {
|
||||||
hash := r.HashProc(key);
|
hash := r.HashProc(key);
|
||||||
Assert(hash != r.InvalidHash, "key collided with invalid hash");
|
Assert(hash != r.InvalidHash, "key collided with invalid hash");
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
FindSlot :: (r: *Record, hash: u32) -> *r.Slot, bool, int {
|
FindSlot :: (r: *Map, hash: u32) -> *r.Slot, bool, int {
|
||||||
for * r.slots if it.hash == hash {
|
for * r.slots if it.hash == hash {
|
||||||
return it, true, it_index;
|
return it, true, it_index;
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +84,7 @@ FindSlot :: (r: *Record, hash: u32) -> *r.Slot, bool, int {
|
||||||
return null, false, -1;
|
return null, false, -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateOrReuseSlot :: (r: *Record) -> *r.Slot {
|
CreateOrReuseSlot :: (r: *Map) -> *r.Slot {
|
||||||
inline LazyInit(r);
|
inline LazyInit(r);
|
||||||
|
|
||||||
if r.free_slots.count > 0 {
|
if r.free_slots.count > 0 {
|
||||||
|
|
@ -99,27 +94,27 @@ CreateOrReuseSlot :: (r: *Record) -> *r.Slot {
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.slots.allocated == 0 {
|
if r.slots.allocated == 0 {
|
||||||
Resize(*r.slots, r.AllocatedItemsAtStart);
|
ArrayGrow(*r.slots, r.AllocatedItemsAtStart);
|
||||||
}
|
}
|
||||||
else if r.slots.count >= r.slots.allocated {
|
else if r.slots.count >= r.slots.allocated {
|
||||||
Resize(*r.slots, NextPowerOfTwo(r.slots.allocated));
|
ArrayGrow(*r.slots, NextPowerOfTwo(r.slots.allocated));
|
||||||
}
|
}
|
||||||
|
|
||||||
slot := Append(*r.slots);
|
slot := ArrayAppend(*r.slots);
|
||||||
r.count = r.slots.count;
|
r.count = r.slots.count;
|
||||||
return slot;
|
return slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
MarkSlotForReuse :: (t: *Record, index: int) {
|
MarkSlotForReuse :: (t: *Map, index: int) {
|
||||||
inline LazyInit(t);
|
inline LazyInit(t);
|
||||||
|
|
||||||
t.count -= 1;
|
t.count -= 1;
|
||||||
t.slots[index] = .{ hash = t.InvalidHash };
|
t.slots[index] = .{ hash = t.InvalidHash };
|
||||||
|
|
||||||
Append(*t.free_slots, index);
|
ArrayAppend(*t.free_slots, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyInit :: inline (t: *Record) {
|
LazyInit :: inline (t: *Map) {
|
||||||
TrySetAllocator(t);
|
TrySetAllocator(t);
|
||||||
TrySetAllocator(*t.slots);
|
TrySetAllocator(*t.slots);
|
||||||
TrySetAllocator(*t.free_slots);
|
TrySetAllocator(*t.free_slots);
|
||||||
|
|
@ -127,52 +122,52 @@ LazyInit :: inline (t: *Record) {
|
||||||
|
|
||||||
|
|
||||||
#if RunTests #run {
|
#if RunTests #run {
|
||||||
Test("kv:basic operations", t => {
|
Test("map:basic operations", t => {
|
||||||
ITERATIONS :: 64;
|
ITERATIONS :: 64;
|
||||||
|
|
||||||
values: Record(int, int);
|
values: Map(int, int);
|
||||||
for 0..ITERATIONS {
|
for 0..ITERATIONS {
|
||||||
Set(*values, it, it * it);
|
MapSet(*values, it, it * it);
|
||||||
}
|
}
|
||||||
|
|
||||||
for 0..ITERATIONS {
|
for 0..ITERATIONS {
|
||||||
v, ok := Get(*values, it);
|
v, ok := MapGet(*values, it);
|
||||||
Expect(v == it * it);
|
Expect(v == it * it);
|
||||||
}
|
}
|
||||||
|
|
||||||
for 0..ITERATIONS if it % 2 == 0 {
|
for 0..ITERATIONS if it % 2 == 0 {
|
||||||
ok := Remove(*values, it);
|
ok := MapRemove(*values, it);
|
||||||
Expect(ok);
|
Expect(ok);
|
||||||
}
|
}
|
||||||
|
|
||||||
for 0..ITERATIONS if it % 2 == 0 {
|
for 0..ITERATIONS if it % 2 == 0 {
|
||||||
_, ok := Get(*values, it);
|
_, ok := MapGet(*values, it);
|
||||||
Expect(!ok);
|
Expect(!ok);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Test("kv:free slots", t => {
|
Test("map:free slots", t => {
|
||||||
values: Record(int, int);
|
values: Map(int, int);
|
||||||
|
|
||||||
Set(*values, 1, 100);
|
MapSet(*values, 1, 100);
|
||||||
Set(*values, 2, 200);
|
MapSet(*values, 2, 200);
|
||||||
Set(*values, 3, 300);
|
MapSet(*values, 3, 300);
|
||||||
Expect(values.count == 3);
|
Expect(values.count == 3);
|
||||||
Expect(values.slots.allocated == values.AllocatedItemsAtStart);
|
Expect(values.slots.allocated == values.AllocatedItemsAtStart);
|
||||||
|
|
||||||
// deleting something that doesn't exist should do nothing
|
// deleting something that doesn't exist should do nothing
|
||||||
ok := Remove(*values, 0);
|
ok := MapRemove(*values, 0);
|
||||||
Expect(!ok);
|
Expect(!ok);
|
||||||
Expect(values.count == 3);
|
Expect(values.count == 3);
|
||||||
|
|
||||||
Remove(*values, 2);
|
MapRemove(*values, 2);
|
||||||
Expect(values.count == 2);
|
Expect(values.count == 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
Test("kv:iteration", t => {
|
Test("map:iteration", t => {
|
||||||
values: Record(int, int);
|
values: Map(int, int);
|
||||||
|
|
||||||
for 0..10 Set(*values, it, it * it);
|
for 0..10 MapSet(*values, it, it * it);
|
||||||
Expect(values.count == 11);
|
Expect(values.count == 11);
|
||||||
|
|
||||||
for v, k: values Expect(v == k * k);
|
for v, k: values Expect(v == k * k);
|
||||||
|
|
@ -2,7 +2,7 @@ Kilobyte :: 1024;
|
||||||
Megabyte :: 1024 * Kilobyte;
|
Megabyte :: 1024 * Kilobyte;
|
||||||
Gigabyte :: 1024 * Megabyte;
|
Gigabyte :: 1024 * Megabyte;
|
||||||
|
|
||||||
DefaultAlign :: #run 2 * align_of(*void);
|
DefaultAlign :: size_of(*void);
|
||||||
|
|
||||||
/// MemEqual checks the equality of two pieces of memory.
|
/// MemEqual checks the equality of two pieces of memory.
|
||||||
///
|
///
|
||||||
|
|
@ -60,22 +60,40 @@ MemReset :: (p: *$T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AlignUpwards :: (ptr: int, align: int = DefaultAlign) -> int {
|
MemAligned :: (p: *void, align: uint = DefaultAlign) -> bool {
|
||||||
Assert(PowerOfTwo(align), "alignment must be a power of two");
|
return Aligned(p.(uint), align);
|
||||||
|
|
||||||
p := ptr;
|
|
||||||
mod := p & (align - 1);
|
|
||||||
if mod != 0 then p += align - mod;
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PowerOfTwo :: (x: int) -> bool {
|
MemAlignForward :: (p: *void, align: uint = DefaultAlign) -> *void {
|
||||||
|
return AlignForward(p.(uint), align).(*void);
|
||||||
|
}
|
||||||
|
|
||||||
|
MemAlignBackward :: (p: *void, align: uint = DefaultAlign) -> *void {
|
||||||
|
return AlignBackward(p.(uint), align).(*void);
|
||||||
|
}
|
||||||
|
|
||||||
|
Aligned :: (a: uint, align: uint = DefaultAlign) -> bool {
|
||||||
|
return (a & (align - 1)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignForward :: (a: uint, align: uint = DefaultAlign) -> uint {
|
||||||
|
Assert(PowerOfTwo(align), "jc: must be a power of two");
|
||||||
|
return (a + align - 1) & ~(align - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
AlignBackward :: (a: uint, align: uint = DefaultAlign) -> uint {
|
||||||
|
Assert(PowerOfTwo(align), "jc: must be a power of two");
|
||||||
|
return a & ~(align - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PowerOfTwo :: (x: uint) -> bool {
|
||||||
if x == 0 return false;
|
if x == 0 return false;
|
||||||
return x & (x - 1) == 0;
|
return x & (x - 1) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
NextPowerOfTwo :: (x: int) -> int #no_aoc {
|
NextPowerOfTwo :: (x: uint) -> uint #no_aoc {
|
||||||
Assert(PowerOfTwo(x), "value must be a power of two");
|
Assert(PowerOfTwo(x), "jc: must be a power of two");
|
||||||
|
|
||||||
// Bit twiddling hacks next power of two
|
// Bit twiddling hacks next power of two
|
||||||
x |= x >> 1;
|
x |= x >> 1;
|
||||||
|
|
@ -87,3 +105,28 @@ NextPowerOfTwo :: (x: int) -> int #no_aoc {
|
||||||
|
|
||||||
return x + 1;
|
return x + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TrySetAllocator :: (thing: *$T, allocator := context.allocator) #modify {
|
||||||
|
info := T.(*Type_Info_Struct);
|
||||||
|
|
||||||
|
ok := false;
|
||||||
|
if info.type == .STRUCT
|
||||||
|
{ ok = true; }
|
||||||
|
|
||||||
|
if ok for info.members if it.name == "allocator" && it.type == Allocator.(*Type_Info) {
|
||||||
|
ok = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok, "can only set allocator on struct with an allocator field or dynamic array";
|
||||||
|
} #expand {
|
||||||
|
if thing.allocator.proc == null {
|
||||||
|
thing.allocator = allocator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TrySetAllocator :: (array: *[..]$T, allocator := context.allocator) #expand {
|
||||||
|
if array.allocator.proc == null {
|
||||||
|
array.allocator = allocator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,33 +27,7 @@
|
||||||
///
|
///
|
||||||
Test :: (name: string, proc: (t: *void) -> (), loc := #caller_location) {
|
Test :: (name: string, proc: (t: *void) -> (), loc := #caller_location) {
|
||||||
// @note(judah): incredibly dumb way to get nicer test runs
|
// @note(judah): incredibly dumb way to get nicer test runs
|
||||||
path := loc.fully_pathed_filename;
|
WriteString(SmallerPath(loc.fully_pathed_filename), ",");
|
||||||
|
|
||||||
i := path.count - 1;
|
|
||||||
found_first_slash := false;
|
|
||||||
|
|
||||||
while i >= 0 {
|
|
||||||
if path[i] == "/" {
|
|
||||||
if found_first_slash {
|
|
||||||
i += 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
found_first_slash = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
i -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !found_first_slash {
|
|
||||||
path = strings.path_filename(loc.fully_pathed_filename);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
path.count -= i;
|
|
||||||
path.data += i;
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteString(path, ",");
|
|
||||||
WriteNumber(loc.line_number);
|
WriteNumber(loc.line_number);
|
||||||
WriteString(": ", name, "... ");
|
WriteString(": ", name, "... ");
|
||||||
|
|
||||||
|
|
@ -111,6 +85,35 @@ TestRun :: struct {
|
||||||
failed: bool;
|
failed: bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SmallerPath :: (path: string) -> string {
|
||||||
|
p := path;
|
||||||
|
i := p.count - 1;
|
||||||
|
found_first_slash := false;
|
||||||
|
|
||||||
|
while i >= 0 {
|
||||||
|
if p[i] == "/" {
|
||||||
|
if found_first_slash {
|
||||||
|
i += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
found_first_slash = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
i -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found_first_slash {
|
||||||
|
p = strings.path_filename(path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
p.count -= i;
|
||||||
|
p.data += i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
basic :: #import "Basic"; // @future
|
basic :: #import "Basic"; // @future
|
||||||
strings :: #import "String"; // @future
|
strings :: #import "String"; // @future
|
||||||
compiler :: #import "Compiler"; // @future
|
compiler :: #import "Compiler"; // @future
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// @todo for jesse these should be PascalCase but I didn't want to give you an annoying merge conflict
|
// @todo for jesse these should be PascalCase but I didn't want to give you an annoying merge conflict
|
||||||
|
|
||||||
check_type_tag :: ($$T: Type, tag: Type_Info_Tag) -> bool, *Type_Info {
|
TypeTagIs :: ($$T: Type, tag: Type_Info_Tag) -> (bool, *Type_Info) {
|
||||||
#if is_constant(T) {
|
#if is_constant(T) {
|
||||||
info :: type_info(T);
|
info :: type_info(T);
|
||||||
if info.type == tag return true, info;
|
if info.type == tag return true, info;
|
||||||
|
|
@ -13,31 +13,36 @@ check_type_tag :: ($$T: Type, tag: Type_Info_Tag) -> bool, *Type_Info {
|
||||||
return false, null;
|
return false, null;
|
||||||
}
|
}
|
||||||
|
|
||||||
type_is_integer :: ($$T: Type) -> bool, *Type_Info_Integer {
|
TypeIsInteger :: ($$T: Type) -> (bool, *Type_Info_Integer) {
|
||||||
ok, info := check_type_tag(T, .INTEGER);
|
ok, info := TypeTagIs(T, .INTEGER);
|
||||||
return ok, info.(*Type_Info_Integer);
|
return ok, info.(*Type_Info_Integer);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_is_float :: ($$T: Type) -> bool, *Type_Info_Float {
|
TypeIsFloat :: ($$T: Type) -> (bool, *Type_Info_Float) {
|
||||||
ok, info := check_type_tag(T, .FLOAT);
|
ok, info := TypeTagIs(T, .FLOAT);
|
||||||
return ok, info.(*Type_Info_Float);
|
return ok, info.(*Type_Info_Float);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_is_scalar :: (t: Type) -> bool {
|
TypeIsScalar :: ($$T: Type) -> bool {
|
||||||
return type_is_integer(t) || type_is_float(t);
|
return TypeIsInteger(T) || TypeIsFloat(T);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_is_array :: ($$T: Type) -> bool, *Type_Info_Array {
|
TypeIsArray :: ($$T: Type) -> (bool, *Type_Info_Array) {
|
||||||
ok, info := check_type_tag(T, .ARRAY);
|
ok, info := TypeTagIs(T, .ARRAY);
|
||||||
return ok, info.(*Type_Info_Array);
|
return ok, info.(*Type_Info_Array);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_is_struct :: ($$T: Type) -> bool, *Type_Info_Struct {
|
TypeIsStruct :: ($$T: Type) -> (bool, *Type_Info_Struct) {
|
||||||
ok, info := check_type_tag(T, .STRUCT);
|
ok, info := TypeTagIs(T, .STRUCT);
|
||||||
return ok, info.(*Type_Info_Struct);
|
return ok, info.(*Type_Info_Struct);
|
||||||
}
|
}
|
||||||
|
|
||||||
type_is_enum :: ($$T: Type) -> bool, *Type_Info_Enum {
|
TypeIsEnum :: ($$T: Type) -> (bool, *Type_Info_Enum) {
|
||||||
ok, info := check_type_tag(T, .ENUM);
|
ok, info := TypeTagIs(T, .ENUM);
|
||||||
return ok, info.(*Type_Info_Enum);
|
return ok, info.(*Type_Info_Enum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TypeIsPointer :: ($$T: Type) -> (bool, *Type_Info_Pointer) {
|
||||||
|
ok, info := TypeTagIs(T, .POINTER);
|
||||||
|
return ok, info.(*Type_Info_Pointer);
|
||||||
|
}
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,3 +2,4 @@
|
||||||
**.dSYM
|
**.dSYM
|
||||||
.DS_Store
|
.DS_Store
|
||||||
docs/
|
docs/
|
||||||
|
*.scratch.*
|
||||||
|
|
|
||||||
17
PLAN.Judah
17
PLAN.Judah
|
|
@ -9,15 +9,28 @@ This is what I'm doing day-to-day.
|
||||||
: notes, todos, etc.
|
: notes, todos, etc.
|
||||||
|
|
||||||
|
|
||||||
|
/ 09.07.25
|
||||||
|
|
||||||
|
+ replaced allocators with custom arenas
|
||||||
|
+ Panic/Unreachable prints stack trace in debug builds
|
||||||
|
+ type_info names are now pascal case
|
||||||
|
|
||||||
|
: not sure how [..]T should interface with arenas
|
||||||
|
|
||||||
|
|
||||||
/ 09.06.25
|
/ 09.06.25
|
||||||
|
|
||||||
+ fixed doc generator
|
+ fixed doc generator
|
||||||
+ encoding is now fmt
|
+ encoding is now fmt
|
||||||
+ math tests pass after reorg
|
+ math tests pass after reorg
|
||||||
+ docs look nicer
|
+ docs look nicer
|
||||||
|
+ talked to Jesse about various things
|
||||||
|
+ name pass to add prefixes
|
||||||
|
+ Record is Map now
|
||||||
|
+ removed sector because it's not good (just use 'for 0..0' or 'while true { defer break; ... }')
|
||||||
|
|
||||||
: need to reimplement dynamic arrays
|
: need to reimplement dynamic arrays
|
||||||
: stable arrays too?
|
< stable arrays too?
|
||||||
|
|
||||||
|
|
||||||
/ 09.05.25
|
/ 09.05.25
|
||||||
|
|
@ -35,7 +48,7 @@ This is what I'm doing day-to-day.
|
||||||
+ error messages now start with 'jc:'
|
+ error messages now start with 'jc:'
|
||||||
+ decided to start using John Carmack-styled plan files to keep track of what I'm working on. would be cool to document the repo as it changes over time.
|
+ decided to start using John Carmack-styled plan files to keep track of what I'm working on. would be cool to document the repo as it changes over time.
|
||||||
|
|
||||||
: planning out how I want allocators to work. I want to go big into arenas, but don't want to completely abandon Jai's builtin allocators
|
> planning out how I want allocators to work. I want to go big into arenas, but don't want to completely abandon Jai's builtin allocators
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,6 @@
|
||||||
#module_parameters(RunTests := false);
|
#module_parameters(RunTests := false);
|
||||||
|
|
||||||
Base64Encode :: (str: string, $for_url := false) -> string, bool {
|
Base64Encode :: (data: []u8, $for_url := false) -> []u8, bool {
|
||||||
enc, ok := Base64Encode(str.([]u8), for_url);
|
|
||||||
return enc.(string), ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
Base64Decode :: (str: string) -> string, bool {
|
|
||||||
enc, ok := Base64Decode(str.([]u8));
|
|
||||||
return enc.(string), ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
Base64Encode :: ($$data: []u8, $for_url := false) -> []u8, bool {
|
|
||||||
if data.count == 0 return .[], false;
|
if data.count == 0 return .[], false;
|
||||||
|
|
||||||
#if for_url {
|
#if for_url {
|
||||||
|
|
@ -144,6 +134,16 @@ Base64Decode :: (data: []u8) -> []u8, bool {
|
||||||
return decoded, true;
|
return decoded, true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Base64Encode :: (str: string, $for_url := false) -> string, bool {
|
||||||
|
enc, ok := Base64Encode(str.([]u8), for_url);
|
||||||
|
return enc.(string), ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
Base64Decode :: (str: string) -> string, bool {
|
||||||
|
enc, ok := Base64Decode(str.([]u8));
|
||||||
|
return enc.(string), ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#scope_file
|
#scope_file
|
||||||
|
|
||||||
|
|
@ -162,10 +162,6 @@ basic :: #import "Basic"; // @future
|
||||||
strings :: #import "String"; // @future
|
strings :: #import "String"; // @future
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
// TESTS
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
|
|
||||||
#if RunTests #run {
|
#if RunTests #run {
|
||||||
Test("encodes", t => {
|
Test("encodes", t => {
|
||||||
str :: "Hello, World";
|
str :: "Hello, World";
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ min :: (x: float, y: float) -> float {
|
||||||
}
|
}
|
||||||
|
|
||||||
abs :: (v: $T) -> T
|
abs :: (v: $T) -> T
|
||||||
#modify { return type_is_scalar(T), "Only accepting scalar types, integer and float"; } {
|
#modify { return TypeIsScalar(T), "Only accepting scalar types, integer and float"; } {
|
||||||
return ifx v < 0 then -v else v;
|
return ifx v < 0 then -v else v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ Transition :: enum {
|
||||||
@Note: Progress must be between 0.0 and 1.0
|
@Note: Progress must be between 0.0 and 1.0
|
||||||
*/
|
*/
|
||||||
ease :: (progress: $T, $$ease: Ease = .linear, $$transition: Transition = .in) -> T
|
ease :: (progress: $T, $$ease: Ease = .linear, $$transition: Transition = .in) -> T
|
||||||
#modify { return type_is_float(T); }
|
#modify { return TypeIsFloat(T); }
|
||||||
{
|
{
|
||||||
p := progress;
|
p := progress;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
RUN_TESTS := false
|
RUN_TESTS := false
|
||||||
);
|
);
|
||||||
|
|
||||||
#assert type_is_scalar(RECT_TYPE);
|
#assert TypeIsScalar(RECT_TYPE);
|
||||||
|
|
||||||
#load "common.jai";
|
#load "common.jai";
|
||||||
#load "vec.jai";
|
#load "vec.jai";
|
||||||
|
|
|
||||||
24
math/vec.jai
24
math/vec.jai
|
|
@ -187,7 +187,7 @@ operator / :: inline (l: Vec, r: Vec(l.N, l.T)) -> Vec(l.N, l.T) #no_abc {
|
||||||
}
|
}
|
||||||
|
|
||||||
operator + :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric
|
operator + :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric
|
||||||
#modify { return type_is_scalar(R), "type is not integer or float"; } {
|
#modify { return TypeIsScalar(R), "type is not integer or float"; } {
|
||||||
res: Vec(l.N, l.T) = ---;
|
res: Vec(l.N, l.T) = ---;
|
||||||
#if l.N <= 4 {
|
#if l.N <= 4 {
|
||||||
res.x = l.x + r;
|
res.x = l.x + r;
|
||||||
|
|
@ -202,7 +202,7 @@ operator + :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric
|
||||||
}
|
}
|
||||||
|
|
||||||
operator - :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc
|
operator - :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc
|
||||||
#modify { return type_is_scalar(R), "type is not integer or float"; } {
|
#modify { return TypeIsScalar(R), "type is not integer or float"; } {
|
||||||
res: Vec(l.N, l.T) = ---;
|
res: Vec(l.N, l.T) = ---;
|
||||||
#if l.N <= 4 {
|
#if l.N <= 4 {
|
||||||
res.x = l.x - r;
|
res.x = l.x - r;
|
||||||
|
|
@ -216,7 +216,7 @@ operator - :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
operator - :: inline (l: $R, r: Vec) -> Vec(l.N, l.T) #no_abc
|
operator - :: inline (l: $R, r: Vec) -> Vec(l.N, l.T) #no_abc
|
||||||
#modify { return type_is_scalar(R), "type is not integer or float"; } {
|
#modify { return TypeIsScalar(R), "type is not integer or float"; } {
|
||||||
res: Vec(l.N, l.T) = ---;
|
res: Vec(l.N, l.T) = ---;
|
||||||
#if l.N <= 4 {
|
#if l.N <= 4 {
|
||||||
res.x = l - r.x;
|
res.x = l - r.x;
|
||||||
|
|
@ -245,7 +245,7 @@ operator- :: inline(v: Vec) -> Vec(v.N, v.T) #no_abc {
|
||||||
}
|
}
|
||||||
|
|
||||||
operator * :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric
|
operator * :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric
|
||||||
#modify { return type_is_scalar(R), "type is not integer or float"; } {
|
#modify { return TypeIsScalar(R), "type is not integer or float"; } {
|
||||||
res: Vec(l.N, l.T) = ---;
|
res: Vec(l.N, l.T) = ---;
|
||||||
#if l.N <= 4 {
|
#if l.N <= 4 {
|
||||||
res.x = l.x*r;
|
res.x = l.x*r;
|
||||||
|
|
@ -260,7 +260,7 @@ operator * :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc #symmetric
|
||||||
}
|
}
|
||||||
|
|
||||||
operator / :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc
|
operator / :: inline (l: Vec, r: $R) -> Vec(l.N, l.T) #no_abc
|
||||||
#modify { return type_is_scalar(R), "type is not integer or float"; } {
|
#modify { return TypeIsScalar(R), "type is not integer or float"; } {
|
||||||
res: Vec(l.N, l.T) = ---;
|
res: Vec(l.N, l.T) = ---;
|
||||||
#if l.N <= 4 {
|
#if l.N <= 4 {
|
||||||
res.x = l.x/r;
|
res.x = l.x/r;
|
||||||
|
|
@ -486,7 +486,7 @@ reflect :: (v: Vec2, p: Vec2) -> Vec2 #no_abc {
|
||||||
}
|
}
|
||||||
|
|
||||||
round :: (v: Vec($N, $T)) -> Vec(N, T) #no_abc
|
round :: (v: Vec($N, $T)) -> Vec(N, T) #no_abc
|
||||||
#modify { return type_is_float(T), "Used non-float vector on round"; } {
|
#modify { return TypeIsFloat(T), "Used non-float vector on round"; } {
|
||||||
r: Vec(N, T) = ---;
|
r: Vec(N, T) = ---;
|
||||||
n := N - 1;
|
n := N - 1;
|
||||||
while n >= 0 {
|
while n >= 0 {
|
||||||
|
|
@ -507,31 +507,31 @@ Vec4 :: Vec(4, float);
|
||||||
Quat :: #type,distinct Vec4; // Note(Jesse): I Had to make this distinct, otherwise operators stomp on eachother
|
Quat :: #type,distinct Vec4; // Note(Jesse): I Had to make this distinct, otherwise operators stomp on eachother
|
||||||
|
|
||||||
v2f :: (x: $T = 0, y: T = 0) -> Vec2
|
v2f :: (x: $T = 0, y: T = 0) -> Vec2
|
||||||
#modify { return type_is_float(T), "use v2i for integer arguments"; }
|
#modify { return TypeIsFloat(T), "use v2i for integer arguments"; }
|
||||||
#expand {
|
#expand {
|
||||||
return .{ x = x, y = y };
|
return .{ x = x, y = y };
|
||||||
}
|
}
|
||||||
|
|
||||||
v2i :: (x: $T = 0, y: T = 0) -> Vec(2, T)
|
v2i :: (x: $T = 0, y: T = 0) -> Vec(2, T)
|
||||||
#modify { return type_is_integer(T), "use v2f for float arguments"; }
|
#modify { return TypeIsInteger(T), "use v2f for float arguments"; }
|
||||||
#expand {
|
#expand {
|
||||||
return .{ x = x, y = y };
|
return .{ x = x, y = y };
|
||||||
}
|
}
|
||||||
|
|
||||||
v3f :: (x: $T = 0, y: T = 0, z: T = 0) -> Vec3
|
v3f :: (x: $T = 0, y: T = 0, z: T = 0) -> Vec3
|
||||||
#modify { return type_is_float(T), "use v3i for integer arguments"; }
|
#modify { return TypeIsFloat(T), "use v3i for integer arguments"; }
|
||||||
#expand {
|
#expand {
|
||||||
return .{ x = x, y = y, z = z };
|
return .{ x = x, y = y, z = z };
|
||||||
}
|
}
|
||||||
|
|
||||||
v3i :: (x: $T = 0, y: T = 0, z: T = 0) -> Vec(3, T)
|
v3i :: (x: $T = 0, y: T = 0, z: T = 0) -> Vec(3, T)
|
||||||
#modify { return type_is_integer(T), "use v3f for float arguments"; }
|
#modify { return TypeIsInteger(T), "use v3f for float arguments"; }
|
||||||
#expand {
|
#expand {
|
||||||
return .{ x = x, y = y, z = z };
|
return .{ x = x, y = y, z = z };
|
||||||
}
|
}
|
||||||
|
|
||||||
v4f :: (x: $T = 0, y: T = 0, z: T = 0, w: T = 0) -> Vec4
|
v4f :: (x: $T = 0, y: T = 0, z: T = 0, w: T = 0) -> Vec4
|
||||||
#modify { return type_is_float(T), "use v4i for integer arguments"; }
|
#modify { return TypeIsFloat(T), "use v4i for integer arguments"; }
|
||||||
#expand {
|
#expand {
|
||||||
return .{ x = x, y = y, z = z, w = w };
|
return .{ x = x, y = y, z = z, w = w };
|
||||||
}
|
}
|
||||||
|
|
@ -541,7 +541,7 @@ v4f :: (v: Vec3, $$w: float) -> Vec4 #expand {
|
||||||
}
|
}
|
||||||
|
|
||||||
v4i :: (x: $T = 0, y: T = 0, z: T = 0, w: T = 0) -> Vec(4, T)
|
v4i :: (x: $T = 0, y: T = 0, z: T = 0, w: T = 0) -> Vec(4, T)
|
||||||
#modify { return type_is_integer(T), "use v4f for float arguments"; }
|
#modify { return TypeIsInteger(T), "use v4f for float arguments"; }
|
||||||
#expand {
|
#expand {
|
||||||
return .{ x = x, y = y, z = z, w = w };
|
return .{ x = x, y = y, z = z, w = w };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,12 @@
|
||||||
/// system.
|
/// system.
|
||||||
#module_parameters(RunTests := false);
|
#module_parameters(RunTests := false);
|
||||||
|
|
||||||
// @note(judah): this causes some very weird issues in tests so it's ifdef'd out
|
|
||||||
#if !RunTests #add_context temp_allocator: Allocator;
|
|
||||||
|
|
||||||
#load "+internal/builtin.jai";
|
#load "+internal/builtin.jai";
|
||||||
#load "+internal/array.jai";
|
#load "+internal/array.jai";
|
||||||
#load "+internal/kv.jai";
|
#load "+internal/map.jai";
|
||||||
#load "+internal/hashing.jai";
|
#load "+internal/hashing.jai";
|
||||||
#load "+internal/memory.jai";
|
#load "+internal/memory.jai";
|
||||||
#load "+internal/allocators.jai";
|
#load "+internal/arenas.jai";
|
||||||
#load "+internal/testing.jai";
|
#load "+internal/testing.jai";
|
||||||
#load "+internal/keywords.jai";
|
#load "+internal/keywords.jai";
|
||||||
#load "+internal/type_info.jai";
|
#load "+internal/type_info.jai";
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue