Compare commits
No commits in common. "5f27b720ea6d9c5d79157319db8a4a61170709e8" and "872689bb421a1ff4877876fbecbf51ba34693f16" have entirely different histories.
5f27b720ea
...
872689bb42
9 changed files with 232 additions and 235 deletions
|
|
@ -1,14 +1,16 @@
|
||||||
|
// @todo(judah): Conditionally added to the context because it causes some weirdness in tests
|
||||||
|
#if !RunTests {
|
||||||
#add_context arena := Arena.{ proc = PagingArenaProc };
|
#add_context arena := Arena.{ proc = PagingArenaProc };
|
||||||
#add_context temp_arena := Arena.{ proc = TempArenaProc };
|
#add_context temp_arena := Arena.{ proc = TempArenaProc };
|
||||||
|
}
|
||||||
|
|
||||||
/// Arena is an interface for an arena-based memory allocation.
|
|
||||||
Arena :: struct {
|
Arena :: struct {
|
||||||
proc: ArenaProc;
|
proc: ArenaProc;
|
||||||
data: *void;
|
data: *void;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArenaProc :: #type (
|
ArenaProc :: #type (
|
||||||
event: ArenaEvent,
|
mode: ArenaMode,
|
||||||
arena_data: *void,
|
arena_data: *void,
|
||||||
|
|
||||||
new_size: int, old_size: int, align: int,
|
new_size: int, old_size: int, align: int,
|
||||||
|
|
@ -17,7 +19,7 @@ ArenaProc :: #type (
|
||||||
caller: Source_Code_Location
|
caller: Source_Code_Location
|
||||||
) -> *void;
|
) -> *void;
|
||||||
|
|
||||||
ArenaEvent :: enum {
|
ArenaMode :: enum {
|
||||||
Setup;
|
Setup;
|
||||||
Teardown;
|
Teardown;
|
||||||
|
|
||||||
|
|
@ -66,13 +68,13 @@ ArenaReset :: (loc := #caller_location) {
|
||||||
context.arena.proc(.Reset, context.arena.data, 0, 0, 0, null, null, loc);
|
context.arena.proc(.Reset, context.arena.data, 0, 0, 0, null, null, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ArenaRelease frees the current context.arena's memory.
|
/// ArenaRelease frees an arena's memory.
|
||||||
ArenaRelease :: (loc := #caller_location) {
|
ArenaRelease :: (loc := #caller_location) {
|
||||||
TrySetupArena(*context.arena, loc);
|
TrySetupArena(*context.arena, loc);
|
||||||
context.arena.proc(.Teardown, context.arena.data, 0, 0, 0, null, null, loc);
|
context.arena.proc(.Teardown, context.arena.data, 0, 0, 0, null, null, loc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ArenaToAllocator wraps the given arena, allowing it to be used with Jai's builtin Allocator system.
|
/// ArenaToAllocator wraps an arena, allowing it to be used with Jai's builtin Allocator system.
|
||||||
ArenaToAllocator :: (arena: *Arena) -> Allocator {
|
ArenaToAllocator :: (arena: *Arena) -> Allocator {
|
||||||
return .{ proc = JaiAllocatorProc, data = arena };
|
return .{ proc = JaiAllocatorProc, data = arena };
|
||||||
}
|
}
|
||||||
|
|
@ -105,10 +107,10 @@ TrySetupArena :: (arena: *Arena, loc := #caller_location) #expand {
|
||||||
data := arena.proc(.Setup, null, 0, 0, 0, null, null, loc);
|
data := arena.proc(.Setup, null, 0, 0, 0, null, null, loc);
|
||||||
if data != null
|
if data != null
|
||||||
{ arena.data = data; }
|
{ arena.data = data; }
|
||||||
} @jc.nodocs
|
}
|
||||||
|
|
||||||
PanicArenaProc :: (
|
PanicArenaProc :: (
|
||||||
event: ArenaEvent,
|
mode: ArenaMode,
|
||||||
arena_data: *void,
|
arena_data: *void,
|
||||||
|
|
||||||
new_size: int, old_size: int, align: int,
|
new_size: int, old_size: int, align: int,
|
||||||
|
|
@ -116,7 +118,7 @@ PanicArenaProc :: (
|
||||||
|
|
||||||
caller: Source_Code_Location
|
caller: Source_Code_Location
|
||||||
) -> *void {
|
) -> *void {
|
||||||
if event == {
|
if mode == {
|
||||||
case .Acquire;
|
case .Acquire;
|
||||||
if new_size > 0
|
if new_size > 0
|
||||||
{ Panic("jc: cannot acquire memory using the PanicArena", loc = caller); }
|
{ Panic("jc: cannot acquire memory using the PanicArena", loc = caller); }
|
||||||
|
|
@ -131,7 +133,7 @@ PanicArenaProc :: (
|
||||||
} @jc.nodocs
|
} @jc.nodocs
|
||||||
|
|
||||||
BumpArenaProc :: (
|
BumpArenaProc :: (
|
||||||
event: ArenaEvent,
|
mode: ArenaMode,
|
||||||
arena_data: *void,
|
arena_data: *void,
|
||||||
|
|
||||||
new_size: int, old_size: int, align: int,
|
new_size: int, old_size: int, align: int,
|
||||||
|
|
@ -140,7 +142,7 @@ BumpArenaProc :: (
|
||||||
caller: Source_Code_Location
|
caller: Source_Code_Location
|
||||||
) -> *void #no_abc {
|
) -> *void #no_abc {
|
||||||
bump := arena_data.(*BumpData);
|
bump := arena_data.(*BumpData);
|
||||||
if event == {
|
if mode == {
|
||||||
case .Setup;
|
case .Setup;
|
||||||
if bump.memory.data == null
|
if bump.memory.data == null
|
||||||
{ Panic("jc: BumpArena has no memory", loc = caller); }
|
{ Panic("jc: BumpArena has no memory", loc = caller); }
|
||||||
|
|
@ -177,7 +179,7 @@ BumpArenaProc :: (
|
||||||
} @jc.nodocs
|
} @jc.nodocs
|
||||||
|
|
||||||
PagingArenaProc :: (
|
PagingArenaProc :: (
|
||||||
event: ArenaEvent,
|
mode: ArenaMode,
|
||||||
arena_data: *void,
|
arena_data: *void,
|
||||||
|
|
||||||
new_size: int, old_size: int, align: int,
|
new_size: int, old_size: int, align: int,
|
||||||
|
|
@ -192,7 +194,7 @@ PagingArenaProc :: (
|
||||||
// @note(judah): TempArenaProc just wraps BumpArenaProc, but provides its own backing memory.
|
// @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.
|
// This is needed so we can use TempArenaProc without forcing the user to set everything up.
|
||||||
TempArenaProc :: (
|
TempArenaProc :: (
|
||||||
event: ArenaEvent,
|
mode: ArenaMode,
|
||||||
arena_data: *void,
|
arena_data: *void,
|
||||||
|
|
||||||
new_size: int, old_size: int, align: int,
|
new_size: int, old_size: int, align: int,
|
||||||
|
|
@ -205,7 +207,7 @@ TempArenaProc :: (
|
||||||
malloc :: (size: int) -> *void #c_call #foreign libc;
|
malloc :: (size: int) -> *void #c_call #foreign libc;
|
||||||
free :: (ptr: *void) #c_call #foreign libc;
|
free :: (ptr: *void) #c_call #foreign libc;
|
||||||
|
|
||||||
if event == {
|
if mode == {
|
||||||
// @note(judah): allows the temp arena to initialize itself without relying on another arena to provide its memory.
|
// @note(judah): allows the temp arena to initialize itself without relying on another arena to provide its memory.
|
||||||
case .Setup;
|
case .Setup;
|
||||||
mcount := AlignForward(size_of(BumpData) + 32768).(int);
|
mcount := AlignForward(size_of(BumpData) + 32768).(int);
|
||||||
|
|
@ -222,13 +224,13 @@ TempArenaProc :: (
|
||||||
free(arena_data);
|
free(arena_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return BumpArenaProc(event, arena_data, new_size, old_size, align, new_ptr, old_ptr, caller);
|
return BumpArenaProc(mode, arena_data, new_size, old_size, align, new_ptr, old_ptr, caller);
|
||||||
} @jc.nodocs
|
} @jc.nodocs
|
||||||
|
|
||||||
|
|
||||||
#scope_file
|
#scope_file
|
||||||
|
|
||||||
JaiAllocatorProc :: (event: Allocator_Mode, requested_size: s64, old_size: s64, old_memory: *void, allocator_data: *void) -> *void {
|
JaiAllocatorProc :: (mode: Allocator_Mode, requested_size: s64, old_size: s64, old_memory: *void, allocator_data: *void) -> *void {
|
||||||
CallerFromStackTrace :: () -> Source_Code_Location #expand {
|
CallerFromStackTrace :: () -> Source_Code_Location #expand {
|
||||||
node := context.stack_trace;
|
node := context.stack_trace;
|
||||||
if node.next != null {
|
if node.next != null {
|
||||||
|
|
@ -238,7 +240,7 @@ JaiAllocatorProc :: (event: Allocator_Mode, requested_size: s64, old_size: s64,
|
||||||
}
|
}
|
||||||
|
|
||||||
arena := allocator_data.(*Arena);
|
arena := allocator_data.(*Arena);
|
||||||
if event == {
|
if mode == {
|
||||||
case .STARTUP;
|
case .STARTUP;
|
||||||
TrySetupArena(arena);
|
TrySetupArena(arena);
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
/// Note: If no allocator has been set, ArrayAppend 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.
|
||||||
ArrayAppend :: inline (arr: *[..]$T, values: ..T) -> *T {
|
ArrayAppend :: inline (arr: *[..]$T, values: ..T) -> *T {
|
||||||
TrySetAllocator(arr,, allocator = ArenaToAllocator(*context.arena));
|
TrySetAllocator(arr);
|
||||||
|
|
||||||
if values.count == 0 {
|
if values.count == 0 {
|
||||||
return basic.array_add(arr);
|
return basic.array_add(arr);
|
||||||
|
|
@ -23,7 +23,7 @@ ArrayGrow :: inline (arr: *[..]$T, new_count: int) {
|
||||||
if new_count <= arr.allocated
|
if new_count <= arr.allocated
|
||||||
{ return; }
|
{ return; }
|
||||||
|
|
||||||
TrySetAllocator(arr,, allocator = ArenaToAllocator(*context.arena));
|
TrySetAllocator(arr);
|
||||||
basic.array_reserve(arr, new_count,, allocator = arr.allocator);
|
basic.array_reserve(arr, new_count,, allocator = arr.allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -58,6 +58,18 @@ ArrayEquals :: (lhs: []$T, rhs: []T) -> bool {
|
||||||
return MemEqual(lhs.data, rhs.data, lhs.count * size_of(T));
|
return MemEqual(lhs.data, rhs.data, lhs.count * size_of(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CheckBounds :: ($$index: $T, $$count: T, loc := #caller_location) #expand {
|
||||||
|
Message :: "bounds check failed!";
|
||||||
|
#if is_constant(index) && is_constant(count) {
|
||||||
|
if index < 0 || index >= count {
|
||||||
|
CompileError(Message, loc = loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if index < 0 || index > count {
|
||||||
|
Panic(Message, loc = loc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ArrayIndex :: struct(T: Type) {
|
ArrayIndex :: struct(T: Type) {
|
||||||
value: T = ---;
|
value: T = ---;
|
||||||
index: int;
|
index: int;
|
||||||
|
|
@ -145,19 +157,89 @@ ArrayTrim :: (view: []$T, cutset: []T, $flags: ArrayTrimFlags = .FromStart) -> [
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
CheckBounds :: ($$index: $T, $$count: T, loc := #caller_location) #expand {
|
|
||||||
Message :: "bounds check failed!";
|
|
||||||
#if is_constant(index) && is_constant(count) {
|
|
||||||
if index < 0 || index >= count {
|
|
||||||
CompileError(Message, loc = loc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if index < 0 || index > count {
|
|
||||||
Panic(Message, loc = loc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#scope_file
|
#scope_file
|
||||||
|
|
||||||
basic :: #import "Basic"; // @future
|
basic :: #import "Basic"; // @future
|
||||||
|
|
||||||
|
#if RunTests #run,stallable {
|
||||||
|
Test("slice", t => {
|
||||||
|
a1 := int.[ 1, 2, 3, 4, 5 ];
|
||||||
|
a2 := ArraySlice(a1, 2);
|
||||||
|
Expect(a2.count == 3);
|
||||||
|
Expect(ArrayEquals(a2, int.[ 3, 4, 5 ]));
|
||||||
|
|
||||||
|
b1 := int.[ 1, 2, 3, 4, 5 ];
|
||||||
|
b2 := ArraySlice(b1, 2, 0);
|
||||||
|
Expect(b2.count == 0);
|
||||||
|
Expect(b2.data == b1.data + 2);
|
||||||
|
|
||||||
|
c1 := int.[ 1, 2, 3, 4, 5 ];
|
||||||
|
c2 := ArraySlice(c1, 3, 1);
|
||||||
|
Expect(c2.count == 1);
|
||||||
|
Expect(ArrayEquals(c2, int.[ 4 ]));
|
||||||
|
|
||||||
|
d1 := int.[ 1, 2, 3 ];
|
||||||
|
d2 := ArraySlice(d1, 2);
|
||||||
|
Expect(d2.count == 1);
|
||||||
|
Expect(ArrayEquals(d2, int.[ 3 ]));
|
||||||
|
});
|
||||||
|
|
||||||
|
Test("find", t => {
|
||||||
|
a := int.[ 1, 2, 3, 4, 5 ];
|
||||||
|
|
||||||
|
ok, res := ArrayFind(a, 3);
|
||||||
|
Expect(ok && res.index == 2);
|
||||||
|
Expect(res.value == 3);
|
||||||
|
|
||||||
|
ok, res = ArrayFind(a, -1);
|
||||||
|
Expect(!ok && res.index == -1);
|
||||||
|
|
||||||
|
b := int.[ 1, 2, 2, 3, 4, 5, 2 ];
|
||||||
|
|
||||||
|
ok, res = ArrayFind(b, 2);
|
||||||
|
Expect(ok && res.index == 1);
|
||||||
|
|
||||||
|
ok, res = ArrayFind(b, 2, .FromEnd);
|
||||||
|
Expect(ok && res.index == 6);
|
||||||
|
|
||||||
|
c := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
||||||
|
|
||||||
|
ok, res = ArrayFind(c, 0, .Last);
|
||||||
|
Expect(ok && res.index == 8);
|
||||||
|
|
||||||
|
ok, res = ArrayFind(c, 0, .FromEnd | .Last);
|
||||||
|
Expect(ok && res.index == 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
Test("contains", t => {
|
||||||
|
a := int.[ 1, 2, 3, 4, 5 ];
|
||||||
|
Expect(ArrayContains(a, 3));
|
||||||
|
Expect(!ArrayContains(a, -1));
|
||||||
|
});
|
||||||
|
|
||||||
|
Test("trim", t => {
|
||||||
|
a1 := int.[ 0, 0, 0, 1, 2, 3 ];
|
||||||
|
a2 := ArrayTrim(a1, .[ 0 ]);
|
||||||
|
Expect(ArrayEquals(a1, .[ 0, 0, 0, 1, 2, 3 ]));
|
||||||
|
Expect(ArrayEquals(a2, .[ 1, 2, 3 ]));
|
||||||
|
|
||||||
|
b1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
||||||
|
b2 := ArrayTrim(b1, .[ 0 ], .FromEnd);
|
||||||
|
Expect(ArrayEquals(b1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
|
||||||
|
Expect(ArrayEquals(b2, .[ 0, 0, 0, 1, 2, 3 ]));
|
||||||
|
|
||||||
|
c1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
||||||
|
c2 := ArrayTrim(c1, .[ 0 ], .FromStart | .FromEnd);
|
||||||
|
Expect(ArrayEquals(c1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
|
||||||
|
Expect(ArrayEquals(c2, .[ 1, 2, 3 ]));
|
||||||
|
|
||||||
|
d1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
||||||
|
d2 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromStart | .MatchInFull);
|
||||||
|
d3 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromEnd | .MatchInFull);
|
||||||
|
d4 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromStart | .FromEnd | .MatchInFull);
|
||||||
|
Expect(ArrayEquals(d1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
|
||||||
|
Expect(ArrayEquals(d2, .[ 1, 2, 3, 0, 0, 0 ]));
|
||||||
|
Expect(ArrayEquals(d3, .[ 0, 0, 0, 1, 2, 3 ]));
|
||||||
|
Expect(ArrayEquals(d4, .[ 1, 2, 3 ]));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -192,3 +192,50 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#scope_file
|
||||||
|
|
||||||
|
#if RunTests #run {
|
||||||
|
Test("min_of/max_of:enums", t => {
|
||||||
|
U8Enum :: enum u8 { lo :: -1; hi :: +1; }
|
||||||
|
S8Enum :: enum s8 { lo :: -2; hi :: -1; }
|
||||||
|
{
|
||||||
|
Expect(min_of(U8Enum) == U8Enum.lo);
|
||||||
|
Expect(min_of(S8Enum) == S8Enum.lo);
|
||||||
|
Expect(max_of(U8Enum) == U8Enum.hi);
|
||||||
|
Expect(max_of(S8Enum) == S8Enum.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
U16Enum :: enum u16 { lo :: -1; hi :: +1; }
|
||||||
|
S16Enum :: enum s16 { lo :: -2; hi :: -1; }
|
||||||
|
{
|
||||||
|
Expect(min_of(U16Enum) == U16Enum.lo);
|
||||||
|
Expect(min_of(S16Enum) == S16Enum.lo);
|
||||||
|
Expect(max_of(U16Enum) == U16Enum.hi);
|
||||||
|
Expect(max_of(S16Enum) == S16Enum.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
U32Enum :: enum u32 { lo :: -1; hi :: +1; }
|
||||||
|
S32Enum :: enum s32 { lo :: -2; hi :: -1; }
|
||||||
|
{
|
||||||
|
Expect(min_of(U32Enum) == U32Enum.lo);
|
||||||
|
Expect(min_of(S32Enum) == S32Enum.lo);
|
||||||
|
Expect(max_of(U32Enum) == U32Enum.hi);
|
||||||
|
Expect(max_of(S32Enum) == S32Enum.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
U64Enum :: enum u64 { lo :: -1; hi :: +1; }
|
||||||
|
S64Enum :: enum s64 { lo :: -2; hi :: -1; }
|
||||||
|
{
|
||||||
|
Expect(min_of(U64Enum) == U64Enum.lo);
|
||||||
|
Expect(min_of(S64Enum) == S64Enum.lo);
|
||||||
|
Expect(max_of(U64Enum) == U64Enum.hi);
|
||||||
|
Expect(max_of(S64Enum) == S64Enum.hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @note(judah): just making sure this compiles
|
||||||
|
lo, hi := range_of(U64Enum);
|
||||||
|
Expect(lo == U64Enum.lo);
|
||||||
|
Expect(hi == U64Enum.hi);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -120,3 +120,57 @@ LazyInit :: inline (t: *Map) {
|
||||||
TrySetAllocator(*t.free_slots);
|
TrySetAllocator(*t.free_slots);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if RunTests #run {
|
||||||
|
Test("map:basic operations", t => {
|
||||||
|
ITERATIONS :: 64;
|
||||||
|
|
||||||
|
values: Map(int, int);
|
||||||
|
for 0..ITERATIONS {
|
||||||
|
MapSet(*values, it, it * it);
|
||||||
|
}
|
||||||
|
|
||||||
|
for 0..ITERATIONS {
|
||||||
|
v, ok := MapGet(*values, it);
|
||||||
|
Expect(v == it * it);
|
||||||
|
}
|
||||||
|
|
||||||
|
for 0..ITERATIONS if it % 2 == 0 {
|
||||||
|
ok := MapRemove(*values, it);
|
||||||
|
Expect(ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
for 0..ITERATIONS if it % 2 == 0 {
|
||||||
|
_, ok := MapGet(*values, it);
|
||||||
|
Expect(!ok);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Test("map:free slots", t => {
|
||||||
|
values: Map(int, int);
|
||||||
|
|
||||||
|
MapSet(*values, 1, 100);
|
||||||
|
MapSet(*values, 2, 200);
|
||||||
|
MapSet(*values, 3, 300);
|
||||||
|
Expect(values.count == 3);
|
||||||
|
Expect(values.slots.allocated == values.AllocatedItemsAtStart);
|
||||||
|
|
||||||
|
// deleting something that doesn't exist should do nothing
|
||||||
|
ok := MapRemove(*values, 0);
|
||||||
|
Expect(!ok);
|
||||||
|
Expect(values.count == 3);
|
||||||
|
|
||||||
|
MapRemove(*values, 2);
|
||||||
|
Expect(values.count == 2);
|
||||||
|
});
|
||||||
|
|
||||||
|
Test("map:iteration", t => {
|
||||||
|
values: Map(int, int);
|
||||||
|
|
||||||
|
for 0..10 MapSet(*values, it, it * it);
|
||||||
|
Expect(values.count == 11);
|
||||||
|
|
||||||
|
for v, k: values Expect(v == k * k);
|
||||||
|
for < v, k: values Expect(v == k * k);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,10 +106,7 @@ NextPowerOfTwo :: (x: int) -> int #no_aoc {
|
||||||
return x + 1;
|
return x + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TrySetAllocator :: (thing: *$T, allocator := context.allocator) #modify {
|
||||||
#scope_module
|
|
||||||
|
|
||||||
TrySetAllocator :: (thing: *$T) #modify {
|
|
||||||
info := T.(*Type_Info_Struct);
|
info := T.(*Type_Info_Struct);
|
||||||
|
|
||||||
ok := false;
|
ok := false;
|
||||||
|
|
@ -124,12 +121,12 @@ TrySetAllocator :: (thing: *$T) #modify {
|
||||||
return ok, "can only set allocator on struct with an allocator field or dynamic array";
|
return ok, "can only set allocator on struct with an allocator field or dynamic array";
|
||||||
} #expand {
|
} #expand {
|
||||||
if thing.allocator.proc == null {
|
if thing.allocator.proc == null {
|
||||||
thing.allocator = context.allocator;
|
thing.allocator = allocator;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} @jc.nodocs
|
|
||||||
|
|
||||||
TrySetAllocator :: (array: *[..]$T) #expand {
|
TrySetAllocator :: (array: *[..]$T, allocator := context.allocator) #expand {
|
||||||
if array.allocator.proc == null {
|
if array.allocator.proc == null {
|
||||||
array.allocator = context.allocator;
|
array.allocator = allocator;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} @jc.nodocs
|
|
||||||
|
|
|
||||||
|
|
@ -1,185 +0,0 @@
|
||||||
// @note(judah): these are put in a separate module to fix some weirdness with #module_parameters
|
|
||||||
// when used in the test runner.
|
|
||||||
|
|
||||||
#import "jc";
|
|
||||||
|
|
||||||
#run,stallable {
|
|
||||||
// arrays
|
|
||||||
Test("slice", t => {
|
|
||||||
a1 := int.[ 1, 2, 3, 4, 5 ];
|
|
||||||
a2 := ArraySlice(a1, 2);
|
|
||||||
Expect(a2.count == 3);
|
|
||||||
Expect(ArrayEquals(a2, int.[ 3, 4, 5 ]));
|
|
||||||
|
|
||||||
b1 := int.[ 1, 2, 3, 4, 5 ];
|
|
||||||
b2 := ArraySlice(b1, 2, 0);
|
|
||||||
Expect(b2.count == 0);
|
|
||||||
Expect(b2.data == b1.data + 2);
|
|
||||||
|
|
||||||
c1 := int.[ 1, 2, 3, 4, 5 ];
|
|
||||||
c2 := ArraySlice(c1, 3, 1);
|
|
||||||
Expect(c2.count == 1);
|
|
||||||
Expect(ArrayEquals(c2, int.[ 4 ]));
|
|
||||||
|
|
||||||
d1 := int.[ 1, 2, 3 ];
|
|
||||||
d2 := ArraySlice(d1, 2);
|
|
||||||
Expect(d2.count == 1);
|
|
||||||
Expect(ArrayEquals(d2, int.[ 3 ]));
|
|
||||||
});
|
|
||||||
|
|
||||||
Test("find", t => {
|
|
||||||
a := int.[ 1, 2, 3, 4, 5 ];
|
|
||||||
|
|
||||||
ok, res := ArrayFind(a, 3);
|
|
||||||
Expect(ok && res.index == 2);
|
|
||||||
Expect(res.value == 3);
|
|
||||||
|
|
||||||
ok, res = ArrayFind(a, -1);
|
|
||||||
Expect(!ok && res.index == -1);
|
|
||||||
|
|
||||||
b := int.[ 1, 2, 2, 3, 4, 5, 2 ];
|
|
||||||
|
|
||||||
ok, res = ArrayFind(b, 2);
|
|
||||||
Expect(ok && res.index == 1);
|
|
||||||
|
|
||||||
ok, res = ArrayFind(b, 2, .FromEnd);
|
|
||||||
Expect(ok && res.index == 6);
|
|
||||||
|
|
||||||
c := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
|
||||||
|
|
||||||
ok, res = ArrayFind(c, 0, .Last);
|
|
||||||
Expect(ok && res.index == 8);
|
|
||||||
|
|
||||||
ok, res = ArrayFind(c, 0, .FromEnd | .Last);
|
|
||||||
Expect(ok && res.index == 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
Test("contains", t => {
|
|
||||||
a := int.[ 1, 2, 3, 4, 5 ];
|
|
||||||
Expect(ArrayContains(a, 3));
|
|
||||||
Expect(!ArrayContains(a, -1));
|
|
||||||
});
|
|
||||||
|
|
||||||
Test("trim", t => {
|
|
||||||
a1 := int.[ 0, 0, 0, 1, 2, 3 ];
|
|
||||||
a2 := ArrayTrim(a1, .[ 0 ]);
|
|
||||||
Expect(ArrayEquals(a1, .[ 0, 0, 0, 1, 2, 3 ]));
|
|
||||||
Expect(ArrayEquals(a2, .[ 1, 2, 3 ]));
|
|
||||||
|
|
||||||
b1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
|
||||||
b2 := ArrayTrim(b1, .[ 0 ], .FromEnd);
|
|
||||||
Expect(ArrayEquals(b1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
|
|
||||||
Expect(ArrayEquals(b2, .[ 0, 0, 0, 1, 2, 3 ]));
|
|
||||||
|
|
||||||
c1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
|
||||||
c2 := ArrayTrim(c1, .[ 0 ], .FromStart | .FromEnd);
|
|
||||||
Expect(ArrayEquals(c1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
|
|
||||||
Expect(ArrayEquals(c2, .[ 1, 2, 3 ]));
|
|
||||||
|
|
||||||
d1 := int.[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ];
|
|
||||||
d2 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromStart | .MatchInFull);
|
|
||||||
d3 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromEnd | .MatchInFull);
|
|
||||||
d4 := ArrayTrim(d1, .[ 0, 0, 0 ], .FromStart | .FromEnd | .MatchInFull);
|
|
||||||
Expect(ArrayEquals(d1, .[ 0, 0, 0, 1, 2, 3, 0, 0, 0 ]));
|
|
||||||
Expect(ArrayEquals(d2, .[ 1, 2, 3, 0, 0, 0 ]));
|
|
||||||
Expect(ArrayEquals(d3, .[ 0, 0, 0, 1, 2, 3 ]));
|
|
||||||
Expect(ArrayEquals(d4, .[ 1, 2, 3 ]));
|
|
||||||
});
|
|
||||||
|
|
||||||
// type_info
|
|
||||||
Test("min_of/max_of:enums", t => {
|
|
||||||
U8Enum :: enum u8 { lo :: -1; hi :: +1; }
|
|
||||||
S8Enum :: enum s8 { lo :: -2; hi :: -1; }
|
|
||||||
{
|
|
||||||
Expect(min_of(U8Enum) == U8Enum.lo);
|
|
||||||
Expect(min_of(S8Enum) == S8Enum.lo);
|
|
||||||
Expect(max_of(U8Enum) == U8Enum.hi);
|
|
||||||
Expect(max_of(S8Enum) == S8Enum.hi);
|
|
||||||
}
|
|
||||||
|
|
||||||
U16Enum :: enum u16 { lo :: -1; hi :: +1; }
|
|
||||||
S16Enum :: enum s16 { lo :: -2; hi :: -1; }
|
|
||||||
{
|
|
||||||
Expect(min_of(U16Enum) == U16Enum.lo);
|
|
||||||
Expect(min_of(S16Enum) == S16Enum.lo);
|
|
||||||
Expect(max_of(U16Enum) == U16Enum.hi);
|
|
||||||
Expect(max_of(S16Enum) == S16Enum.hi);
|
|
||||||
}
|
|
||||||
|
|
||||||
U32Enum :: enum u32 { lo :: -1; hi :: +1; }
|
|
||||||
S32Enum :: enum s32 { lo :: -2; hi :: -1; }
|
|
||||||
{
|
|
||||||
Expect(min_of(U32Enum) == U32Enum.lo);
|
|
||||||
Expect(min_of(S32Enum) == S32Enum.lo);
|
|
||||||
Expect(max_of(U32Enum) == U32Enum.hi);
|
|
||||||
Expect(max_of(S32Enum) == S32Enum.hi);
|
|
||||||
}
|
|
||||||
|
|
||||||
U64Enum :: enum u64 { lo :: -1; hi :: +1; }
|
|
||||||
S64Enum :: enum s64 { lo :: -2; hi :: -1; }
|
|
||||||
{
|
|
||||||
Expect(min_of(U64Enum) == U64Enum.lo);
|
|
||||||
Expect(min_of(S64Enum) == S64Enum.lo);
|
|
||||||
Expect(max_of(U64Enum) == U64Enum.hi);
|
|
||||||
Expect(max_of(S64Enum) == S64Enum.hi);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @note(judah): just making sure this compiles
|
|
||||||
lo, hi := range_of(U64Enum);
|
|
||||||
Expect(lo == U64Enum.lo);
|
|
||||||
Expect(hi == U64Enum.hi);
|
|
||||||
});
|
|
||||||
|
|
||||||
// map
|
|
||||||
Test("map:basic operations", t => {
|
|
||||||
ITERATIONS :: 64;
|
|
||||||
|
|
||||||
values: Map(int, int);
|
|
||||||
for 0..ITERATIONS {
|
|
||||||
MapSet(*values, it, it * it);
|
|
||||||
}
|
|
||||||
|
|
||||||
for 0..ITERATIONS {
|
|
||||||
v, ok := MapGet(*values, it);
|
|
||||||
Expect(v == it * it);
|
|
||||||
}
|
|
||||||
|
|
||||||
for 0..ITERATIONS if it % 2 == 0 {
|
|
||||||
ok := MapRemove(*values, it);
|
|
||||||
Expect(ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
for 0..ITERATIONS if it % 2 == 0 {
|
|
||||||
_, ok := MapGet(*values, it);
|
|
||||||
Expect(!ok);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Test("map:free slots", t => {
|
|
||||||
values: Map(int, int);
|
|
||||||
|
|
||||||
MapSet(*values, 1, 100);
|
|
||||||
MapSet(*values, 2, 200);
|
|
||||||
MapSet(*values, 3, 300);
|
|
||||||
Expect(values.count == 3);
|
|
||||||
Expect(values.slots.allocated == values.AllocatedItemsAtStart);
|
|
||||||
|
|
||||||
// deleting something that doesn't exist should do nothing
|
|
||||||
ok := MapRemove(*values, 0);
|
|
||||||
Expect(!ok);
|
|
||||||
Expect(values.count == 3);
|
|
||||||
|
|
||||||
MapRemove(*values, 2);
|
|
||||||
Expect(values.count == 2);
|
|
||||||
});
|
|
||||||
|
|
||||||
Test("map:iteration", t => {
|
|
||||||
values: Map(int, int);
|
|
||||||
|
|
||||||
for 0..10 MapSet(*values, it, it * it);
|
|
||||||
Expect(values.count == 11);
|
|
||||||
|
|
||||||
for v, k: values Expect(v == k * k);
|
|
||||||
for < v, k: values Expect(v == k * k);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
@ -14,9 +14,9 @@ This is what I'm doing day-to-day.
|
||||||
+ replaced allocators with custom arenas
|
+ replaced allocators with custom arenas
|
||||||
+ Panic/Unreachable prints stack trace in debug builds
|
+ Panic/Unreachable prints stack trace in debug builds
|
||||||
+ type_info names are now pascal case
|
+ type_info names are now pascal case
|
||||||
+ fixed +internal's weird behavior in the test runner
|
|
||||||
+ ArrayAppend/ArrayGrow now wrap the current arena as the attached Allocator
|
: not sure how [..]T should interface with arenas
|
||||||
+ ArenaMode -> ArenaEvent
|
|
||||||
|
|
||||||
/ 09.06.25
|
/ 09.06.25
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@
|
||||||
compiler :: #import "Compiler";
|
compiler :: #import "Compiler";
|
||||||
compiler.set_build_options_dc(.{ do_output = false });
|
compiler.set_build_options_dc(.{ do_output = false });
|
||||||
|
|
||||||
_ :: #import "jc";
|
_ :: #import "jc"(true);
|
||||||
_ :: #import "jc/+internal/tests";
|
|
||||||
_ :: #import "jc/math"(RUN_TESTS = true);
|
_ :: #import "jc/math"(RUN_TESTS = true);
|
||||||
_ :: #import "jc/fmt/base64"(true);
|
_ :: #import "jc/fmt/base64"(true);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@
|
||||||
/// Additionally, it provides a platform-independant
|
/// Additionally, it provides a platform-independant
|
||||||
/// interface for interacting with the target operating
|
/// interface for interacting with the target operating
|
||||||
/// system.
|
/// system.
|
||||||
|
#module_parameters(RunTests := false);
|
||||||
|
|
||||||
#load "+internal/builtin.jai";
|
#load "+internal/builtin.jai";
|
||||||
#load "+internal/arenas.jai";
|
#load "+internal/array.jai";
|
||||||
#load "+internal/memory.jai";
|
|
||||||
#load "+internal/arrays.jai";
|
|
||||||
#load "+internal/map.jai";
|
#load "+internal/map.jai";
|
||||||
#load "+internal/hashing.jai";
|
#load "+internal/hashing.jai";
|
||||||
|
#load "+internal/memory.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