refactor: apply project style guidelines
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import "@std/mem"
|
||||
|
||||
#! owns item storage and retains its allocator; release storage with deinit.
|
||||
#! items is a borrowed view invalidated by growth or deinit.
|
||||
ArrayList proc($T type) type {
|
||||
return struct {
|
||||
items []mut T
|
||||
@@ -8,6 +10,7 @@ ArrayList proc($T type) type {
|
||||
}
|
||||
}
|
||||
|
||||
#! retain allocator for later growth and cleanup; this call does not allocate.
|
||||
init proc($T type, allocator mem.Allocator) ArrayList(T) {
|
||||
return ArrayList(T) {
|
||||
items = mem.empty(T),
|
||||
@@ -16,6 +19,7 @@ init proc($T type, allocator mem.Allocator) ArrayList(T) {
|
||||
}
|
||||
}
|
||||
|
||||
#! release owned storage using the retained allocator and reset the list.
|
||||
deinit proc($T type, list @mut ArrayList(T)) void {
|
||||
allocation :: list.items.ptr[..list.capacity]
|
||||
mem.free(list.allocator, allocation)
|
||||
@@ -23,10 +27,13 @@ deinit proc($T type, list @mut ArrayList(T)) void {
|
||||
list.capacity = 0
|
||||
}
|
||||
|
||||
reserve proc($T type, list @mut ArrayList(T), min_capacity usize) void ! mem.AllocError {
|
||||
if min_capacity <= list.capacity {
|
||||
return
|
||||
}
|
||||
#! grow owned storage with the retained allocator; growth invalidates borrowed item views.
|
||||
reserve proc(
|
||||
$T type,
|
||||
list @mut ArrayList(T),
|
||||
min_capacity usize,
|
||||
) void ! mem.AllocError {
|
||||
if (min_capacity <= list.capacity) return
|
||||
|
||||
new_capacity := 8
|
||||
if list.capacity >= 8 {
|
||||
@@ -41,9 +48,9 @@ reserve proc($T type, list @mut ArrayList(T), min_capacity usize) void ! mem.All
|
||||
new_capacity = min_capacity
|
||||
}
|
||||
|
||||
length usize :: list.items.len
|
||||
allocation []mut T :: list.items.ptr[..list.capacity]
|
||||
grown []mut T :: mem.realloc(list.allocator, allocation, new_capacity) catch |_| {
|
||||
length :: list.items.len
|
||||
allocation :: list.items.ptr[..list.capacity]
|
||||
grown :: mem.realloc(list.allocator, allocation, new_capacity) catch |_| {
|
||||
return .out_of_memory
|
||||
}
|
||||
list.items = grown.ptr[..length]
|
||||
@@ -52,7 +59,7 @@ reserve proc($T type, list @mut ArrayList(T), min_capacity usize) void ! mem.All
|
||||
}
|
||||
|
||||
append proc($T type, list @mut ArrayList(T), value T) void ! mem.AllocError {
|
||||
length usize :: list.items.len
|
||||
length :: list.items.len
|
||||
if length == maxval!(usize) {
|
||||
return .out_of_memory
|
||||
}
|
||||
|
||||
+8
-8
@@ -1,11 +1,11 @@
|
||||
# Build configuration surface for `brolang build` (v0).
|
||||
#
|
||||
# A project's `build.bro` imports this module and declares a top-level constant
|
||||
# named `config` of type `BuildConfig`. `brolang build [root]` type-checks
|
||||
# build.bro, reads the config, and writes root/build/name.
|
||||
#
|
||||
# Declarative and literal-only: one executable per build. List fields take an
|
||||
# address-of an array literal (`&["raylib"]`) and default to empty.
|
||||
#! build configuration surface for `brolang build` (v0).
|
||||
#!
|
||||
#! a project's `build.bro` imports this module and declares a top-level constant
|
||||
#! named `config` of type `BuildConfig`. `brolang build [root]` type-checks
|
||||
#! build.bro, reads the config, and writes root/build/name.
|
||||
#!
|
||||
#! declarative and literal-only: one executable per build. list fields take an
|
||||
#! address-of an array literal (`&["raylib"]`) and default to empty.
|
||||
BuildConfig :: struct {
|
||||
name []u8 # output executable name under root/build
|
||||
source []u8 # program package directory, relative to build.bro
|
||||
|
||||
+5
-11
@@ -17,23 +17,17 @@ print proc($format []u8, $Args type, args Args) void {
|
||||
handle = io.Handle{ file_desc = c_int(io.Stream.stderr) },
|
||||
write = write,
|
||||
}
|
||||
io.print(writer, format, Args, args) catch |_| {}
|
||||
io.print(format, Args, writer, args) catch |_| {}
|
||||
}
|
||||
|
||||
@hide
|
||||
@hide:file
|
||||
write proc(_ ?@mut anyopaque, handle io.Handle, bytes []u8) usize ! io.WriteError {
|
||||
request := bytes.len
|
||||
maximum :: usize(maxval!(c_long))
|
||||
if request > maximum {
|
||||
request = maximum
|
||||
}
|
||||
if (request > maximum) request = maximum
|
||||
while true {
|
||||
count :: c.write(handle.file_desc, bytes.ptr, c_ulong(request))
|
||||
if count >= 0 {
|
||||
return usize(count)
|
||||
}
|
||||
if c.__error()?^ != c.EINTR {
|
||||
return .write_failed
|
||||
}
|
||||
if (count >= 0) return usize(count)
|
||||
if (c.__error()?^ != c.EINTR) return .write_failed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ EnumMap proc($E, $V type) type {
|
||||
match typeinfo!(E) {
|
||||
.enum |info|: return struct {
|
||||
present [info.fields.len]mut bool # fixme: replace with bitset
|
||||
values [info.fields.len]mut V
|
||||
values [info.fields.len]mut V
|
||||
}
|
||||
else: compile_error!("EnumMap key must be an enum")
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import "@std/mem"
|
||||
import "@std/testing"
|
||||
|
||||
@hide:file
|
||||
TestEnum :: enum(u8) {
|
||||
ident = 3
|
||||
int = 8
|
||||
@@ -8,7 +9,7 @@ TestEnum :: enum(u8) {
|
||||
}
|
||||
|
||||
handles_sparse_enum_get test {
|
||||
names EnumMap(TestEnum, []u8) := init({
|
||||
names EnumMap(TestEnum, []u8) :: init({
|
||||
ident = "identifier",
|
||||
int = "integer",
|
||||
})
|
||||
|
||||
+8
-10
@@ -11,6 +11,8 @@ Entry proc($K, $V type) type {
|
||||
}
|
||||
}
|
||||
|
||||
#! owns entry storage and retains its allocator; release storage with deinit.
|
||||
#! keys and values are stored by value; any resources they reference remain caller-owned.
|
||||
HashMap proc(
|
||||
$K, $V type,
|
||||
$hash_key proc(key K) usize,
|
||||
@@ -27,6 +29,7 @@ StringHashMap proc($V type) type {
|
||||
return HashMap([]u8, V, str_hash, str_eql)
|
||||
}
|
||||
|
||||
#! retain allocator for later growth and cleanup; this call does not allocate.
|
||||
init proc(
|
||||
$K, $V type,
|
||||
$hash_key proc(key K) usize,
|
||||
@@ -40,7 +43,7 @@ init proc(
|
||||
}
|
||||
}
|
||||
|
||||
#! free the entries in the hash map.
|
||||
#! free owned entry storage using the retained allocator, not resources referenced by keys or values.
|
||||
#! note: this operation invalidates the map.
|
||||
deinit proc(
|
||||
$K, $V type,
|
||||
@@ -81,21 +84,17 @@ put proc(
|
||||
key K,
|
||||
value V,
|
||||
) void ! (PutError | mem.AllocError) {
|
||||
# check grow
|
||||
threshold :: map.entries.len - divtrunc!(map.entries.len, 4)
|
||||
if (map.entries.len == 0 or map.count + 1 > threshold) {
|
||||
old_entries :: map.entries
|
||||
new_size :: if (old_entries.len > 0) old_entries.len * 2 else 8
|
||||
new_entries :: try mem.alloc(Entry(K, V), map.allocator, new_size)
|
||||
|
||||
# zero new entries
|
||||
for (0..new_entries.len) |i| new_entries[i].hash = 0
|
||||
|
||||
# move old entries
|
||||
for old_entries |entry| {
|
||||
if (entry.hash == 0) continue
|
||||
|
||||
# find an empty slot
|
||||
idx := entry.hash & (new_entries.len - 1)
|
||||
while new_entries[idx].hash != 0 {
|
||||
idx = (idx + 1) & (new_entries.len - 1)
|
||||
@@ -108,7 +107,6 @@ put proc(
|
||||
mem.free(map.allocator, old_entries)
|
||||
}
|
||||
|
||||
# put new entry
|
||||
hash :: normalize(hash_key(key))
|
||||
idx := hash & (map.entries.len - 1)
|
||||
|
||||
@@ -132,7 +130,7 @@ put proc(
|
||||
}
|
||||
}
|
||||
|
||||
@hide
|
||||
@hide:file
|
||||
normalize proc(hash usize) usize {
|
||||
# mapping both 0 and 1 to 1 is safe because equality resolves
|
||||
# collisions (since hash and key must both be equal).
|
||||
@@ -142,10 +140,10 @@ normalize proc(hash usize) usize {
|
||||
|
||||
#! FNV-1a hash implementation.
|
||||
#! note: vulnerable to collision attacks.
|
||||
@hide
|
||||
@hide:file
|
||||
str_hash proc(key []u8) usize {
|
||||
hash u32 := 2166136261 # offset basis
|
||||
prime u32 := 16777619
|
||||
prime u32 :: 16777619
|
||||
|
||||
for key |byte| {
|
||||
product u64 :: u64(hash xor u32(byte)) * prime
|
||||
@@ -155,5 +153,5 @@ str_hash proc(key []u8) usize {
|
||||
return usize(hash)
|
||||
}
|
||||
|
||||
@hide
|
||||
@hide:file
|
||||
str_eql proc(a, b []u8) bool { return mem.eql(a, b) }
|
||||
|
||||
+20
-8
@@ -19,15 +19,18 @@ CloseError :: enum {
|
||||
close_failed
|
||||
}
|
||||
|
||||
#! open a caller-owned handle; release it with close.
|
||||
open proc(io Io, path [;0]u8, mode FileMode) File ! OpenError {
|
||||
handle Handle :: try io.vtable.open(io.context, path, mode)
|
||||
return File{ io = io, handle = handle }
|
||||
}
|
||||
|
||||
#! close the owned handle, invalidating its borrowed readers and writers.
|
||||
close proc(file File) void ! CloseError {
|
||||
try file.io.vtable.close(file.io.context, file.handle)
|
||||
}
|
||||
|
||||
#! borrow the file handle; the reader must not outlive the open file.
|
||||
reader proc(file File) Reader {
|
||||
return Reader {
|
||||
context = file.io.context,
|
||||
@@ -36,6 +39,7 @@ reader proc(file File) Reader {
|
||||
}
|
||||
}
|
||||
|
||||
#! borrow the file handle; the writer must not outlive the open file.
|
||||
writer proc(file File) Writer {
|
||||
return Writer {
|
||||
context = file.io.context,
|
||||
@@ -44,7 +48,8 @@ writer proc(file File) Writer {
|
||||
}
|
||||
}
|
||||
|
||||
@hide system_read proc(_ ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError {
|
||||
@hide:file
|
||||
system_read proc(_ ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError {
|
||||
request := buffer.len
|
||||
maximum usize :: usize(maxval!(c_long))
|
||||
if (request > maximum) request = maximum
|
||||
@@ -60,7 +65,8 @@ writer proc(file File) Writer {
|
||||
}
|
||||
}
|
||||
|
||||
@hide system_write proc(_ ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError {
|
||||
@hide:file
|
||||
system_write proc(_ ?@mut anyopaque, handle Handle, bytes []u8) usize ! WriteError {
|
||||
request := bytes.len
|
||||
maximum usize :: usize(maxval!(c_long))
|
||||
if (request > maximum) request = maximum
|
||||
@@ -76,7 +82,8 @@ writer proc(file File) Writer {
|
||||
}
|
||||
}
|
||||
|
||||
@hide system_open proc(_ ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError {
|
||||
@hide:file
|
||||
system_open proc(_ ?@mut anyopaque, path [;0]u8, mode FileMode) Handle ! OpenError {
|
||||
flags := c.O_RDONLY
|
||||
match mode {
|
||||
.read_only: flags = c.O_RDONLY
|
||||
@@ -90,23 +97,28 @@ writer proc(file File) Writer {
|
||||
}
|
||||
}
|
||||
|
||||
@hide system_close proc(_ ?@mut anyopaque, handle Handle) void ! CloseError {
|
||||
@hide:file
|
||||
system_close proc(_ ?@mut anyopaque, handle Handle) void ! CloseError {
|
||||
if (c.close(handle.file_desc) != 0) return .close_failed
|
||||
}
|
||||
|
||||
@hide system_stdin proc(_ ?@mut anyopaque) Handle {
|
||||
@hide:file
|
||||
system_stdin proc(_ ?@mut anyopaque) Handle {
|
||||
return Handle{ file_desc = c_int(Stream.stdin) }
|
||||
}
|
||||
|
||||
@hide system_stdout proc(_ ?@mut anyopaque) Handle {
|
||||
@hide:file
|
||||
system_stdout proc(_ ?@mut anyopaque) Handle {
|
||||
return Handle{ file_desc = c_int(Stream.stdout) }
|
||||
}
|
||||
|
||||
@hide system_stderr proc(_ ?@mut anyopaque) Handle {
|
||||
@hide:file
|
||||
system_stderr proc(_ ?@mut anyopaque) Handle {
|
||||
return Handle{ file_desc = c_int(Stream.stderr) }
|
||||
}
|
||||
|
||||
@hide system_vtable IoVTable :: IoVTable {
|
||||
@hide:file
|
||||
system_vtable IoVTable :: IoVTable {
|
||||
read = system_read,
|
||||
write = system_write,
|
||||
open = system_open,
|
||||
|
||||
+33
-14
@@ -1,4 +1,6 @@
|
||||
import "@ffi/c"
|
||||
import "@std/mem"
|
||||
# typeinfo! requires this import even without a qualified meta reference.
|
||||
import "@std/meta"
|
||||
|
||||
ReadError :: enum {
|
||||
@@ -23,6 +25,7 @@ Stream :: enum(c_int) {
|
||||
stderr = c.STDERR_FILENO
|
||||
}
|
||||
|
||||
#! context and vtable are borrowed and must outlive derived readers and writers.
|
||||
Io :: struct {
|
||||
context ?@mut anyopaque
|
||||
vtable @IoVTable
|
||||
@@ -38,12 +41,14 @@ IoVTable :: struct {
|
||||
stderr @proc(context ?@mut anyopaque) Handle
|
||||
}
|
||||
|
||||
#! borrows its context and handle; it does not close the handle.
|
||||
Reader :: struct {
|
||||
context ?@mut anyopaque
|
||||
handle Handle
|
||||
read @proc(context ?@mut anyopaque, handle Handle, buffer []mut u8) usize ! ReadError
|
||||
}
|
||||
|
||||
#! borrows its context and handle; it does not close the handle.
|
||||
Writer :: struct {
|
||||
context ?@mut anyopaque
|
||||
handle Handle
|
||||
@@ -101,7 +106,7 @@ stderr proc(io Io) Writer {
|
||||
}
|
||||
}
|
||||
|
||||
print proc(output Writer, $format []u8, $Args type, args Args) void ! WriteError {
|
||||
print proc($format []u8, $Args type, output Writer, args Args) void ! WriteError {
|
||||
inline for parse_format(format.len, format, Args) |token| {
|
||||
match token.kind {
|
||||
.unused: break
|
||||
@@ -120,14 +125,19 @@ print proc(output Writer, $format []u8, $Args type, args Args) void ! WriteError
|
||||
}
|
||||
|
||||
@hide:file
|
||||
write_integer_signed proc(output Writer, value i64, base u64, uppercase bool) void ! WriteError {
|
||||
write_integer_signed proc(
|
||||
output Writer,
|
||||
value i64,
|
||||
base u64,
|
||||
uppercase bool,
|
||||
) void ! WriteError {
|
||||
buffer [65]mut u8 := undefined
|
||||
end := buffer.len
|
||||
current := value
|
||||
|
||||
while true {
|
||||
digit_value i64 :: rem!(current, i64(base))
|
||||
digit := if (digit_value < 0)
|
||||
digit :: if (digit_value < 0)
|
||||
u8(-digit_value)
|
||||
else
|
||||
u8(digit_value)
|
||||
@@ -152,7 +162,12 @@ write_integer_signed proc(output Writer, value i64, base u64, uppercase bool) vo
|
||||
}
|
||||
|
||||
@hide:file
|
||||
write_integer_unsigned proc(output Writer, value u64, base u64, uppercase bool) void ! WriteError {
|
||||
write_integer_unsigned proc(
|
||||
output Writer,
|
||||
value u64,
|
||||
base u64,
|
||||
uppercase bool,
|
||||
) void ! WriteError {
|
||||
buffer [65]mut u8 := undefined
|
||||
end := buffer.len
|
||||
current := value
|
||||
@@ -201,9 +216,7 @@ FormatToken :: struct {
|
||||
@hide:file
|
||||
parse_format proc($N usize, $format []u8, $Args type) [N]mut FormatToken {
|
||||
tokens [N]mut FormatToken := undefined
|
||||
for (usize(0))..format.len |index| {
|
||||
tokens[index] = FormatToken{ kind = .unused, start = 0, end = 0, field = "" }
|
||||
}
|
||||
mem.set(FormatToken, tokens[..], FormatToken{ kind = .unused, start = 0, end = 0, field = "" })
|
||||
|
||||
field_count := 0
|
||||
match typeinfo!(Args) {
|
||||
@@ -314,7 +327,7 @@ parse_format proc($N usize, $format []u8, $Args type) [N]mut FormatToken {
|
||||
if literal_start < format.len {
|
||||
tokens[token_count] = FormatToken{
|
||||
kind = .literal,
|
||||
start = literal_start,
|
||||
start = literal_start,
|
||||
end = format.len,
|
||||
field = "",
|
||||
}
|
||||
@@ -351,7 +364,13 @@ scalar_or_distinct_type proc($T type) bool {
|
||||
}
|
||||
|
||||
@hide:file
|
||||
write_integer proc(output Writer, $T type, value T, base u64, uppercase bool) void ! WriteError {
|
||||
write_integer proc(
|
||||
$T type,
|
||||
output Writer,
|
||||
value T,
|
||||
base u64,
|
||||
uppercase bool,
|
||||
) void ! WriteError {
|
||||
match typeinfo!(T) {
|
||||
.integer: if minval!(T) < 0 {
|
||||
try write_integer_signed(output, i64(value), base, uppercase)
|
||||
@@ -369,7 +388,7 @@ write_integer proc(output Writer, $T type, value T, base u64, uppercase bool) vo
|
||||
|
||||
# note: libc keeps float formatting small; replace it with a native shortest-roundtrip writer if locale independence matters.
|
||||
@hide:file
|
||||
write_float proc(output Writer, $T type, value T, scientific bool) void ! WriteError {
|
||||
write_float proc($T type, output Writer, value T, scientific bool) void ! WriteError {
|
||||
match typeinfo!(T) {
|
||||
.float: {
|
||||
buffer [64]mut u8 := undefined
|
||||
@@ -395,7 +414,7 @@ write_float proc(output Writer, $T type, value T, scientific bool) void ! WriteE
|
||||
}
|
||||
|
||||
@hide:file
|
||||
write_decimal proc(output Writer, $T type, value T) void ! WriteError {
|
||||
write_decimal proc($T type, output Writer, value T) void ! WriteError {
|
||||
match typeinfo!(T) {
|
||||
.integer: try write_integer(output, value, 10, false)
|
||||
.float: try write_float(output, value, false)
|
||||
@@ -409,13 +428,13 @@ write_decimal proc(output Writer, $T type, value T) void ! WriteError {
|
||||
}
|
||||
|
||||
@hide:file
|
||||
write_character proc(output Writer, $T type, value T) void ! WriteError {
|
||||
write_character proc($T type, output Writer, value T) void ! WriteError {
|
||||
match typeinfo!(T) {
|
||||
.integer: {
|
||||
if minval!(T) < 0 or maxval!(T) > 255 {
|
||||
compile_error!("io.print '{c}' requires an unsigned integer that fits in u8")
|
||||
}
|
||||
buffer [1]u8 := [u8(value)]
|
||||
buffer [1]u8 :: [u8(value)]
|
||||
try write_all(output, buffer[..])
|
||||
}
|
||||
.distinct |backing|: if scalar_or_distinct_type(backing) {
|
||||
@@ -428,7 +447,7 @@ write_character proc(output Writer, $T type, value T) void ! WriteError {
|
||||
}
|
||||
|
||||
@hide:file
|
||||
write_default proc(output Writer, $T type, value T) void ! WriteError {
|
||||
write_default proc($T type, output Writer, value T) void ! WriteError {
|
||||
match typeinfo!(T) {
|
||||
.bool: if value {
|
||||
try write_all(output, "true")
|
||||
|
||||
+47
-21
@@ -4,6 +4,7 @@ AllocError :: enum {
|
||||
out_of_memory
|
||||
}
|
||||
|
||||
#! allocator context and vtable are borrowed and must outlive allocations made through them.
|
||||
Allocator :: struct {
|
||||
context ?@mut anyopaque
|
||||
vtable @AllocatorVTable
|
||||
@@ -15,23 +16,40 @@ AllocatorVTable :: struct {
|
||||
free @proc(context ?@mut anyopaque, memory ?*mut u8, size usize, alignment usize) void
|
||||
}
|
||||
|
||||
raw_alloc proc(allocator Allocator, size usize, alignment usize) ?*mut u8 {
|
||||
#! returned storage belongs to the caller; release it with raw_free using the same allocator, size, and alignment.
|
||||
raw_alloc proc(
|
||||
allocator Allocator,
|
||||
size usize,
|
||||
alignment usize,
|
||||
) ?*mut u8 {
|
||||
return allocator.vtable.alloc(allocator.context, size, alignment)
|
||||
}
|
||||
|
||||
raw_realloc proc(allocator Allocator, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 {
|
||||
#! resize caller-owned storage with its original allocator and alignment.
|
||||
#! on allocation failure with nonzero new_size, original storage remains caller-owned; release the result with raw_free.
|
||||
raw_realloc proc(
|
||||
allocator Allocator,
|
||||
memory ?*mut u8,
|
||||
old_size usize,
|
||||
new_size usize,
|
||||
alignment usize,
|
||||
) ?*mut u8 {
|
||||
return allocator.vtable.realloc(allocator.context, memory, old_size, new_size, alignment)
|
||||
}
|
||||
|
||||
raw_free proc(allocator Allocator, memory ?*mut u8, size usize, alignment usize) void {
|
||||
#! release caller-owned storage with its original allocator, size, and alignment.
|
||||
raw_free proc(
|
||||
allocator Allocator,
|
||||
memory ?*mut u8,
|
||||
size usize,
|
||||
alignment usize,
|
||||
) void {
|
||||
allocator.vtable.free(allocator.context, memory, size, alignment)
|
||||
}
|
||||
|
||||
eql proc($T type, left, right []T) bool {
|
||||
if (left.len != right.len) return false
|
||||
for (0..left.len) |i| if (left[i] != right[i]) {
|
||||
return false
|
||||
}
|
||||
for (0..left.len) |i| if (left[i] != right[i]) return false
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -42,17 +60,18 @@ set proc($T type, destination []mut T, value T) void {
|
||||
}
|
||||
|
||||
#! allocate memory for a slice of type `T` with `count` elements.
|
||||
#! returned storage belongs to the caller; release it with free using the same allocator.
|
||||
alloc proc($T type, allocator Allocator, count usize) []mut T ! AllocError {
|
||||
if (count == 0) return empty_slice(T, 0)
|
||||
|
||||
element_size usize :: sizeof!(T)
|
||||
element_size :: sizeof!(T)
|
||||
if (element_size == 0) return empty_slice(T, count)
|
||||
|
||||
if count > divtrunc!(maxval!(usize), element_size) {
|
||||
return .out_of_memory
|
||||
}
|
||||
|
||||
memory := raw_alloc(allocator, count * element_size, alignof!(T))
|
||||
memory :: raw_alloc(allocator, count * element_size, alignof!(T))
|
||||
if memory |bytes| {
|
||||
pointer *mut T :: ptrcast!(bytes)
|
||||
return pointer[..count]
|
||||
@@ -64,17 +83,22 @@ alloc proc($T type, allocator Allocator, count usize) []mut T ! AllocError {
|
||||
#! reallocate memory for a slice of type `T` with `new_count` elements.
|
||||
#! reallocating with `new_count == 0` will free the memory and return an empty slice.
|
||||
#! note: memory must be reallocated with the same allocator that was used to allocate it.
|
||||
realloc proc($T type, allocator Allocator, memory []mut T, new_count usize) []mut T ! AllocError {
|
||||
if new_count == memory.len {
|
||||
return memory
|
||||
}
|
||||
#! the result belongs to the caller; release it with free using that allocator.
|
||||
#! on failure the original slice remains valid and caller-owned.
|
||||
realloc proc(
|
||||
$T type,
|
||||
allocator Allocator,
|
||||
memory []mut T,
|
||||
new_count usize,
|
||||
) []mut T ! AllocError {
|
||||
if (new_count == memory.len) return memory
|
||||
|
||||
if new_count == 0 {
|
||||
free(allocator, memory)
|
||||
return empty_slice(T, 0)
|
||||
}
|
||||
|
||||
element_size usize :: sizeof!(T)
|
||||
element_size :: sizeof!(T)
|
||||
if (element_size == 0) return empty_slice(T, new_count)
|
||||
if (new_count > divtrunc!(maxval!(usize), element_size)) return .out_of_memory
|
||||
|
||||
@@ -85,7 +109,7 @@ realloc proc($T type, allocator Allocator, memory []mut T, new_count usize) []mu
|
||||
old_size = memory.len * element_size
|
||||
}
|
||||
|
||||
resized := raw_realloc(
|
||||
resized :: raw_realloc(
|
||||
allocator,
|
||||
old_memory,
|
||||
old_size,
|
||||
@@ -105,8 +129,9 @@ realloc proc($T type, allocator Allocator, memory []mut T, new_count usize) []mu
|
||||
#! note: memory must be freed with the same allocator that was used to allocate it.
|
||||
free proc($T type, allocator Allocator, memory []T) void {
|
||||
if (memory.len == 0 or sizeof!(T) == 0) return
|
||||
raw_free(allocator, ptrcast!(
|
||||
constcast!(memory).ptr),
|
||||
raw_free(
|
||||
allocator,
|
||||
ptrcast!(constcast!(memory).ptr),
|
||||
memory.len * sizeof!(T),
|
||||
alignof!(T),
|
||||
)
|
||||
@@ -122,14 +147,15 @@ empty_slice proc($T type, count usize) []mut T {
|
||||
}
|
||||
|
||||
#! get an empty slice of type `T` with 0 elements.
|
||||
#! the view borrows shared empty storage and requires no cleanup.
|
||||
empty proc($T type) []mut T {
|
||||
return empty_slice(T, 0)
|
||||
}
|
||||
|
||||
@hide
|
||||
@hide:file
|
||||
empty_storage [1]mut u64 := [0]
|
||||
|
||||
@hide
|
||||
@hide:file
|
||||
malloc_alignment usize :: 16 # note: aarch64-macos libc malloc alignment assumption.
|
||||
|
||||
@hide:file
|
||||
@@ -137,7 +163,7 @@ power_of_two proc(value usize) bool {
|
||||
if (value == 0) return false
|
||||
current := value
|
||||
while current > 1 {
|
||||
half := divtrunc!(current, 2)
|
||||
half :: divtrunc!(current, 2)
|
||||
if (half * 2 != current) return false
|
||||
current = half
|
||||
}
|
||||
@@ -150,7 +176,7 @@ c_alloc proc(_ ?@mut anyopaque, size usize, alignment usize) ?*mut u8 {
|
||||
if (alignment <= malloc_alignment) return ptrcast!(c.malloc(c_ulong(size)))
|
||||
|
||||
memory [1]mut ?*mut anyopaque := [null]
|
||||
status := c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
|
||||
status :: c.posix_memalign((&memory).ptr, c_ulong(alignment), c_ulong(size))
|
||||
if (status != 0) return null
|
||||
|
||||
return ptrcast!(memory[0])
|
||||
@@ -176,7 +202,7 @@ c_realloc proc(
|
||||
return ptrcast!(c.realloc(old_memory, c_ulong(new_size)))
|
||||
}
|
||||
|
||||
new_memory := c_alloc(null, new_size, alignment)
|
||||
new_memory :: c_alloc(null, new_size, alignment)
|
||||
if new_memory |new_bytes| {
|
||||
copy_size := old_size
|
||||
if (new_size < copy_size) copy_size = new_size
|
||||
|
||||
@@ -15,7 +15,10 @@ fills_typed_values_within_slice_boundaries test {
|
||||
try testing.expect_equal(u32(305419896), values[2])
|
||||
try testing.expect_equal(u32(4), values[3])
|
||||
|
||||
records [2]mut Record := [Record{ tag = 0, id = 0 }, Record{ tag = 0, id = 0 }]
|
||||
records [2]mut Record := [
|
||||
Record{ tag = 0, id = 0 },
|
||||
Record{ tag = 0, id = 0 },
|
||||
]
|
||||
set(records[..], Record{ tag = 3, id = maxval!(u32) })
|
||||
for records |record| {
|
||||
try testing.expect_equal(u8(3), record.tag)
|
||||
|
||||
+10
-4
@@ -1,18 +1,24 @@
|
||||
import "@std/testing"
|
||||
|
||||
@hide:file
|
||||
TestTokenKind :: enum(u8) {
|
||||
ident = 3
|
||||
int = 8
|
||||
eof = 21
|
||||
}
|
||||
|
||||
@hide:file
|
||||
TestNames :: alias EnumFieldStruct(TestTokenKind, ?[]u8, some!(null))
|
||||
@hide:file
|
||||
TestArrayAlias :: alias [3]u16
|
||||
@hide:file
|
||||
TestInner :: distinct u16
|
||||
@hide:file
|
||||
TestOuter :: distinct TestInner
|
||||
@hide:file
|
||||
TestOuterAlias :: alias TestOuter
|
||||
|
||||
@hide
|
||||
@hide:file
|
||||
array_info_matches proc($Array, $Child type, $len usize) bool {
|
||||
return match typeinfo!(Array) {
|
||||
.array |info|: info.child == Child and info.len == len
|
||||
@@ -20,7 +26,7 @@ array_info_matches proc($Array, $Child type, $len usize) bool {
|
||||
}
|
||||
}
|
||||
|
||||
@hide
|
||||
@hide:file
|
||||
distinct_info_matches proc($Distinct, $Backing type) bool {
|
||||
return match typeinfo!(Distinct) {
|
||||
.distinct |backing|: backing == Backing
|
||||
@@ -43,7 +49,7 @@ distinct_reflection_exposes_immediate_backing test {
|
||||
}
|
||||
|
||||
enum_field_struct_defaults test {
|
||||
names TestNames := {
|
||||
names TestNames :: {
|
||||
ident = "identifier",
|
||||
int = "integer",
|
||||
}
|
||||
@@ -60,6 +66,6 @@ enum_field_struct_defaults test {
|
||||
|
||||
if (field!(names, "eof")) |_| try testing.expect(false)
|
||||
|
||||
empty TestNames := {}
|
||||
empty TestNames :: {}
|
||||
if (field!(empty, "ident")) |_| try testing.expect(false)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ Pair proc($V type) type {
|
||||
}
|
||||
|
||||
#! initializes a static string map from a list of key-value pairs (constructed at compile-time).
|
||||
#! returned views use compile-time storage and require no cleanup.
|
||||
init proc($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
|
||||
if N > usize(maxval!(u32)) {
|
||||
compile_error!("static string map has too many entries")
|
||||
@@ -39,7 +40,7 @@ init proc($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
|
||||
}
|
||||
|
||||
if N == 0 {
|
||||
len_indexes [0]u32 := undefined
|
||||
len_indexes [0]u32 :: undefined
|
||||
return StaticStringMap(V){
|
||||
keys = keys[..],
|
||||
values = values[..],
|
||||
@@ -83,7 +84,7 @@ init proc($V type, $N usize, $entries [N]Pair(V)) StaticStringMap(V) {
|
||||
get proc($V type, map @StaticStringMap(V), key []u8) ?V {
|
||||
if (map.keys.len == 0 or key.len > maxval!(u32)) return null
|
||||
|
||||
length := u32(key.len)
|
||||
length :: u32(key.len)
|
||||
if (length < map.min_len or length > map.max_len) return null
|
||||
|
||||
idx := usize(map.len_indexes[usize(length)])
|
||||
|
||||
Reference in New Issue
Block a user