From aea8a35c1c22a464ff8c419e82b8cd6f38a8260b Mon Sep 17 00:00:00 2001 From: StunxFS Date: Mon, 1 Jan 2024 11:02:21 -0400 Subject: [PATCH] feat(core): add a `mem` module for memory management Rename `Box` to `Boxedptr` and add more useful functions to it, in addition to renaming some existing functions. --- lib/core/src/Box.ri | 36 -------- lib/core/src/DynArray.ri | 38 +++++---- lib/core/src/Slice.ri | 6 +- lib/core/src/StringBuilder.c.ri | 3 +- lib/core/src/array.c.ri | 3 +- lib/core/src/backtrace.c.ri | 3 +- lib/core/src/int.ri | 10 ++- lib/core/src/mem.c.ri | 118 -------------------------- lib/core/src/mem/Boxedptr.c.ri | 75 ++++++++++++++++ lib/core/src/mem/alloc.c.ri | 65 ++++++++++++++ lib/core/src/mem/mod.c.ri | 68 +++++++++++++++ lib/core/src/rune.ri | 4 +- lib/core/src/string.c.ri | 19 +++-- lib/core/src/uint.ri | 10 ++- lib/rivet/src/builder/mod.ri | 10 +-- lib/rivet/src/checker/builtin_call.ri | 3 +- lib/rivet/src/checker/types.ri | 28 +++--- lib/rivet/src/parser/decls.ri | 2 +- lib/rivet/src/resolver/Register.ri | 15 +++- lib/rivet/src/resolver/types.ri | 2 +- lib/std/src/fs/File.c.ri | 4 +- lib/std/src/fs/Path.c.ri | 18 ++-- lib/std/src/mem/mod.ri | 12 +-- rivetc/src/__init__.py | 2 +- rivetc/src/codegen/__init__.py | 16 ++-- rivetc/src/parser.py | 2 +- rivetc/src/register.py | 12 ++- 27 files changed, 331 insertions(+), 253 deletions(-) delete mode 100644 lib/core/src/Box.ri delete mode 100644 lib/core/src/mem.c.ri create mode 100644 lib/core/src/mem/Boxedptr.c.ri create mode 100644 lib/core/src/mem/alloc.c.ri create mode 100644 lib/core/src/mem/mod.c.ri diff --git a/lib/core/src/Box.ri b/lib/core/src/Box.ri deleted file mode 100644 index 72d1f36b0..000000000 --- a/lib/core/src/Box.ri +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (C) 2023-present Jose Mendoza - All rights reserved. Use of this -// source code is governed by an MIT license that can be found in the LICENSE -// file. - -struct Box { - mut value: rawptr; - mut ref_count: uint; - mut dtor: ?func(rawptr); - - func new(size: uint, dtor: ?func(rawptr)) -> rawptr { - boxed := unsafe { @as(&mut Self, mem_alloc(@size_of(Self) + size)) }; - boxed.ref_count = 1; - boxed.dtor = dtor; - boxed.value = unsafe { @ptr_add(@as([&]uint8, boxed), @size_of(Self)) }; - return boxed.value; - } - - func retain(ptr: rawptr) -> rawptr { - boxed := unsafe { @as(&mut Self, @ptr_sub(@as([&]uint8, ptr), @size_of(Self))) }; - boxed.ref_count += 1; - return ptr; - } - - func release(ptr: rawptr) { - boxed := unsafe { @as(&mut Self, @ptr_sub(@as([&]uint8, ptr), @size_of(Self))) }; - boxed.ref_count -= 1; - if boxed.ref_count == 0 { - if dtor := boxed.dtor { - dtor(boxed.value); - } - unsafe { - mem_dealloc(boxed); - } - } - } -} diff --git a/lib/core/src/DynArray.ri b/lib/core/src/DynArray.ri index cc76d6739..f69395d50 100644 --- a/lib/core/src/DynArray.ri +++ b/lib/core/src/DynArray.ri @@ -2,6 +2,8 @@ // source code is governed by an MIT license that can be found in the LICENSE // file. +import ./mem; + #[boxed] struct DynArray { mut ptr: rawptr; @@ -11,19 +13,19 @@ struct DynArray { #[unsafe; inline] func new(elem_size: uint, cap: uint) -> Self { - return Self(mem_zeroed(cap * elem_size), elem_size, 0, cap); + return Self(mem.raw_zeroed(cap * elem_size), elem_size, 0, cap); } #[unsafe; inline] func new_with_len(elem_size: uint, len: uint, cap: uint) -> Self { cap_ := if cap < len { len } else { cap }; - return Self(mem_zeroed(cap_ * elem_size), elem_size, len, cap); + return Self(mem.raw_zeroed(cap_ * elem_size), elem_size, len, cap); } #[unsafe; inline] func new_with_init(init: rawptr, elem_size: uint, len: uint, cap: uint) -> Self { cap_ := if cap < len { len } else { cap }; - dyn_array := Self(mem_alloc(cap_ * elem_size), elem_size, len, cap); + dyn_array := Self(mem.raw_alloc(cap_ * elem_size), elem_size, len, cap); mut i: uint := 0; while i < len : i += 1 { unsafe { @@ -35,8 +37,8 @@ struct DynArray { #[unsafe] func from_array(arr: rawptr, elem_size: uint, len: uint) -> Self { - dyn_array := Self(mem_alloc(len * elem_size), elem_size, len, len); - mem_copy(dyn_array.ptr, arr, len * elem_size); + dyn_array := Self(mem.raw_alloc(len * elem_size), elem_size, len, len); + mem.copy(dyn_array.ptr, arr, len * elem_size); return dyn_array; } @@ -60,7 +62,7 @@ struct DynArray { #[unsafe; inline] func raw_set(self, idx: uint, val: rawptr) { unsafe { - mem_copy( + mem.copy( @ptr_add(@as([&]mut uint8, self.ptr), self.elem_size * idx), val, self.elem_size ); @@ -72,7 +74,7 @@ struct DynArray { runtime_error("dynamic array index out of range (index: {}, len: {})", idx, self.len); } unsafe { - mem_copy( + mem.copy( @ptr_add(@as([&]mut uint8, self.ptr), self.elem_size * idx), val, self.elem_size ); @@ -84,7 +86,7 @@ struct DynArray { self.reserve(self.len + 1); } unsafe { - mem_copy(@ptr_add(self.ptr, self.elem_size * self.len), val, self.elem_size); + mem.copy(@ptr_add(self.ptr, self.elem_size * self.len), val, self.elem_size); } self.len += 1; } @@ -117,16 +119,16 @@ struct DynArray { old_ptr := self.ptr; new_size := self.len - size; new_cap: uint := if new_size == 0 { 1 } else { new_size }; - self.ptr = mem_zeroed(new_cap * self.elem_size); - mem_copy(self.ptr, old_ptr, i * self.elem_size); + self.ptr = mem.raw_zeroed(new_cap * self.elem_size); + mem.copy(self.ptr, old_ptr, i * self.elem_size); unsafe { - mem_copy( + mem.copy( @ptr_add(self.ptr, i * self.elem_size), @ptr_add(old_ptr, (i + size) * self.elem_size), (self.len - i - size) * self.elem_size ); if no_slices { - mem_dealloc(old_ptr); + mem.raw_dealloc(old_ptr); } } self.len = new_size; @@ -154,7 +156,7 @@ struct DynArray { if self.len != rhs.len { return false; } - return mem_cmp(self.ptr, rhs.ptr, self.len) == 0; + return mem.cmp(self.ptr, rhs.ptr, self.len) == 0; } #[inline] @@ -171,8 +173,8 @@ struct DynArray { cap *= 2; } new_size := cap * self.elem_size; - new_ptr := mem_alloc(new_size); - mem_copy(new_ptr, self.ptr, self.len * self.elem_size); + new_ptr := mem.raw_alloc(new_size); + mem.copy(new_ptr, self.ptr, self.len * self.elem_size); self.ptr = new_ptr; self.cap = cap; } @@ -204,14 +206,14 @@ struct DynArray { if size == 0 { size = 1; } - dyn_array := Self(mem_zeroed(size), self.elem_size, self.len, self.cap); - mem_copy(dyn_array.ptr, self.ptr, size); + dyn_array := Self(mem.raw_zeroed(size), self.elem_size, self.len, self.cap); + mem.copy(dyn_array.ptr, self.ptr, size); return dyn_array; } func __destroy__(self) { unsafe { - mem_dealloc(self.ptr); + mem.raw_dealloc(self.ptr); } } } diff --git a/lib/core/src/Slice.ri b/lib/core/src/Slice.ri index f7800362b..f03ea5111 100644 --- a/lib/core/src/Slice.ri +++ b/lib/core/src/Slice.ri @@ -2,6 +2,8 @@ // source code is governed by an MIT license that can be found in the LICENSE // file. +import ./mem; + struct Slice { ptr: rawptr; elem_size: uint; @@ -29,7 +31,7 @@ struct Slice { runtime_error("slice index out of range (index: {}, len: {})", idx, self.len); } unsafe { - mem_copy( + mem.copy( @ptr_add(@as([&]mut uint8, self.ptr), self.elem_size * idx), val, self.elem_size ); @@ -71,7 +73,7 @@ struct Slice { if self.len != rhs.len { return false; } - return mem_cmp(self.ptr, rhs.ptr, self.len) == 0; + return mem.cmp(self.ptr, rhs.ptr, self.len) == 0; } #[inline] diff --git a/lib/core/src/StringBuilder.c.ri b/lib/core/src/StringBuilder.c.ri index 7074bc85e..59ff3e619 100644 --- a/lib/core/src/StringBuilder.c.ri +++ b/lib/core/src/StringBuilder.c.ri @@ -3,6 +3,7 @@ // file. import c/libc; +import ./mem; #[boxed] pub struct StringBuilder < Stringable { @@ -42,7 +43,7 @@ pub struct StringBuilder < Stringable { #[unsafe] pub func write_raw_with_len(mut self, s: [&]uint8, len: uint) { self.inner.reserve(self.inner.len + len); - mem_copy( + mem.copy( unsafe { @ptr_add(self.inner.ptr, self.inner.elem_size * self.inner.len) }, s, len ); diff --git a/lib/core/src/array.c.ri b/lib/core/src/array.c.ri index 1bc2ed653..5df42a400 100644 --- a/lib/core/src/array.c.ri +++ b/lib/core/src/array.c.ri @@ -3,12 +3,13 @@ // file. import c/libc; +import ./mem; func array_init_set(arr: rawptr, elem_size: uint, len: uint, value: rawptr) { mut i: uint := 0; while i < len : i += 1 { unsafe { - mem_copy(@ptr_add(@as([&]mut uint8, arr), elem_size * i), value, elem_size); + mem.copy(@ptr_add(@as([&]mut uint8, arr), elem_size * i), value, elem_size); } } } diff --git a/lib/core/src/backtrace.c.ri b/lib/core/src/backtrace.c.ri index 20f2c78c0..3453bd946 100644 --- a/lib/core/src/backtrace.c.ri +++ b/lib/core/src/backtrace.c.ri @@ -6,6 +6,7 @@ $if !_RELEASE_ { // skip backtrace in release mode #![compile_c_source("../thirdparty/libbacktrace/backtrace.c")] import c; + import ./mem; struct BacktraceState; @@ -40,7 +41,7 @@ $if !_RELEASE_ { // skip backtrace in release mode ) -> int32 { unsafe { if safe_fn_ptr := fn_ptr { - if mem_cmp(safe_fn_ptr, c"_R4core4mainF", 13) == 0 { + if mem.cmp(safe_fn_ptr, c"_R4core4mainF", 13) == 0 { return -1; // stop backtracing } } diff --git a/lib/core/src/int.ri b/lib/core/src/int.ri index 74cac38aa..1b786ad38 100644 --- a/lib/core/src/int.ri +++ b/lib/core/src/int.ri @@ -2,6 +2,8 @@ // source code is governed by an MIT license that can be found in the LICENSE // file. +import ./mem; + var digit_pairs := "0010203040506070809001112131415161718191021222324252627" "28292031323334353637383930414243444546474849405152535455565758595061626364656667" "68696071727374757677787970818283848586878889809192939495969798999"; @@ -54,7 +56,7 @@ extend int32 < Stringable { } unsafe { mut index := @as(uint, max); - buf := @as([&]mut uint8, mem_alloc(index + 1)); + buf := @as([&]mut uint8, mem.raw_alloc(index + 1)); buf[index] = 0; index -= 1; @@ -82,7 +84,7 @@ extend int32 < Stringable { } diff := @as(uint, max) - index; - mem_move(buf, @ptr_add(buf, index), diff + 1); + mem.move(buf, @ptr_add(buf, index), diff + 1); return string.from_raw_with_len(buf, diff); } } @@ -114,7 +116,7 @@ extend int64 < Stringable { unsafe { max := 20; mut index := @as(uint, max); - buf := @as([&]mut uint8, mem_alloc(index + 1)); + buf := @as([&]mut uint8, mem.raw_alloc(index + 1)); buf[index] = 0; index -= 1; @@ -142,7 +144,7 @@ extend int64 < Stringable { } diff := @as(uint, max) - index; - mem_move(buf, @ptr_add(buf, index), diff + 1); + mem.move(buf, @ptr_add(buf, index), diff + 1); return string.from_raw_with_len(buf, diff); } } diff --git a/lib/core/src/mem.c.ri b/lib/core/src/mem.c.ri deleted file mode 100644 index 702521599..000000000 --- a/lib/core/src/mem.c.ri +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (C) 2023-present Jose Mendoza - All rights reserved. Use of this -// source code is governed by an MIT license that can be found in the LICENSE -// file. - -import c/libc; - -/// Allocates dynamically a `size` bytes block of memory on the heap. -/// Returns a pointer to the memory address of the allocated space. -/// -/// NOTE: Unlike the `mem.zeroed` function, `mem.alloc` will not zero -/// the memory block. -pub func mem_alloc(size: uint) -> rawptr { - if new := unsafe { libc.malloc(size) } { - return new; - } - runtime_error("cannot allocate memory"); -} - -/// Allocates dynamically a zeroed `size` bytes block of memory on the heap. -/// Returns a pointer to the memory address of the allocated space. -pub func mem_zeroed(size: uint) -> rawptr { - if ptr := unsafe { libc.calloc(1, size) } { - return ptr; - } - runtime_error("cannot allocate zeroed memory"); -} - -/// Resizes the memory block `ptr` with `sz` bytes. -/// -/// NOTE: The `ptr` must be a pointer to an existing memory block previously -/// allocated with `mem.alloc` or `mem.zeroed`. -pub func mem_resize(ptr: ?rawptr, sz: uint) -> rawptr { - if sz == 0 { - if safe_ptr := ptr { - unsafe { - mem_dealloc(safe_ptr); - } - } - runtime_error("argument `sz` is 0"); - } - if new := unsafe { libc.realloc(ptr, sz) } { - return new; - } - runtime_error("cannot resize memory"); -} - -/// Fills the first `n` bytes of the memory area pointed to by `s`, with the -/// byte `c`. -#[inline] -pub func mem_set(s: rawptr, c: uint8, n: uint) { - unsafe { - _ = libc.memset(s, @as(int32, c), n); - } -} - -/// Copies `sz` bytes from memory area `src` to memory area `dest`. -/// -/// NOTE: The memory areas *MUST NOT OVERLAP*. Use `mem.move`, if the memory -/// areas do overlap. -#[inline] -pub func mem_copy(dest: rawptr, src: rawptr, sz: uint) { - unsafe { - _ = libc.memcpy(dest, src, sz); - } -} - -/// Copies `sz` bytes from memory area `src` to memory area `dest`. -/// -/// NOTE: The memory areas *MAY* overlap: copying takes place as though the bytes -/// in `src` are first copied into a temporary array that does not overlap -/// `src` or `dest`, and the bytes are then copied from the temporary array to -/// `dest`. -#[inline] -pub func mem_move(dest: rawptr, src: rawptr, sz: uint) { - unsafe { - _ = libc.memmove(dest, src, sz); - } -} - -/// Allocates dynamically a `sz` bytes block of memory on the heap, then copies -/// the contents of `src` into the allocated space and returns a pointer to -/// the newly allocated space. -pub func mem_dup(src: rawptr, sz: uint) -> rawptr { - if sz == 0 { - return mem_zeroed(1); - } - new := mem_alloc(sz); - mem_copy(new, src, sz); - return new; -} - -/// Compares the first `n` bytes (each interpreted as `uint8`) of the memory areas -/// `s1` and `s2`. It returns an integer less than, equal to, or greater than -/// zero, if the first n bytes of `s1` is found, respectively, to be less than, -/// to match, or be greater than the first n bytes of `s2`. -/// -/// For a nonzero return value, the sign is determined by the sign of the -/// difference between the first pair of bytes (interpreted as `uint8`) that -/// differ in `s1` and `s2`. -/// -/// If n is zero, the return value is zero. -/// -/// NOTE: Do NOT use `mem.cmp` to compare security critical data, such -/// as cryptographic secrets, because the required CPU time depends on the -/// number of equal bytes. You should use a function that performs comparisons -/// in constant time for this. -#[inline] -pub func mem_cmp(s1: rawptr, s2: rawptr, n: uint) -> int32 { - return unsafe { libc.memcmp(s1, s2, n) }; -} - -/// Deallocates manually the memory referenced by `ptr`. -#[unsafe; inline] -pub func mem_dealloc(ptr: rawptr) { - unsafe { - libc.free(ptr); - } -} diff --git a/lib/core/src/mem/Boxedptr.c.ri b/lib/core/src/mem/Boxedptr.c.ri new file mode 100644 index 000000000..37cc739e8 --- /dev/null +++ b/lib/core/src/mem/Boxedptr.c.ri @@ -0,0 +1,75 @@ +// Copyright (C) 2023-present Jose Mendoza - All rights reserved. Use of this +// source code is governed by an MIT license that can be found in the LICENSE +// file. + +alias BoxedptrDestroy := func(boxedptr); + +const BOXEDPTR_SIZE := @size_of(Boxedptr); + +struct Boxedptr { + mut value: boxedptr; + mut ref_count: int; + mut dtor: ?BoxedptrDestroy; + + #[inline] + pub func from_boxedptr(ptr: boxedptr) -> &mut Self { + return unsafe { @as(&mut Boxedptr, @ptr_sub(@as([&]uint8, ptr), BOXEDPTR_SIZE)) }; + } +} + +pub func boxed_alloc(size: uint, dtor: ?BoxedptrDestroy) -> boxedptr { + boxed := unsafe { @as(&mut Boxedptr, raw_alloc(BOXEDPTR_SIZE + size)) }; + boxed.value = unsafe { @as(boxedptr, @ptr_add(@as([&]uint8, boxed), BOXEDPTR_SIZE)) }; + boxed.ref_count = 1; + boxed.dtor = dtor; + return boxed.value; +} + +pub func boxed_zeroed(size: uint, dtor: ?BoxedptrDestroy) -> boxedptr { + boxed := unsafe { @as(&mut Boxedptr, raw_zeroed(BOXEDPTR_SIZE + size)) }; + boxed.value = unsafe { @as(boxedptr, @ptr_add(@as([&]uint8, boxed), BOXEDPTR_SIZE)) }; + boxed.ref_count = 1; + boxed.dtor = dtor; + return boxed.value; +} + +pub func boxed_resize(ptr: boxedptr, size: uint) -> boxedptr { + old_boxed := Boxedptr.from_boxedptr(ptr); + new_boxed := unsafe { + @as(&mut Boxedptr, raw_resize(@as(rawptr, ptr), BOXEDPTR_SIZE + size)) + }; + new_boxed.value = unsafe { + @as(boxedptr, @ptr_add(@as([&]uint8, new_boxed), BOXEDPTR_SIZE)) + }; + new_boxed.ref_count = 1; + new_boxed.dtor = old_boxed.dtor; + boxed_release(old_boxed.value); + return new_boxed.value; +} + +#[inline] +pub func boxed_ref_count(ptr: boxedptr) -> int { + return Boxedptr.from_boxedptr(ptr).ref_count; +} + +pub func boxed_retain(ptr: boxedptr) -> boxedptr { + boxed := Boxedptr.from_boxedptr(ptr); + boxed.ref_count += 1; + return boxed.value; +} + +pub func boxed_release(ptr: boxedptr) { + boxed := Boxedptr.from_boxedptr(ptr); + if boxed.ref_count > 0 { + boxed.ref_count -= 1; + if boxed.ref_count == 0 { + if dtor := boxed.dtor { + dtor(boxed.value); + } + unsafe { + raw_dealloc(boxed); + } + boxed.ref_count = -1; + } + } +} diff --git a/lib/core/src/mem/alloc.c.ri b/lib/core/src/mem/alloc.c.ri new file mode 100644 index 000000000..01cddab9d --- /dev/null +++ b/lib/core/src/mem/alloc.c.ri @@ -0,0 +1,65 @@ +// Copyright (C) 2023-present Jose Mendoza - All rights reserved. Use of this +// source code is governed by an MIT license that can be found in the LICENSE +// file. + +import c/libc; + +/// Allocates dynamically a `size` bytes block of memory on the heap. +/// Returns a pointer to the memory address of the allocated space. +/// +/// NOTE: Unlike the `mem.raw_zeroed` function, `mem.raw_alloc` will not zero +/// the memory block. +pub func raw_alloc(size: uint) -> rawptr { + if new := unsafe { libc.malloc(size) } { + return new; + } + runtime_error("cannot allocate memory"); +} + +/// Allocates dynamically a zeroed `size` bytes block of memory on the heap. +/// Returns a pointer to the memory address of the allocated space. +pub func raw_zeroed(size: uint) -> rawptr { + if ptr := unsafe { libc.calloc(1, size) } { + return ptr; + } + runtime_error("cannot allocate zeroed memory"); +} + +/// Allocates dynamically a `sz` bytes block of memory on the heap, then copies +/// the contents of `src` into the allocated space and returns a pointer to +/// the newly allocated space. +pub func raw_dup(src: rawptr, sz: uint) -> rawptr { + if sz == 0 { + return raw_zeroed(1); + } + new := raw_alloc(sz); + copy(new, src, sz); + return new; +} + +/// Resizes the memory block `ptr` with `sz` bytes. +/// +/// NOTE: The `ptr` must be a pointer to an existing memory block previously +/// allocated with `mem.raw_alloc` or `mem.raw_zeroed`. +pub func raw_resize(ptr: ?rawptr, sz: uint) -> rawptr { + if sz == 0 { + if safe_ptr := ptr { + unsafe { + raw_dealloc(safe_ptr); + } + } + runtime_error("argument `sz` is 0"); + } + if new := unsafe { libc.realloc(ptr, sz) } { + return new; + } + runtime_error("cannot resize memory"); +} + +/// Deallocates manually the memory referenced by `ptr`. +#[unsafe; inline] +pub func raw_dealloc(ptr: rawptr) { + unsafe { + libc.free(ptr); + } +} diff --git a/lib/core/src/mem/mod.c.ri b/lib/core/src/mem/mod.c.ri new file mode 100644 index 000000000..f6278e54a --- /dev/null +++ b/lib/core/src/mem/mod.c.ri @@ -0,0 +1,68 @@ +// Copyright (C) 2023-present Jose Mendoza - All rights reserved. Use of this +// source code is governed by an MIT license that can be found in the LICENSE +// file. + +import c/libc; + +extern (Rivet) { + #[export("_R4core13runtime_errorF")] + func core_runtime_error(msg: string, args: ...string) -> never; +} + +#[inline] +func runtime_error(msg: string, args: ...string) -> never { + core_runtime_error(msg, args); +} + +/// Fills the first `n` bytes of the memory area pointed to by `s`, with the +/// byte `c`. +#[inline] +pub func set(s: rawptr, c: uint8, n: uint) { + unsafe { + _ = libc.memset(s, @as(int32, c), n); + } +} + +/// Copies `sz` bytes from memory area `src` to memory area `dest`. +/// +/// NOTE: The memory areas *MUST NOT OVERLAP*. Use `mem.move`, if the memory +/// areas do overlap. +#[inline] +pub func copy(dest: rawptr, src: rawptr, sz: uint) { + unsafe { + _ = libc.memcpy(dest, src, sz); + } +} + +/// Copies `sz` bytes from memory area `src` to memory area `dest`. +/// +/// NOTE: The memory areas *MAY* overlap: copying takes place as though the bytes +/// in `src` are first copied into a temporary array that does not overlap +/// `src` or `dest`, and the bytes are then copied from the temporary array to +/// `dest`. +#[inline] +pub func move(dest: rawptr, src: rawptr, sz: uint) { + unsafe { + _ = libc.memmove(dest, src, sz); + } +} + +/// Compares the first `n` bytes (each interpreted as `uint8`) of the memory areas +/// `s1` and `s2`. It returns an integer less than, equal to, or greater than +/// zero, if the first n bytes of `s1` is found, respectively, to be less than, +/// to match, or be greater than the first n bytes of `s2`. +/// +/// For a nonzero return value, the sign is determined by the sign of the +/// difference between the first pair of bytes (interpreted as `uint8`) that +/// differ in `s1` and `s2`. +/// +/// If n is zero, the return value is zero. +/// +/// NOTE: Do NOT use `mem.cmp` to compare security critical data, such +/// as cryptographic secrets, because the required CPU time depends on the +/// number of equal bytes. You should use a function that performs comparisons +/// in constant time for this. +#[inline] +pub func cmp(s1: rawptr, s2: rawptr, n: uint) -> int32 { + return unsafe { libc.memcmp(s1, s2, n) }; +} diff --git a/lib/core/src/rune.ri b/lib/core/src/rune.ri index 9fbfee401..f5cbc9ddc 100644 --- a/lib/core/src/rune.ri +++ b/lib/core/src/rune.ri @@ -2,6 +2,8 @@ // source code is governed by an MIT license that can be found in the LICENSE // file. +import ./mem; + const MAX_ONE_B: uint32 := 0x80; const MAX_TWO_B: uint32 := 0x800; const MAX_THREE_B: uint32 := 0x10000; @@ -62,7 +64,7 @@ extend rune < Stringable { pub func to_string(&self) -> string { len := self.len_utf8(); - res := unsafe { @as([&]mut uint8, mem_alloc(len + 1)) }; + res := unsafe { @as([&]mut uint8, mem.raw_alloc(len + 1)) }; unsafe { _ = utf32_decode_to_buffer(self.*, res); return string.from_raw_with_len(res, len); diff --git a/lib/core/src/string.c.ri b/lib/core/src/string.c.ri index 62c613d7c..b95d375c1 100644 --- a/lib/core/src/string.c.ri +++ b/lib/core/src/string.c.ri @@ -3,6 +3,7 @@ // file. import c/libc; +import ./mem; var empty_string := string(c"", 0, true); @@ -38,7 +39,7 @@ pub struct string < Stringable, Hashable, Throwable { } pub func from_byte(byte: uint8) -> Self { - res := unsafe { @as([&]mut uint8, mem_alloc(2)) }; + res := unsafe { @as([&]mut uint8, mem.raw_alloc(2)) }; unsafe { res[0] = byte; res[1] = 0; @@ -52,9 +53,9 @@ pub struct string < Stringable, Hashable, Throwable { if bytes.len == 0 { return empty_string; } - res := unsafe { @as([&]mut uint8, mem_alloc(bytes.len + 1)) }; + res := unsafe { @as([&]mut uint8, mem.raw_alloc(bytes.len + 1)) }; unsafe { - mem_copy(res, &bytes[0], bytes.len); + mem.copy(res, &bytes[0], bytes.len); res[bytes.len] = 0; } return Self(res, bytes.len); @@ -75,11 +76,11 @@ pub struct string < Stringable, Hashable, Throwable { self } else { len := self.len * count; - res := unsafe { @as([&]mut uint8, mem_alloc(len)) }; + res := unsafe { @as([&]mut uint8, mem.raw_alloc(len)) }; unsafe { mut i: uint := 0; while i < count : i += 1 { - mem_copy(@ptr_add(res, i * self.len), self.ptr, self.len); + mem.copy(@ptr_add(res, i * self.len), self.ptr, self.len); } res[len] = 0; } @@ -305,9 +306,9 @@ pub struct string < Stringable, Hashable, Throwable { if self.len == 0 { return empty_string; } - res := unsafe { @as([&]mut uint8, mem_alloc(self.len + 1)) }; + res := unsafe { @as([&]mut uint8, mem.raw_alloc(self.len + 1)) }; unsafe { - mem_copy(res, self.ptr, self.len); + mem.copy(res, self.ptr, self.len); res[self.len] = 0; } return Self(res, self.len); @@ -333,7 +334,7 @@ pub struct string < Stringable, Hashable, Throwable { return false; } } - return mem_cmp(self.ptr, rhs.ptr, rhs.len) == 0; + return mem.cmp(self.ptr, rhs.ptr, rhs.len) == 0; } #[inline] @@ -376,7 +377,7 @@ pub struct string < Stringable, Hashable, Throwable { func __destroy__(self) { if !self.is_ref { unsafe { - mem_dealloc(self.ptr); + mem.raw_dealloc(self.ptr); } } } diff --git a/lib/core/src/uint.ri b/lib/core/src/uint.ri index fc88eeff7..aa939c29c 100644 --- a/lib/core/src/uint.ri +++ b/lib/core/src/uint.ri @@ -2,6 +2,8 @@ // source code is governed by an MIT license that can be found in the LICENSE // file. +import ./mem; + extend uint8 < Stringable { pub const MAX: uint8 := 255; @@ -126,7 +128,7 @@ extend uint32 < Stringable { mut d: uint32 := 0; mut index := max; - buf := @as([&]mut uint8, mem_alloc(max + 1)); + buf := @as([&]mut uint8, mem.raw_alloc(max + 1)); buf[index] = 0; index -= 1; @@ -148,7 +150,7 @@ extend uint32 < Stringable { } diff := max - index; - mem_move(buf, @ptr_add(buf, index), diff + 1); + mem.move(buf, @ptr_add(buf, index), diff + 1); return string.from_raw_with_len(buf, diff); } } @@ -168,7 +170,7 @@ extend uint64 < Stringable { max := 20; mut index := @as(uint, max); - buf := @as([&]mut uint8, mem_alloc(index + 1)); + buf := @as([&]mut uint8, mem.raw_alloc(index + 1)); buf[index] = 0; index -= 1; @@ -190,7 +192,7 @@ extend uint64 < Stringable { } diff := @as(uint, max) - index; - mem_move(buf, @ptr_add(buf, index), diff + 1); + mem.move(buf, @ptr_add(buf, index), diff + 1); return string.from_raw_with_len(buf, diff); } } diff --git a/lib/rivet/src/builder/mod.ri b/lib/rivet/src/builder/mod.ri index cd675c3be..a7ba028c4 100644 --- a/lib/rivet/src/builder/mod.ri +++ b/lib/rivet/src/builder/mod.ri @@ -2,12 +2,12 @@ // source code is governed by an MIT license that can be found in the LICENSE // file. -import std/{ - traits, console, process, fs.{ Path, Directory } +import std/{ + traits, console, process, fs.{ Path, Directory } }; -import ../{ - ast, utils, prefs, token, parser, report, checker, codegen, resolver, depgraph +import ../{ + ast, utils, prefs, token, parser, report, checker, codegen, resolver, depgraph }; pub struct Builder { @@ -328,7 +328,7 @@ pub struct Builder { mut g := depgraph.DepGraph.new(); for mut pf in self.env.source_files { mut deps := []string(); - if pf.mod.name !in ["c.ctypes", "c.libc", "c", "core"] { + if pf.mod.name !in ["c.ctypes", "c.libc", "c", "core", "core.mem"] { deps.push("core"); } self.import_graph_decls(pf, deps, pf.decls); diff --git a/lib/rivet/src/checker/builtin_call.ri b/lib/rivet/src/checker/builtin_call.ri index d9ef88489..ac51dfa70 100644 --- a/lib/rivet/src/checker/builtin_call.ri +++ b/lib/rivet/src/checker/builtin_call.ri @@ -112,8 +112,9 @@ extend Checker { "attempt to cast an expression that is already of type `{}`".fmt(to_type), builtin_call.pos ); - } else if (from_type is .Pointer && to_type is .Rawptr) + } else if (from_type is .Pointer && (to_type is .Rawptr || to_type is .Boxedptr)) || (from_type is .Rawptr && to_type is .Pointer) + || (from_type is .Boxedptr && (to_type is .Pointer || to_type is .Rawptr)) || (from_type is .Pointer && to_type is .Pointer) || (from_type is .Pointer && self.env.is_int(to_type)) { if !self.inside_unsafe() { diff --git a/lib/rivet/src/checker/types.ri b/lib/rivet/src/checker/types.ri index 7e415d0b0..3583a51f8 100644 --- a/lib/rivet/src/checker/types.ri +++ b/lib/rivet/src/checker/types.ri @@ -194,20 +194,22 @@ extend Checker { return expected == got; } -} -func check_pointer(expected: ast.Type, got: ast.Type) -> bool { - if expected is .Rawptr { - // rawptr == &T, is valid - return got is .Rawptr || got is .Pointer; - } else if expected is .Pointer(ptr) && got is .Pointer(ptr2) { - if ptr.is_mut && !ptr2.is_mut { - return false; - } - if ptr.is_indexable && !ptr2.is_indexable { - return false; + func check_pointer(expected: ast.Type, got: ast.Type) -> bool { + if expected is .Rawptr { + // rawptr == &T, is valid + return got is .Rawptr || got is .Pointer || got is .Boxedptr; + } else if expected is .Pointer(ptr) && got is .Pointer(ptr2) { + if ptr.is_mut && !ptr2.is_mut { + return false; + } + if ptr.is_indexable && !ptr2.is_indexable { + return false; + } + return ptr.inner == ptr2.inner; + } else if expected is .Boxedptr && got is .Boxedptr { + return true; } - return ptr.inner == ptr2.inner; + return false; } - return false; } diff --git a/lib/rivet/src/parser/decls.ri b/lib/rivet/src/parser/decls.ri index 03f4889d3..efa332e69 100644 --- a/lib/rivet/src/parser/decls.ri +++ b/lib/rivet/src/parser/decls.ri @@ -412,7 +412,7 @@ extend Parser { }, self.accept(.KwFunc) -> return self.parse_func_decl( doc_comment, attributes, is_public, attributes.has("unsafe") || ( - self.inside_extern && self.extern_abi != .Rivet + self.inside_extern && self.extern_abi != .Rivet && !attributes.has("trusted") ), if self.inside_extern { self.extern_abi } else { .Rivet } ), self.accept(.KwTest) -> { diff --git a/lib/rivet/src/resolver/Register.ri b/lib/rivet/src/resolver/Register.ri index ba83ff82f..b6b3ea5ca 100644 --- a/lib/rivet/src/resolver/Register.ri +++ b/lib/rivet/src/resolver/Register.ri @@ -282,9 +282,18 @@ pub struct Register { continue; } if self.check_imported_symbol(sym.name, import_decl.pos, true) { - self.source_file.imported_symbols.add( - sym.name, sym, import_decl.pos, true - ); + if import_decl.is_public { + self.sym.scope.add(ast.SymRef( + is_public: true, + name: sym.name, + ref: sym, + ref_resolved: true + )) catch |err| report.error(err.to_string(), import_decl.pos); + } else { + self.source_file.imported_symbols.add( + sym.name, sym, import_decl.pos, true + ); + } } } } else if import_decl.import_list.is_empty() { diff --git a/lib/rivet/src/resolver/types.ri b/lib/rivet/src/resolver/types.ri index f33e7bd97..7559bb13e 100644 --- a/lib/rivet/src/resolver/types.ri +++ b/lib/rivet/src/resolver/types.ri @@ -7,7 +7,7 @@ import ../{ ast, report }; extend Resolver { func resolve_type(mut self, mut type: ast.Type) -> bool { return match type { - .Void, .Never, .Rawptr -> true, + .Void, .Never, .Rawptr, .Boxedptr -> true, .Option(mut opt) -> self.resolve_type(opt.inner), .Result(mut res) -> self.resolve_type(res.inner), .Variadic(mut variadic) -> if self.resolve_type(variadic.inner) { diff --git a/lib/std/src/fs/File.c.ri b/lib/std/src/fs/File.c.ri index 8805cf4c0..144344a7b 100644 --- a/lib/std/src/fs/File.c.ri +++ b/lib/std/src/fs/File.c.ri @@ -90,11 +90,11 @@ pub struct File { ); } - res := @as([&]mut uint8, mem.alloc(@as(uint, allocate) + 1)); + res := @as([&]mut uint8, mem.raw_alloc(@as(uint, allocate) + 1)); nelements := libc.fread(res, 1, @as(uint, allocate), self.f); if self.is_eof() && libc.ferror(self.f) != 0 { - mem.dealloc(res); + mem.raw_dealloc(res); throw OperationFailedError("fread failed"); } diff --git a/lib/std/src/fs/Path.c.ri b/lib/std/src/fs/Path.c.ri index f7796707c..cbd64ddd6 100644 --- a/lib/std/src/fs/Path.c.ri +++ b/lib/std/src/fs/Path.c.ri @@ -173,16 +173,16 @@ pub struct Path { } mut result_index: uint := 0; result := if have_abs { - unsafe { @as([&]mut uint8, mem.alloc(max_size)) } + unsafe { @as([&]mut uint8, mem.raw_alloc(max_size)) } } else { cwd := core.process_get_cwd()!; - tmp := unsafe { @as([&]mut uint8, mem.alloc(max_size + cwd.len + 1)) }; + tmp := unsafe { @as([&]mut uint8, mem.raw_alloc(max_size + cwd.len + 1)) }; mem.copy(tmp, cwd.ptr, cwd.len); result_index += cwd.len; tmp }; defer(error) unsafe { - mem.dealloc(result) + mem.raw_dealloc(result) } for p in paths[first_index:] { mut components := p.tokenize(SEPARATOR); @@ -225,7 +225,7 @@ pub struct Path { } return unsafe { string.from_raw_with_len( - @as([&]uint8, mem.resize(result, result_index)), result_index + @as([&]uint8, mem.raw_resize(result, result_index)), result_index ) }; } @@ -251,9 +251,9 @@ pub struct Path { up_count += 1; } up_index_end := up_count * 3; - result := unsafe { @as([&]mut uint8, mem.alloc(up_index_end + to_rest.len)) }; + result := unsafe { @as([&]mut uint8, mem.raw_alloc(up_index_end + to_rest.len)) }; defer(error) unsafe { - mem.dealloc(result); + mem.raw_dealloc(result); } mut result_index: uint := 0; while result_index < up_index_end { @@ -268,7 +268,7 @@ pub struct Path { } if to_rest.len == 0 { // shave off the trailing slash - buf := unsafe { @as([&]mut uint8, mem.resize(result, result_index - 1)) }; + buf := unsafe { @as([&]mut uint8, mem.raw_resize(result, result_index - 1)) }; unsafe { buf[result_index - 1] = 0; } @@ -323,10 +323,10 @@ pub struct Path { } sum + 1 // + 1 = NLL character }; - buf := unsafe { @as([&]mut uint8, mem.alloc(total_len)) }; + buf := unsafe { @as([&]mut uint8, mem.raw_alloc(total_len)) }; unsafe { buf[total_len - 1] = 0; - defer(error) mem.dealloc(buf); + defer(error) mem.raw_dealloc(buf); } paths_first_path_index_value := paths[first_path_index]; mem.copy(buf, paths_first_path_index_value.ptr, paths_first_path_index_value.len); diff --git a/lib/std/src/mem/mod.ri b/lib/std/src/mem/mod.ri index 16a49c510..f25421d0a 100644 --- a/lib/std/src/mem/mod.ri +++ b/lib/std/src/mem/mod.ri @@ -2,14 +2,4 @@ // source code is governed by an MIT license that can be found in the LICENSE // file. -import core; - -pub alias alloc := core.mem_alloc; -pub alias zeroed := core.mem_zeroed; -pub alias resize := core.mem_resize; -pub alias set := core.mem_set; -pub alias copy := core.mem_copy; -pub alias move := core.mem_move; -pub alias dup := core.mem_dup; -pub alias cmp := core.mem_cmp; -pub alias dealloc := core.mem_dealloc; +pub import core/mem.*; diff --git a/rivetc/src/__init__.py b/rivetc/src/__init__.py index e6f141387..e5287300d 100644 --- a/rivetc/src/__init__.py +++ b/rivetc/src/__init__.py @@ -152,7 +152,7 @@ def import_graph(self): if not fp.sym: continue deps = [] - if fp.sym.name not in ["c.libc", "c", "c.ctypes", "core"]: + if fp.sym.name not in ["c.libc", "c", "c.ctypes", "core", "core.mem"]: deps.append("core") self.import_graph_decls(fp, deps, fp.decls) g.add(fp.sym.name, deps) diff --git a/rivetc/src/codegen/__init__.py b/rivetc/src/codegen/__init__.py index d8631b7d2..32b95be28 100644 --- a/rivetc/src/codegen/__init__.py +++ b/rivetc/src/codegen/__init__.py @@ -274,7 +274,7 @@ def gen_decl(self, decl): if isinstance(value, ir.ArrayLit) and len(value.elems) > 0: self.cur_func.add_call( - "_R4core8mem_copyF", + "_R4core3mem4copyF", [ident, value, ir.IntLit(ir.UINT_T, str(size))] ) @@ -533,7 +533,7 @@ def gen_stmt(self, stmt): val = self.gen_expr_with_cast(left.typ, stmt.right, ident) if isinstance(val, ir.ArrayLit) and len(val.elems) > 0: self.cur_func.add_call( - "_R4core8mem_copyF", + "_R4core3mem4copyF", [ident, val, ir.IntLit(ir.UINT_T, str(size))] ) @@ -555,7 +555,7 @@ def gen_stmt(self, stmt): size, _ = self.comp.type_size(left.typ) self.cur_func.alloca(ident) self.cur_func.add_call( - "_R4core8mem_copyF", [ + "_R4core3mem4copyF", [ ident, ir.Selector( left_ir_typ, right, ir.Name(f"f{i}") @@ -1323,7 +1323,7 @@ def gen_expr(self, expr, custom_tmp = None): id = ir.Ident(self.ir_type(expr.sym.ret_typ), tmp) self.cur_func.alloca(id) self.cur_func.add_call( - "_R4core8mem_copyF", [ + "_R4core3mem4copyF", [ id, ir.Selector( self.ir_type(expr.sym.ret_typ), inst, @@ -1627,7 +1627,7 @@ def gen_expr(self, expr, custom_tmp = None): if custom_tmp: size, _ = self.comp.type_size(expr.typ) self.cur_func.add_call( - "_R4core8mem_copyF", + "_R4core3mem4copyF", [custom_tmp, arr_lit, ir.IntLit(ir.UINT_T, str(size))] ) @@ -2397,7 +2397,7 @@ def gen_expr(self, expr, custom_tmp = None): ir.Type(self.cur_func.arr_ret_struct) ) self.cur_func.add_call( - "_R4core8mem_copyF", [ + "_R4core3mem4copyF", [ ir.Selector( self.ir_type(ret_typ), tmp, ir.Name("arr") ), expr_, @@ -2768,7 +2768,7 @@ def boxed_instance(self, name, custom_name = None): to_fn = self.init_string_lits_fn if custom_name else self.cur_func inst = ir.Inst( ir.InstKind.Call, - [ir.Name("_R4core9mem_allocF"), + [ir.Name("_R4core3mem9raw_allocF"), ir.Name(f"sizeof({name})")] ) if custom_name: @@ -2798,7 +2798,7 @@ def trait_value(self, value, value_typ, trait_typ): value = ir.Inst(ir.InstKind.GetPtr, [value]) value = value if is_ptr else ir.Inst( ir.InstKind.Call, [ - ir.Name("_R4core7mem_dupF"), value, + ir.Name("_R4core3mem7raw_dupF"), value, ir.IntLit(ir.UINT_T, str(size)) ] ) diff --git a/rivetc/src/parser.py b/rivetc/src/parser.py index 01956f917..8b2b183c1 100644 --- a/rivetc/src/parser.py +++ b/rivetc/src/parser.py @@ -424,7 +424,7 @@ def parse_decl(self): return self.parse_func_decl( doc_comment, attributes, is_public, attributes.has("unsafe") - or (self.inside_extern and self.extern_abi != sym.ABI.Rivet), + or (self.inside_extern and self.extern_abi != sym.ABI.Rivet and not attributes.has("trusted")), self.extern_abi if self.inside_extern else sym.ABI.Rivet ) elif self.accept(Kind.KwTest): diff --git a/rivetc/src/register.py b/rivetc/src/register.py index df1568124..0f5a4100d 100644 --- a/rivetc/src/register.py +++ b/rivetc/src/register.py @@ -210,12 +210,20 @@ def walk_import_decl(self, decl): if not symbol.is_public: continue self.check_imported_symbol(symbol, decl.pos) - self.source_file.imported_symbols[symbol.name] = symbol + if decl.is_public: + try: + self.sym.add( + sym.SymRef(True, symbol.name, symbol) + ) + except utils.CompilerError as e: + report.error(e.args[0], decl.pos) + else: + self.source_file.imported_symbols[symbol.name] = symbol elif len(decl.import_list) == 0: if decl.is_public: try: self.sym.add( - sym.SymRef(decl.is_public, decl.alias, decl.mod_sym) + sym.SymRef(True, decl.alias, decl.mod_sym) ) except utils.CompilerError as e: report.error(e.args[0], decl.pos)