split up mem_allocator examples
This commit is contained in:
+1
-1
@@ -74,7 +74,7 @@ roadmap and milestone history.
|
|||||||
|
|
||||||
### standard packages
|
### standard packages
|
||||||
|
|
||||||
- `std/mem` allocator contract with a context pointer plus shared `AllocatorVTable`, raw byte operations `raw_alloc` / `raw_realloc` / `raw_free`, and fallible typed `alloc(T, allocator, count)`; failed nonzero raw reallocation preserves the original allocation, while zero size frees it
|
- `std/mem` allocator contract with a context pointer plus shared `AllocatorVTable`, raw byte operations `raw_alloc` / `raw_realloc` / `raw_free`, fallible typed `alloc(T, allocator, count)`, and typed `free(T, allocator, memory)`; failed nonzero raw reallocation preserves the original allocation, while zero size frees it
|
||||||
|
|
||||||
### compiler behavior
|
### compiler behavior
|
||||||
|
|
||||||
|
|||||||
@@ -732,7 +732,9 @@
|
|||||||
- deferred: build graph / steps / caching, multiple artifacts, computed paths
|
- deferred: build graph / steps / caching, multiple artifacts, computed paths
|
||||||
(needs string building), struct field defaults to drop `&[]` on empty lists
|
(needs string building), struct field defaults to drop `&[]` on empty lists
|
||||||
|
|
||||||
29. disallow arbitrary integer division
|
29. basic `std/arraylist` implementation using the new `std/mem` typed allocation
|
||||||
|
|
||||||
|
30. disallow arbitrary integer division
|
||||||
- take inspiration from zig
|
- take inspiration from zig
|
||||||
- see also below for a word on unchecked casts
|
- see also below for a word on unchecked casts
|
||||||
- the user should be explicit about what they mean with integer division (e.g. `div`, `rem`)
|
- the user should be explicit about what they mean with integer division (e.g. `div`, `rem`)
|
||||||
|
|||||||
@@ -1,376 +1,9 @@
|
|||||||
mem :: import "@std/mem"
|
|
||||||
|
|
||||||
_probe_count func(context ?*mut anyopaque) void {
|
|
||||||
if context |raw| {
|
|
||||||
counts *mut usize :: ptr_cast(usize, raw)
|
|
||||||
counts[0] += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_probe_alloc func(context ?*mut anyopaque, _ usize, _ usize) ?*mut u8 {
|
|
||||||
_probe_count(context)
|
|
||||||
return none
|
|
||||||
}
|
|
||||||
|
|
||||||
_probe_realloc func(context ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize, _ usize) ?*mut u8 {
|
|
||||||
_probe_count(context)
|
|
||||||
return none
|
|
||||||
}
|
|
||||||
|
|
||||||
_probe_free func(context ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize) void {
|
|
||||||
_probe_count(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
_probe_vtable mem.AllocatorVTable :: mem.AllocatorVTable {
|
|
||||||
alloc = _probe_alloc,
|
|
||||||
realloc = _probe_realloc,
|
|
||||||
free = _probe_free,
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskList :: struct {
|
|
||||||
ids ?[]mut i32
|
|
||||||
priorities ?[]mut i32
|
|
||||||
durations ?[]mut i32
|
|
||||||
len usize
|
|
||||||
capacity usize
|
|
||||||
allocator mem.Allocator
|
|
||||||
}
|
|
||||||
|
|
||||||
task_list_init func(allocator mem.Allocator) TaskList {
|
|
||||||
return TaskList {
|
|
||||||
ids = none,
|
|
||||||
priorities = none,
|
|
||||||
durations = none,
|
|
||||||
len = 0,
|
|
||||||
capacity = 0,
|
|
||||||
allocator = allocator,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
alloc_i32s func(allocator mem.Allocator, count usize) ?[]mut i32 {
|
|
||||||
fallback [1]mut i32 = undefined
|
|
||||||
failed bool = false
|
|
||||||
values []mut i32 = mem.alloc(i32, allocator, count) catch |_| {
|
|
||||||
failed = true
|
|
||||||
yield (&fallback).ptr[..0]
|
|
||||||
}
|
|
||||||
if (failed) return none
|
|
||||||
return values
|
|
||||||
}
|
|
||||||
|
|
||||||
free_i32s func(allocator mem.Allocator, values ?[]mut i32) void {
|
|
||||||
if values |slice| {
|
|
||||||
mem.raw_free(allocator, ptr_cast(u8, slice.ptr), slice.len * size_of(i32), align_of(i32))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task_list_reserve func(list @mut TaskList, capacity usize) bool {
|
|
||||||
if capacity <= list.capacity {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
new_ids ?[]mut i32 = alloc_i32s(list.allocator, capacity)
|
|
||||||
new_priorities ?[]mut i32 = alloc_i32s(list.allocator, capacity)
|
|
||||||
new_durations ?[]mut i32 = alloc_i32s(list.allocator, capacity)
|
|
||||||
|
|
||||||
if (new_ids and new_priorities and new_durations) |ids, priorities, durations| {
|
|
||||||
if list.len > 0 {
|
|
||||||
if (list.ids and list.priorities and list.durations) |old_ids, old_priorities, old_durations| {
|
|
||||||
i usize = 0
|
|
||||||
while i < list.len : i += 1 {
|
|
||||||
ids[i] = old_ids[i]
|
|
||||||
priorities[i] = old_priorities[i]
|
|
||||||
durations[i] = old_durations[i]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
free_i32s(list.allocator, new_ids)
|
|
||||||
free_i32s(list.allocator, new_priorities)
|
|
||||||
free_i32s(list.allocator, new_durations)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
free_i32s(list.allocator, list.ids)
|
|
||||||
free_i32s(list.allocator, list.priorities)
|
|
||||||
free_i32s(list.allocator, list.durations)
|
|
||||||
|
|
||||||
list.ids = new_ids
|
|
||||||
list.priorities = new_priorities
|
|
||||||
list.durations = new_durations
|
|
||||||
list.capacity = capacity
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
free_i32s(list.allocator, new_ids)
|
|
||||||
free_i32s(list.allocator, new_priorities)
|
|
||||||
free_i32s(list.allocator, new_durations)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
task_list_push func(list @mut TaskList, id i32, priority i32, duration i32) bool {
|
|
||||||
if list.len == list.capacity {
|
|
||||||
new_capacity usize = 2
|
|
||||||
if list.capacity != 0 {
|
|
||||||
new_capacity = list.capacity * 2
|
|
||||||
}
|
|
||||||
if task_list_reserve(list, new_capacity) == false {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list.ids and list.priorities and list.durations) |ids, priorities, durations| {
|
|
||||||
index usize = list.len
|
|
||||||
ids[index] = id
|
|
||||||
priorities[index] = priority
|
|
||||||
durations[index] = duration
|
|
||||||
list.len += 1
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
task_score func(priority i32, duration i32) i32 {
|
|
||||||
return priority * 10 - duration
|
|
||||||
}
|
|
||||||
|
|
||||||
task_list_best_id func(list @mut TaskList) i32 {
|
|
||||||
if list.len == 0 {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
if (list.ids and list.priorities and list.durations) |ids, priorities, durations| {
|
|
||||||
best_index usize = 0
|
|
||||||
best_score i32 = task_score(priorities[0], durations[0])
|
|
||||||
i usize = 1
|
|
||||||
while i < list.len : i += 1 {
|
|
||||||
score i32 = task_score(priorities[i], durations[i])
|
|
||||||
if score > best_score {
|
|
||||||
best_score = score
|
|
||||||
best_index = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ids[best_index]
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
task_list_total_duration func(list @mut TaskList) i32 {
|
|
||||||
total i32 = 0
|
|
||||||
if list.durations |durations| {
|
|
||||||
i usize = 0
|
|
||||||
while i < list.len : i += 1 {
|
|
||||||
total += durations[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return total
|
|
||||||
}
|
|
||||||
|
|
||||||
task_list_deinit func(list @mut TaskList) void {
|
|
||||||
free_i32s(list.allocator, list.ids)
|
|
||||||
free_i32s(list.allocator, list.priorities)
|
|
||||||
free_i32s(list.allocator, list.durations)
|
|
||||||
list.ids = none
|
|
||||||
list.priorities = none
|
|
||||||
list.durations = none
|
|
||||||
list.len = 0
|
|
||||||
list.capacity = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
main func() i32 {
|
main func() i32 {
|
||||||
if (size_of(mem.Allocator) != 16) return 31
|
typed_result i32 :: typed_allocator_test()
|
||||||
|
if (typed_result != 0) return typed_result
|
||||||
|
|
||||||
first_calls [1]mut usize = [0]
|
raw_result i32 :: raw_allocator_test()
|
||||||
second_calls [1]mut usize = [0]
|
if (raw_result != 0) return raw_result
|
||||||
first_allocator mem.Allocator :: mem.Allocator {
|
|
||||||
context = (&first_calls).ptr,
|
|
||||||
vtable = &_probe_vtable,
|
|
||||||
}
|
|
||||||
second_allocator mem.Allocator :: mem.Allocator {
|
|
||||||
context = (&second_calls).ptr,
|
|
||||||
vtable = &_probe_vtable,
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = mem.raw_alloc(first_allocator, 1, 1)
|
return task_list_test()
|
||||||
_ = mem.raw_realloc(first_allocator, none, 0, 1, 1)
|
|
||||||
mem.raw_free(first_allocator, none, 0, 1)
|
|
||||||
_ = mem.raw_alloc(second_allocator, 1, 1)
|
|
||||||
if (first_calls[0] != 3 or second_calls[0] != 1) return 32
|
|
||||||
|
|
||||||
i32_fallback [1]mut i32 = undefined
|
|
||||||
empty_failed bool = false
|
|
||||||
empty []mut i32 = mem.alloc(i32, first_allocator, 0) catch |_| {
|
|
||||||
empty_failed = true
|
|
||||||
yield (&i32_fallback).ptr[..0]
|
|
||||||
}
|
|
||||||
if (empty_failed or empty.len != 0 or first_calls[0] != 3) return 34
|
|
||||||
|
|
||||||
zero_sized_fallback [1]mut [0]u8 = undefined
|
|
||||||
zero_sized_failed bool = false
|
|
||||||
zero_sized []mut [0]u8 = mem.alloc([0]u8, first_allocator, 3) catch |_| {
|
|
||||||
zero_sized_failed = true
|
|
||||||
yield (&zero_sized_fallback).ptr[..0]
|
|
||||||
}
|
|
||||||
if (zero_sized_failed or zero_sized.len != 3 or first_calls[0] != 3) return 36
|
|
||||||
_ = zero_sized[2]
|
|
||||||
|
|
||||||
u64_fallback [1]mut u64 = undefined
|
|
||||||
overflow_fallback_failed bool = false
|
|
||||||
overflow_fallback []mut u64 = mem.alloc(u64, first_allocator, 0) catch |_| {
|
|
||||||
overflow_fallback_failed = true
|
|
||||||
yield (&u64_fallback).ptr[..0]
|
|
||||||
}
|
|
||||||
if (overflow_fallback_failed) return 37
|
|
||||||
overflow_failed bool = false
|
|
||||||
_ = mem.alloc(u64, first_allocator, max_value(usize)) catch |_| {
|
|
||||||
overflow_failed = true
|
|
||||||
yield overflow_fallback
|
|
||||||
}
|
|
||||||
if (overflow_failed == false or first_calls[0] != 3) return 40
|
|
||||||
|
|
||||||
typed_failed bool = false
|
|
||||||
typed []mut i32 = mem.alloc(i32, mem.c_allocator, 4) catch |_| {
|
|
||||||
typed_failed = true
|
|
||||||
yield (&i32_fallback).ptr[..0]
|
|
||||||
}
|
|
||||||
if (typed_failed) return 38
|
|
||||||
defer mem.raw_free(mem.c_allocator, ptr_cast(u8, typed.ptr), typed.len * size_of(i32), align_of(i32))
|
|
||||||
typed[0] = 10
|
|
||||||
typed[3] = 20
|
|
||||||
if (typed[0] + typed[3] != 30) return 39
|
|
||||||
|
|
||||||
resized ?*mut u8 = mem.raw_realloc(mem.c_allocator, none, 0, 4, 1)
|
|
||||||
if resized |bytes| {
|
|
||||||
bytes[0] = 10
|
|
||||||
bytes[1] = 20
|
|
||||||
bytes[2] = 30
|
|
||||||
bytes[3] = 40
|
|
||||||
} else {
|
|
||||||
return 20
|
|
||||||
}
|
|
||||||
|
|
||||||
grown ?*mut u8 = mem.raw_realloc(mem.c_allocator, resized, 4, 8, 1)
|
|
||||||
if grown |bytes| {
|
|
||||||
resized = grown
|
|
||||||
if bytes[0] != 10 or bytes[1] != 20 or bytes[2] != 30 or bytes[3] != 40 {
|
|
||||||
mem.raw_free(mem.c_allocator, grown, 8, 1)
|
|
||||||
return 21
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mem.raw_free(mem.c_allocator, resized, 4, 1)
|
|
||||||
return 22
|
|
||||||
}
|
|
||||||
|
|
||||||
shrunk ?*mut u8 = mem.raw_realloc(mem.c_allocator, resized, 8, 2, 1)
|
|
||||||
if shrunk |bytes| {
|
|
||||||
resized = shrunk
|
|
||||||
if bytes[0] != 10 or bytes[1] != 20 {
|
|
||||||
mem.raw_free(mem.c_allocator, shrunk, 2, 1)
|
|
||||||
return 23
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mem.raw_free(mem.c_allocator, resized, 8, 1)
|
|
||||||
return 24
|
|
||||||
}
|
|
||||||
|
|
||||||
invalid ?*mut u8 = mem.raw_realloc(mem.c_allocator, resized, 2, 4, 24)
|
|
||||||
if invalid |memory| {
|
|
||||||
mem.raw_free(mem.c_allocator, memory, 4, 24)
|
|
||||||
mem.raw_free(mem.c_allocator, resized, 2, 1)
|
|
||||||
return 25
|
|
||||||
}
|
|
||||||
if resized |bytes| {
|
|
||||||
if bytes[0] != 10 or bytes[1] != 20 {
|
|
||||||
mem.raw_free(mem.c_allocator, resized, 2, 1)
|
|
||||||
return 26
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resized = mem.raw_realloc(mem.c_allocator, resized, 2, 0, 1)
|
|
||||||
if resized |memory| {
|
|
||||||
mem.raw_free(mem.c_allocator, memory, 0, 1)
|
|
||||||
return 27
|
|
||||||
}
|
|
||||||
|
|
||||||
over_aligned ?*mut u8 = mem.raw_alloc(mem.c_allocator, 4, 32)
|
|
||||||
if over_aligned |bytes| {
|
|
||||||
bytes[0] = 11
|
|
||||||
bytes[1] = 22
|
|
||||||
} else {
|
|
||||||
return 28
|
|
||||||
}
|
|
||||||
over_aligned_grown ?*mut u8 = mem.raw_realloc(mem.c_allocator, over_aligned, 4, 8, 32)
|
|
||||||
if over_aligned_grown |bytes| {
|
|
||||||
if bytes[0] != 11 or bytes[1] != 22 {
|
|
||||||
mem.raw_free(mem.c_allocator, over_aligned_grown, 8, 32)
|
|
||||||
return 29
|
|
||||||
}
|
|
||||||
mem.raw_free(mem.c_allocator, over_aligned_grown, 8, 32)
|
|
||||||
} else {
|
|
||||||
mem.raw_free(mem.c_allocator, over_aligned, 4, 32)
|
|
||||||
return 30
|
|
||||||
}
|
|
||||||
|
|
||||||
zero_alignment ?*mut u8 = mem.raw_alloc(mem.c_allocator, 8, 0)
|
|
||||||
if zero_alignment |memory| {
|
|
||||||
mem.raw_free(mem.c_allocator, memory, 8, 0)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
bad_alignment ?*mut u8 = mem.raw_alloc(mem.c_allocator, 8, 24)
|
|
||||||
if bad_alignment |memory| {
|
|
||||||
mem.raw_free(mem.c_allocator, memory, 8, 24)
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
|
|
||||||
aligned ?*mut u8 = mem.raw_alloc(mem.c_allocator, 64, 32)
|
|
||||||
defer mem.raw_free(mem.c_allocator, aligned, 64, 32)
|
|
||||||
if aligned |bytes| {
|
|
||||||
bytes[0] = 1
|
|
||||||
bytes[63] = 2
|
|
||||||
if bytes[0] + bytes[63] != 3 {
|
|
||||||
return 4
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return 3
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks TaskList = task_list_init(mem.c_allocator)
|
|
||||||
defer task_list_deinit(&tasks)
|
|
||||||
|
|
||||||
if task_list_push(&tasks, 101, 3, 5) == false {
|
|
||||||
return 5
|
|
||||||
}
|
|
||||||
if tasks.capacity != 2 {
|
|
||||||
return 6
|
|
||||||
}
|
|
||||||
|
|
||||||
if task_list_push(&tasks, 202, 1, 4) == false {
|
|
||||||
return 7
|
|
||||||
}
|
|
||||||
if tasks.capacity != 2 {
|
|
||||||
return 8
|
|
||||||
}
|
|
||||||
|
|
||||||
if task_list_push(&tasks, 303, 4, 8) == false {
|
|
||||||
return 9
|
|
||||||
}
|
|
||||||
if tasks.capacity != 4 {
|
|
||||||
return 10
|
|
||||||
}
|
|
||||||
|
|
||||||
if tasks.len != 3 {
|
|
||||||
return 11
|
|
||||||
}
|
|
||||||
|
|
||||||
if task_list_best_id(&tasks) != 303 {
|
|
||||||
return 12
|
|
||||||
}
|
|
||||||
|
|
||||||
if task_list_total_duration(&tasks) != 17 {
|
|
||||||
return 13
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
mem :: import "@std/mem"
|
||||||
|
|
||||||
|
raw_allocator_test func() i32 {
|
||||||
|
resized ?*mut u8 = mem.raw_realloc(mem.c_allocator, none, 0, 4, 1)
|
||||||
|
if resized |bytes| {
|
||||||
|
bytes[0] = 10
|
||||||
|
bytes[1] = 20
|
||||||
|
bytes[2] = 30
|
||||||
|
bytes[3] = 40
|
||||||
|
} else {
|
||||||
|
return 20
|
||||||
|
}
|
||||||
|
|
||||||
|
grown ?*mut u8 = mem.raw_realloc(mem.c_allocator, resized, 4, 8, 1)
|
||||||
|
if grown |bytes| {
|
||||||
|
resized = grown
|
||||||
|
if bytes[0] != 10 or bytes[1] != 20 or bytes[2] != 30 or bytes[3] != 40 {
|
||||||
|
mem.raw_free(mem.c_allocator, grown, 8, 1)
|
||||||
|
return 21
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mem.raw_free(mem.c_allocator, resized, 4, 1)
|
||||||
|
return 22
|
||||||
|
}
|
||||||
|
|
||||||
|
shrunk ?*mut u8 = mem.raw_realloc(mem.c_allocator, resized, 8, 2, 1)
|
||||||
|
if shrunk |bytes| {
|
||||||
|
resized = shrunk
|
||||||
|
if bytes[0] != 10 or bytes[1] != 20 {
|
||||||
|
mem.raw_free(mem.c_allocator, shrunk, 2, 1)
|
||||||
|
return 23
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mem.raw_free(mem.c_allocator, resized, 8, 1)
|
||||||
|
return 24
|
||||||
|
}
|
||||||
|
|
||||||
|
invalid ?*mut u8 = mem.raw_realloc(mem.c_allocator, resized, 2, 4, 24)
|
||||||
|
if invalid |memory| {
|
||||||
|
mem.raw_free(mem.c_allocator, memory, 4, 24)
|
||||||
|
mem.raw_free(mem.c_allocator, resized, 2, 1)
|
||||||
|
return 25
|
||||||
|
}
|
||||||
|
if resized |bytes| {
|
||||||
|
if bytes[0] != 10 or bytes[1] != 20 {
|
||||||
|
mem.raw_free(mem.c_allocator, resized, 2, 1)
|
||||||
|
return 26
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resized = mem.raw_realloc(mem.c_allocator, resized, 2, 0, 1)
|
||||||
|
if resized |memory| {
|
||||||
|
mem.raw_free(mem.c_allocator, memory, 0, 1)
|
||||||
|
return 27
|
||||||
|
}
|
||||||
|
|
||||||
|
over_aligned ?*mut u8 = mem.raw_alloc(mem.c_allocator, 4, 32)
|
||||||
|
if over_aligned |bytes| {
|
||||||
|
bytes[0] = 11
|
||||||
|
bytes[1] = 22
|
||||||
|
} else {
|
||||||
|
return 28
|
||||||
|
}
|
||||||
|
over_aligned_grown ?*mut u8 = mem.raw_realloc(mem.c_allocator, over_aligned, 4, 8, 32)
|
||||||
|
if over_aligned_grown |bytes| {
|
||||||
|
if bytes[0] != 11 or bytes[1] != 22 {
|
||||||
|
mem.raw_free(mem.c_allocator, over_aligned_grown, 8, 32)
|
||||||
|
return 29
|
||||||
|
}
|
||||||
|
mem.raw_free(mem.c_allocator, over_aligned_grown, 8, 32)
|
||||||
|
} else {
|
||||||
|
mem.raw_free(mem.c_allocator, over_aligned, 4, 32)
|
||||||
|
return 30
|
||||||
|
}
|
||||||
|
|
||||||
|
zero_alignment ?*mut u8 = mem.raw_alloc(mem.c_allocator, 8, 0)
|
||||||
|
if zero_alignment |memory| {
|
||||||
|
mem.raw_free(mem.c_allocator, memory, 8, 0)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
bad_alignment ?*mut u8 = mem.raw_alloc(mem.c_allocator, 8, 24)
|
||||||
|
if bad_alignment |memory| {
|
||||||
|
mem.raw_free(mem.c_allocator, memory, 8, 24)
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned ?*mut u8 = mem.raw_alloc(mem.c_allocator, 64, 32)
|
||||||
|
defer mem.raw_free(mem.c_allocator, aligned, 64, 32)
|
||||||
|
if aligned |bytes| {
|
||||||
|
bytes[0] = 1
|
||||||
|
bytes[63] = 2
|
||||||
|
if bytes[0] + bytes[63] != 3 {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
mem :: import "@std/mem"
|
||||||
|
|
||||||
|
TaskList :: struct {
|
||||||
|
ids ?[]mut i32
|
||||||
|
priorities ?[]mut i32
|
||||||
|
durations ?[]mut i32
|
||||||
|
len usize
|
||||||
|
capacity usize
|
||||||
|
allocator mem.Allocator
|
||||||
|
}
|
||||||
|
|
||||||
|
task_list_init func(allocator mem.Allocator) TaskList {
|
||||||
|
return TaskList {
|
||||||
|
ids = none,
|
||||||
|
priorities = none,
|
||||||
|
durations = none,
|
||||||
|
len = 0,
|
||||||
|
capacity = 0,
|
||||||
|
allocator = allocator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc_i32s func(allocator mem.Allocator, count usize) ?[]mut i32 {
|
||||||
|
fallback [1]mut i32 = undefined
|
||||||
|
failed bool = false
|
||||||
|
values []mut i32 = mem.alloc(i32, allocator, count) catch |_| {
|
||||||
|
failed = true
|
||||||
|
yield (&fallback).ptr[..0]
|
||||||
|
}
|
||||||
|
if (failed) return none
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
free_i32s func(allocator mem.Allocator, values ?[]mut i32) void {
|
||||||
|
if values |slice| {
|
||||||
|
mem.free(i32, allocator, slice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task_list_reserve func(list @mut TaskList, capacity usize) bool {
|
||||||
|
if capacity <= list.capacity {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
new_ids ?[]mut i32 = alloc_i32s(list.allocator, capacity)
|
||||||
|
new_priorities ?[]mut i32 = alloc_i32s(list.allocator, capacity)
|
||||||
|
new_durations ?[]mut i32 = alloc_i32s(list.allocator, capacity)
|
||||||
|
|
||||||
|
if (new_ids and new_priorities and new_durations) |ids, priorities, durations| {
|
||||||
|
if list.len > 0 {
|
||||||
|
if (list.ids and list.priorities and list.durations) |old_ids, old_priorities, old_durations| {
|
||||||
|
i usize = 0
|
||||||
|
while i < list.len : i += 1 {
|
||||||
|
ids[i] = old_ids[i]
|
||||||
|
priorities[i] = old_priorities[i]
|
||||||
|
durations[i] = old_durations[i]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
free_i32s(list.allocator, new_ids)
|
||||||
|
free_i32s(list.allocator, new_priorities)
|
||||||
|
free_i32s(list.allocator, new_durations)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free_i32s(list.allocator, list.ids)
|
||||||
|
free_i32s(list.allocator, list.priorities)
|
||||||
|
free_i32s(list.allocator, list.durations)
|
||||||
|
|
||||||
|
list.ids = new_ids
|
||||||
|
list.priorities = new_priorities
|
||||||
|
list.durations = new_durations
|
||||||
|
list.capacity = capacity
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
free_i32s(list.allocator, new_ids)
|
||||||
|
free_i32s(list.allocator, new_priorities)
|
||||||
|
free_i32s(list.allocator, new_durations)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
task_list_push func(list @mut TaskList, id i32, priority i32, duration i32) bool {
|
||||||
|
if list.len == list.capacity {
|
||||||
|
new_capacity usize = 2
|
||||||
|
if list.capacity != 0 {
|
||||||
|
new_capacity = list.capacity * 2
|
||||||
|
}
|
||||||
|
if task_list_reserve(list, new_capacity) == false {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.ids and list.priorities and list.durations) |ids, priorities, durations| {
|
||||||
|
index usize = list.len
|
||||||
|
ids[index] = id
|
||||||
|
priorities[index] = priority
|
||||||
|
durations[index] = duration
|
||||||
|
list.len += 1
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
task_score func(priority i32, duration i32) i32 {
|
||||||
|
return priority * 10 - duration
|
||||||
|
}
|
||||||
|
|
||||||
|
task_list_best_id func(list @mut TaskList) i32 {
|
||||||
|
if list.len == 0 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list.ids and list.priorities and list.durations) |ids, priorities, durations| {
|
||||||
|
best_index usize = 0
|
||||||
|
best_score i32 = task_score(priorities[0], durations[0])
|
||||||
|
i usize = 1
|
||||||
|
while i < list.len : i += 1 {
|
||||||
|
score i32 = task_score(priorities[i], durations[i])
|
||||||
|
if score > best_score {
|
||||||
|
best_score = score
|
||||||
|
best_index = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ids[best_index]
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
task_list_total_duration func(list @mut TaskList) i32 {
|
||||||
|
total i32 = 0
|
||||||
|
if list.durations |durations| {
|
||||||
|
i usize = 0
|
||||||
|
while i < list.len : i += 1 {
|
||||||
|
total += durations[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
task_list_deinit func(list @mut TaskList) void {
|
||||||
|
free_i32s(list.allocator, list.ids)
|
||||||
|
free_i32s(list.allocator, list.priorities)
|
||||||
|
free_i32s(list.allocator, list.durations)
|
||||||
|
list.ids = none
|
||||||
|
list.priorities = none
|
||||||
|
list.durations = none
|
||||||
|
list.len = 0
|
||||||
|
list.capacity = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
task_list_test func() i32 {
|
||||||
|
tasks TaskList = task_list_init(mem.c_allocator)
|
||||||
|
defer task_list_deinit(&tasks)
|
||||||
|
|
||||||
|
if task_list_push(&tasks, 101, 3, 5) == false {
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
if tasks.capacity != 2 {
|
||||||
|
return 6
|
||||||
|
}
|
||||||
|
|
||||||
|
if task_list_push(&tasks, 202, 1, 4) == false {
|
||||||
|
return 7
|
||||||
|
}
|
||||||
|
if tasks.capacity != 2 {
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
|
||||||
|
if task_list_push(&tasks, 303, 4, 8) == false {
|
||||||
|
return 9
|
||||||
|
}
|
||||||
|
if tasks.capacity != 4 {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
|
||||||
|
if tasks.len != 3 {
|
||||||
|
return 11
|
||||||
|
}
|
||||||
|
|
||||||
|
if task_list_best_id(&tasks) != 303 {
|
||||||
|
return 12
|
||||||
|
}
|
||||||
|
|
||||||
|
if task_list_total_duration(&tasks) != 17 {
|
||||||
|
return 13
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
mem :: import "@std/mem"
|
||||||
|
|
||||||
|
_probe_count func(context ?*mut anyopaque) void {
|
||||||
|
if context |raw| {
|
||||||
|
counts *mut usize :: ptr_cast(usize, raw)
|
||||||
|
counts[0] += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_probe_alloc func(context ?*mut anyopaque, _ usize, _ usize) ?*mut u8 {
|
||||||
|
_probe_count(context)
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
|
||||||
|
_probe_realloc func(context ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize, _ usize) ?*mut u8 {
|
||||||
|
_probe_count(context)
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
|
||||||
|
_probe_free func(context ?*mut anyopaque, _ ?*mut u8, _ usize, _ usize) void {
|
||||||
|
_probe_count(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
_probe_vtable mem.AllocatorVTable :: mem.AllocatorVTable {
|
||||||
|
alloc = _probe_alloc,
|
||||||
|
realloc = _probe_realloc,
|
||||||
|
free = _probe_free,
|
||||||
|
}
|
||||||
|
|
||||||
|
typed_allocator_test func() i32 {
|
||||||
|
if (size_of(mem.Allocator) != 16) return 31
|
||||||
|
|
||||||
|
first_calls [1]mut usize = [0]
|
||||||
|
second_calls [1]mut usize = [0]
|
||||||
|
first_allocator mem.Allocator :: mem.Allocator {
|
||||||
|
context = (&first_calls).ptr,
|
||||||
|
vtable = &_probe_vtable,
|
||||||
|
}
|
||||||
|
second_allocator mem.Allocator :: mem.Allocator {
|
||||||
|
context = (&second_calls).ptr,
|
||||||
|
vtable = &_probe_vtable,
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = mem.raw_alloc(first_allocator, 1, 1)
|
||||||
|
_ = mem.raw_realloc(first_allocator, none, 0, 1, 1)
|
||||||
|
mem.raw_free(first_allocator, none, 0, 1)
|
||||||
|
_ = mem.raw_alloc(second_allocator, 1, 1)
|
||||||
|
if (first_calls[0] != 3 or second_calls[0] != 1) return 32
|
||||||
|
|
||||||
|
i32_fallback [1]mut i32 = undefined
|
||||||
|
empty_failed bool = false
|
||||||
|
empty []mut i32 = mem.alloc(i32, first_allocator, 0) catch |_| {
|
||||||
|
empty_failed = true
|
||||||
|
yield (&i32_fallback).ptr[..0]
|
||||||
|
}
|
||||||
|
if (empty_failed or empty.len != 0 or first_calls[0] != 3) return 34
|
||||||
|
mem.free(i32, first_allocator, empty)
|
||||||
|
if (first_calls[0] != 3) return 33
|
||||||
|
|
||||||
|
zero_sized_fallback [1]mut [0]u8 = undefined
|
||||||
|
zero_sized_failed bool = false
|
||||||
|
zero_sized []mut [0]u8 = mem.alloc([0]u8, first_allocator, 3) catch |_| {
|
||||||
|
zero_sized_failed = true
|
||||||
|
yield (&zero_sized_fallback).ptr[..0]
|
||||||
|
}
|
||||||
|
if (zero_sized_failed or zero_sized.len != 3 or first_calls[0] != 3) return 36
|
||||||
|
_ = zero_sized[2]
|
||||||
|
mem.free([0]u8, first_allocator, zero_sized)
|
||||||
|
if (first_calls[0] != 3) return 35
|
||||||
|
|
||||||
|
u64_fallback [1]mut u64 = undefined
|
||||||
|
overflow_fallback_failed bool = false
|
||||||
|
overflow_fallback []mut u64 = mem.alloc(u64, first_allocator, 0) catch |_| {
|
||||||
|
overflow_fallback_failed = true
|
||||||
|
yield (&u64_fallback).ptr[..0]
|
||||||
|
}
|
||||||
|
if (overflow_fallback_failed) return 37
|
||||||
|
overflow_failed bool = false
|
||||||
|
_ = mem.alloc(u64, first_allocator, max_value(usize)) catch |_| {
|
||||||
|
overflow_failed = true
|
||||||
|
yield overflow_fallback
|
||||||
|
}
|
||||||
|
if (overflow_failed == false or first_calls[0] != 3) return 40
|
||||||
|
|
||||||
|
typed_failed bool = false
|
||||||
|
typed []mut i32 = mem.alloc(i32, mem.c_allocator, 4) catch |_| {
|
||||||
|
typed_failed = true
|
||||||
|
yield (&i32_fallback).ptr[..0]
|
||||||
|
}
|
||||||
|
if (typed_failed) return 38
|
||||||
|
defer mem.free(i32, mem.c_allocator, typed)
|
||||||
|
typed[0] = 10
|
||||||
|
typed[3] = 20
|
||||||
|
if (typed[0] + typed[3] != 30) return 39
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -55,6 +55,12 @@ alloc func($T type, allocator Allocator, count usize) []mut T ! AllocError {
|
|||||||
return .out_of_memory
|
return .out_of_memory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free func($T type, allocator Allocator, memory []mut T) void {
|
||||||
|
if memory.len != 0 and size_of(T) != 0 {
|
||||||
|
raw_free(allocator, ptr_cast(u8, memory.ptr), memory.len * size_of(T), align_of(T))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_malloc_alignment usize :: 16 # ponytail: aarch64-macos libc malloc alignment assumption.
|
_malloc_alignment usize :: 16 # ponytail: aarch64-macos libc malloc alignment assumption.
|
||||||
|
|
||||||
_power_of_two func(value usize) bool {
|
_power_of_two func(value usize) bool {
|
||||||
|
|||||||
Reference in New Issue
Block a user