Compare commits
No commits in common. "77ae783dbb898676bf5fd304ab42f17a9a5408ef" and "8ca5903c7f7cb449c63e2072d723b9fbd4e059cb" have entirely different histories.
77ae783dbb
...
8ca5903c7f
5 changed files with 4 additions and 209 deletions
2
TODO
2
TODO
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
[Judah]
|
[Judah]
|
||||||
065 [array] add dynamic, but stable array implementation (values should not move in memory once appended to the array; should mirror procedures on 'Static_Array')
|
065 [array] add dynamic, but stable array implementation (values should not move in memory once appended to the array; should mirror procedures on 'Static_Array')
|
||||||
012 [map] create a simple arena-backed hash map implementation 'Map(K, V)', should be able to hash a key of any type (must include: get, set, remove, for_expansion) - possibly blocked by 032
|
|
||||||
|
|
||||||
[Jesse]
|
[Jesse]
|
||||||
011 [math] add more Vec math procedures
|
011 [math] add more Vec math procedures
|
||||||
|
|
@ -18,6 +17,7 @@
|
||||||
007 [bytes] create byte utilities module (should include Buffer type)
|
007 [bytes] create byte utilities module (should include Buffer type)
|
||||||
008 [strings] create string utilities module (include Buffer type) - blocked by 007
|
008 [strings] create string utilities module (include Buffer type) - blocked by 007
|
||||||
009 [encoding] jai-friendly binary serialization support - blocked by 007
|
009 [encoding] jai-friendly binary serialization support - blocked by 007
|
||||||
|
012 [map] create a simple arena-backed hash map implementation 'Map(K, V)', should be able to hash a key of any type (must include: get, set, remove, for_expansion) - possibly blocked by 032
|
||||||
013 [bindings] create build/binding utilitites module (since we do this a lot)
|
013 [bindings] create build/binding utilitites module (since we do this a lot)
|
||||||
014 [platform] add support for read file, write file, etc.
|
014 [platform] add support for read file, write file, etc.
|
||||||
015 [meta] create metaprogramming utilitites module (AST rewriting, code generation/introspection, etc.)
|
015 [meta] create metaprogramming utilitites module (AST rewriting, code generation/introspection, etc.)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@
|
||||||
#import,file "./memory/module.jai"(RUN_TESTS = true);
|
#import,file "./memory/module.jai"(RUN_TESTS = true);
|
||||||
#import,file "./meta/module.jai"(RUN_TESTS = true);
|
#import,file "./meta/module.jai"(RUN_TESTS = true);
|
||||||
#import,file "./platform/module.jai"(RUN_TESTS = true);
|
#import,file "./platform/module.jai"(RUN_TESTS = true);
|
||||||
#import,file "./kv/module.jai"(RUN_TESTS = true);
|
|
||||||
|
|
||||||
rmath :: #import,file "./math/module.jai"(.radians, RUN_TESTS = true);
|
rmath :: #import,file "./math/module.jai"(.radians, RUN_TESTS = true);
|
||||||
dmath :: #import,file "./math/module.jai"(.degrees, RUN_TESTS = true);
|
dmath :: #import,file "./math/module.jai"(.degrees, RUN_TESTS = true);
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,19 @@
|
||||||
// @todo(judah): replace array_add
|
// @todo(judah): replace array_add
|
||||||
|
|
||||||
append :: inline (arr: *[..]$T, value: T) -> *T {
|
append :: inline (arr: *[..]$T, value: T) -> *T {
|
||||||
ptr := basic.array_add(arr,, allocator = arr.allocator);
|
ptr := basic.array_add(arr);
|
||||||
ptr.* = value;
|
ptr.* = value;
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
append :: inline (arr: *[..]$T, values: ..T) -> *T {
|
append :: inline (arr: *[..]$T, values: ..T) -> *T {
|
||||||
count := arr.count;
|
count := arr.count;
|
||||||
basic.array_add(arr, ..values,, allocator = arr.allocator);
|
basic.array_add(arr, ..values);
|
||||||
return *arr.data[count];
|
return *arr.data[count];
|
||||||
}
|
}
|
||||||
|
|
||||||
append :: inline (arr: *[..]$T) -> *T {
|
append :: inline (arr: *[..]$T) -> *T {
|
||||||
return basic.array_add(arr,, allocator = arr.allocator);
|
return basic.array_add(arr);
|
||||||
}
|
|
||||||
|
|
||||||
resize :: inline (arr: *[..]$T, new_size: int) {
|
|
||||||
if new_size <= arr.allocated return;
|
|
||||||
basic.array_reserve(arr, new_size,, allocator = arr.allocator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
reset :: inline (arr: *[..]$T, $keep_memory := true) {
|
reset :: inline (arr: *[..]$T, $keep_memory := true) {
|
||||||
|
|
|
||||||
185
kv/module.jai
185
kv/module.jai
|
|
@ -1,185 +0,0 @@
|
||||||
#module_parameters(RUN_TESTS := false);
|
|
||||||
|
|
||||||
// Dead simple key-value pair type (aka. hash table or hash map)
|
|
||||||
Kv :: struct(Key: Type, Value: Type) {
|
|
||||||
allocator: Allocator;
|
|
||||||
slots: [..]Slot;
|
|
||||||
free_slots: [..]int;
|
|
||||||
count: int;
|
|
||||||
|
|
||||||
Slot :: struct {
|
|
||||||
hash: u32 = invalid_hash;
|
|
||||||
key: Key = ---;
|
|
||||||
value: Value = ---;
|
|
||||||
}
|
|
||||||
|
|
||||||
hash_proc :: hash.murmur32;
|
|
||||||
invalid_hash :: (0x8000_dead).(u32); // @note(judah): I'm curious what values would hit this hash on accident
|
|
||||||
number_of_items_to_allocate_initially :: 16; // @note(judah): must be a power of two
|
|
||||||
}
|
|
||||||
|
|
||||||
init :: (kv: *Kv, allocator: Allocator) {
|
|
||||||
kv.allocator = allocator;
|
|
||||||
kv.slots.allocator = allocator;
|
|
||||||
kv.free_slots.allocator = allocator;
|
|
||||||
}
|
|
||||||
|
|
||||||
get :: (kv: *Kv, key: kv.Key) -> kv.Value, bool {
|
|
||||||
slot, ok := find_slot(kv, kv.hash_proc(key));
|
|
||||||
if !ok {
|
|
||||||
return mem.zero_of(kv.Value), false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return slot.value, true;
|
|
||||||
}
|
|
||||||
|
|
||||||
set :: (kv: *Kv, key: kv.Key, value: kv.Value) {
|
|
||||||
hash := kv.hash_proc(key);
|
|
||||||
slot, exists := find_slot(kv, hash);
|
|
||||||
if !exists {
|
|
||||||
slot = create_or_reuse_slot(kv);
|
|
||||||
slot.hash = hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
slot.key = key;
|
|
||||||
slot.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @note(judah): we use 'evict' instead of 'remove' because it's a keyword...
|
|
||||||
evict :: (kv: *Kv, key: kv.Key) -> kv.Value, bool {
|
|
||||||
slot, ok, idx := find_slot(kv, kv.hash_proc(key));
|
|
||||||
if !ok return mem.zero_of(kv.Value), false;
|
|
||||||
|
|
||||||
last_value := slot.value;
|
|
||||||
mark_slot_for_reuse(kv, idx);
|
|
||||||
kv.count -= 1;
|
|
||||||
|
|
||||||
return last_value, true;
|
|
||||||
}
|
|
||||||
|
|
||||||
reset :: (kv: *Kv) {
|
|
||||||
kv.count = 0;
|
|
||||||
kv.slots.count = 0;
|
|
||||||
kv.free_slots.count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for_expansion :: (kv: *Kv, body: Code, flags: For_Flags) #expand {
|
|
||||||
#assert (flags & .POINTER == 0) "cannot iterate by pointer";
|
|
||||||
for <=(flags & .REVERSE == .REVERSE) slot: kv.slots if slot.hash != kv.invalid_hash {
|
|
||||||
`it := slot.value;
|
|
||||||
`it_index := slot.key;
|
|
||||||
#insert,scope(body)(break = break slot) body;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#scope_file;
|
|
||||||
|
|
||||||
find_slot :: (kv: *Kv, hash: u32) -> *kv.Slot, bool, int {
|
|
||||||
for * kv.slots if it.hash == hash {
|
|
||||||
return it, true, it_index;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null, false, -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
create_or_reuse_slot :: (kv: *Kv) -> *kv.Slot {
|
|
||||||
inline try_lazy_init(kv);
|
|
||||||
|
|
||||||
if kv.free_slots.count > 0 {
|
|
||||||
slot_idx := kv.free_slots[kv.free_slots.count - 1];
|
|
||||||
kv.free_slots.count -= 1;
|
|
||||||
return *kv.slots[slot_idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
if kv.slots.allocated == 0 {
|
|
||||||
array.resize(*kv.slots, kv.number_of_items_to_allocate_initially);
|
|
||||||
}
|
|
||||||
else if kv.slots.count >= kv.slots.allocated {
|
|
||||||
array.resize(*kv.slots, mem.next_power_of_two(kv.slots.allocated));
|
|
||||||
}
|
|
||||||
|
|
||||||
slot := array.append(*kv.slots);
|
|
||||||
kv.count = kv.slots.count;
|
|
||||||
return slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
mark_slot_for_reuse :: (kv: *Kv, index: int) {
|
|
||||||
inline try_lazy_init(kv);
|
|
||||||
|
|
||||||
kv.slots[index] = .{ hash = kv.invalid_hash };
|
|
||||||
array.append(*kv.free_slots, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
try_lazy_init :: inline (kv: *Kv) {
|
|
||||||
if kv.allocator.proc == null {
|
|
||||||
init(kv, context.allocator);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mem :: #import "jc/memory";
|
|
||||||
array :: #import "jc/array";
|
|
||||||
hash :: #import "jc/hash";
|
|
||||||
|
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
// TESTS
|
|
||||||
// ----------------------------------------------------------
|
|
||||||
|
|
||||||
#if RUN_TESTS {
|
|
||||||
test :: #import "jc/test";
|
|
||||||
|
|
||||||
#run {
|
|
||||||
test.run("basic operations", t => {
|
|
||||||
ITERATIONS :: 64;
|
|
||||||
|
|
||||||
values: Kv(int, int);
|
|
||||||
for 0..ITERATIONS {
|
|
||||||
set(*values, it, it * it);
|
|
||||||
}
|
|
||||||
|
|
||||||
for 0..ITERATIONS {
|
|
||||||
v, ok := get(*values, it);
|
|
||||||
test.expect(t, v == it * it);
|
|
||||||
}
|
|
||||||
|
|
||||||
for 0..ITERATIONS if it % 2 == 0 {
|
|
||||||
_, ok := evict(*values, it);
|
|
||||||
test.expect(t, ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
for 0..ITERATIONS if it % 2 == 0 {
|
|
||||||
_, ok := get(*values, it);
|
|
||||||
test.expect(t, !ok);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test.run("free slots", t => {
|
|
||||||
values: Kv(int, int);
|
|
||||||
|
|
||||||
set(*values, 1, 100);
|
|
||||||
set(*values, 2, 200);
|
|
||||||
set(*values, 3, 300);
|
|
||||||
test.expect(t, values.count == 3);
|
|
||||||
test.expect(t, values.slots.allocated == values.number_of_items_to_allocate_initially);
|
|
||||||
|
|
||||||
// evicting something that doesn't exist should do nothing
|
|
||||||
_, ok := evict(*values, 0);
|
|
||||||
test.expect(t, !ok);
|
|
||||||
test.expect(t, values.count == 3);
|
|
||||||
|
|
||||||
evict(*values, 2);
|
|
||||||
test.expect(t, values.count == 2);
|
|
||||||
});
|
|
||||||
|
|
||||||
test.run("iteration", t => {
|
|
||||||
values: Kv(int, int);
|
|
||||||
|
|
||||||
for 0..10 set(*values, it, it * it);
|
|
||||||
test.expect(t, values.count == 11);
|
|
||||||
|
|
||||||
for v, k: values test.expect(t, v == k * k);
|
|
||||||
for < v, k: values test.expect(t, v == k * k);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -34,20 +34,6 @@ power_of_two :: (x: int) -> bool {
|
||||||
return x & (x - 1) == 0;
|
return x & (x - 1) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
next_power_of_two :: (x: int) -> int #no_aoc {
|
|
||||||
basic.assert(power_of_two(x), "value (%) must be a power of two", x);
|
|
||||||
|
|
||||||
// Bit twiddling hacks next power of two
|
|
||||||
x |= x >> 1;
|
|
||||||
x |= x >> 2;
|
|
||||||
x |= x >> 4;
|
|
||||||
x |= x >> 8;
|
|
||||||
x |= x >> 16;
|
|
||||||
x |= x >> 32;
|
|
||||||
|
|
||||||
return x + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
align_to :: (ptr: int, align: int = Default_Align) -> int {
|
align_to :: (ptr: int, align: int = Default_Align) -> int {
|
||||||
basic.assert(power_of_two(align), "alignment must be a power of two");
|
basic.assert(power_of_two(align), "alignment must be a power of two");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue