Compare commits

..

2 Commits
v0.5.5 ... test

Author SHA1 Message Date
Christoffer Lerno
2bd66dcf18 Revert malloc reduced init. 2023-11-22 11:16:36 +01:00
Christoffer Lerno
3e31d20603 Test 2023-11-22 11:16:01 +01:00
366 changed files with 11541 additions and 15233 deletions

14
.github/FUNDING.yml vendored
View File

@@ -1,14 +0,0 @@
# These are supported funding model platforms
github: [c3lang]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -61,14 +61,6 @@ jobs:
run: |
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
- name: Try raylib
run: |
cd resources
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_arkanoid.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_snake.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib --wincrt=none examples\raylib\raylib_tetris.c3
- name: run compiler tests
run: |
cd test
@@ -79,11 +71,6 @@ jobs:
cd test
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1
- name: Test python script
run: |
py msvc_build_libraries.py --accept-license
dir msvc_sdk
- name: upload artifacts
uses: actions/upload-artifact@v3
with:
@@ -112,8 +99,8 @@ jobs:
install: git binutils mingw-w64-x86_64-clang mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-python
- shell: msys2 {0}
run: |
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-18.1.1-3-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-18.1.1-3-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-17.0.4-1-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-17.0.4-1-any.pkg.tar.zst
- name: CMake
run: |
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
@@ -206,7 +193,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [15, 16, 17, 18, 19]
llvm_version: [15, 16, 17, 18]
steps:
- uses: actions/checkout@v4
@@ -220,7 +207,7 @@ jobs:
if [[ "${{matrix.llvm_version}}" < 16 ]]; then
sudo apt remove libllvm15
fi
if [[ "${{matrix.llvm_version}}" < 19 ]]; then
if [[ "${{matrix.llvm_version}}" < 18 ]]; then
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
sudo apt-get update
sudo apt-get install -y -t llvm-toolchain-focal-${{matrix.llvm_version}} libpolly-${{matrix.llvm_version}}-dev \
@@ -235,7 +222,6 @@ jobs:
libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
fi
- name: CMake
if: matrix.llvm_version != 18
run: |
cmake -B build \
-G Ninja \
@@ -248,24 +234,10 @@ jobs:
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
cmake --build build
- name: CMake18
if: matrix.llvm_version == 18
run: |
cmake -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \
-DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \
-DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DC3_LLVM_VERSION=18.1
cmake --build build
- name: Compile and run some examples
run: |
cd resources
cd resources
../build/c3c compile examples/base64.c3
../build/c3c compile examples/binarydigits.c3
../build/c3c compile examples/brainfk.c3
@@ -285,7 +257,7 @@ jobs:
../build/c3c compile examples/contextfree/dynscope.c3
../build/c3c compile examples/contextfree/guess_number.c3
../build/c3c compile examples/contextfree/multi.c3
../build/c3c compile examples/contextfree/cleanup.c3
../build/c3c compile examples/contextfree/cleanup.c3
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
@@ -446,7 +418,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [15, 16, 17]
llvm_version: [15, 16]
steps:
- uses: actions/checkout@v4
- name: Download LLVM
@@ -551,6 +523,8 @@ jobs:
- run: cp -r lib c3-windows-Debug
- run: cp msvc_build_libraries.py c3-windows-Release
- run: cp msvc_build_libraries.py c3-windows-Debug
- run: cp install_win_reqs.bat c3-windows-Release
- run: cp install_win_reqs.bat c3-windows-Debug
- run: zip -r c3-windows-Release.zip c3-windows-Release
- run: zip -r c3-windows-Debug.zip c3-windows-Debug

View File

@@ -75,7 +75,7 @@ if (NOT WIN32)
find_package(CURL)
endif()
if (NOT C3_LLVM_VERSION STREQUAL "auto")
if (${C3_LLVM_VERSION} VERSION_LESS 15 OR ${C3_LLVM_VERSION} VERSION_GREATER 19)
if (${C3_LLVM_VERSION} VERSION_LESS 15 OR ${C3_LLVM_VERSION} VERSION_GREATER 18)
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
endif()
endif()

View File

@@ -35,7 +35,7 @@ whole new language.
The following code shows [generic modules](http://www.c3-lang.org/generics/) (more examples can be found at http://www.c3-lang.org/examples/).
```c++
module stack (<Type>);
module stack <Type>;
// Above: the parameterized type is applied to the entire module.
struct Stack
@@ -319,21 +319,6 @@ You should now have a `c3c` executable.
You can try it out by running some sample code: `./c3c compile ../resources/examples/hash.c3`
#### Compiling on Void Linux
1. As root, ensure that all project dependencies are installed: `xbps-install git cmake llvm15 lld-devel libcurl-devel ncurses-devel zlib-devel libzstd-devel libxml2-devel`
2. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git`
- If you only need the latest commit, you may want to make a shallow clone instead: `git clone https://github.com/c3lang/c3c.git --depth=1`
3. Enter the directory: `cd c3c`
4. Create a build directory: `mkdir build`
5. Enter the build directory: `cd build`
6. Create the CMake build cache: `cmake ..`
7. Build: `cmake --build .`
Your c3c executable should have compiled properly. You may want to test it: `./c3c compile ../resources/examples/hash.c3`
For a sytem-wide installation, run the following as root: `cmake --install .`
#### Compiling on other Linux / Unix variants
1. Install CMake.

17
install_win_reqs.bat Normal file
View File

@@ -0,0 +1,17 @@
@echo off
set DOWNLOAD_URL=https://aka.ms/vs/17/release
mkdir tmp 2> NUL
if not exist "tmp\vs_buildtools.exe" (
bitsadmin /transfer /download /priority foreground %DOWNLOAD_URL%/vs_buildtools.exe %CD%\tmp\vs_buildtools.exe
)
echo Preparing Build Tools, please wait...
tmp\vs_BuildTools.exe --quiet --wait --layout tmp\ --add Microsoft.VisualStudio.Component.Windows10SDK.19041
echo Installing Build Tools, please wait...
tmp\vs_BuildTools.exe --quiet --wait --noweb --add Microsoft.VisualStudio.Component.Windows10SDK.19041
REM rmdir tmp /s /q

View File

@@ -86,29 +86,15 @@ struct GrowableBitSet
* @param initial_capacity
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
**/
fn GrowableBitSet* GrowableBitSet.new_init(&self, usz initial_capacity = 1, Allocator* allocator = allocator::heap())
fn GrowableBitSet* GrowableBitSet.init_new(&self, usz initial_capacity = 1, Allocator* allocator = mem::heap())
{
self.data.new_init(initial_capacity, allocator);
self.data.init_new(initial_capacity, allocator);
return self;
}
/**
* @param initial_capacity
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
**/
fn GrowableBitSet* GrowableBitSet.init_new(&self, usz initial_capacity = 1, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
fn GrowableBitSet* GrowableBitSet.init_temp(&self, usz initial_capacity = 1)
{
return self.new_init(initial_capacity, allocator) @inline;
}
fn GrowableBitSet* GrowableBitSet.temp_init(&self, usz initial_capacity = 1)
{
return self.new_init(initial_capacity, allocator::temp()) @inline;
}
fn GrowableBitSet* GrowableBitSet.init_temp(&self, usz initial_capacity = 1) @deprecated("Replaced by temp_init")
{
return self.temp_init(initial_capacity);
return self.init_new(initial_capacity, mem::temp()) @inline;
}
fn void GrowableBitSet.free(&self)

View File

@@ -1,5 +1,5 @@
module std::collections::enummap(<Enum, ValueType>);
import std::io;
struct EnumMap (Printable)
{
ValueType[Enum.len] values;
@@ -25,7 +25,7 @@ fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic
return n;
}
fn String EnumMap.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
fn String EnumMap.to_new_string(&self, Allocator* allocator = mem::heap()) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
@@ -6,9 +6,8 @@
* @require Enum.kindof == TypeKind.ENUM : "Only enums maybe be used with an enumset"
**/
module std::collections::enumset(<Enum>);
import std::io;
def EnumSetType = $typefrom(private::type_for_enum_elements(Enum.elements)) @private;
def EnumSetType = $typefrom(private::type_for_enum_elements(Enum.elements)) @private ;
const IS_CHAR_ARRAY = Enum.elements > 128;
distinct EnumSet (Printable) = EnumSetType;
@@ -141,7 +140,7 @@ fn usz! EnumSet.to_format(&set, Formatter* formatter) @dynamic
return n;
}
fn String EnumSet.to_new_string(&set, Allocator* allocator = allocator::heap()) @dynamic
fn String EnumSet.to_new_string(&set, Allocator* allocator = mem::heap()) @dynamic
{
return string::new_format("%s", *set, .allocator = allocator);
}

View File

@@ -1,467 +0,0 @@
// Copyright (c) 2024 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::generic_list;
import std::io,std::math;
def GenericPredicate = fn bool(any* value);
def GenericTest = fn bool(any* type, any* context);
struct GenericList (Printable)
{
usz size;
usz capacity;
Allocator* allocator;
any** entries;
}
/**
* @param initial_capacity "The initial capacity to reserve"
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
**/
fn GenericList* GenericList.new_init(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap())
{
self.allocator = allocator;
self.size = 0;
if (initial_capacity > 0)
{
initial_capacity = math::next_power_of_2(initial_capacity);
self.entries = allocator::alloc_array(allocator, any*, initial_capacity);
}
else
{
self.entries = null;
}
self.capacity = initial_capacity;
return self;
}
/**
* @param initial_capacity "The initial capacity to reserve"
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
**/
fn GenericList* GenericList.init_new(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
return self.new_init(initial_capacity, allocator) @inline;
}
/**
* Initialize the list using the temp allocator.
*
* @param initial_capacity "The initial capacity to reserve"
**/
fn GenericList* GenericList.temp_init(&self, usz initial_capacity = 16)
{
return self.new_init(initial_capacity, allocator::temp()) @inline;
}
fn usz! GenericList.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.size)
{
case 0:
return formatter.print("[]")!;
case 1:
return formatter.printf("[%s]", self.entries[0])!;
default:
usz n = formatter.print("[")!;
foreach (i, element : self.entries[:self.size])
{
if (i != 0) formatter.print(", ")!;
n += formatter.printf("%s", element)!;
}
n += formatter.print("]")!;
return n;
}
}
fn String GenericList.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
}
fn String GenericList.to_tstring(&self)
{
return string::tformat("%s", *self);
}
/**
* Push an element on the list by cloning it.
**/
macro void GenericList.push(&self, element)
{
if (!self.allocator) self.allocator = allocator::heap();
self.append_internal(allocator::clone(self.allocator, element));
}
fn void GenericList.append_internal(&self, any* element) @local
{
self.ensure_capacity();
self.entries[self.size++] = element;
}
/**
* Free a retained element removed using *_retained.
**/
fn void GenericList.free_element(&self, any* element) @inline
{
allocator::free(self.allocator, element.ptr);
}
/**
* Pop a value who's type is known. If the type is incorrect, this
* will still pop the element.
*
* @return! CastResult.TYPE_MISMATCH, IteratorResult.NO_MORE_ELEMENT
**/
macro GenericList.pop(&self, $Type)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
return *anycast(self.entries[--self.size], $Type);
}
/**
* Pop the last value and allocate the copy using the given allocator.
* @return! IteratorResult.NO_MORE_ELEMENT
**/
fn any*! GenericList.new_pop(&self, Allocator* allocator = allocator::heap())
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
return allocator::clone_any(allocator, self.entries[--self.size]);
}
/**
* Pop the last value and allocate the copy using the temp allocator
* @return! IteratorResult.NO_MORE_ELEMENT
**/
fn any*! GenericList.temp_pop(&self) => self.new_pop(allocator::temp());
/**
* Pop the last value. It must later be released using list.free_element()
* @return! IteratorResult.NO_MORE_ELEMENT
**/
fn any*! GenericList.pop_retained(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
return self.entries[--self.size];
}
fn void GenericList.clear(&self)
{
for (usz i = 0; i < self.size; i++)
{
self.free_element(self.entries[i]);
}
self.size = 0;
}
/**
* Same as pop() but pops the first value instead.
**/
macro GenericList.pop_first(&self, $Type)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.remove_at(0);
return *anycast(self.entries[0], $Type);
}
/**
* Same as pop_retained() but pops the first value instead.
**/
fn any*! GenericList.pop_first_retained(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.remove_at(0);
return self.entries[0];
}
/**
* Same as new_pop() but pops the first value instead.
**/
fn any*! GenericList.new_pop_first(&self, Allocator* allocator = allocator::heap())
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
defer self.remove_at(0);
return allocator::clone_any(allocator, self.entries[0]);
}
/**
* Same as temp_pop() but pops the first value instead.
**/
fn any*! GenericList.temp_pop_first(&self) => self.new_pop_first(allocator::temp());
/**
* @require index < self.size
**/
fn void GenericList.remove_at(&self, usz index)
{
if (!--self.size || index == self.size) return;
self.free_element(self.entries[index]);
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
}
fn void GenericList.add_all(&self, GenericList* other_list)
{
if (!other_list.size) return;
self.reserve(other_list.size);
foreach (value : other_list)
{
self.entries[self.size++] = allocator::clone_any(self.allocator, value);
}
}
/**
* Reverse the elements in a list.
**/
fn void GenericList.reverse(&self)
{
if (self.size < 2) return;
usz half = self.size / 2U;
usz end = self.size - 1;
for (usz i = 0; i < half; i++)
{
self.swap(i, end - i);
}
}
fn any*[] GenericList.array_view(&self)
{
return self.entries[:self.size];
}
/**
* Push an element to the front of the list.
**/
macro void GenericList.push_front(&self, type)
{
self.insert_at(0, type);
}
/**
* @require index < self.size
**/
macro void GenericList.insert_at(&self, usz index, type) @local
{
any* value = allocator::copy(self.allocator, type);
self.insert_at_internal(self, index, value);
}
/**
* @require index < self.size
**/
fn void GenericList.insert_at_internal(&self, usz index, any* value) @local
{
self.ensure_capacity();
for (usz i = self.size; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
}
self.size++;
self.entries[index] = value;
}
/**
* @require self.size > 0
**/
fn void GenericList.remove_last(&self)
{
self.free_element(self.entries[--self.size]);
}
/**
* @require self.size > 0
**/
fn void GenericList.remove_first(&self)
{
self.remove_at(0);
}
macro GenericList.first(&self, $Type)
{
return *anycast(self.first_any(), $Type);
}
fn any*! GenericList.first_any(&self) @inline
{
return self.size ? self.entries[0] : IteratorResult.NO_MORE_ELEMENT?;
}
macro GenericList.last(&self, $Type)
{
return *anycast(self.last_any(), $Type);
}
fn any*! GenericList.last_any(&self) @inline
{
return self.size ? self.entries[self.size - 1] : IteratorResult.NO_MORE_ELEMENT?;
}
fn bool GenericList.is_empty(&self) @inline
{
return !self.size;
}
fn usz GenericList.len(&self) @operator(len) @inline
{
return self.size;
}
/**
* @require index < self.size "Index out of range"
**/
macro GenericList.get(&self, usz index, $Type)
{
return *anycast(self.entries[index], $Type);
}
/**
* @require index < self.size "Index out of range"
**/
fn any* GenericList.get_any(&self, usz index) @inline
{
return self.entries[index];
}
fn void GenericList.free(&self)
{
if (!self.allocator) return;
self.clear();
allocator::free(self.allocator, self.entries);
self.capacity = 0;
self.entries = null;
}
fn void GenericList.swap(&self, usz i, usz j)
{
any* temp = self.entries[i];
self.entries[i] = self.entries[j];
self.entries[j] = temp;
}
/**
* @param filter "The function to determine if it should be removed or not"
* @return "the number of deleted elements"
**/
fn usz GenericList.remove_if(&self, GenericPredicate filter)
{
return self._remove_if(filter, false);
}
/**
* @param selection "The function to determine if it should be kept or not"
* @return "the number of deleted elements"
**/
fn usz GenericList.retain_if(&self, GenericPredicate selection)
{
return self._remove_if(selection, true);
}
macro usz GenericList._remove_if(&self, GenericPredicate filter, bool $invert) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && filter(&self.entries[i - 1])) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$endif
}
return size - self.size;
}
fn usz GenericList.remove_using_test(&self, GenericTest filter, any* context)
{
return self._remove_using_test(filter, false, context);
}
fn usz GenericList.retain_using_test(&self, GenericTest filter, any* context)
{
return self._remove_using_test(filter, true, context);
}
macro usz GenericList._remove_using_test(&self, GenericTest filter, bool $invert, ctx) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$endif
}
return size - self.size;
}
/**
* Reserve at least min_capacity
**/
fn void GenericList.reserve(&self, usz min_capacity)
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = allocator::heap();
min_capacity = math::next_power_of_2(min_capacity);
self.entries = allocator::realloc(self.allocator, self.entries, any*.sizeof * min_capacity);
self.capacity = min_capacity;
}
macro any* GenericList.@item_at(&self, usz index) @operator([])
{
return self.entries[index];
}
/**
* @require index <= self.size "Index out of range"
**/
macro void GenericList.set(&self, usz index, value)
{
if (index == self.size)
{
self.push(value);
return;
}
self.free_element(self.entries[index]);
self.entries[index] = allocator::copy(self.allocator, value);
}
fn void GenericList.ensure_capacity(&self, usz added = 1) @inline @private
{
usz new_size = self.size + added;
if (self.capacity >= new_size) return;
assert(new_size < usz.max / 2U);
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
while (new_capacity < new_size) new_capacity *= 2U;
self.reserve(new_capacity);
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::linkedlist(<Type>);
@@ -32,29 +32,15 @@ fn void LinkedList.push_last(&self, Type value)
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
* @return "the initialized list"
**/
fn LinkedList* LinkedList.new_init(&self, Allocator* allocator = allocator::heap())
fn LinkedList* LinkedList.init_new(&self, Allocator* allocator = mem::heap())
{
*self = { .allocator = allocator };
return self;
}
/**
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
* @return "the initialized list"
**/
fn LinkedList* LinkedList.init_new(&self, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
fn LinkedList* LinkedList.init_temp(&self)
{
return self.new_init(allocator);
}
fn LinkedList* LinkedList.temp_init(&self)
{
return self.new_init(allocator::temp()) @inline;
}
fn LinkedList* LinkedList.init_temp(&self) @deprecated("Replaced by temp_init")
{
return self.temp_init() @inline;
return self.init_new(mem::temp()) @inline;
}
/**
@@ -62,13 +48,12 @@ fn LinkedList* LinkedList.init_temp(&self) @deprecated("Replaced by temp_init")
**/
macro void LinkedList.free_node(&self, Node* node) @private
{
allocator::free(self.allocator, node);
self.allocator.free(node);
}
macro Node* LinkedList.alloc_node(&self) @private
{
if (!self.allocator) self.allocator = allocator::heap();
return allocator::alloc(self.allocator, Node);
if (!self.allocator) self.allocator = mem::heap();
return self.allocator.new(Node);
}
fn void LinkedList.link_first(&self, Type value) @private

View File

@@ -1,8 +1,9 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::list(<Type>);
import std::io,std::math;
import std::io;
import std::math;
def ElementPredicate = fn bool(Type *type);
def ElementTest = fn bool(Type *type, any* context);
@@ -17,19 +18,18 @@ struct List (Printable)
Type *entries;
}
/**
* @param initial_capacity "The initial capacity to reserve"
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
**/
fn List* List.new_init(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap())
fn List* List.init_new(&self, usz initial_capacity = 16, Allocator* allocator = mem::heap())
{
self.allocator = allocator;
self.size = 0;
if (initial_capacity > 0)
{
initial_capacity = math::next_power_of_2(initial_capacity);
self.entries = allocator::malloc_aligned(allocator, Type.sizeof * initial_capacity, .alignment = Type[1].alignof)!!;
self.entries = allocator.alloc_aligned(Type.sizeof * initial_capacity, .alignment = Type[1].alignof)!!;
}
else
{
@@ -39,39 +39,20 @@ fn List* List.new_init(&self, usz initial_capacity = 16, Allocator* allocator =
return self;
}
/**
* @param initial_capacity "The initial capacity to reserve"
* @param [&inout] allocator "The allocator to use, defaults to the heap allocator"
**/
fn List* List.init_new(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
return self.new_init(initial_capacity, allocator) @inline;
}
/**
* Initialize the list using the temp allocator.
*
* @param initial_capacity "The initial capacity to reserve"
**/
fn List* List.temp_init(&self, usz initial_capacity = 16)
fn List* List.init_temp(&self, usz initial_capacity = 16)
{
return self.new_init(initial_capacity, allocator::temp()) @inline;
}
/**
* Initialize the list using the temp allocator.
*
* @param initial_capacity "The initial capacity to reserve"
**/
fn List* List.init_temp(&self, usz initial_capacity = 16) @deprecated("Replaced by temp_init")
{
return self.temp_init(initial_capacity) @inline;
return self.init_new(initial_capacity, mem::temp()) @inline;
}
/**
* @require self.size == 0 "The List must be empty"
**/
fn void List.init_wrapping_array(&self, Type[] types, Allocator* allocator = allocator::heap())
fn void List.init_wrapping_array(&self, Type[] types, Allocator* allocator = mem::heap())
{
self.allocator = allocator;
self.size = types.len;
@@ -99,7 +80,7 @@ fn usz! List.to_format(&self, Formatter* formatter) @dynamic
}
}
fn String List.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
fn String List.to_new_string(&self, Allocator* allocator = mem::heap()) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
}
@@ -148,8 +129,11 @@ fn Type List.pop_first(&self)
**/
fn void List.remove_at(&self, usz index)
{
if (!--self.size || index == self.size) return;
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
for (usz i = index + 1; i < self.size; i++)
{
self.entries[i - 1] = self.entries[i];
}
self.size--;
}
fn void List.add_all(&self, List* other_list)
@@ -163,17 +147,17 @@ fn void List.add_all(&self, List* other_list)
}
fn Type[] List.to_new_array(&self, Allocator* allocator = allocator::heap())
fn Type[] List.to_new_array(&self, Allocator* allocator = mem::heap())
{
if (!self.size) return Type[] {};
Type[] result = allocator::alloc_array(allocator, Type, self.size);
Type[] result = allocator.new_array(Type, self.size);
result[..] = self.entries[:self.size];
return result;
}
fn Type[] List.to_tarray(&self)
{
return self.to_new_array(allocator::temp());
return self.to_new_array(mem::temp());
}
/**
@@ -263,11 +247,6 @@ fn bool List.is_empty(&self) @inline
return !self.size;
}
fn usz List.byte_size(&self) @inline
{
return Type.sizeof * self.size;
}
fn usz List.len(&self) @operator(len) @inline
{
return self.size;
@@ -281,7 +260,7 @@ fn Type List.get(&self, usz index) @inline
fn void List.free(&self)
{
if (!self.allocator) return;
allocator::free_aligned(self.allocator, self.entries);
self.allocator.free_aligned(self.entries);
self.capacity = 0;
self.size = 0;
self.entries = null;
@@ -377,9 +356,9 @@ fn void List.reserve(&self, usz min_capacity)
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = allocator::heap();
if (!self.allocator) self.allocator = mem::heap();
min_capacity = math::next_power_of_2(min_capacity);
self.entries = allocator::realloc_aligned(self.allocator, self.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof) ?? null;
self.entries = self.allocator.realloc_aligned(self.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof) ?? null;
self.capacity = min_capacity;
}

View File

@@ -23,40 +23,17 @@ struct HashMap
* @param [&inout] allocator "The allocator to use"
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !self.allocator "Map was already initialized"
* @require !map.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn HashMap* HashMap.new_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = allocator::heap())
fn HashMap* HashMap.init_new(&map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = mem::heap())
{
capacity = math::next_power_of_2(capacity);
self.allocator = allocator;
self.load_factor = load_factor;
self.threshold = (uint)(capacity * load_factor);
self.table = allocator::new_array(allocator, Entry*, capacity);
return self;
}
/**
* @param [&inout] allocator "The allocator to use"
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !map.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn HashMap* HashMap.init_new(&map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
return map.new_init(capacity, load_factor, allocator);
}
/**
* @require capacity > 0 "The capacity must be 1 or higher"
* @require load_factor > 0.0 "The load factor must be higher than 0"
* @require !self.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.new_init(capacity, load_factor, allocator::temp()) @inline;
map.allocator = allocator;
map.load_factor = load_factor;
map.threshold = (uint)(capacity * load_factor);
map.table = allocator.new_zero_array(Entry*, capacity);
return map;
}
/**
@@ -65,9 +42,9 @@ fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, f
* @require !map.allocator "Map was already initialized"
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
**/
fn HashMap* HashMap.init_temp(&map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) @deprecated("Replaced by temp_init")
fn HashMap* HashMap.init_temp(&map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return map.temp_init(capacity, load_factor) @inline;
return map.init_new(capacity, load_factor, mem::temp());
}
/**
@@ -85,40 +62,21 @@ fn bool HashMap.is_initialized(&map)
* @param [&inout] allocator "The allocator to use"
* @param [&in] other_map "The map to copy from."
**/
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map, Allocator* allocator = allocator::heap())
fn HashMap* HashMap.init_new_from_map(&self, HashMap* other_map, Allocator* allocator = mem::heap())
{
self.new_init(other_map.table.len, other_map.load_factor, allocator);
self.init_new(other_map.table.len, other_map.load_factor, allocator);
self.put_all_for_create(other_map);
return self;
}
/**
* @param [&inout] allocator "The allocator to use"
* @param [&in] other_map "The map to copy from."
**/
fn HashMap* HashMap.init_new_from_map(&self, HashMap* other_map, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init_from_map")
fn HashMap* HashMap.init_temp_from_map(&map, HashMap* other_map)
{
return self.new_init_from_map(other_map, allocator) @inline;
return map.init_new_from_map(other_map, mem::temp()) @inline;
}
/**
* @param [&in] other_map "The map to copy from."
**/
fn HashMap* HashMap.temp_init_from_map(&map, HashMap* other_map)
{
return map.new_init_from_map(other_map, allocator::temp()) @inline;
}
/**
* @param [&in] other_map "The map to copy from."
**/
fn HashMap* HashMap.init_temp_from_map(&map, HashMap* other_map) @deprecated("Replaced by temp_init_from_map")
{
return map.temp_init_from_map(other_map) @inline;
}
fn bool HashMap.is_empty(&map) @inline
{
return !map.count;
@@ -153,7 +111,6 @@ fn Entry*! HashMap.get_entry(&map, Key key)
/**
* Get the value or update and
* @require $assignable(#expr, Value)
**/
macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
{
@@ -189,7 +146,7 @@ fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
// If the map isn't initialized, use the defaults to initialize it.
if (!map.allocator)
{
map.new_init();
map.init_new();
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
@@ -240,14 +197,14 @@ fn void HashMap.free(&map)
fn Key[] HashMap.key_tlist(&map)
{
return map.key_new_list(allocator::temp()) @inline;
return map.key_new_list(mem::temp()) @inline;
}
fn Key[] HashMap.key_new_list(&map, Allocator* allocator = allocator::heap())
fn Key[] HashMap.key_new_list(&map, Allocator* allocator = mem::heap())
{
if (!map.count) return {};
Key[] list = allocator::alloc_array(allocator, Key, map.count);
Key[] list = allocator.new_array(Key, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
@@ -284,13 +241,13 @@ macro HashMap.@each_entry(map; @body(entry))
fn Value[] HashMap.value_tlist(&map)
{
return map.value_new_list(allocator::temp()) @inline;
return map.value_new_list(mem::temp()) @inline;
}
fn Value[] HashMap.value_new_list(&map, Allocator* allocator = allocator::heap())
fn Value[] HashMap.value_new_list(&map, Allocator* allocator = mem::heap())
{
if (!map.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, map.count);
Value[] list = allocator.new_array(Value, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
@@ -321,10 +278,11 @@ fn bool HashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
{
Entry* entry = map.allocator.new(Entry);
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
map.table[bucket_index] = entry;
if (map.count++ >= map.threshold)
{
@@ -341,7 +299,7 @@ fn void HashMap.resize(&map, uint new_capacity) @private
map.threshold = uint.max;
return;
}
Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity);
Entry*[] new_table = map.allocator.new_zero_array(Entry*, new_capacity);
map.transfer(new_table);
map.table = new_table;
map.free_internal(old_table.ptr);
@@ -405,7 +363,7 @@ fn void HashMap.put_for_create(&map, Key key, Value value) @private
fn void HashMap.free_internal(&map, void* ptr) @inline @private
{
allocator::free(map.allocator, ptr);
map.allocator.free(ptr);
}
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
@@ -440,10 +398,11 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private
fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
{
Entry *e = map.table[bucket_index];
Entry* entry = map.allocator.new(Entry);
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
map.table[bucket_index] = entry;
map.count++;
}
@@ -451,7 +410,7 @@ fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_i
fn void HashMap.free_entry(&self, Entry *entry) @local
{
$if COPY_KEYS:
allocator::free(self.allocator, entry.key);
self.allocator.free(entry.key);
$endif
self.free_internal(entry);
}

View File

@@ -2,7 +2,9 @@
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::object;
import std::collections::map, std::collections::list, std::io;
import std::collections::map;
import std::collections::list;
import std::io;
const Object TRUE_OBJECT = { .b = true, .type = bool.typeid };
const Object FALSE_OBJECT = { .b = false, .type = bool.typeid };
@@ -78,7 +80,9 @@ fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
fn Object* new_obj(Allocator* allocator)
{
return allocator::new(allocator, Object, { .allocator = allocator, .type = void.typeid });
Object* o = allocator.new(Object);
*o = { .allocator = allocator, .type = void.typeid };
return o;
}
fn Object* new_null()
@@ -88,22 +92,30 @@ fn Object* new_null()
fn Object* new_int(int128 i, Allocator* allocator)
{
return allocator::new(allocator, Object, { .i = i, .allocator = allocator, .type = int128.typeid });
Object* o = allocator.new(Object);
*o = { .i = i, .allocator = allocator, .type = int128.typeid };
return o;
}
macro Object* new_enum(e, Allocator* allocator)
{
return allocator::new(allocator, Object, { .i = (int128)e, .allocator = allocator, .type = @typeid(e) });
Object* o = allocator.new(Object);
*o = { .i = (int128)e, .allocator = allocator, .type = @typeid(e) };
return o;
}
fn Object* new_float(double f, Allocator* allocator)
{
return allocator::new(allocator, Object, { .f = f, .allocator = allocator, .type = double.typeid });
Object* o = allocator.new(Object);
*o = { .f = f, .allocator = allocator, .type = double.typeid };
return o;
}
fn Object* new_string(String s, Allocator* allocator)
{
return allocator::new(allocator, Object, { .s = s.copy(allocator), .allocator = allocator, .type = String.typeid });
Object* o = allocator.new(Object);
*o = { .s = s.copy(allocator), .allocator = allocator, .type = String.typeid };
return o;
}
@@ -119,7 +131,7 @@ fn void Object.free(&self)
case void:
break;
case String:
allocator::free(self.allocator, self.s);
self.allocator.free(self.s);
case ObjectInternalList:
foreach (ol : self.array)
{
@@ -128,13 +140,13 @@ fn void Object.free(&self)
self.array.free();
case ObjectInternalMap:
self.map.@each_entry(; ObjectInternalMapEntry* entry) {
allocator::free(self.allocator, entry.key);
self.allocator.free(entry.key);
entry.value.free();
};
default:
break;
}
if (self.allocator) allocator::free(self.allocator, self);
if (self.allocator) self.allocator.free(self);
}
fn bool Object.is_null(&self) @inline => self == &NULL_OBJECT;
@@ -156,7 +168,7 @@ fn void Object.init_map_if_needed(&self) @private
if (self.is_empty())
{
self.type = ObjectInternalMap.typeid;
self.map.new_init(.allocator = self.allocator);
self.map.init_new(.allocator = self.allocator);
}
}
@@ -168,7 +180,7 @@ fn void Object.init_array_if_needed(&self) @private
if (self.is_empty())
{
self.type = ObjectInternalList.typeid;
self.array.new_init(.allocator = self.allocator);
self.array.init_new(.allocator = self.allocator);
}
}
@@ -181,7 +193,7 @@ fn void Object.set_object(&self, String key, Object* new_object) @private
ObjectInternalMapEntry*! entry = self.map.get_entry(key);
defer
{
(void)allocator::free(self.allocator, entry.key);
(void)self.allocator.free(entry.key);
(void)entry.value.free();
}
self.map.set(key.copy(self.map.allocator), new_object);

View File

@@ -27,7 +27,7 @@ distinct PriorityQueue = inline PrivatePriorityQueue(<Type, false>);
distinct PriorityQueueMax = inline PrivatePriorityQueue(<Type, true>);
module std::collections::priorityqueue::private(<Type, MAX>);
import std::collections::list, std::io;
import std::collections::list;
def Heap = List(<Type>);
@@ -36,24 +36,14 @@ struct PrivatePriorityQueue (Printable)
Heap heap;
}
fn void PrivatePriorityQueue.init_new(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @inline @deprecated("Replaced by new_init")
fn void PrivatePriorityQueue.init_new(&self, usz initial_capacity = 16, Allocator* allocator = mem::heap()) @inline
{
return self.new_init(initial_capacity, allocator);
self.heap.init_new(initial_capacity, allocator);
}
fn void PrivatePriorityQueue.new_init(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @inline
fn void PrivatePriorityQueue.init_temp(&self, usz initial_capacity = 16) @inline
{
self.heap.new_init(initial_capacity, allocator);
}
fn void PrivatePriorityQueue.temp_init(&self, usz initial_capacity = 16) @inline
{
self.heap.new_init(initial_capacity, allocator::temp()) @inline;
}
fn void PrivatePriorityQueue.init_temp(&self, usz initial_capacity = 16) @inline @deprecated("Replaced by temp_init")
{
return self.temp_init(initial_capacity) @inline;
self.heap.init_new(initial_capacity, mem::temp()) @inline;
}
fn void PrivatePriorityQueue.push(&self, Type element)
@@ -84,32 +74,29 @@ fn Type! PrivatePriorityQueue.pop(&self)
usz i = 0;
usz len = self.heap.len();
if (!len) return IteratorResult.NO_MORE_ELEMENT?;
usz new_count = len - 1;
self.heap.swap(0, new_count);
while OUTER: ((2 * i + 1) < new_count)
usz newCount = len - 1;
self.heap.swap(0, newCount);
while ((2 * i + 1) < newCount)
{
usz j = 2 * i + 1;
Type left = self.heap[j];
Type item = self.heap[i];
switch
Type itemj = self.heap[j];
if ((j + 1) < newCount)
{
case j + 1 < new_count:
Type right = self.heap[j + 1];
$if MAX:
if (!greater(right, left)) nextcase;
if (!greater(right, item)) break OUTER;
$else
if (!greater(left, right)) nextcase;
if (!greater(item, right)) break OUTER;
$endif
j++;
default:
$if MAX:
if (!greater(left, item)) break OUTER;
$else
if (!greater(item, left)) break OUTER;
$endif
Type nextj = self.heap[j + 1];
$if MAX:
bool ok = greater(nextj, itemj);
$else
bool ok = less(nextj, itemj);
$endif
if (ok) j++;
}
Type item = self.heap[i];
$if MAX:
bool ok = less(item, itemj);
$else
bool ok = greater(item, itemj);
$endif
if (!ok) break;
self.heap.swap(i, j);
i = j;
}
@@ -151,7 +138,7 @@ fn usz! PrivatePriorityQueue.to_format(&self, Formatter* formatter) @dynamic
return self.heap.to_format(formatter);
}
fn String PrivatePriorityQueue.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
fn String PrivatePriorityQueue.to_new_string(&self, Allocator* allocator = mem::heap()) @dynamic
{
return self.heap.to_new_string(allocator);
}

View File

@@ -2,7 +2,6 @@
* @require Type.is_ordered : "The type must be ordered"
**/
module std::collections::range(<Type>);
import std::io;
struct Range (Printable)
{
@@ -29,14 +28,14 @@ fn Type Range.get(&self, usz index) @operator([])
return (Type)(self.start + (usz)index);
}
fn String Range.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
fn String Range.to_new_string(&self, Allocator* allocator = mem::heap()) @dynamic
{
return string::new_format("[%s..%s]", self.start, self.end, .allocator = allocator);
}
fn String Range.to_tstring(&self)
{
return self.to_new_string(allocator::temp());
return self.to_new_string(mem::temp());
}
fn usz! Range.to_format(&self, Formatter* formatter) @dynamic
@@ -66,14 +65,14 @@ fn usz! ExclusiveRange.to_format(&self, Formatter* formatter) @dynamic
return formatter.printf("[%s..<%s]", self.start, self.end)!;
}
fn String ExclusiveRange.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
fn String ExclusiveRange.to_new_string(&self, Allocator* allocator = mem::heap()) @dynamic
{
return string::new_format("[%s..<%s]", self.start, self.end, .allocator = allocator);
}
fn String ExclusiveRange.to_tstring(&self)
{
return self.to_new_string(allocator::temp());
return self.to_new_string(mem::temp());
}
/**

View File

@@ -2,7 +2,6 @@
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator;
import std::math;
struct ArenaAllocator (Allocator)
{
@@ -30,11 +29,9 @@ struct ArenaAllocatorHeader @local
char[*] data;
}
/*
* @require ptr != null
**/
fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
{
if (!ptr) return;
assert((uptr)ptr >= (uptr)self.data.ptr, "Pointer originates from a different allocator.");
ArenaAllocatorHeader* header = ptr - ArenaAllocatorHeader.sizeof;
// Reclaim memory if it's the last element.
@@ -43,26 +40,29 @@ fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
self.used -= header.size + ArenaAllocatorHeader.sizeof;
}
}
fn usz ArenaAllocator.mark(&self) @dynamic => self.used;
fn void ArenaAllocator.reset(&self, usz mark) @dynamic => self.used = mark;
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require size > 0
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
**/
fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
alignment = alignment_for_allocation(alignment);
usz total_len = self.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
void* start_mem = self.data.ptr;
void* unaligned_pointer_to_offset = start_mem + self.used + ArenaAllocatorHeader.sizeof;
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(mem - self.data.ptr) + size;
void* unaligned_pointer_to_offset = start_mem + self.used + ArenaAllocatorHeader.sizeof + offset;
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(aligned_pointer_to_offset - self.data.ptr) + size - offset;
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY?;
self.used = end;
void* mem = aligned_pointer_to_offset - offset;
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
header.size = size;
if (clear) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
@@ -72,11 +72,21 @@ fn void*! ArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require old_pointer != null
* @require size > 0
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
**/
fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!size)
{
self.release(old_pointer, alignment > 0);
return null;
}
if (!old_pointer)
{
return self.acquire(size, true, alignment, offset);
}
alignment = alignment_for_allocation(alignment);
assert(old_pointer >= self.data.ptr, "Pointer originates from a different allocator.");
usz total_len = self.data.len;
@@ -84,7 +94,7 @@ fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
usz old_size = header.size;
// Do last allocation and alignment match?
if (&self.data[self.used] == old_pointer + old_size && mem::ptr_is_aligned(old_pointer, alignment))
if (&self.data[self.used] == old_pointer + old_size && mem::ptr_is_aligned(old_pointer + offset, alignment))
{
if (old_size >= size)
{
@@ -100,7 +110,7 @@ fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
return old_pointer;
}
// Otherwise just allocate new memory.
void* mem = self.acquire(size, false, alignment, 0)!;
void* mem = self.acquire(size, false, alignment, offset)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}

View File

@@ -1,8 +1,7 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator;
import std::math;
struct DynamicArenaAllocator (Allocator)
{
@@ -30,14 +29,14 @@ fn void DynamicArenaAllocator.free(&self)
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
allocator::free(self.backing_allocator, page);
self.backing_allocator.free(page);
page = next_page;
}
page = self.unused_page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
allocator::free(self.backing_allocator, page);
self.backing_allocator.free(page);
page = next_page;
}
self.page = null;
@@ -59,11 +58,11 @@ struct DynamicArenaChunk @local
}
/**
* @require ptr
* @require self.page `tried to free pointer on invalid allocator`
*/
fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
{
if (!ptr) return;
DynamicArenaPage* current_page = self.page;
if (ptr == current_page.current_stack_ptr)
{
@@ -73,12 +72,19 @@ fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
}
/**
* @require size > 0 `Resize doesn't support zeroing`
* @require old_pointer != null `Resize doesn't handle null pointers`
* @require self.page `tried to realloc pointer on invalid allocator`
*/
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!size)
{
self.release(old_pointer, alignment > 0);
return null;
}
if (!old_pointer)
{
return self.acquire(size, true, alignment, offset);
}
DynamicArenaPage* current_page = self.page;
alignment = alignment_for_allocation(alignment);
usz* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX;
@@ -102,7 +108,7 @@ fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz a
current_page.used += add_size;
return old_pointer;
}
void* new_mem = self.acquire(size, false, alignment, 0)!;
void* new_mem = self.acquire(size, false, alignment, offset)!;
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
return new_mem;
}
@@ -128,21 +134,21 @@ fn void DynamicArenaAllocator.reset(&self, usz mark = 0) @dynamic
* @require math::is_power_of_2(alignment)
* @require size > 0
*/
fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local
fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment, usz offset) @local
{
// First, make sure that we can align it, extending the page size if needed.
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof, alignment));
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + offset, alignment) - offset);
// Grab the page without alignment (we do it ourselves)
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
DynamicArenaPage*! page = allocator::new_try(self.backing_allocator, DynamicArenaPage);
void* mem = self.backing_allocator.alloc_checked(page_size)!;
DynamicArenaPage*! page = self.backing_allocator.new(DynamicArenaPage);
if (catch err = page)
{
allocator::free(self.backing_allocator, mem);
self.backing_allocator.free(mem);
return err?;
}
page.memory = mem;
void* mem_start = mem::aligned_pointer(mem + DynamicArenaChunk.sizeof, alignment);
void* mem_start = mem::aligned_pointer(mem + offset + DynamicArenaChunk.sizeof, alignment) - offset;
assert(mem_start + size < mem + page_size);
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1;
chunk.size = size;
@@ -155,11 +161,11 @@ fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @loca
}
/**
* @require size > 0 `acquire expects size > 0`
* @require !alignment || math::is_power_of_2(alignment)
*/
fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
alignment = alignment_for_allocation(alignment);
DynamicArenaPage* page = self.page;
void* ptr = {|
@@ -169,14 +175,14 @@ fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignme
self.unused_page = page.prev_arena;
page.prev_arena = null;
}
if (!page) return self._alloc_new(size, alignment);
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
if (!page) return self._alloc_new(size, alignment, offset);
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
usz new_used = start - page.memory + size;
if ALLOCATE_NEW: (new_used > page.total)
{
if ((page = self.unused_page))
{
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
new_used = start + size - page.memory;
if (page.total >= new_used)
{
@@ -186,7 +192,7 @@ fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignme
break ALLOCATE_NEW;
}
}
return self._alloc_new(size, alignment);
return self._alloc_new(size, alignment, offset);
}
page.used = new_used;
assert(start + size == page.memory + page.used);

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator;
import std::math;
struct SimpleHeapAllocator (Allocator)
{
@@ -23,17 +23,27 @@ fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator)
fn void*! SimpleHeapAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
if (clear)
{
return alignment > 0 ? @aligned_alloc(self._calloc, size, alignment) : self._calloc(size);
return alignment > 0 ? @aligned_calloc(self._calloc, size, alignment, offset) : self._calloc(size);
}
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : self._alloc(size);
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment, offset) : self._alloc(size);
}
fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!size)
{
self.release(old_pointer, alignment > 0);
return null;
}
if (!old_pointer)
{
return self.acquire(size, true, alignment, offset);
}
return alignment > 0
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment)
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment, offset)
: self._realloc(old_pointer, size);
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
@@ -6,133 +6,21 @@ module std::core::mem::allocator;
import libc;
const LibcAllocator LIBC_ALLOCATOR = {};
distinct LibcAllocator (Allocator) = uptr;
module std::core::mem::allocator @if(env::POSIX);
import std::os;
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
{
assert(alignment != 0 || offset == 0);
if (clear)
{
void* data @noinit;
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
if (posix::posix_memalign(&data, alignment, bytes)) return AllocationFailure.OUT_OF_MEMORY?;
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
}
else
{
void* data @noinit;
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
if (posix::posix_memalign(&data, alignment, bytes)) return AllocationFailure.OUT_OF_MEMORY?;
}
else
{
if (!(data = libc::malloc(bytes))) return AllocationFailure.OUT_OF_MEMORY?;
}
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
return data;
}
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
{
if (!new_bytes)
{
self.release(old_ptr, alignment > 0);
return null;
}
if (!old_ptr)
{
return self.acquire(new_bytes, false, alignment, 0);
}
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
void* new_ptr;
if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return AllocationFailure.OUT_OF_MEMORY?;
$switch
$case env::DARWIN:
usz old_usable_size = darwin::malloc_size(old_ptr);
$case env::LINUX:
usz old_usable_size = linux::malloc_usable_size(old_ptr);
$default:
usz old_usable_size = new_bytes;
$endswitch
usz copy_size = new_bytes < old_usable_size ? new_bytes : old_usable_size;
mem::copy(new_ptr, old_ptr, copy_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
libc::free(old_ptr);
return new_ptr;
}
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
{
libc::free(old_ptr);
}
module std::core::mem::allocator @if(env::WIN32);
import std::os::win32;
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
{
if (clear)
{
if (alignment > 0)
{
return win32::_aligned_recalloc(null, bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
}
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
}
void* data = alignment > 0 ? win32::_aligned_malloc(bytes, alignment) : libc::malloc(bytes);
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
return data;
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
{
if (alignment)
{
return win32::_aligned_realloc(old_ptr, new_bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
}
return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
}
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
{
if (aligned)
{
win32::_aligned_free(old_ptr);
return;
}
libc::free(old_ptr);
}
module std::core::mem::allocator @if(!env::WIN32 && !env::POSIX);
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz offset) @dynamic
{
if (clear)
{
void* data = alignment ? @aligned_alloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment)!! : libc::calloc(bytes, 1);
void* data = alignment ? @aligned_calloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment, offset)!! : libc::calloc(bytes, 1);
return data ?: AllocationFailure.OUT_OF_MEMORY?;
}
else
{
void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment)!! : libc::malloc(bytes);
void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment, offset)!! : libc::malloc(bytes);
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
@@ -141,12 +29,21 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, bool clear, usz alignment, usz
}
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment, usz offset) @dynamic
{
assert(alignment != 0 || offset == 0);
if (!new_bytes)
{
self.release(old_ptr, alignment > 0);
return null;
}
if (!old_ptr)
{
return self.acquire(new_bytes, true, alignment, offset);
}
if (alignment)
{
void* data = @aligned_realloc(fn void*(usz bytes) => libc::malloc(bytes), libc::free, old_ptr, new_bytes, alignment)!!;
void* data = @aligned_realloc(fn void*(usz bytes) => libc::calloc(bytes, 1), libc::free, old_ptr, new_bytes, alignment, offset)!!;
return data ?: AllocationFailure.OUT_OF_MEMORY?;
}
return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;

View File

@@ -34,15 +34,15 @@ fn void OnStackAllocator.free(&self)
{
if (chunk.is_aligned)
{
allocator::free_aligned(self.backing_allocator, chunk.data);
self.backing_allocator.free_aligned(chunk.data);
}
else
{
allocator::free(self.backing_allocator, chunk.data);
self.backing_allocator.free(chunk.data);
}
void* old = chunk;
chunk = chunk.prev;
allocator::free(self.backing_allocator, old);
self.backing_allocator.free(old);
}
self.chunk = null;
self.used = 0;
@@ -54,11 +54,9 @@ struct OnStackAllocatorHeader
char[*] data;
}
/**
* @require old_pointer
**/
fn void OnStackAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
{
if (!old_pointer) return;
if (allocation_in_stack_mem(self, old_pointer)) return;
on_stack_allocator_remove_chunk(self, old_pointer);
self.release(old_pointer, aligned);
@@ -78,7 +76,7 @@ fn void on_stack_allocator_remove_chunk(OnStackAllocator* a, void* ptr) @local
if (chunk.data == ptr)
{
*addr = chunk.prev;
allocator::free(a.backing_allocator, chunk);
a.backing_allocator.free(chunk);
return;
}
addr = &chunk.prev;
@@ -100,49 +98,56 @@ fn OnStackAllocatorExtraChunk* on_stack_allocator_find_chunk(OnStackAllocator* a
/**
* @require size > 0
* @require old_pointer != null
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, OnStackAllocatorExtraChunk.alignof) == offset
**/
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz deprecated) @dynamic
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!allocation_in_stack_mem(self, old_pointer))
{
OnStackAllocatorExtraChunk* chunk = on_stack_allocator_find_chunk(self, old_pointer);
assert(chunk, "Tried to realloc pointer not belonging to the allocator");
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment, 0)!;
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment, offset)!;
}
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
usz old_size = header.size;
void* mem = self.acquire(size, false, alignment, 0)!;
void* mem = self.acquire(size, true, alignment, offset)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
/**
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require size > 0
* @require offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require offset == 0 || alignment > 0
* @require mem::aligned_offset(offset, OnStackAllocatorHeader.alignof) == offset
**/
fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (size == 0) return null;
bool aligned = alignment > 0;
alignment = alignment_for_allocation(alignment);
usz total_len = self.data.len;
void* start_mem = self.data.ptr;
void* unaligned_pointer_to_offset = start_mem + self.used + OnStackAllocatorHeader.sizeof ;
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(mem - self.data.ptr) + size;
void* unaligned_pointer_to_offset = start_mem + self.used + OnStackAllocatorHeader.sizeof + offset;
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(aligned_pointer_to_offset - self.data.ptr) + size - offset;
Allocator* backing_allocator = self.backing_allocator;
if (end > total_len)
{
OnStackAllocatorExtraChunk* chunk = allocator::alloc_try(backing_allocator, OnStackAllocatorExtraChunk)!;
defer catch allocator::free(backing_allocator, chunk);
OnStackAllocatorExtraChunk* chunk = backing_allocator.alloc_checked(OnStackAllocatorExtraChunk.sizeof)!;
defer catch backing_allocator.free(chunk);
defer try self.chunk = chunk;
*chunk = { .prev = self.chunk, .is_aligned = aligned };
return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, 0)!;
return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, offset)!;
}
self.used = end;
void *mem = aligned_pointer_to_offset - offset;
OnStackAllocatorHeader* header = mem - OnStackAllocatorHeader.sizeof;
header.size = size;
return mem;

View File

@@ -1,5 +1,5 @@
module std::core::mem::allocator;
import std::io, std::math;
import std::io;
struct TempAllocatorChunk @local
{
@@ -35,9 +35,9 @@ macro bool TempAllocatorPage.is_aligned(&self) => self.size & PAGE_IS_ALIGNED ==
/**
* @require size >= 16
**/
fn TempAllocator*! new_temp_allocator(usz size, Allocator* allocator)
fn TempAllocator*! new_temp(usz size, Allocator* allocator)
{
TempAllocator* temp = allocator::alloc_with_padding(allocator, TempAllocator, size)!;
TempAllocator* temp = allocator.alloc_checked(TempAllocator.sizeof + size)!;
temp.last_page = null;
temp.backing_allocator = allocator;
temp.used = 0;
@@ -45,11 +45,6 @@ fn TempAllocator*! new_temp_allocator(usz size, Allocator* allocator)
return temp;
}
fn TempAllocator*! new_temp(usz size, Allocator* allocator) @deprecated("Use new_temp_allocator")
{
return new_temp_allocator(size, allocator);
}
fn usz TempAllocator.mark(&self) @dynamic => self.used;
fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
@@ -76,10 +71,11 @@ fn void TempAllocator.reset(&self, usz mark) @dynamic
fn void! TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local
{
void* mem = page.start;
return self.backing_allocator.release(mem, page.is_aligned());
if (page.is_aligned()) return self.backing_allocator.free_aligned(mem);
return self.backing_allocator.free(mem);
}
fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment, usz offset) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
@@ -94,37 +90,53 @@ fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size,
*pointer_to_prev = page.prev_page;
usz page_size = page.pagesize();
// Clear on size > original size.
void* data = self.acquire(size, size > page_size, alignment, 0)!;
void* data = self.acquire(size, size > page_size, alignment, offset)!;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
self.backing_allocator.release(real_pointer, page.is_aligned());
if (page.is_aligned())
{
self.backing_allocator.free_aligned(real_pointer);
}
else
{
self.backing_allocator.free(real_pointer);
}
return data;
}
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, usz deprecated) @dynamic
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, usz offset) @dynamic
{
if (!size)
{
self.release(pointer, alignment > 0);
return null;
}
if (!pointer)
{
return self.acquire(size, true, alignment, offset);
}
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
if (chunk.size == (usz)-1)
{
assert(self.last_page, "Realloc of non temp pointer");
// First grab the page
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
return self._realloc_page(page, size, alignment);
return self._realloc_page(page, size, alignment, offset);
}
// TODO optimize last allocation
TempAllocatorChunk* data = self.acquire(size, size > chunk.size, alignment, 0)!;
TempAllocatorChunk* data = self.acquire(size, size > chunk.size, alignment, offset)!;
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
/**
* @require size > 0
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
**/
fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
if (!size) return null;
alignment = alignment_for_allocation(alignment);
void* start_mem = &self.data;
void* starting_ptr = start_mem + self.used;
@@ -132,7 +144,7 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
void* mem = aligned_header_start + TempAllocatorChunk.sizeof;
if (alignment > TempAllocatorChunk.alignof)
{
mem = mem::aligned_pointer(mem, alignment);
mem = mem::aligned_pointer(mem + offset, alignment) - offset;
}
usz new_usage = (usz)(mem - start_mem) + size;
@@ -150,20 +162,19 @@ fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz
TempAllocatorPage* page;
// We have something we need to align.
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
if (alignment > mem::DEFAULT_MEM_ALIGNMENT || offset)
{
// This is actually simpler, since it will create the offset for us.
usz total_alloc_size = mem::aligned_offset(TempAllocatorPage.sizeof + size, alignment);
usz total_alloc_size = TempAllocatorPage.sizeof + size;
if (clear)
{
mem = allocator::calloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
page = self.backing_allocator.calloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
}
else
{
mem = allocator::malloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
page = self.backing_allocator.alloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
}
page = (TempAllocatorPage*)mem - 1;
page.start = mem;
page.start = page;
page.size = size | PAGE_IS_ALIGNED;
}
else

View File

@@ -1,9 +1,10 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem::allocator;
import std::collections, std::io, std::os::backtrace;
import std::collections::map;
import std::collections::list;
const MAX_BACKTRACE = 16;
struct Allocation
@@ -34,7 +35,7 @@ struct TrackingAllocator (Allocator)
fn void TrackingAllocator.init(&self, Allocator* allocator)
{
*self = { .inner_allocator = allocator };
self.map.new_init(.allocator = allocator);
self.map.init_new(.allocator = allocator);
}
/**
@@ -79,36 +80,48 @@ fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator* allocator)
**/
fn usz TrackingAllocator.allocation_count(&self) => self.map.count;
fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
{
void* data = self.inner_allocator.acquire(size, clear, alignment, 0)!;
self.allocs_total++;
void*[MAX_BACKTRACE] bt;
backtrace::capture_current(&bt);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
void* data = self.inner_allocator.acquire(size, clear, alignment, offset)!;
self.allocs_total++;
if (data)
{
void*[MAX_BACKTRACE] bt;
backtrace::capture_current(&bt);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
self.allocs_total++;
}
return data;
}
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz deprecated) @dynamic
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz offset) @dynamic
{
void* data = self.inner_allocator.resize(old_pointer, size, alignment, 0)!;
self.map.remove((uptr)old_pointer);
void*[MAX_BACKTRACE] bt;
backtrace::capture_current(&bt);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
self.allocs_total++;
void* data = self.inner_allocator.resize(old_pointer, size, alignment, offset)!;
if (old_pointer)
{
self.map.remove((uptr)old_pointer);
}
if (data)
{
void*[MAX_BACKTRACE] bt;
backtrace::capture_current(&bt);
self.map.set((uptr)data, { data, size, bt });
self.mem_total += size;
self.allocs_total++;
}
return data;
}
fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dynamic
{
if (catch self.map.remove((uptr)old_pointer))
{
assert(false, "Attempt to release untracked pointer %p, this is likely a bug.", old_pointer);
}
if (old_pointer)
{
if (catch self.map.remove((uptr)old_pointer))
{
assert(false, "Attempt to release untracked pointer %p, this is likely a bug.", old_pointer);
}
}
self.inner_allocator.release(old_pointer, is_aligned);
}
@@ -155,7 +168,7 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream* out)
Backtrace trace = backtrace::BACKTRACE_UNKNOWN;
if (allocation.backtrace[3])
{
trace = backtrace::symbolize_backtrace(allocation.backtrace[3:1], allocator::temp()).get(0) ?? backtrace::BACKTRACE_UNKNOWN;
trace = backtrace::symbolize_backtrace(allocation.backtrace[3:1], mem::temp()).get(0) ?? backtrace::BACKTRACE_UNKNOWN;
}
if (trace.function.len) leaks = true;
io::fprintfn(out, "%13s %p %s:%d", allocation.size,
@@ -194,7 +207,7 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream* out)
break;
}
}
BacktraceList list = backtrace::symbolize_backtrace(allocation.backtrace[3..(end - 1)], allocator::temp())!;
BacktraceList list = backtrace::symbolize_backtrace(allocation.backtrace[3..(end - 1)], mem::temp())!;
io::fprintfn(out, "Allocation %d (%d bytes): ", i + 1, allocation.size)!;
foreach (trace : list)
{

View File

@@ -1,5 +1,4 @@
module std::core::array;
import std::core::array::slice;
/**
* @param [in] array
@@ -16,19 +15,6 @@ macro index_of(array, element)
return SearchResult.MISSING?;
}
/**
* @require @typekind(array) == VECTOR || @typekind(array) == ARRAY
* @require @typekind(array[0]) == VECTOR || @typekind(array[0]) == ARRAY
**/
macro slice2d(array, x = 0, xlen = 0, y = 0, ylen = 0)
{
if (xlen < 1) xlen = $typeof(array[0]).len + xlen;
if (ylen < 1) ylen = $typeof(array).len + ylen;
var $ElementType = $typeof(array[0][0]);
return Slice2d(<$ElementType>) { ($ElementType*)&array, $typeof(array[0]).len, y, ylen, x, xlen };
}
/**
* @param [in] array
* @param [in] element
@@ -55,10 +41,10 @@ macro rindex_of(array, element)
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
* @ensure result.len == arr1.len + arr2.len
**/
macro concat_new(arr1, arr2, Allocator* allocator = allocator::heap())
macro concat_new(arr1, arr2, Allocator* allocator = mem::heap())
{
var $Type = $typeof(arr1[0]);
$Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len);
$Type[] result = allocator.new_array($Type, arr1.len + arr2.len);
if (arr1.len > 0)
{
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $Type.alignof, $Type.alignof);
@@ -81,67 +67,4 @@ macro concat_new(arr1, arr2, Allocator* allocator = allocator::heap())
* @require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
* @ensure result.len == arr1.len + arr2.len
**/
macro tconcat(arr1, arr2) => concat(arr1, arr2, allocator::temp());
module std::core::array::slice(<Type>);
struct Slice2d
{
Type* ptr;
usz inner_len;
usz ystart;
usz ylen;
usz xstart;
usz xlen;
}
fn usz Slice2d.len(&self) @operator(len)
{
return self.ylen;
}
fn usz Slice2d.count(&self)
{
return self.ylen * self.xlen;
}
macro void Slice2d.@each(&self; @body(usz[<2>], Type))
{
foreach (y, line : *self)
{
foreach (x, val : line)
{
@body({ x, y }, val);
}
}
}
macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
{
foreach (y, line : *self)
{
foreach (x, &val : line)
{
@body({ x, y }, val);
}
}
}
/**
* @require idy >= 0 && idy < self.ylen
**/
macro Type[] Slice2d.get(self, usz idy) @operator([])
{
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
}
/**
* @require y >= 0 && y < self.ylen
* @require x >= 0 && x < self.xlen
**/
fn Slice2d Slice2d.slice(&self, isz x = 0, isz xlen = 0, isz y = 0, isz ylen = 0)
{
if (xlen < 1) xlen = self.xlen + xlen;
if (ylen < 1) ylen = self.ylen + ylen;
return { self.ptr, self.inner_len, y + self.ystart, ylen, x + self.xstart, xlen };
}
macro tconcat(arr1, arr2) => concat(arr1, arr2, mem::temp());

View File

@@ -1,8 +1,9 @@
// Copyright (c) 2021-2024 Christoffer Lerno and contributors. All rights reserved.
// Copyright (c) 2021-2022 Christoffer Lerno and contributors. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::builtin;
import libc, std::hash, std::io, std::os::backtrace;
import libc;
import std::hash;
/**
* Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
@@ -74,7 +75,7 @@ fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIV
void*[256] buffer;
void*[] backtraces = backtrace::capture_current(&buffer);
backtraces_to_ignore++;
BacktraceList! backtrace = backtrace::symbolize_backtrace(backtraces, allocator::temp());
BacktraceList! backtrace = backtrace::symbolize_backtrace(backtraces, mem::temp());
if (catch backtrace) return false;
if (backtrace.len() <= backtraces_to_ignore) return false;
io::eprint("\nERROR: '");
@@ -129,7 +130,7 @@ fn void panicf(String fmt, String file, String function, uint line, args...)
@stack_mem(512; Allocator* allocator)
{
DString s;
s.new_init(.allocator = allocator);
s.init_new(.allocator = allocator);
s.appendf(fmt, ...args);
panic(s.str_view(), file, function, line);
};
@@ -614,7 +615,7 @@ macro void* get_returnaddress(int n)
}
module std::core::builtin @if((env::LINUX || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS);
import libc, std::io;
import libc;
fn void sig_panic(String message)
{

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021-2024 Christoffer Lerno and contributors. All rights reserved.
// Copyright (c) 2021-2022 Christoffer Lerno and contributors. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::builtin;

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::cinterop;

View File

@@ -8,10 +8,10 @@ const usz MIN_CAPACITY @private = 16;
/**
* @require !self.data() "String already initialized"
**/
fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator* allocator = allocator::heap())
fn DString DString.init_new(&self, usz capacity = MIN_CAPACITY, Allocator* allocator = mem::heap())
{
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
StringData* data = allocator::alloc_with_padding(allocator, StringData, capacity)!!;
StringData* data = allocator.new(StringData, .end_padding = capacity);
data.allocator = allocator;
data.len = 0;
data.capacity = capacity;
@@ -21,36 +21,20 @@ fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator* alloc
/**
* @require !self.data() "String already initialized"
**/
fn DString DString.init_new(&self, usz capacity = MIN_CAPACITY, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
fn DString DString.init_temp(&self, usz capacity = MIN_CAPACITY)
{
return self.new_init(capacity, allocator) @inline;
}
/**
* @require !self.data() "String already initialized"
**/
fn DString DString.temp_init(&self, usz capacity = MIN_CAPACITY)
{
self.new_init(capacity, allocator::temp()) @inline;
self.init_new(capacity, mem::temp()) @inline;
return *self;
}
/**
* @require !self.data() "String already initialized"
**/
fn DString DString.init_temp(&self, usz capacity = MIN_CAPACITY) @deprecated("Replaced by temp_init")
fn DString new_with_capacity(usz capacity, Allocator* allocator = mem::heap())
{
return self.temp_init(capacity) @inline;
return DString{}.init_new(capacity, allocator);
}
fn DString new_with_capacity(usz capacity, Allocator* allocator = allocator::heap())
{
return DString{}.new_init(capacity, allocator);
}
fn DString temp_with_capacity(usz capacity) => new_with_capacity(capacity, mem::temp()) @inline;
fn DString temp_with_capacity(usz capacity) => new_with_capacity(capacity, allocator::temp()) @inline;
fn DString new(String c = "", Allocator* allocator = allocator::heap())
fn DString new(String c = "", Allocator* allocator = mem::heap())
{
usz len = c.len;
StringData* data = (StringData*)new_with_capacity(len, allocator);
@@ -62,20 +46,18 @@ fn DString new(String c = "", Allocator* allocator = allocator::heap())
return (DString)data;
}
fn DString temp_new(String s = "") => new(s, allocator::temp()) @inline;
fn DString temp_new(String s = "") => new(s, mem::temp()) @inline;
fn DString DString.new_concat(self, DString b, Allocator* allocator = allocator::heap())
fn DString DString.new_concat(self, DString b, Allocator* allocator = mem::heap())
{
DString string;
string.new_init(self.len() + b.len(), allocator);
string.init_new(self.len() + b.len(), allocator);
string.append(self);
string.append(b);
return string;
}
fn DString DString.temp_concat(self, DString b) => self.new_concat(b, allocator::temp());
fn DString DString.new_tconcat(self, DString b) @deprecated("Replaced by temp_concat") => self.new_concat(b, allocator::temp());
fn DString DString.new_tconcat(self, DString b) => self.new_concat(b, mem::temp());
fn ZString DString.zstr_view(&self)
{
@@ -164,7 +146,7 @@ fn void DString.append_char32(&self, Char32 c)
data.len += n;
}
fn DString DString.tcopy(&self) => self.copy(allocator::temp());
fn DString DString.tcopy(&self) => self.copy(mem::temp());
fn DString DString.copy(self, Allocator* allocator = null)
{
@@ -174,32 +156,32 @@ fn DString DString.copy(self, Allocator* allocator = null)
return (DString)null;
}
StringData* data = self.data();
if (!allocator) allocator = allocator::heap();
if (!allocator) allocator = mem::heap();
DString new_string = new_with_capacity(data.capacity, allocator);
mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len);
return new_string;
}
fn ZString DString.copy_zstr(self, Allocator* allocator = allocator::heap())
fn ZString DString.copy_zstr(self, Allocator* allocator = mem::heap())
{
usz str_len = self.len();
if (!str_len)
{
return (ZString)allocator::calloc(allocator, 1);
return (ZString)allocator.calloc(1);
}
char* zstr = allocator::malloc(allocator, str_len + 1);
char* zstr = allocator.alloc(str_len + 1);
StringData* data = self.data();
mem::copy(zstr, &data.chars, str_len);
zstr[str_len] = 0;
return (ZString)zstr;
}
fn String DString.copy_str(self, Allocator* allocator = allocator::heap())
fn String DString.copy_str(self, Allocator* allocator = mem::heap())
{
return (String)self.copy_zstr(allocator)[:self.len()];
}
fn String DString.tcopy_str(self) => self.copy_str(allocator::temp()) @inline;
fn String DString.tcopy_str(self) => self.copy_str(mem::temp()) @inline;
fn bool DString.equals(self, DString other_string)
{
@@ -222,7 +204,7 @@ fn void DString.free(&self)
if (!*self) return;
StringData* data = self.data();
if (!data) return;
allocator::free(data.allocator, data);
data.allocator.free(data);
*self = (DString)null;
}
@@ -258,7 +240,7 @@ fn void DString.append_chars(&self, String str)
data.len += other_len;
}
fn Char32[] DString.copy_utf32(&self, Allocator* allocator = allocator::heap())
fn Char32[] DString.copy_utf32(&self, Allocator* allocator = mem::heap())
{
return self.str_view().to_new_utf32(allocator) @inline!!;
}
@@ -298,37 +280,6 @@ fn void DString.append_char(&self, char c)
data.chars[data.len++] = c;
}
/**
* @require start < self.len()
* @require end < self.len()
* @require end >= start "End must be same or equal to the start"
**/
fn void DString.delete_range(&self, usz start, usz end)
{
self.delete(start, end - start + 1);
}
/**
* @require start < self.len()
* @require start + len <= self.len()
**/
fn void DString.delete(&self, usz start, usz len = 1)
{
if (!len) return;
StringData* data = self.data();
usz new_len = data.len - len;
if (new_len == 0)
{
data.len = 0;
return;
}
usz len_after = data.len - start - len;
if (len_after > 0)
{
data.chars[start:len_after] = data.chars[start + len:len_after];
}
data.len = new_len;
}
macro void DString.append(&self, value)
{
@@ -403,7 +354,7 @@ fn usz! DString.appendfn(&self, String format, args...) @maydiscard
return len + 1;
}
fn DString new_join(String[] s, String joiner, Allocator* allocator = allocator::heap())
fn DString new_join(String[] s, String joiner, Allocator* allocator = mem::heap())
{
if (!s.len) return (DString)null;
usz total_size = joiner.len * s.len;
@@ -447,7 +398,7 @@ fn void DString.reserve(&self, usz addition)
if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY;
while (new_capacity < len) new_capacity *= 2;
data.capacity = new_capacity;
*self = (DString)allocator::realloc(data.allocator, data, StringData.sizeof + new_capacity);
*self = (DString)data.allocator.realloc(data, StringData.sizeof + new_capacity);
}
fn usz! DString.read_from_stream(&self, InStream* reader)

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::env;
@@ -131,7 +131,6 @@ const bool TESTING = $$TESTING;
const MemoryEnvironment MEMORY_ENV = (MemoryEnvironment)$$MEMORY_ENVIRONMENT;
const bool TRACK_MEMORY = DEBUG_SYMBOLS && (COMPILER_SAFE_MODE || TESTING);
const bool X86_64 = ARCH_TYPE == X86_64;
const bool X86 = ARCH_TYPE == X86;
const bool AARCH64 = ARCH_TYPE == AARCH64;
const bool NATIVE_STACKTRACE = LINUX || DARWIN || WIN32;
const bool LINUX = LIBC && OS_TYPE == LINUX;

View File

@@ -2,8 +2,6 @@
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem;
import std::core::mem::allocator @public;
import std::math;
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
@@ -253,12 +251,11 @@ fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
}
macro void clear_inline(void* dst, usz $len, usz $dst_align = 0, bool $is_volatile = false)
{
$$memset_inline(dst, (char)0, $len, $is_volatile, $dst_align);
$if $inlined:
$$memset_inline(dst, (char)0, len, $is_volatile, $dst_align);
$else
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
$endif
}
/**
@@ -270,30 +267,17 @@ macro void clear_inline(void* dst, usz $len, usz $dst_align = 0, bool $is_volati
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
* @param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
* @param $inlined "True if this copy should never call the OS memcpy."
*
* @require len == 0 || dst + len <= src || src + len <= dst : "Ranges may not overlap"
**/
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
}
/**
* Copy memory from src to dst efficiently, assuming the memory ranges do not overlap, it
* will always be inlined and never call memcopy
*
* @param [&out] dst "The destination to copy to"
* @param [&in] src "The source to copy from"
* @param $len "The number of bytes to copy"
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
* @param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
*
* @require $len == 0 || dst + $len <= src || src + $len <= dst : "Ranges may not overlap"
**/
macro void copy_inline(void* dst, void* src, usz $len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
{
$$memcpy_inline(dst, src, $len, $is_volatile, $dst_align, $src_align);
$if $inlined:
$$memcpy_inline(dst, src, len, $is_volatile, $dst_align, $src_align);
$else
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
$endif
}
/**
@@ -319,29 +303,19 @@ macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_alig
* @param len "The number of bytes to copy"
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
* @param $inlined "True if this copy should never call the OS memset."
*
* @ensure !len || (dst[0] == val && dst[len - 1] == val)
**/
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false)
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
{
$$memset(dst, val, len, $is_volatile, $dst_align);
$if $inlined:
$$memset_inline(dst, val, len, $is_volatile, $dst_align);
$else
$$memset(dst, val, len, $is_volatile, $dst_align);
$endif
}
/**
* Sets all memory in a region to that of the provided byte. Never calls OS memset.
*
* @param [&out] dst "The destination to copy to"
* @param val "The value to copy into memory"
* @param $len "The number of bytes to copy"
* @param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
* @param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
*
* @ensure !$len || (dst[0] == val && dst[$len - 1] == val)
**/
macro void set_inline(void* dst, char val, usz $len, usz $dst_align = 0, bool $is_volatile = false)
{
$$memset_inline(dst, val, $len, $is_volatile, $dst_align);
}
/**
* @require values::@inner_kind(a) == TypeKind.SUBARRAY || values::@inner_kind(a) == TypeKind.POINTER
* @require values::@inner_kind(b) == TypeKind.SUBARRAY || values::@inner_kind(b) == TypeKind.POINTER
@@ -405,21 +379,21 @@ macro type_alloc_must_be_aligned($Type)
**/
macro void @scoped(Allocator* allocator; @body())
{
Allocator* old_allocator = allocator::thread_allocator;
allocator::thread_allocator = allocator;
defer allocator::thread_allocator = old_allocator;
Allocator* old_allocator = thread_allocator;
thread_allocator = allocator;
defer thread_allocator = old_allocator;
@body();
}
macro void @report_heap_allocs_in_scope(;@body())
{
TrackingAllocator tracker;
tracker.init(allocator::thread_allocator);
Allocator* old_allocator = allocator::thread_allocator;
allocator::thread_allocator = &tracker;
tracker.init(thread_allocator);
Allocator* old_allocator = thread_allocator;
thread_allocator = &tracker;
defer
{
allocator::thread_allocator = old_allocator;
thread_allocator = old_allocator;
tracker.print_report();
tracker.free();
}
@@ -430,7 +404,7 @@ macro void @stack_mem(usz $size; @body(Allocator* mem)) @builtin
{
char[$size] buffer;
OnStackAllocator allocator;
allocator.init(&buffer, allocator::heap());
allocator.init(&buffer, mem::heap());
defer allocator.free();
@body(&allocator);
}
@@ -439,7 +413,7 @@ macro void @stack_pool(usz $size; @body) @builtin
{
char[$size] buffer;
OnStackAllocator allocator;
allocator.init(&buffer, allocator::heap());
allocator.init(&buffer, mem::heap());
defer allocator.free();
mem::@scoped(&allocator)
{
@@ -447,67 +421,76 @@ macro void @stack_pool(usz $size; @body) @builtin
};
}
struct TempState
{
TempAllocator* old;
TempAllocator* current;
usz mark;
}
/**
* Push the current temp allocator. A push must always be balanced with a pop using the current state.
**/
fn TempState temp_push(TempAllocator* other = null)
{
TempAllocator* current = allocator::temp();
TempAllocator* old = current;
if (other == current)
{
current = allocator::temp_allocator_next();
}
return { old, current, current.used };
}
/**
* Pop the current temp allocator. A pop must always be balanced with a push.
**/
fn void temp_pop(TempState old_state)
{
assert(allocator::thread_temp_allocator == old_state.current, "Tried to pop temp allocators out of order.");
assert(old_state.current.used >= old_state.mark, "Tried to pop temp allocators out of order.");
old_state.current.reset(old_state.mark);
allocator::thread_temp_allocator = old_state.old;
}
macro void @pool(TempAllocator* #other_temp = null; @body) @builtin
{
TempAllocator* current = allocator::temp();
TempAllocator* current = temp();
var $has_arg = !$is_const(#other_temp);
$if $has_arg:
TempAllocator* original = current;
if (current == (void*)#other_temp) current = allocator::temp_allocator_next();
if (current == (void*)#other_temp) current = temp_allocator_next();
$endif
usz mark = current.used;
defer
{
current.reset(mark);
$if $has_arg:
allocator::thread_temp_allocator = original;
thread_temp_allocator = original;
$endif;
}
@body();
}
tlocal Allocator* thread_allocator @private = &allocator::LIBC_ALLOCATOR;
tlocal TempAllocator* thread_temp_allocator @private = null;
tlocal TempAllocator*[2] temp_allocator_pair @private;
Allocator* temp_base_allocator @private = &allocator::LIBC_ALLOCATOR;
macro TempAllocator* create_default_sized_temp_allocator(Allocator* allocator) @local
{
$switch (env::MEMORY_ENV)
$case NORMAL:
return allocator::new_temp(1024 * 256, allocator)!!;
$case SMALL:
return allocator::new_temp(1024 * 16, allocator)!!;
$case TINY:
return allocator::new_temp(1024 * 2, allocator)!!;
$case NONE:
unreachable("Temp allocator must explicitly created when memory-env is set to 'none'.");
$endswitch
}
fn TempAllocator *temp_allocator_next() @private
{
if (!thread_temp_allocator)
{
init_default_temp_allocators();
return thread_temp_allocator;
}
usz index = thread_temp_allocator == temp_allocator_pair[0] ? 1 : 0;
return thread_temp_allocator = temp_allocator_pair[index];
}
import libc;
fn void init_default_temp_allocators() @private
{
temp_allocator_pair[0] = create_default_sized_temp_allocator(temp_base_allocator);
temp_allocator_pair[1] = create_default_sized_temp_allocator(temp_base_allocator);
thread_temp_allocator = temp_allocator_pair[0];
}
macro TempAllocator* temp()
{
if (!thread_temp_allocator)
{
init_default_temp_allocators();
}
return thread_temp_allocator;
}
macro Allocator* current_allocator() => thread_allocator;
macro Allocator* heap() => thread_allocator;
macro TempAllocator* temp() @deprecated("Use allocator::temp()") => allocator::temp();
macro Allocator* current_allocator() @deprecated("Use allocator::heap()") => allocator::heap();
macro Allocator* heap() @deprecated("Use allocator::heap()") => allocator::heap();
module std::core::mem @if(WASM_NOLIBC);
@@ -522,7 +505,7 @@ fn void initialize_wasm_mem() @init(1) @private
if (start > mem::DEFAULT_MEM_ALIGNMENT) allocator::wasm_memory.use = start;
wasm_allocator.init(fn (x) => allocator::wasm_memory.allocate_block(x));
temp_base_allocator = &wasm_allocator;
allocator::thread_allocator = &wasm_allocator;
thread_allocator = &wasm_allocator;
}
module std::core::mem;
@@ -537,218 +520,88 @@ macro TrackingEnv* get_tracking_env()
$endif
}
macro @clone(value) @builtin @nodiscard
macro @clone(value) @builtin
{
return allocator::clone(allocator::heap(), value);
return mem::heap().clone(value);
}
macro @tclone(value) @builtin @nodiscard
macro @tclone(value) @builtin
{
return temp_new($typeof(value), value);
return mem::temp().clone(value);
}
fn void* malloc(usz size) @builtin @inline @nodiscard
fn void* malloc(usz size) @builtin @inline
{
return allocator::malloc(allocator::heap(), size);
return mem::heap().alloc(size);
}
fn void* tmalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline @nodiscard
fn void* tmalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline
{
if (!size) return null;
return allocator::temp().acquire(size, false, alignment, 0)!!;
return temp().acquire(size, false, alignment, offset)!!;
}
/**
* @require $vacount < 2 : "Too many arguments."
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
**/
macro new($Type, ...) @nodiscard
macro new($Type)
{
$if $vacount == 0:
return ($Type*)calloc($Type.sizeof);
$else
$Type* val = malloc($Type.sizeof);
*val = $vaexpr(0);
return val;
$endif
return heap().new($Type);
}
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
* @require $vacount < 2 : "Too many arguments."
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
**/
macro new_aligned($Type, ...) @nodiscard
macro new_clear($Type)
{
$if $vacount == 0:
return ($Type*)calloc_aligned($Type.sizeof, $Type.alignof);
$else
$Type* val = malloc_aligned($Type.sizeof, $Type.alignof);
*val = $vaexpr(0);
return val;
$endif
return heap().new_clear($Type);
}
/**
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
**/
macro alloc($Type) @nodiscard
{
return ($Type*)malloc($Type.sizeof);
}
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
**/
macro alloc_aligned($Type) @nodiscard
{
return ($Type*)malloc_aligned($Type.sizeof, $Type.alignof);
}
macro new_clear($Type) @deprecated("Use mem::new")
{
return new($Type);
}
macro new_temp($Type) @deprecated("Use mem::temp_alloc or mem::temp_new")
macro new_temp($Type)
{
return tmalloc($Type.sizeof);
}
/**
* @require $vacount < 2 : "Too many arguments."
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
**/
macro temp_new($Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)tcalloc($Type.sizeof) @inline;
$else
$Type* val = tmalloc($Type.sizeof) @inline;
*val = $vaexpr(0);
return val;
$endif
}
macro temp_alloc($Type) @nodiscard
{
return tmalloc($Type.sizeof);
}
macro new_temp_clear($Type) @deprecated("use mem::temp_new")
macro new_temp_clear($Type)
{
return tcalloc($Type.sizeof);
}
/**
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
**/
macro new_array($Type, usz elements) @nodiscard
macro new_array($Type, usz elements)
{
return allocator::new_array(allocator::heap(), $Type, elements);
return heap().new_array($Type, elements);
}
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
**/
macro new_array_aligned($Type, usz elements) @nodiscard
{
return allocator::new_array_aligned(allocator::heap(), $Type, elements);
}
/**
* @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
**/
macro alloc_array($Type, usz elements) @nodiscard
{
return allocator::alloc_array(allocator::heap(), $Type, elements);
}
/**
* Allocate using an aligned allocation. This is necessary for types with a default memory alignment
* exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
**/
macro alloc_array_aligned($Type, usz elements) @nodiscard
{
return allocator::alloc_array(allocator::heap(), $Type, elements);
}
macro talloc_array($Type, usz elements) @nodiscard @deprecated("use mem::temp_alloc_array")
{
return temp_alloc_array($Type, elements);
}
macro temp_alloc_array($Type, usz elements) @nodiscard
macro temp_array($Type, usz elements)
{
return (($Type*)tmalloc($Type.sizeof * elements, $Type.alignof))[:elements];
}
macro temp_array($Type, usz elements) @nodiscard @deprecated("use mem::temp_alloc_array")
macro new_zero_array($Type, usz elements)
{
return temp_alloc_array($Type, elements);
return heap().new_zero_array($Type, elements);
}
macro temp_new_array($Type, usz elements) @nodiscard
macro temp_zero_array($Type, usz elements)
{
return (($Type*)tcalloc($Type.sizeof * elements, $Type.alignof))[:elements];
}
macro new_zero_array($Type, usz elements) @deprecated("Use new_array")
fn void* calloc(usz size) @builtin @inline
{
return new_array($Type, elements);
return heap().calloc(size);
}
macro temp_zero_array($Type, usz elements) @deprecated("Use temp_new_array")
fn void* tcalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline
{
return temp_new_array($Type, elements);
return temp().acquire(size, false, alignment, offset)!!;
}
fn void* calloc(usz size) @builtin @inline @nodiscard
fn void* realloc(void *ptr, usz new_size) @builtin @inline
{
return allocator::calloc(allocator::heap(), size);
}
fn void* calloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard
{
return allocator::calloc_aligned(allocator::heap(), size, alignment)!!;
}
fn void* tcalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline @nodiscard
{
if (!size) return null;
return allocator::temp().acquire(size, false, alignment, 0)!!;
}
fn void* realloc(void *ptr, usz new_size) @builtin @inline @nodiscard
{
return allocator::realloc(allocator::heap(), ptr, new_size);
}
fn void* realloc_aligned(void *ptr, usz new_size, usz alignment) @builtin @inline @nodiscard
{
return allocator::realloc_aligned(allocator::heap(), ptr, new_size, alignment)!!;
return heap().realloc(ptr, new_size);
}
fn void free(void* ptr) @builtin @inline
{
return allocator::free(allocator::heap(), ptr);
heap().free(ptr);
}
fn void free_aligned(void* ptr) @builtin @inline
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline
{
return allocator::free_aligned(allocator::heap(), ptr);
}
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline @nodiscard
{
if (!size) return null;
if (!ptr) return tmalloc(size, alignment);
return allocator::temp().resize(ptr, size, alignment, 0)!!;
return temp().resize(ptr, size, alignment, 0)!!;
}

View File

@@ -15,27 +15,83 @@ interface Allocator
{
fn void reset(usz mark) @optional;
fn usz mark() @optional;
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset == 0 `offset no longer supported`
* @require size > 0
**/
fn void*! acquire(usz size, bool clear, usz alignment, usz offset);
/**
* @require !alignment || math::is_power_of_2(alignment)
* @require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
* @require offset == 0 `offset no longer supported`
* @require ptr != null
* @require new_size > 0
**/
fn void*! resize(void* ptr, usz new_size, usz alignment, usz offset);
/**
* @require ptr != null
**/
fn void release(void* ptr, bool aligned);
}
struct AlignedBlock
{
usz len;
void* start;
}
/**
* @require bytes > 0
* @require alignment > 0
**/
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment, usz offset)
{
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
$if @typekind(#alloc_fn(bytes)) == OPTIONAL:
void* data = #alloc_fn(header + bytes)!;
$else
void* data = #alloc_fn(header + bytes);
$endif
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
assert(mem > data);
AlignedBlock* desc = (AlignedBlock*)mem - 1;
*desc = { bytes, data };
return mem;
}
/**
* @require bytes > 0
* @require alignment > 0
**/
macro void*! @aligned_calloc(#calloc_fn, usz bytes, usz alignment, usz offset)
{
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
$if @typekind(#calloc_fn(bytes)) == OPTIONAL:
void* data = #calloc_fn(header + bytes)!;
$else
void* data = #calloc_fn(header + bytes);
$endif
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
AlignedBlock* desc = (AlignedBlock*)mem - 1;
assert(mem > data);
*desc = { bytes, data };
return mem;
}
/**
* @require bytes > 0
* @require alignment > 0
**/
macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment, usz offset)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
void* data_start = desc.start;
void* new_data = @aligned_calloc(#calloc_fn, bytes, alignment, offset)!;
mem::copy(new_data, old_pointer, desc.len < bytes ? desc.len : bytes, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
$if @typekind(#free_fn(data_start)) == OPTIONAL:
#free_fn(data_start)!;
$else
#free_fn(data_start);
$endif
return new_data;
}
macro void! @aligned_free(#free_fn, void* old_pointer)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
$if @typekind(#free_fn(desc.start)) == OPTIONAL:
#free_fn(desc.start)!;
$else
#free_fn(desc.start);
$endif
}
def MemoryAllocFn = fn char[]!(usz);
fault AllocationFailure
@@ -49,396 +105,120 @@ fn usz alignment_for_allocation(usz alignment) @inline @private
return alignment < mem::DEFAULT_MEM_ALIGNMENT ? alignment = mem::DEFAULT_MEM_ALIGNMENT : alignment;
}
macro void* malloc(Allocator* allocator, usz size) @nodiscard
{
return malloc_try(allocator, size)!!;
}
macro void*! malloc_try(Allocator* allocator, usz size) @nodiscard
{
if (!size) return null;
$if env::TESTING:
char* data = allocator.acquire(size, false, 0, 0)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
return data;
$else
return allocator.acquire(size, false, 0, 0);
$endif
}
macro void* calloc(Allocator* allocator, usz size) @nodiscard
{
return calloc_try(allocator, size)!!;
}
macro void*! calloc_try(Allocator* allocator, usz size) @nodiscard
{
if (!size) return null;
return allocator.acquire(size, true, 0, 0);
}
macro void* realloc(Allocator* allocator, void* ptr, usz new_size) @nodiscard
{
return realloc_try(allocator, ptr, new_size)!!;
}
macro void*! realloc_try(Allocator* allocator, void* ptr, usz new_size) @nodiscard
{
if (!new_size)
{
free(allocator, ptr);
return null;
}
if (!ptr) return allocator.acquire(new_size, false, 0, 0);
return allocator.resize(ptr, new_size, 0, 0);
}
macro void free(Allocator* allocator, void* ptr)
{
if (!ptr) return;
$if env::TESTING:
((char*)ptr)[0] = 0xBA;
$endif
allocator.release(ptr, false);
}
macro void*! malloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @nodiscard
{
if (!size) return null;
$if env::TESTING:
char* data = allocator.acquire(size, false, alignment, offset)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
return data;
$else
return allocator.acquire(size, false, alignment, offset);
$endif
}
macro void*! calloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @nodiscard
{
if (!size) return null;
return allocator.acquire(size, true, alignment, offset);
}
macro void*! realloc_aligned(Allocator* allocator, void* ptr, usz new_size, usz alignment, usz offset = 0) @nodiscard
{
if (!new_size)
{
free_aligned(allocator, ptr);
return null;
}
if (!ptr)
{
return malloc_aligned(allocator, new_size, alignment);
}
return allocator.resize(ptr, new_size, alignment, offset);
}
macro void free_aligned(Allocator* allocator, void* ptr)
{
if (!ptr) return;
$if env::TESTING:
((char*)ptr)[0] = 0xBA;
$endif
allocator.release(ptr, true);
}
/**
* @require $vacount < 2 : "Too many arguments."
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
**/
macro new(Allocator* allocator, $Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)calloc(allocator, $Type.sizeof);
$else
$Type* val = malloc(allocator, $Type.sizeof);
*val = $vaexpr(0);
return val;
$endif
}
/**
* @require $vacount < 2 : "Too many arguments."
* @require $or($vacount == 0, $assignable($vaexpr(0), $Type)) : "The second argument must be an initializer for the type"
**/
macro new_try(Allocator* allocator, $Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)calloc_try(allocator, $Type.sizeof);
$else
$Type* val = malloc_try(allocator, $Type.sizeof)!;
*val = $vaexpr(0);
return val;
$endif
}
macro new_with_padding(Allocator* allocator, $Type, usz padding) @nodiscard
{
return ($Type*)calloc_try(allocator, $Type.sizeof + padding);
}
macro alloc(Allocator* allocator, $Type) @nodiscard
{
return ($Type*)malloc(allocator, $Type.sizeof);
}
macro alloc_try(Allocator* allocator, $Type) @nodiscard
{
return ($Type*)malloc_try(allocator, $Type.sizeof);
}
macro alloc_with_padding(Allocator* allocator, $Type, usz padding) @nodiscard
{
return ($Type*)malloc_try(allocator, $Type.sizeof + padding);
}
macro new_array(Allocator* allocator, $Type, usz elements) @nodiscard
{
return new_array_try(allocator, $Type, elements)!!;
}
macro new_array_try(Allocator* allocator, $Type, usz elements) @nodiscard
{
return (($Type*)calloc_try(allocator, $Type.sizeof * elements))[:elements];
}
macro new_array_aligned(Allocator* allocator, $Type, usz elements) @nodiscard
{
return ((Type*)calloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
}
macro alloc_array(Allocator* allocator, $Type, usz elements) @nodiscard
{
return alloc_array_try(allocator, $Type, elements)!!;
}
macro alloc_array_aligned(Allocator* allocator, $Type, usz elements) @nodiscard
{
return ((Type*)malloc_aligned(allocator, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
}
macro alloc_array_try(Allocator* allocator, $Type, usz elements) @nodiscard
{
return (($Type*)malloc_try(allocator, $Type.sizeof * elements))[:elements];
}
macro clone(Allocator* allocator, value) @nodiscard
{
return new(allocator, $typeof(value), value);
}
fn any* clone_any(Allocator* allocator, any* value) @nodiscard
{
usz size = value.type.sizeof;
void* data = malloc(allocator, size);
mem::copy(data, value.ptr, size);
return any_make(data, value.type);
}
// Allocator "functions"
macro void*! Allocator.alloc_checked(&self, usz size) @deprecated("Use allocator::malloc_try")
macro void*! Allocator.alloc_checked(&self, usz size)
{
return malloc_try(self, size);
$if env::TESTING:
char* data = self.acquire(size, false, 0, 0)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
return data;
$else
return self.acquire(size, false, 0, 0);
$endif
}
macro void*! Allocator.calloc_checked(&self, usz size) @deprecated("Use allocator::calloc_try")
macro void*! Allocator.calloc_checked(&self, usz size)
{
return calloc_try(self, size);
return self.acquire(size, true, 0, 0);
}
macro void*! Allocator.realloc_checked(&self, void* ptr, usz new_size)
{
return self.resize(ptr, new_size, 0, 0);
}
macro void*! Allocator.realloc_checked(&self, void* ptr, usz new_size) @deprecated("Use allocator::realloc_try")
{
return realloc_try(ptr, new_size);
}
macro Allocator.new_array(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::alloc_array")
macro Allocator.new_array(&self, $Type, usz size, usz end_padding = 0)
{
return (($Type*)self.alloc_checked($Type.sizeof * size + end_padding))[:size]!!;
}
macro Allocator.new_array_checked(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::alloc_array_try")
macro Allocator.new_array_checked(&self, $Type, usz size, usz end_padding = 0)
{
return (($Type*)self.alloc_checked($Type.sizeof * size + end_padding))[:size];
}
macro Allocator.new_zero_array(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::new_array")
macro Allocator.new_zero_array(&self, $Type, usz size, usz end_padding = 0)
{
return (($Type*)self.calloc_checked($Type.sizeof * size + end_padding))[:size]!!;
}
macro Allocator.new_zero_array_checked(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::new_array_try")
macro Allocator.new_zero_array_checked(&self, $Type, usz size, usz end_padding = 0)
{
return (($Type*)self.calloc_checked($Type.sizeof * size + end_padding))[:size];
}
macro Allocator.new(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::alloc")
macro Allocator.new(&self, $Type, usz end_padding = 0) @nodiscard
{
return ($Type*)self.alloc_checked($Type.sizeof + end_padding)!!;
}
macro Allocator.new_checked(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::alloc_try")
macro Allocator.new_checked(&self, $Type, usz end_padding = 0) @nodiscard
{
return ($Type*)self.alloc_checked($Type.sizeof + end_padding);
}
macro Allocator.new_clear(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::new")
macro Allocator.new_clear(&self, $Type, usz end_padding = 0) @nodiscard
{
return ($Type*)self.calloc_checked($Type.sizeof + end_padding)!!;
}
macro Allocator.new_clear_checked(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::new_try")
macro Allocator.new_clear_checked(&self, $Type, usz end_padding = 0) @nodiscard
{
return ($Type*)self.calloc_checked($Type.sizeof + end_padding);
}
macro Allocator.clone(&self, value) @deprecated("Use allocator::clone")
macro Allocator.clone(&self, value)
{
var x = self.alloc($typeof(value));
*x = value;
return x;
}
fn void* Allocator.alloc(&self, usz size) @nodiscard @deprecated("Use allocator::malloc")
macro void* Allocator.alloc(&self, usz size) @nodiscard
{
return malloc(self, size);
return self.alloc_checked(size)!!;
}
fn void* Allocator.calloc(&self, usz size) @nodiscard @deprecated("Use allocator::calloc")
macro void* Allocator.calloc(&self, usz size) @nodiscard
{
return calloc(self, size);
return self.acquire(size, true, 0, 0)!!;
}
fn void* Allocator.realloc(&self, void* ptr, usz new_size) @nodiscard @deprecated("Use allocator::realloc")
macro void* Allocator.realloc(&self, void* ptr, usz new_size) @nodiscard
{
return realloc(self, ptr, new_size);
return self.resize(ptr, new_size, 0, 0)!!;
}
fn void*! Allocator.alloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::malloc_aligned")
macro void*! Allocator.alloc_aligned(&self, usz size, usz alignment, usz offset = 0)
{
return malloc_aligned(self, size, alignment, 0);
}
fn void*! Allocator.calloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::calloc_aligned")
{
return calloc_aligned(self, size, alignment, 0);
}
fn void*! Allocator.realloc_aligned(&self, void* ptr, usz new_size, usz alignment = 0, usz offset = 0) @deprecated("Use allocator::realloc_aligned")
{
return realloc_aligned(self, ptr, new_size, alignment, 0);
}
fn void Allocator.free(&self, void* ptr) @deprecated("Use allocator::free")
{
free(self, ptr);
}
fn void Allocator.free_aligned(&self, void* ptr) @deprecated("Use allocator::free_aligned")
{
free_aligned(self, ptr);
}
/**
* @require bytes > 0
* @require alignment > 0
* @require bytes <= isz.max
**/
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment)
{
if (alignment < void*.alignof) alignment = void*.alignof;
usz header = AlignedBlock.sizeof + alignment;
usz alignsize = bytes + header;
$if @typekind(#alloc_fn(bytes)) == OPTIONAL:
void* data = #alloc_fn(alignsize)!;
$if env::TESTING:
char* data = self.acquire(size, false, alignment, offset)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
return data;
$else
void* data = #alloc_fn(alignsize);
$endif
void* mem = mem::aligned_pointer(data + AlignedBlock.sizeof, alignment);
AlignedBlock* desc = (AlignedBlock*)mem - 1;
assert(mem > data);
*desc = { bytes, data };
return mem;
}
struct AlignedBlock
{
usz len;
void* start;
}
macro void! @aligned_free(#free_fn, void* old_pointer)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
$if @typekind(#free_fn(desc.start)) == OPTIONAL:
#free_fn(desc.start)!;
$else
#free_fn(desc.start);
return self.acquire(size, false, alignment, offset);
$endif
}
/**
* @require bytes > 0
* @require alignment > 0
**/
macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment)
macro void*! Allocator.calloc_aligned(&self, usz size, usz alignment, usz offset = 0)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
void* data_start = desc.start;
void* new_data = @aligned_alloc(#calloc_fn, bytes, alignment)!;
mem::copy(new_data, old_pointer, desc.len < bytes ? desc.len : bytes, 1, 1);
$if @typekind(#free_fn(data_start)) == OPTIONAL:
#free_fn(data_start)!;
$else
#free_fn(data_start);
return self.acquire(size, true, alignment, offset);
}
macro void*! Allocator.realloc_aligned(&self, void* ptr, usz new_size, usz alignment = 0, usz offset = 0)
{
return self.resize(ptr, new_size, alignment, offset);
}
macro void Allocator.free(&self, void* ptr)
{
$if env::TESTING:
if (ptr) ((char*)ptr)[0] = 0xBA;
$endif
return new_data;
self.release(ptr, false);
}
macro void Allocator.free_aligned(&self, void* ptr)
{
$if env::TESTING:
if (ptr) ((char*)ptr)[0] = 0xBA;
$endif
self.release(ptr, true);
}
// All allocators
tlocal Allocator* thread_allocator @private = &allocator::LIBC_ALLOCATOR;
tlocal TempAllocator* thread_temp_allocator @private = null;
tlocal TempAllocator*[2] temp_allocator_pair @private;
Allocator* temp_base_allocator @private = &allocator::LIBC_ALLOCATOR;
macro TempAllocator* create_default_sized_temp_allocator(Allocator* allocator) @local
{
$switch (env::MEMORY_ENV)
$case NORMAL:
return new_temp_allocator(1024 * 256, allocator)!!;
$case SMALL:
return new_temp_allocator(1024 * 16, allocator)!!;
$case TINY:
return new_temp_allocator(1024 * 2, allocator)!!;
$case NONE:
unreachable("Temp allocator must explicitly created when memory-env is set to 'none'.");
$endswitch
}
macro Allocator* heap() => thread_allocator;
macro TempAllocator* temp()
{
if (!thread_temp_allocator)
{
init_default_temp_allocators();
}
return thread_temp_allocator;
}
fn void init_default_temp_allocators() @private
{
temp_allocator_pair[0] = create_default_sized_temp_allocator(temp_base_allocator);
temp_allocator_pair[1] = create_default_sized_temp_allocator(temp_base_allocator);
thread_temp_allocator = temp_allocator_pair[0];
}
fn TempAllocator *temp_allocator_next() @private
{
if (!thread_temp_allocator)
{
init_default_temp_allocators();
return thread_temp_allocator;
}
usz index = thread_temp_allocator == temp_allocator_pair[0] ? 1 : 0;
return thread_temp_allocator = temp_allocator_pair[index];
}

View File

@@ -1,258 +0,0 @@
module std::core::cpudetect @if(env::X86 || env::X86_64);
struct CpuId
{
uint eax, ebx, ecx, edx;
}
fn CpuId x86_cpuid(uint eax, uint ecx = 0)
{
int edx;
int ebx;
asm
{
movl $eax, eax;
movl $ecx, ecx;
cpuid;
movl eax, $eax;
movl ebx, $ebx;
movl ecx, $ecx;
movl edx, $edx;
}
return { eax, ebx, ecx, edx };
}
enum X86Feature
{
ADX,
AES,
AMX_BF16,
AMX_COMPLEX,
AMX_FP16,
AMX_INT8,
AMX_TILE,
AVX,
AVX10_1_256,
AVX10_1_512,
AVX2,
AVX5124FMAPS,
AVX5124VNNIW,
AVX512BF16,
AVX512BITALG,
AVX512BW,
AVX512CD,
AVX512DQ,
AVX512ER,
AVX512F,
AVX512FP16,
AVX512IFMA,
AVX512PF,
AVX512VBMI,
AVX512VBMI2,
AVX512VL,
AVX512VNNI,
AVX512VP2INTERSECT,
AVX512VPOPCNTDQ,
AVXIFMA,
AVXNECONVERT,
AVXVNNI,
AVXVNNIINT16,
AVXVNNIINT8,
BMI,
BMI2,
CLDEMOTE,
CLFLUSHOPT,
CLWB,
CLZERO,
CMOV,
CMPCCXADD,
CMPXCHG16B,
CX8,
ENQCMD,
F16C,
FMA,
FMA4,
FSGSBASE,
FXSR,
GFNI,
HRESET,
INVPCID,
KL,
LWP,
LZCNT,
MMX,
MOVBE,
MOVDIR64B,
MOVDIRI,
MWAITX,
PCLMUL,
PCONFIG,
PKU,
POPCNT,
PREFETCHI,
PREFETCHWT1,
PRFCHW,
PTWRITE,
RAOINT,
RDPID,
RDPRU,
RDRND,
RDSEED,
RTM,
SAHF,
SERIALIZE,
SGX,
SHA,
SHA512,
SHSTK,
SM3,
SM4,
SSE,
SSE2,
SSE3,
SSE4_1,
SSE4_2,
SSE4_A,
SSSE3,
TBM,
TSXLDTRK,
UINTR,
USERMSR,
VAES,
VPCLMULQDQ,
WAITPKG,
WBNOINVD,
WIDEKL,
X87,
XOP,
XSAVE,
XSAVEC,
XSAVEOPT,
XSAVES,
}
uint128 x86_features;
fn void add_feature_if_bit(X86Feature feature, uint register, int bit)
{
if (register & 1U << bit) x86_features |= 1u128 << feature.ordinal;
}
fn void x86_initialize_cpu_features()
{
uint max_level = x86_cpuid(0).eax;
CpuId feat = x86_cpuid(1);
CpuId leaf7 = max_level >= 8 ? x86_cpuid(7) : CpuId {};
CpuId leaf7s1 = leaf7.eax >= 1 ? x86_cpuid(7, 1) : CpuId {};
CpuId ext1 = x86_cpuid(0x80000000).eax >= 0x80000001 ? x86_cpuid(0x80000001) : CpuId {};
CpuId ext8 = x86_cpuid(0x80000000).eax >= 0x80000008 ? x86_cpuid(0x80000008) : CpuId {};
CpuId leaf_d = max_level >= 0xd ? x86_cpuid(0xd, 0x1) : CpuId {};
CpuId leaf_14 = max_level >= 0x14 ? x86_cpuid(0x14) : CpuId {};
CpuId leaf_19 = max_level >= 0x19 ? x86_cpuid(0x19) : CpuId {};
CpuId leaf_24 = max_level >= 0x24 ? x86_cpuid(0x24) : CpuId {};
add_feature_if_bit(ADX, leaf7.ebx, 19);
add_feature_if_bit(AES, feat.ecx, 25);
add_feature_if_bit(AMX_BF16, leaf7.edx, 22);
add_feature_if_bit(AMX_COMPLEX, leaf7s1.edx, 8);
add_feature_if_bit(AMX_FP16, leaf7s1.eax, 21);
add_feature_if_bit(AMX_INT8, leaf7.edx, 25);
add_feature_if_bit(AMX_TILE, leaf7.edx, 24);
add_feature_if_bit(AVX, feat.ecx, 28);
add_feature_if_bit(AVX10_1_256, leaf7s1.edx, 19);
add_feature_if_bit(AVX10_1_512, leaf_24.ebx, 18);
add_feature_if_bit(AVX2, leaf7.ebx, 5);
add_feature_if_bit(AVX5124FMAPS, leaf7.edx, 3);
add_feature_if_bit(AVX5124VNNIW, leaf7.edx, 2);
add_feature_if_bit(AVX512BF16, leaf7s1.eax, 5);
add_feature_if_bit(AVX512BITALG, leaf7.ecx, 12);
add_feature_if_bit(AVX512BW, leaf7.ebx, 30);
add_feature_if_bit(AVX512CD, leaf7.ebx, 28);
add_feature_if_bit(AVX512DQ, leaf7.ebx, 17);
add_feature_if_bit(AVX512ER, leaf7.ebx, 27);
add_feature_if_bit(AVX512F, leaf7.ebx, 16);
add_feature_if_bit(AVX512FP16, leaf7.edx, 23);
add_feature_if_bit(AVX512IFMA, leaf7.ebx, 21);
add_feature_if_bit(AVX512PF, leaf7.ebx, 26);
add_feature_if_bit(AVX512VBMI, leaf7.ecx, 1);
add_feature_if_bit(AVX512VBMI2, leaf7.ecx, 6);
add_feature_if_bit(AVX512VL, leaf7.ebx, 31);
add_feature_if_bit(AVX512VNNI, leaf7.ecx, 11);
add_feature_if_bit(AVX512VP2INTERSECT, leaf7.edx, 8);
add_feature_if_bit(AVX512VPOPCNTDQ, leaf7.ecx, 14);
add_feature_if_bit(AVXIFMA, leaf7s1.eax, 23);
add_feature_if_bit(AVXNECONVERT, leaf7s1.edx, 5);
add_feature_if_bit(AVXVNNI, leaf7s1.eax, 4);
add_feature_if_bit(AVXVNNIINT16, leaf7s1.edx, 10);
add_feature_if_bit(AVXVNNIINT8, leaf7s1.edx, 4);
add_feature_if_bit(BMI, leaf7.ebx, 3);
add_feature_if_bit(BMI2, leaf7.ebx, 8);
add_feature_if_bit(CLDEMOTE, leaf7.ecx, 25);
add_feature_if_bit(CLFLUSHOPT, leaf7.ebx, 23);
add_feature_if_bit(CLWB, leaf7.ebx, 24);
add_feature_if_bit(CLZERO, ext8.ecx, 0);
add_feature_if_bit(CMOV, feat.edx, 15);
add_feature_if_bit(CMPCCXADD, leaf7s1.eax, 7);
add_feature_if_bit(CMPXCHG16B, feat.ecx, 12);
add_feature_if_bit(CX8, feat.edx, 8);
add_feature_if_bit(ENQCMD, leaf7.ecx, 29);
add_feature_if_bit(F16C, feat.ecx, 29);
add_feature_if_bit(FMA, feat.ecx, 12);
add_feature_if_bit(FMA4, ext1.ecx, 16);
add_feature_if_bit(FSGSBASE, leaf7.ebx, 0);
add_feature_if_bit(FXSR, feat.edx, 24);
add_feature_if_bit(GFNI, leaf7.ecx, 8);
add_feature_if_bit(HRESET, leaf7s1.eax, 22);
add_feature_if_bit(INVPCID, leaf7.ebx, 10);
add_feature_if_bit(KL, leaf7.ecx, 23);
add_feature_if_bit(LWP, ext1.ecx, 15);
add_feature_if_bit(LZCNT, ext1.ecx, 5);
add_feature_if_bit(MMX, feat.edx, 23);
add_feature_if_bit(MOVBE, feat.ecx, 22);
add_feature_if_bit(MOVDIR64B, leaf7.ecx, 28);
add_feature_if_bit(MOVDIRI, leaf7.ecx, 27);
add_feature_if_bit(MWAITX, ext1.ecx, 29);
add_feature_if_bit(PCLMUL, feat.ecx, 1);
add_feature_if_bit(PCONFIG, leaf7.edx, 18);
add_feature_if_bit(PKU, leaf7.ecx, 4);
add_feature_if_bit(POPCNT, feat.ecx, 23);
add_feature_if_bit(PREFETCHI, leaf7s1.edx, 14);
add_feature_if_bit(PREFETCHWT1, leaf7.ecx, 0);
add_feature_if_bit(PRFCHW, ext1.ecx, 8);
add_feature_if_bit(PTWRITE, leaf_14.ebx, 4);
add_feature_if_bit(RAOINT, leaf7s1.eax, 3);
add_feature_if_bit(RDPID, leaf7.ecx, 22);
add_feature_if_bit(RDPRU, ext8.ecx, 4);
add_feature_if_bit(RDRND, feat.ecx, 30);
add_feature_if_bit(RDSEED, leaf7.ebx, 18);
add_feature_if_bit(RTM, leaf7.ebx, 11);
add_feature_if_bit(SAHF, ext1.ecx, 0);
add_feature_if_bit(SERIALIZE, leaf7.edx, 14);
add_feature_if_bit(SGX, leaf7.ebx, 2);
add_feature_if_bit(SHA, leaf7.ebx, 29);
add_feature_if_bit(SHA512, leaf7s1.eax, 0);
add_feature_if_bit(SHSTK, leaf7.ecx, 7);
add_feature_if_bit(SM3, leaf7s1.eax, 1);
add_feature_if_bit(SM4, leaf7s1.eax, 2);
add_feature_if_bit(SSE, feat.edx, 25);
add_feature_if_bit(SSE2, feat.edx, 26);
add_feature_if_bit(SSE3, feat.ecx, 0);
add_feature_if_bit(SSE4_1, feat.ecx, 19);
add_feature_if_bit(SSE4_2, feat.ecx, 20);
add_feature_if_bit(SSE4_A, ext1.ecx, 6);
add_feature_if_bit(SSSE3, feat.ecx, 9);
add_feature_if_bit(TBM, ext1.ecx, 21);
add_feature_if_bit(TSXLDTRK, leaf7.edx, 16);
add_feature_if_bit(UINTR, leaf7.edx, 5);
add_feature_if_bit(USERMSR, leaf7s1.edx, 15);
add_feature_if_bit(VAES, leaf7.ecx, 9);
add_feature_if_bit(VPCLMULQDQ, leaf7.ecx, 10);
add_feature_if_bit(WAITPKG, leaf7.ecx, 5);
add_feature_if_bit(WBNOINVD, ext8.ecx, 9);
add_feature_if_bit(WIDEKL, leaf_19.ebx, 2);
add_feature_if_bit(X87, feat.edx, 0);
add_feature_if_bit(XOP, ext1.ecx, 11);
add_feature_if_bit(XSAVE, feat.ecx, 26);
add_feature_if_bit(XSAVEC, leaf_d.eax, 1);
add_feature_if_bit(XSAVEOPT, leaf_d.eax, 0);
add_feature_if_bit(XSAVES, leaf_d.eax, 3);
}

View File

@@ -1,254 +0,0 @@
module std::core::machoruntime @if(env::DARWIN) @private;
struct SegmentCommand64
{
uint cmd;
uint cmdsize;
char[16] segname;
ulong vmaddr;
ulong vmsize;
ulong fileoff;
ulong filesize;
uint maxprot;
uint initprot;
uint nsects;
uint flags;
}
struct LoadCommand
{
uint cmd;
uint cmdsize;
}
struct Section64
{
char[16] sectname;
char[16] segname;
ulong addr;
ulong size;
uint offset;
uint align;
uint reloff;
uint nreloc;
uint flags;
uint reserved1;
uint reserved2;
uint reserved3;
}
struct MachHeader
{
uint magic;
uint cputype;
uint cpusubtype;
uint filetype;
uint ncmds;
uint sizeofcmds;
uint flags;
}
struct MachHeader64
{
inline MachHeader header;
uint reserved;
}
const LC_SEGMENT_64 = 0x19;
fault MachoSearch
{
NOT_FOUND
}
fn bool name_cmp(char* a, char[16]* b)
{
for (usz i = 0; i < 16; i++)
{
if (a[i] != (*b)[i]) return false;
if (a[i] == '\0') return true;
}
return false;
}
fn SegmentCommand64*! find_segment(MachHeader* header, char* segname)
{
LoadCommand* command = (void*)header + MachHeader64.sizeof;
for (uint i = 0; i < header.ncmds; i++)
{
if (command.cmd == LC_SEGMENT_64)
{
SegmentCommand64* segment = (SegmentCommand64*)command;
if (name_cmp(segname, &segment.segname)) return segment;
}
command = (void*)command + command.cmdsize;
}
return MachoSearch.NOT_FOUND?;
}
fn Section64*! find_section(SegmentCommand64* command, char* sectname)
{
Section64* section = (void*)command + SegmentCommand64.sizeof;
for (uint i = 0; i < command.nsects; i++)
{
if (name_cmp(sectname, &section.sectname)) return section;
section++;
}
return MachoSearch.NOT_FOUND?;
}
macro find_segment_section_body(MachHeader* header, char* segname, char* sectname, $Type)
{
Section64*! section = find_section(find_segment(header, segname), sectname);
if (catch section)
{
return $Type[] {};
}
$Type* ptr = (void*)header + section.offset;
return ptr[:section.size / $Type.sizeof];
}
def DyldCallback = fn void (MachHeader* mh, isz vmaddr_slide);
extern fn void _dyld_register_func_for_add_image(DyldCallback);
struct DlInfo
{
char* dli_fname;
void* dli_fbase;
char* dli_sname;
void* dli_saddr;
}
extern fn void printf(char*, ...);
extern fn int dladdr(MachHeader* mh, DlInfo* dlinfo);
extern fn void* realloc(void* ptr, usz size);
extern fn void* malloc(usz size);
extern fn void free(void* ptr);
def CallbackFn = fn void();
struct Callback
{
uint priority;
CallbackFn xtor;
Callback* next;
}
struct DynamicMethod
{
void* fn_ptr;
char* sel;
union
{
DynamicMethod* next;
TypeId* type;
}
}
enum StartupState
{
NOT_STARTED,
INIT,
RUN_CTORS,
READ_DYLIB,
RUN_DYLIB_CTORS,
RUN_DTORS,
SHUTDOWN
}
StartupState runtime_state = NOT_STARTED;
Callback* ctor_first;
Callback* dtor_first;
fn void runtime_startup() @public @export("__c3_runtime_startup")
{
if (runtime_state != NOT_STARTED) return;
runtime_state = INIT;
_dyld_register_func_for_add_image(&dl_reg_callback);
assert(runtime_state == INIT);
runtime_state = RUN_CTORS;
Callback* ctor = ctor_first;
while (ctor)
{
ctor.xtor();
ctor = ctor.next;
}
assert(runtime_state == RUN_CTORS);
runtime_state = READ_DYLIB;
ctor = null;
}
fn void runtime_finalize() @public @export("__c3_runtime_finalize")
{
if (runtime_state != READ_DYLIB) return;
runtime_state = RUN_DTORS;
Callback* dtor = dtor_first;
while (dtor)
{
dtor.xtor();
dtor = dtor.next;
}
assert(runtime_state == RUN_DTORS);
runtime_state = SHUTDOWN;
}
fn void append_xxlizer(Callback** ref, Callback* cb)
{
while (Callback* current = *ref, current)
{
if (current.priority > cb.priority)
{
cb.next = current;
break;
}
ref = &current.next;
}
*ref = cb;
}
struct TypeId
{
char type;
TypeId* parentof;
DynamicMethod* dtable;
usz sizeof;
TypeId* inner;
usz len;
typeid[*] additional;
}
fn void dl_reg_callback(MachHeader* mh, isz vmaddr_slide)
{
usz size = 0;
assert(runtime_state == INIT || runtime_state == READ_DYLIB, "State was %s", runtime_state);
foreach (&dm : find_segment_section_body(mh, "__DATA", "__c3_dynamic", DynamicMethod))
{
TypeId* type = dm.type;
dm.next = type.dtable;
type.dtable = dm;
DynamicMethod* m = dm;
while (m)
{
m = m.next;
}
}
foreach (&cb : find_segment_section_body(mh, "__DATA", "__c3dtor", Callback))
{
append_xxlizer(&dtor_first, cb);
}
foreach (&cb : find_segment_section_body(mh, "__DATA", "__c3ctor", Callback))
{
append_xxlizer(&ctor_first, cb);
}
if (runtime_state != READ_DYLIB) return;
runtime_state = RUN_DYLIB_CTORS;
Callback* ctor = ctor_first;
ctor_first = null;
while (ctor)
{
ctor.xtor();
ctor = ctor.next;
}
assert(runtime_state == RUN_DYLIB_CTORS);
runtime_state = READ_DYLIB;
}

View File

@@ -21,7 +21,7 @@ macro int @main_to_void_main(#m, int, char**)
macro String[] args_to_strings(int argc, char** argv) @private
{
String[] list = mem::alloc_array(String, argc);
String[] list = mem::new_array(String, argc);
for (int i = 0; i < argc; i++)
{
char* arg = argv[i];
@@ -68,7 +68,7 @@ macro String[] win_command_line_to_strings(ushort* cmd_line) @private
macro String[] wargs_strings(int argc, Char16** argv) @private
{
String[] list = mem::alloc_array(String, argc);
String[] list = mem::new_array(String, argc);
for (int i = 0; i < argc; i++)
{
Char16* arg = argv[i];

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::runtime;
import libc, std::time, std::io, std::sort;
import libc;
struct AnyStruct
{
@@ -24,11 +24,11 @@ struct BenchmarkUnit
BenchmarkFn func;
}
fn BenchmarkUnit[] benchmark_collection_create(Allocator* allocator = allocator::heap())
fn BenchmarkUnit[] benchmark_collection_create(Allocator* allocator = mem::heap())
{
BenchmarkFn[] fns = $$BENCHMARK_FNS;
String[] names = $$BENCHMARK_NAMES;
BenchmarkUnit[] benchmarks = allocator::alloc_array(allocator, BenchmarkUnit, names.len);
BenchmarkUnit[] benchmarks = allocator.new_array(BenchmarkUnit, names.len);
foreach (i, benchmark : fns)
{
benchmarks[i] = { names[i], fns[i] };
@@ -130,7 +130,7 @@ fn bool default_benchmark_runner()
{
@pool()
{
return run_benchmarks(benchmark_collection_create(allocator::temp()));
return run_benchmarks(benchmark_collection_create(mem::temp()));
};
}
@@ -142,11 +142,11 @@ struct TestUnit
TestFn func;
}
fn TestUnit[] test_collection_create(Allocator* allocator = allocator::heap())
fn TestUnit[] test_collection_create(Allocator* allocator = mem::heap())
{
TestFn[] fns = $$TEST_FNS;
String[] names = $$TEST_NAMES;
TestUnit[] tests = allocator::alloc_array(allocator, TestUnit, names.len);
TestUnit[] tests = allocator.new_array(TestUnit, names.len);
foreach (i, test : fns)
{
tests[i] = { names[i], fns[i] };
@@ -236,7 +236,7 @@ fn bool default_test_runner()
{
@pool()
{
return run_tests(test_collection_create(allocator::temp()));
return run_tests(test_collection_create(mem::temp()));
};
}

View File

@@ -38,7 +38,7 @@ macro String tformat(String fmt, ...)
return str.str_view();
}
macro String new_format(String fmt, ..., Allocator* allocator = allocator::heap())
macro String new_format(String fmt, ..., Allocator* allocator = mem::heap())
{
@pool(allocator)
{
@@ -55,11 +55,11 @@ macro bool char_in_set(char c, String set)
return false;
}
fn String join_new(String[] s, String joiner, Allocator* allocator = allocator::heap())
fn String join_new(String[] s, String joiner, Allocator* allocator = mem::heap())
{
if (!s)
{
return (String)allocator::new_array(allocator, char, 2)[:0];
return (String)allocator.new_zero_array(char, 2)[:0];
}
usz total_size = joiner.len * s.len;
@@ -153,11 +153,11 @@ fn String String.strip_end(string, String needle)
* @require needle.len > 0 "The needle must be at least 1 character long"
* @ensure return.len > 0
**/
fn String[] String.split(s, String needle, usz max = 0, Allocator* allocator = allocator::heap())
fn String[] String.split(s, String needle, usz max = 0, Allocator* allocator = mem::heap())
{
usz capacity = 16;
usz i = 0;
String* holder = allocator::alloc_array(allocator, String, capacity);
String* holder = allocator.new_array(String, capacity);
bool no_more = false;
while (!no_more)
{
@@ -176,7 +176,7 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator* allocator = a
if (i == capacity)
{
capacity *= 2;
holder = allocator::realloc(allocator, holder, String.sizeof * capacity);
holder = allocator.realloc(holder, String.sizeof * capacity);
}
holder[i++] = res;
}
@@ -193,7 +193,7 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator* allocator = a
**/
fn String[] String.tsplit(s, String needle, usz max = 0)
{
return s.split(needle, max, allocator::temp()) @inline;
return s.split(needle, max, mem::temp()) @inline;
}
fn bool String.contains(s, String needle)
@@ -312,19 +312,19 @@ fn usz ZString.len(str)
}
fn ZString String.zstr_copy(s, Allocator* allocator = allocator::heap())
fn ZString String.zstr_copy(s, Allocator* allocator = mem::heap())
{
usz len = s.len;
char* str = allocator::malloc(allocator, len + 1);
char* str = allocator.alloc(len + 1);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (ZString)str;
}
fn String String.concat(s1, String s2, Allocator* allocator = allocator::heap())
fn String String.concat(s1, String s2, Allocator* allocator = mem::heap())
{
usz full_len = s1.len + s2.len;
char* str = allocator::malloc(allocator, full_len + 1);
char* str = allocator.alloc(full_len + 1);
usz s1_len = s1.len;
mem::copy(str, s1.ptr, s1_len);
mem::copy(str + s1_len, s2.ptr, s2.len);
@@ -332,37 +332,37 @@ fn String String.concat(s1, String s2, Allocator* allocator = allocator::heap())
return (String)str[:full_len];
}
fn String String.tconcat(s1, String s2) => s1.concat(s2, allocator::temp());
fn String String.tconcat(s1, String s2) => s1.concat(s2, mem::temp());
fn ZString String.zstr_tcopy(s) => s.zstr_copy(allocator::temp()) @inline;
fn ZString String.zstr_tcopy(s) => s.zstr_copy(mem::temp()) @inline;
fn String String.copy(s, Allocator* allocator = allocator::heap())
fn String String.copy(s, Allocator* allocator = mem::heap())
{
usz len = s.len;
char* str = allocator::malloc(allocator, len + 1);
char* str = allocator.alloc(len + 1);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (String)str[:len];
}
fn void String.free(&s, Allocator* allocator = allocator::heap())
fn void String.free(&s, Allocator* allocator = mem::heap())
{
if (!s.len) return;
allocator::free(allocator, s.ptr);
allocator.free(s.ptr);
*s = "";
}
fn String String.tcopy(s) => s.copy(allocator::temp()) @inline;
fn String String.tcopy(s) => s.copy(mem::temp()) @inline;
fn String ZString.copy(z, Allocator* allocator = allocator::temp())
fn String ZString.copy(z, Allocator* allocator = mem::temp())
{
return z.str_view().copy(allocator) @inline;
}
fn String ZString.tcopy(z)
{
return z.str_view().copy(allocator::temp()) @inline;
return z.str_view().copy(mem::temp()) @inline;
}
/**
@@ -371,10 +371,10 @@ fn String ZString.tcopy(z)
* @return! UnicodeResult.INVALID_UTF8 "If the string contained an invalid UTF-8 sequence"
* @return! AllocationFailure "If allocation of the string fails"
**/
fn Char16[]! String.to_new_utf16(s, Allocator* allocator = allocator::heap())
fn Char16[]! String.to_new_utf16(s, Allocator* allocator = mem::heap())
{
usz len16 = conv::utf16len_for_utf8(s);
Char16* data = allocator::alloc_array_try(allocator, Char16, len16 + 1)!;
Char16* data = allocator.new_array_checked(Char16, len16 + 1)!;
conv::utf8to16_unsafe(s, data)!;
data[len16] = 0;
return data[:len16];
@@ -388,10 +388,10 @@ fn Char16[]! String.to_new_utf16(s, Allocator* allocator = allocator::heap())
**/
fn Char16[]! String.to_temp_utf16(s)
{
return s.to_new_utf16(allocator::temp());
return s.to_new_utf16(mem::temp());
}
fn WString! String.to_new_wstring(s, Allocator* allocator = allocator::heap())
fn WString! String.to_new_wstring(s, Allocator* allocator = mem::heap())
{
return (WString)s.to_new_utf16(allocator).ptr;
}
@@ -401,10 +401,10 @@ fn WString! String.to_temp_wstring(s)
return (WString)s.to_temp_utf16().ptr;
}
fn Char32[]! String.to_new_utf32(s, Allocator* allocator = allocator::heap())
fn Char32[]! String.to_new_utf32(s, Allocator* allocator = mem::heap())
{
usz codepoints = conv::utf8_codepoints(s);
Char32* data = allocator::alloc_array_try(allocator, Char32, codepoints + 1)!;
Char32* data = allocator.new_array(Char32, codepoints + 1);
conv::utf8to32_unsafe(s, data)!;
data[codepoints] = 0;
return data[:codepoints];
@@ -412,7 +412,7 @@ fn Char32[]! String.to_new_utf32(s, Allocator* allocator = allocator::heap())
fn Char32[]! String.to_temp_utf32(s)
{
return s.to_new_utf32(allocator::temp());
return s.to_new_utf32(mem::temp());
}
fn void String.convert_ascii_to_lower(s)
@@ -420,16 +420,16 @@ fn void String.convert_ascii_to_lower(s)
foreach (&c : s) if (c.is_upper()) *c += 'a' - 'A';
}
fn String String.new_ascii_to_lower(s, Allocator* allocator = allocator::heap())
fn String String.new_ascii_to_lower(s, Allocator* allocator = mem::heap())
{
String copy = s.copy(allocator);
copy.convert_ascii_to_lower();
return copy;
}
fn String String.temp_ascii_to_lower(s, Allocator* allocator = allocator::heap())
fn String String.temp_ascii_to_lower(s, Allocator* allocator = mem::heap())
{
return s.new_ascii_to_lower(allocator::temp());
return s.new_ascii_to_lower(mem::temp());
}
fn void String.convert_ascii_to_upper(s)
@@ -437,44 +437,39 @@ fn void String.convert_ascii_to_upper(s)
foreach (&c : s) if (c.is_lower()) *c -= 'a' - 'A';
}
fn String String.new_ascii_to_upper(s, Allocator* allocator = allocator::heap())
fn String String.new_ascii_to_upper(s, Allocator* allocator = mem::heap())
{
String copy = s.copy(allocator);
copy.convert_ascii_to_upper();
return copy;
}
fn StringIterator String.iterator(s)
{
return { s, 0 };
}
fn String String.temp_ascii_to_upper(s)
{
return s.new_ascii_to_upper(allocator::temp());
return s.new_ascii_to_upper(mem::temp());
}
fn String! new_from_utf32(Char32[] utf32, Allocator* allocator = allocator::heap())
fn String! new_from_utf32(Char32[] utf32, Allocator* allocator = mem::heap())
{
usz len = conv::utf8len_for_utf32(utf32);
char* data = allocator::malloc_try(allocator, len + 1)!;
defer catch allocator::free(allocator, data);
char* data = allocator.alloc_checked(len + 1)!;
defer catch allocator.free(data);
conv::utf32to8_unsafe(utf32, data);
data[len] = 0;
return (String)data[:len];
}
fn String! new_from_utf16(Char16[] utf16, Allocator* allocator = allocator::heap())
fn String! new_from_utf16(Char16[] utf16, Allocator* allocator = mem::heap())
{
usz len = conv::utf8len_for_utf16(utf16);
char* data = allocator::malloc_try(allocator, len + 1)!;
defer catch allocator::free(allocator, data);
char* data = allocator.alloc_checked(len + 1)!;
defer catch allocator.free(data);
conv::utf16to8_unsafe(utf16, data)!;
data[len] = 0;
return (String)data[:len];
}
fn String! new_from_wstring(WString wstring, Allocator* allocator = allocator::heap())
fn String! new_from_wstring(WString wstring, Allocator* allocator = mem::heap())
{
usz utf16_len;
while (wstring[utf16_len] != 0) utf16_len++;
@@ -482,8 +477,8 @@ fn String! new_from_wstring(WString wstring, Allocator* allocator = allocator::h
return new_from_utf16(utf16, allocator);
}
fn String! temp_from_wstring(WString wstring) => new_from_wstring(wstring, allocator::temp()) @inline;
fn String! temp_from_utf16(Char16[] utf16) => new_from_utf16(utf16, allocator::temp()) @inline;
fn String! temp_from_wstring(WString wstring) => new_from_wstring(wstring, mem::temp()) @inline;
fn String! temp_from_utf16(Char16[] utf16) => new_from_utf16(utf16, mem::temp()) @inline;
fn usz String.utf8_codepoints(s)
{
@@ -495,11 +490,7 @@ fn usz String.utf8_codepoints(s)
return len;
}
/**
* @require (base <= 10 && base > 1) || base == 16 : "Unsupported base"
**/
macro String.to_integer(string, $Type, int base = 10)
macro String.to_integer(string, $Type)
{
usz len = string.len;
usz index = 0;
@@ -519,8 +510,8 @@ macro String.to_integer(string, $Type, int base = 10)
break;
}
if (len == index) return NumberConversion.MALFORMED_INTEGER?;
$Type base_used = ($Type)base;
if (string[index] == '0' && base == 10)
$Type base = 10;
if (string[index] == '0')
{
index++;
if (index == len) return ($Type)0;
@@ -528,15 +519,15 @@ macro String.to_integer(string, $Type, int base = 10)
{
case 'x':
case 'X':
base_used = 16;
base = 16;
index++;
case 'b':
case 'B':
base_used = 2;
base = 2;
index++;
case 'o':
case 'O':
base_used = 8;
base = 8;
index++;
default:
break;
@@ -548,21 +539,21 @@ macro String.to_integer(string, $Type, int base = 10)
{
char c = {|
char ch = string[index++];
if (base_used != 16 || ch < 'A') return (char)(ch - '0');
if (ch <= 'F') return (char)(ch - 'A' + 10);
if (base != 16 || ch < 'A') return (char)(ch - '0');
if (ch <= 'F') return (char)(ch - 'A');
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER?;
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER?;
return (char)(ch - 'a' + 10);
return (char)(ch - 'a');
|}!;
if (c >= base_used) return NumberConversion.MALFORMED_INTEGER?;
if (c >= base) return NumberConversion.MALFORMED_INTEGER?;
value = {|
if (is_negative)
{
$Type new_value = value * base_used - c;
$Type new_value = value * base - c;
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW?;
return new_value;
}
$Type new_value = value * base_used + c;
$Type new_value = value * base + c;
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW?;
return new_value;
|}!;
@@ -570,52 +561,17 @@ macro String.to_integer(string, $Type, int base = 10)
return value;
}
fn int128! String.to_int128(s, int base = 10) => s.to_integer(int128, base);
fn long! String.to_long(s, int base = 10) => s.to_integer(long, base);
fn int! String.to_int(s, int base = 10) => s.to_integer(int, base);
fn short! String.to_short(s, int base = 10) => s.to_integer(short, base);
fn ichar! String.to_ichar(s, int base = 10) => s.to_integer(ichar, base);
fn int128! String.to_int128(s) => s.to_integer(int128);
fn long! String.to_long(s) => s.to_integer(long);
fn int! String.to_int(s) => s.to_integer(int);
fn short! String.to_short(s) => s.to_integer(short);
fn ichar! String.to_ichar(s) => s.to_integer(ichar);
fn uint128! String.to_uint128(s, int base = 10) => s.to_integer(uint128, base);
fn ulong! String.to_ulong(s, int base = 10) => s.to_integer(ulong, base);
fn uint! String.to_uint(s, int base = 10) => s.to_integer(uint, base);
fn ushort! String.to_ushort(s, int base = 10) => s.to_integer(ushort, base);
fn char! String.to_uchar(s, int base = 10) => s.to_integer(char, base);
fn uint128! String.to_uint128(s) => s.to_integer(uint128);
fn ulong! String.to_ulong(s) => s.to_integer(ulong);
fn uint! String.to_uint(s) => s.to_integer(uint);
fn ushort! String.to_ushort(s) => s.to_integer(ushort);
fn char! String.to_uchar(s) => s.to_integer(char);
fn double! String.to_double(s) => s.to_real(double);
fn float! String.to_float(s) => s.to_real(float);
fn Splitter String.splitter(self, String split)
{
return Splitter { self, split, 0 };
}
struct Splitter
{
String string;
String split;
usz current;
}
fn void Splitter.reset(&self)
{
self.current = 0;
}
fn String! Splitter.next(&self)
{
usz len = self.string.len;
usz current = self.current;
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
String remaining = self.string[current..];
usz! next = remaining.index_of(self.split);
if (try next)
{
defer self.current = current + next + self.split.len;
return remaining[:next];
}
self.current = len;
return remaining;
}

View File

@@ -20,4 +20,4 @@ fn Char32! StringIterator.next(&self)
Char32 res = conv::utf8_to_char32(&self.utf8[current], &read)!;
self.current += read;
return res;
}
}

View File

@@ -13,12 +13,12 @@ fn void CsvReader.init(&self, InStream* stream, String separator = ",")
self.separator = separator;
}
fn String[]! CsvReader.read_new_row(self, Allocator* allocator = allocator::heap())
fn String[]! CsvReader.read_new_row(self, Allocator* allocator = mem::heap())
{
return self.read_new_row_with_allocator(allocator::temp()) @inline;
return self.read_new_row_with_allocator(mem::temp()) @inline;
}
fn String[]! CsvReader.read_new_row_with_allocator(self, Allocator* allocator = allocator::heap())
fn String[]! CsvReader.read_new_row_with_allocator(self, Allocator* allocator = mem::heap())
{
@pool(allocator)
{
@@ -28,7 +28,7 @@ fn String[]! CsvReader.read_new_row_with_allocator(self, Allocator* allocator =
fn String[]! CsvReader.read_temp_row(self)
{
return self.read_new_row_with_allocator(allocator::temp()) @inline;
return self.read_new_row_with_allocator(mem::temp()) @inline;
}
fn void! CsvReader.skip_row(self) @maydiscard

View File

@@ -15,7 +15,7 @@ fault JsonParsingError
INVALID_NUMBER,
}
fn Object*! parse(InStream* s, Allocator* allocator = allocator::heap())
fn Object*! parse(InStream* s, Allocator* allocator = mem::heap())
{
JsonContext context = { .last_string = dstring::new_with_capacity(64, allocator), .stream = s, .allocator = allocator };
defer context.last_string.free();

View File

@@ -7,7 +7,7 @@ struct File (InStream, OutStream)
}
module std::io::file;
import libc, std::io::path, std::io::os;
import libc;
fn File! open(String filename, String mode)
{
@@ -138,50 +138,6 @@ fn char! File.read_byte(&self) @dynamic
return (char)c;
}
/**
* Load up to buffer.len characters. Returns IoError.OVERFLOW if the file is longer
* than the buffer.
*
* @param filename "The path to the file to read"
* @param [in] buffer "The buffer to read to"
**/
fn char[]! load_buffer(String filename, char[] buffer)
{
File file = open(filename, "rb")!;
defer (void)file.close();
usz len = file.seek(0, END)!;
if (len > buffer.len) return IoError.OVERFLOW?;
file.seek(0, SET)!;
usz read = 0;
while (read < len)
{
read += file.read(buffer[read:len - read])!;
}
return buffer[:len];
}
fn char[]! load_new(String filename, Allocator* allocator = allocator::heap())
{
File file = open(filename, "rb")!;
defer (void)file.close();
usz len = file.seek(0, END)!;
file.seek(0, SET)!;
char* data = allocator::malloc_try(allocator, len)!;
defer catch allocator::free(allocator, data);
usz read = 0;
while (read < len)
{
read += file.read(data[read:len - read])!;
}
return data[:len];
}
fn char[]! load_temp(String filename)
{
return load_new(filename, allocator::temp());
}
/**
* @require self.file `File must be initialized`
*/

View File

@@ -98,9 +98,9 @@ fn usz! Formatter.print_with_function(&self, Printable* arg)
self.width = old_width;
self.prec = old_prec;
}
@stack_mem(1024; Allocator* mem)
@pool()
{
return self.out_substr(arg.to_new_string(mem));
return self.out_substr(arg.to_new_string(mem::temp()));
};
}
return SearchResult.MISSING?;

View File

@@ -1,5 +1,4 @@
module std::io;
import std::math;
const char[16] XDIGITS_H = "0123456789ABCDEF";
const char[16] XDIGITS_L = "0123456789abcdef";

View File

@@ -49,7 +49,7 @@ fault IoError
* @param stream
* @require @is_instream(stream)
**/
macro String! readline(stream = io::stdin(), Allocator* allocator = allocator::heap())
macro String! readline(stream = io::stdin(), Allocator* allocator = mem::heap())
{
bool $is_stream = @typeid(stream) == InStream*.typeid;
$if $is_stream:
@@ -84,7 +84,7 @@ macro String! readline(stream = io::stdin(), Allocator* allocator = allocator::h
};
}
macro String! treadline(stream = io::stdin()) => readline(stream, allocator::temp()) @inline;
macro String! treadline(stream = io::stdin()) => readline(stream, mem::temp()) @inline;
/**
* @require @is_outstream(out) "The output must implement OutStream"

View File

@@ -1,5 +1,5 @@
module std::io::os;
import std::io::path, libc, std::os;
import libc;
macro void! native_chdir(Path path)
{

View File

@@ -1,5 +1,5 @@
module std::io::os;
import libc, std::os, std::io;
import libc;
fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX)
{

View File

@@ -1,7 +1,7 @@
module std::io::os;
import libc, std::os;
import libc;
macro String! getcwd(Allocator* allocator = allocator::heap())
macro String! getcwd(Allocator* allocator = mem::heap())
{
$switch
$case env::WIN32:

View File

@@ -1,10 +1,9 @@
module std::io::os @if(env::POSIX);
import std::io, std::os;
module std::io::file::os @if(env::POSIX);
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* allocator)
{
PathList list;
list.new_init(.allocator = allocator);
list.init_new(.allocator = allocator);
DIRPtr directory = posix::opendir(dir.str_view() ? dir.as_zstr() : (ZString)".");
defer if (directory) posix::closedir(directory);
if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?;
@@ -22,12 +21,11 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
}
module std::io::os @if(env::WIN32);
import std::time, std::os, std::io;
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* allocator)
{
PathList list;
list.new_init(.allocator = allocator);
list.init_new(.allocator = allocator);
@pool(allocator)
{

View File

@@ -1,5 +1,5 @@
module std::io::os @if(env::POSIX);
import std::io, std::os, libc;
module std::io::file::os @if(env::POSIX);
import libc;
/**
* @require dir.str_view()
@@ -33,7 +33,6 @@ fn void! native_rmtree(Path dir)
}
module std::io::os @if(env::WIN32);
import std::io, std::time, std::os;
fn void! native_rmtree(Path path)
{
@@ -47,7 +46,7 @@ fn void! native_rmtree(Path path)
{
@pool()
{
String filename = string::new_from_wstring((WString)&find_data.cFileName, allocator::temp())!;
String filename = string::new_from_wstring((WString)&find_data.cFileName, mem::temp())!;
if (filename == "." || filename == "..") continue;
Path file_path = path.tappend(filename)!;
if (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY)

View File

@@ -1,7 +1,6 @@
module std::io::os @if(env::LIBC);
import std::io::path, std::os;
fn Path! native_temp_directory(Allocator* allocator = allocator::heap()) @if(!env::WIN32)
fn Path! native_temp_directory(Allocator* allocator = mem::heap()) @if(!env::WIN32)
{
foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
{
@@ -11,13 +10,13 @@ fn Path! native_temp_directory(Allocator* allocator = allocator::heap()) @if(!en
return path::new("/tmp", allocator);
}
fn Path! native_temp_directory(Allocator* allocator = allocator::heap()) @if(env::WIN32)
fn Path! native_temp_directory(Allocator* allocator = mem::heap()) @if(env::WIN32)
{
@pool(allocator)
{
Win32_DWORD len = win32::getTempPathW(0, null);
if (!len) return IoError.GENERAL_ERROR?;
Char16[] buff = mem::temp_alloc_array(Char16, len + (usz)1);
Char16[] buff = mem::temp_array(Char16, len + (usz)1);
if (!win32::getTempPathW(len, buff)) return IoError.GENERAL_ERROR?;
return path::new(string::temp_from_utf16(buff[:len]), allocator);
};
@@ -25,7 +24,7 @@ fn Path! native_temp_directory(Allocator* allocator = allocator::heap()) @if(env
module std::io::os @if(env::NO_LIBC);
macro Path! native_temp_directory(Allocator* allocator = allocator::heap())
macro Path! native_temp_directory(Allocator* allocator = mem::heap())
{
return IoError.UNSUPPORTED_OPERATION?;
}

View File

@@ -1,5 +1,5 @@
module std::io::path;
import std::collections::list, std::io::os;
import std::collections::list;
const PathEnv DEFAULT_PATH_ENV = env::WIN32 ? PathEnv.WIN32 : PathEnv.POSIX;
const char PREFERRED_SEPARATOR_WIN32 = '\\';
@@ -26,11 +26,11 @@ enum PathEnv
POSIX
}
fn Path! getcwd(Allocator* allocator = allocator::heap())
fn Path! getcwd(Allocator* allocator = mem::heap())
{
@pool(allocator)
{
return new(os::getcwd(allocator::temp()), allocator);
return new(os::getcwd(mem::temp()), allocator);
};
}
@@ -38,9 +38,9 @@ fn bool is_dir(Path path) => os::native_is_dir(path.str_view());
fn bool is_file(Path path) => os::native_is_file(path.str_view());
fn usz! file_size(Path path) => os::native_file_size(path.str_view());
fn bool exists(Path path) => os::native_file_or_dir_exists(path.str_view());
fn Path! tgetcwd() => getcwd(allocator::temp()) @inline;
fn Path! tgetcwd() => getcwd(mem::temp()) @inline;
fn void! chdir(Path path) => os::native_chdir(path) @inline;
fn Path! temp_directory(Allocator* allocator = allocator::heap()) => os::native_temp_directory(allocator);
fn Path! temp_directory(Allocator* allocator = mem::heap()) => os::native_temp_directory(allocator);
fn void! delete(Path path) => os::native_remove(path.str_view()) @inline;
macro bool is_separator(char c, PathEnv path_env = DEFAULT_PATH_ENV)
@@ -58,7 +58,7 @@ macro bool is_win32_separator(char c)
return c == '/' || c == '\\';
}
fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator* allocator = allocator::heap())
fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator* allocator = mem::heap())
{
$if $defined(os::native_ls):
return os::native_ls(dir, no_dirs, no_symlinks, mask, allocator);
@@ -105,17 +105,17 @@ fn void! rmtree(Path path)
$endif
}
fn Path! new(String path, Allocator* allocator = allocator::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
fn Path! new(String path, Allocator* allocator = mem::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
{
return { normalize(path.copy(allocator), path_env), path_env };
}
fn Path! temp_new(String path, PathEnv path_env = DEFAULT_PATH_ENV)
{
return new(path, allocator::temp(), path_env);
return new(path, mem::temp(), path_env);
}
fn Path! new_win32_wstring(WString path, Allocator* allocator = allocator::heap())
fn Path! new_win32_wstring(WString path, Allocator* allocator = mem::heap())
{
@pool(allocator)
{
@@ -123,12 +123,12 @@ fn Path! new_win32_wstring(WString path, Allocator* allocator = allocator::heap(
};
}
fn Path! new_windows(String path, Allocator* allocator = allocator::heap())
fn Path! new_windows(String path, Allocator* allocator = mem::heap())
{
return new(path, allocator, WIN32);
}
fn Path! new_posix(String path, Allocator* allocator = allocator::heap())
fn Path! new_posix(String path, Allocator* allocator = mem::heap())
{
return new(path, allocator, POSIX);
}
@@ -143,7 +143,7 @@ fn bool Path.equals(self, Path p2)
*
* @param [in] filename
**/
fn Path! Path.append(self, String filename, Allocator* allocator = allocator::heap())
fn Path! Path.append(self, String filename, Allocator* allocator = mem::heap())
{
if (!self.path_string.len) return new(filename, allocator, self.env)!;
assert(!is_separator(self.path_string[^1], self.env));
@@ -158,7 +158,7 @@ fn Path! Path.append(self, String filename, Allocator* allocator = allocator::he
};
}
fn Path! Path.tappend(self, String filename) => self.append(filename, allocator::temp());
fn Path! Path.tappend(self, String filename) => self.append(filename, mem::temp());
fn usz Path.start_of_base_name(self) @local
{
@@ -179,13 +179,13 @@ fn bool! Path.is_absolute(self)
return path_start < path_str.len && is_separator(path_str[path_start], self.env);
}
fn Path! Path.absolute(self, Allocator* allocator = allocator::heap())
fn Path! Path.absolute(self, Allocator* allocator = mem::heap())
{
String path_str = self.str_view();
if (!path_str.len) path_str = ".";
if (path_str == ".")
{
String cwd = os::getcwd(allocator::temp())!;
String cwd = os::getcwd(mem::temp())!;
return new(cwd, allocator, self.env);
}
switch (self.env)
@@ -196,7 +196,7 @@ fn Path! Path.absolute(self, Allocator* allocator = allocator::heap())
case POSIX:
if (path_str[0] == PREFERRED_SEPARATOR_POSIX) return self;
}
String cwd = os::getcwd(allocator::temp())!;
String cwd = os::getcwd(mem::temp())!;
return Path{ cwd, self.env }.append(path_str, allocator)!;
}
@@ -459,7 +459,7 @@ fn usz! Path.to_format(&self, Formatter* formatter) @dynamic
return formatter.print(self.str_view());
}
fn String Path.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
fn String Path.to_new_string(&self, Allocator* allocator = mem::heap()) @dynamic
{
return self.str_view().copy(allocator);
}

View File

@@ -1,5 +1,4 @@
module std::io;
import std::math;
interface InStream
{
@@ -143,12 +142,12 @@ fn usz! copy_to(InStream* in, OutStream* dst, char[] buffer = {})
$case NORMAL:
@pool()
{
return copy_through_buffer(in, dst, mem::temp_alloc_array(char, 4096));
return copy_through_buffer(in, dst, mem::temp_array(char, 4096));
};
$case SMALL:
@pool()
{
return copy_through_buffer(in, dst, mem::temp_alloc_array(char, 1024));
return copy_through_buffer(in, dst, mem::temp_array(char, 1024));
};
$case TINY:
$case NONE:

View File

@@ -16,17 +16,7 @@ struct ByteBuffer (InStream, OutStream)
* max_read defines how many bytes might be kept before its internal buffer is shrinked.
* @require self.bytes.len == 0 "Buffer already initialized."
**/
fn ByteBuffer*! ByteBuffer.init_new(&self, usz max_read, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
return self.new_init(max_read, initial_capacity, allocator) @inline;
}
/**
* ByteBuffer provides a streamable read/write buffer.
* max_read defines how many bytes might be kept before its internal buffer is shrinked.
* @require self.bytes.len == 0 "Buffer already initialized."
**/
fn ByteBuffer*! ByteBuffer.new_init(&self, usz max_read, usz initial_capacity = 16, Allocator* allocator = allocator::heap())
fn ByteBuffer*! ByteBuffer.init_new(&self, usz max_read, usz initial_capacity = 16, Allocator* allocator = mem::heap())
{
*self = { .allocator = allocator, .max_read = max_read };
initial_capacity = max(initial_capacity, 16);
@@ -34,14 +24,9 @@ fn ByteBuffer*! ByteBuffer.new_init(&self, usz max_read, usz initial_capacity =
return self;
}
fn ByteBuffer*! ByteBuffer.init_temp(&self, usz max_read, usz initial_capacity = 16) @deprecated("Replaced by temp_init")
fn ByteBuffer*! ByteBuffer.init_temp(&self, usz max_read, usz initial_capacity = 16)
{
return self.temp_init(max_read, initial_capacity) @inline;
}
fn ByteBuffer*! ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = 16)
{
return self.new_init(max_read, initial_capacity, allocator::temp());
return self.init_new(max_read, initial_capacity, mem::temp());
}
/**
@@ -56,7 +41,7 @@ fn ByteBuffer*! ByteBuffer.init_with_buffer(&self, char[] buf)
fn void ByteBuffer.free(&self)
{
if (self.allocator) allocator::free(self.allocator, self.bytes);
if (self.allocator) self.allocator.free(self.bytes);
*self = {};
}
@@ -146,7 +131,7 @@ fn usz! ByteBuffer.available(&self) @inline @dynamic
fn void! ByteBuffer.grow(&self, usz n)
{
n = math::next_power_of_2(n);
char* p = allocator::realloc_aligned(self.allocator, self.bytes, n, .alignment = char.alignof)!;
char* p = self.allocator.realloc_aligned(self.bytes, n, .alignment = char.alignof)!;
self.bytes = p[:n];
}

View File

@@ -1,5 +1,4 @@
module std::io;
import std::math;
struct ByteWriter (OutStream)
{
@@ -14,41 +13,20 @@ struct ByteWriter (OutStream)
* @require self.bytes.len == 0 "Init may not run on on already initialized data"
* @ensure (bool)allocator, self.index == 0
**/
fn ByteWriter* ByteWriter.new_init(&self, Allocator* allocator = allocator::heap())
fn ByteWriter* ByteWriter.init_new(&self, Allocator* allocator = mem::heap())
{
*self = { .bytes = {}, .allocator = allocator };
return self;
}
/**
* @param [&inout] self
* @param [&inout] allocator
* @require self.bytes.len == 0 "Init may not run on on already initialized data"
* @ensure (bool)allocator, self.index == 0
**/
fn ByteWriter* ByteWriter.init_new(&self, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
return self.new_init(allocator) @inline;
}
/**
* @param [&inout] self
* @require self.bytes.len == 0 "Init may not run on on already initialized data"
* @ensure self.index == 0
**/
fn ByteWriter* ByteWriter.temp_init(&self)
fn ByteWriter* ByteWriter.init_temp(&self)
{
return self.new_init(allocator::temp()) @inline;
}
/**
* @param [&inout] self
* @require self.bytes.len == 0 "Init may not run on on already initialized data"
* @ensure self.index == 0
**/
fn ByteWriter* ByteWriter.init_temp(&self) @deprecated("Replaced by temp_init")
{
return self.temp_init() @inline;
return self.init_new(mem::temp());
}
fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data)
@@ -60,7 +38,7 @@ fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data)
fn void! ByteWriter.destroy(&self) @dynamic
{
if (!self.allocator) return;
if (void* ptr = self.bytes.ptr) allocator::free(self.allocator, ptr);
if (void* ptr = self.bytes.ptr) self.allocator.free(ptr);
*self = { };
}
@@ -75,7 +53,7 @@ fn void! ByteWriter.ensure_capacity(&self, usz len) @inline
if (!self.allocator) return IoError.OUT_OF_SPACE?;
if (len < 16) len = 16;
usz new_capacity = math::next_power_of_2(len);
char* new_ptr = allocator::realloc_try(self.allocator, self.bytes.ptr, new_capacity)!;
char* new_ptr = self.allocator.realloc_checked(self.bytes.ptr, new_capacity)!;
self.bytes = new_ptr[:new_capacity];
}

View File

@@ -1,15 +1,5 @@
module libc @if(env::POSIX);
extern fn void* dlopen(ZString path, int flags);
extern fn CInt dlclose(void*);
extern fn void* dlsym(void* handle, ZString symbol);
const int RTLD_LAZY = 0x1;
const int RTLD_NOW = 0x2;
const int RTLD_LOCAL = 0x4;
const int RTLD_GLOBAL = 0x8;
def Pid_t = int;
def Uid_t = uint;
def Gid_t = uint;

View File

@@ -97,10 +97,8 @@ def complex_identity = complex::identity(<double>);
def Quaternionf = Quaternion(<float>);
def Quaternion = Quaternion(<double>);
def QUATERNION_IDENTITY = quaternion::IDENTITY(<double>);
def QUATERNIONF_IDENTITY = quaternion::IDENTITY(<float>);
def quaternion_identity = quaternion::identity(<double>);
def quaternionf_identity = quaternion::identity(<float>);
def quaternion_identity = quaternion::identity(<double>);
def Matrix2f = Matrix2x2(<float>);
def Matrix2 = Matrix2x2(<double>);
@@ -120,14 +118,6 @@ def MATRIX3F_IDENTITY = matrix::IDENTITY3(<float>);
def MATRIX4_IDENTITY = matrix::IDENTITY4(<double>);
def MATRIX4F_IDENTITY = matrix::IDENTITY4(<float>);
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
**/
macro deg_to_rad(x) {
return x * PI / 180;
}
/**
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
**/
@@ -400,14 +390,14 @@ macro nearbyint(x) => $$nearbyint(x);
/**
* @require values::@is_promotable_to_floatlike(x) `The input must be a number or a float vector`
* @require $assignable(exp, $typeof(values::promote_int(x))) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
* @require $assignable(exp, $typeof(x)) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
**/
macro pow(x, exp)
{
$if types::is_floatlike($typeof(exp)):
return $$pow(values::promote_int(x), ($typeof(values::promote_int(x)))exp);
return $$pow(x, ($typeof(x))exp);
$else
return $$pow_int(values::promote_int(x), exp);
return $$pow_int(x, exp);
$endif
}
@@ -449,16 +439,6 @@ macro rint(x) => $$rint(x);
**/
macro round(x) => $$round(x);
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/
macro round_to_decimals(x, int decimal_places)
{
var div = $$pow_int(($typeof(x))10, decimal_places);
return round(div * x) / div;
}
/**
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
**/

View File

@@ -401,13 +401,13 @@ fn Matrix4x4 ortho(Real left, Real right, Real top, Real bottom, Real near, Real
fn Matrix4x4 perspective(Real fov, Real aspect_ratio, Real near, Real far)
{
Real f = (Real)math::tan(math::PI * 0.5 - 0.5 * fov);
Real range_inv = (Real)1.0 / (near - far);
Real rangeInv = (Real)1.0 / (near - far);
return {
f / aspect_ratio, 0, 0, 0,
0, f, 0, 0,
0, 0, (near + far) * range_inv, near * far * range_inv * 2,
0, 0, -1, 0,
0, 0, (near + far) * rangeInv, -1,
0, 0, near * far * rangeInv * 2, 0,
};
}

View File

@@ -9,9 +9,7 @@ union Quaternion
Real[<4>] v;
}
const Quaternion IDENTITY = { 0, 0, 0, 1 };
macro Quaternion identity() @deprecated("Replaced with QUATERNION_IDENTITY constant") => { 0, 0, 0, 1 };
macro Quaternion identity() => { 0, 0, 0, 1 };
macro Quaternion Quaternion.add(Quaternion a, Quaternion b) => Quaternion { .v = a.v + b.v };
macro Quaternion Quaternion.add_each(Quaternion a, Real b) => Quaternion { .v = a.v + b };
macro Quaternion Quaternion.sub(Quaternion a, Quaternion b) => Quaternion { .v = a.v - b.v };
@@ -71,16 +69,30 @@ fn Quaternion Quaternion.mul(a, Quaternion b)
macro into_matrix(Quaternion* q, $Type) @private
{
Quaternion rotation = q.normalize();
var x = rotation.i;
var y = rotation.j;
var z = rotation.k;
var w = rotation.l;
$Type result = { .m = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 } };
Quaternion norm = q.normalize();
return $Type {
1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0,
2*x*z - 2*y*w, 2*y*z + 2*x*w , 1 - 2*x*x - 2*y*y, 0,
0.0, 0.0, 0.0, 1.0,
};
var ii = norm.i*norm.i;
var jj = norm.j*norm.j;
var kk = norm.k*norm.k;
var ik = norm.i*norm.k;
var ij = norm.i*q.j;
var jk = norm.j*norm.k;
var li = norm.l*norm.i;
var lj = norm.l*norm.j;
var lk = norm.l*norm.k;
result.m00 = 1 - 2*(jj + kk);
result.m01 = 2*(ik + lk);
result.m02 = 2*(ij - lj);
result.m10 = 2*(ij - lk);
result.m11 = 1 - 2*(ii + kk);
result.m12 = 2*(jk + li);
result.m20 = 2*(ik + lj);
result.m21 = 2*(jk - li);
result.m22 = 1 - 2*(ii + jj);
return result;
}

View File

@@ -203,10 +203,10 @@ macro matrix_look_at($Type, eye, target, up) @private
var vy = vz.cross(vx);
return $Type {
vx[0], vx[1], vx[2], - vx.dot(eye),
vy[0], vy[1], vy[2], - vy.dot(eye),
vz[0], vz[1], vz[2], - vz.dot(eye),
0.0, 0.0, 0.0, 1
vx[0], vy[0], vz[0], 0,
vx[1], vy[1], vz[1], 0,
vx[2], vy[2], vz[2], 0,
- vx.dot(eye), - vy.dot(eye), - vz.dot(eye), 1
};
}

View File

@@ -1,5 +1,5 @@
module std::math::random;
import std::hash::fnv32a, std::time;
import std::hash::fnv32a;
const ODD_PHI64 @local = 0x9e3779b97f4a7c15;
const MUL_MCG64 @local = 0xf1357aea2e62a9c5;
@@ -26,7 +26,7 @@ fn void seeder(char[] input, char[] out_buffer)
usz out_chars = out_buffer.len;
@pool()
{
ulong[] words = mem::temp_alloc_array(ulong, (out_chars + 7) / 8);
ulong[] words = mem::temp_array(ulong, (out_chars + 7) / 8);
words[..] = ODD_PHI64;
usz words_len_2 = words.len * 2;
@@ -85,8 +85,8 @@ fn char[8 * 4] entropy()
hash(&entropy),
random_int,
hash(clock::now()),
hash(&DString.new_init),
hash(allocator::heap())
hash(&DString.init_new),
hash(mem::heap())
};
return bitcast(entropy_data, char[8 * 4]);
}

View File

@@ -56,7 +56,7 @@ fn usz! InetAddress.to_format(InetAddress* addr, Formatter* formatter) @dynamic
return formatter.printf("%d.%d.%d.%d", addr.ipv4.a, addr.ipv4.b, addr.ipv4.c, addr.ipv4.d)!;
}
fn String InetAddress.to_new_string(InetAddress* addr, Allocator* allocator = allocator::heap()) @dynamic
fn String InetAddress.to_new_string(InetAddress* addr, Allocator* allocator = mem::heap()) @dynamic
{
if (addr.is_ipv6)
{

View File

@@ -1,5 +1,4 @@
module std::net;
import std::io;
fault NetError
{
@@ -57,7 +56,7 @@ fn uint! ipv4toint(String s)
return out;
}
fn String! int_to_new_ipv4(uint val, Allocator* allocator = allocator::heap())
fn String! int_to_new_ipv4(uint val, Allocator* allocator = mem::heap())
{
char[3 * 4 + 3 + 1] buffer;
String res = (String)io::bprintf(&buffer, "%d.%d.%d.%d", val >> 24, (val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)!;
@@ -66,5 +65,5 @@ fn String! int_to_new_ipv4(uint val, Allocator* allocator = allocator::heap())
fn String! int_to_temp_ipv4(uint val)
{
return int_to_new_ipv4(val, allocator::temp());
return int_to_new_ipv4(val, mem::temp());
}

View File

@@ -1,5 +1,5 @@
module std::net::os @if(env::POSIX && SUPPORTS_INET);
import std::io, libc;
import libc;
const int F_GETFL = 3;
const int F_SETFL = 4;

View File

@@ -1,5 +1,6 @@
module std::net::os @if(env::WIN32);
import std::os, std::io, libc;
import std::os::win32;
import libc;
const AIFamily PLATFORM_AF_IPX = 6;
const AIFamily PLATFORM_AF_APPLETALK = 16;

View File

@@ -1,5 +1,6 @@
module std::net @if(os::SUPPORTS_INET);
import std::io, std::os, std::time, libc;
import std::io;
import libc;
struct Socket (InStream, OutStream)
{

View File

@@ -1,5 +1,5 @@
module std::net @if(os::SUPPORTS_INET);
import std::time, libc, std::os;
import libc;
macro apply_sockoptions(sockfd, options) @private
{

View File

@@ -1,6 +1,6 @@
module std::net::tcp @if(os::SUPPORTS_INET);
import std::net @public;
import std::time, libc;
import libc;
distinct TcpSocket = inline Socket;
distinct TcpServerSocket = inline Socket;

View File

@@ -1,5 +1,5 @@
module std::os::backtrace;
import std::collections::list, std::os, std::io;
import std::collections::list;
fault BacktraceFault
{
@@ -48,9 +48,9 @@ fn usz! Backtrace.to_format(&self, Formatter* formatter) @dynamic
fn void Backtrace.free(&self)
{
if (!self.allocator) return;
allocator::free(self.allocator, self.function);
allocator::free(self.allocator, self.object_file);
allocator::free(self.allocator, self.file);
self.allocator.free(self.function);
self.allocator.free(self.object_file);
self.allocator.free(self.file);
}
fn Backtrace* Backtrace.init(&self, uptr offset, String function, String object_file, String file = "", uint line = 0, Allocator* allocator)

View File

@@ -2,14 +2,14 @@
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::os::env;
import std::io::path, libc, std::os;
import libc;
/**
* @param [in] name
* @require name.len > 0
* @return! SearchResult.MISSING
**/
fn String! get_var(String name, Allocator* allocator = allocator::heap())
fn String! get_var(String name, Allocator* allocator = mem::heap())
{
@pool(allocator)
{
@@ -38,7 +38,7 @@ fn String! get_var(String name, Allocator* allocator = allocator::heap())
fn String! get_var_temp(String name)
{
return get_var(name, allocator::temp());
return get_var(name, mem::temp());
}
/**
@@ -72,7 +72,7 @@ fn bool set_var(String name, String value, bool overwrite = true)
/**
* Returns the current user's home directory.
**/
fn String! get_home_dir(Allocator* using = allocator::heap())
fn String! get_home_dir(Allocator* using = mem::heap())
{
String home;
$if !env::WIN32:
@@ -86,7 +86,7 @@ fn String! get_home_dir(Allocator* using = allocator::heap())
/**
* Returns the current user's config directory.
**/
fn Path! get_config_dir(Allocator* allocator = allocator::heap())
fn Path! get_config_dir(Allocator* allocator = mem::heap())
{
@pool(allocator)
{
@@ -126,7 +126,7 @@ fn bool clear_var(String name)
};
}
fn String! executable_path(Allocator *allocator = allocator::heap())
fn String! executable_path(Allocator *allocator = mem::heap())
{
$if env::DARWIN:
return darwin::executable_path(allocator);

View File

@@ -1,2 +0,0 @@
module std::os::linux @if(env::LINUX);
extern fn usz malloc_usable_size(void* ptr);

View File

@@ -1,5 +1,8 @@
module std::os::linux @if(env::LINUX);
import libc, std::os, std::io, std::collections::list;
import libc;
import std::os::posix;
import std::io;
import std::collections::list;
extern fn isz readlink(ZString path, char* buf, usz bufsize);
@@ -130,7 +133,7 @@ fn ulong! elf_module_image_base(String path) @local
fn Backtrace! backtrace_load_from_exec(void* addr, Allocator* allocator) @local
{
char[] buf = mem::temp_alloc_array(char, 1024);
char[] buf = mem::temp_array(char, 1024);
String exec_path = process::execute_stdout_to_buffer(buf, {"realpath", "-e", string::tformat("/proc/%d/exe", posix::getpid())})!;
String obj_name = exec_path.copy(allocator);
@@ -140,7 +143,7 @@ fn Backtrace! backtrace_load_from_exec(void* addr, Allocator* allocator) @local
fn Backtrace! backtrace_load_from_dlinfo(void* addr, Linux_Dl_info* info, Allocator* allocator) @local
{
char[] buf = mem::temp_alloc_array(char, 1024);
char[] buf = mem::temp_array(char, 1024);
void* obj_addr = addr - (uptr)info.dli_fbase + (uptr)elf_module_image_base(info.dli_fname.str_view())!;
ZString obj_path = info.dli_fname;
@@ -182,7 +185,7 @@ fn Backtrace! backtrace_from_addr2line(void* addr, String addr2line, String obj_
};
}
fn Backtrace! backtrace_load_element(void* addr, Allocator* allocator = allocator::heap()) @local
fn Backtrace! backtrace_load_element(void* addr, Allocator* allocator = mem::heap()) @local
{
if (!addr) return backtrace::BACKTRACE_UNKNOWN;
@@ -200,7 +203,7 @@ fn Backtrace! backtrace_load_element(void* addr, Allocator* allocator = allocato
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
{
BacktraceList list;
list.new_init(backtrace.len, allocator);
list.init_new(backtrace.len, allocator);
defer catch
{
foreach (trace : list)

View File

@@ -1,18 +1,18 @@
module std::os::macos::cf @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework");
module std::os::macos::cf @if(env::DARWIN);
distinct CFAllocatorRef = void*;
distinct CFAllocatorContextRef = void*;
def CFOptionFlags = usz;
macro CFAllocatorRef default_allocator() => macos_CFAllocatorGetDefault();
macro void CFAllocatorRef.dealloc(CFAllocatorRef allocator, void* ptr) => macos_CFAllocatorDeallocate(allocator, ptr);
macro void* CFAllocatorRef.alloc(CFAllocatorRef allocator, usz size) => macos_CFAllocatorAllocate(allocator, size, 0);
macro usz CFAllocatorRef.get_preferred_size(CFAllocatorRef allocator, usz req_size) => macos_CFAllocatorGetPreferredSizeForSize(allocator, req_size, 0);
macro void CFAllocatorRef.set_default(CFAllocatorRef allocator) => macos_CFAllocatorSetDefault(allocator);
macro CFAllocatorRef default_allocator() => _macos_CFAllocatorGetDefault();
macro void CFAllocatorRef.dealloc(CFAllocatorRef allocator, void* ptr) => _macos_CFAllocatorDeallocate(allocator, ptr);
macro void* CFAllocatorRef.alloc(CFAllocatorRef allocator, usz size) => _macos_CFAllocatorAllocate(allocator, size, 0);
macro usz CFAllocatorRef.get_preferred_size(CFAllocatorRef allocator, usz req_size) => _macos_CFAllocatorGetPreferredSizeForSize(allocator, req_size, 0);
macro void CFAllocatorRef.set_default(CFAllocatorRef allocator) => _macos_CFAllocatorSetDefault(allocator);
extern fn CFAllocatorRef macos_CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContextRef context) @extern("CFAllocatorCreate") @builtin;
extern fn void macos_CFAllocatorDeallocate(CFAllocatorRef allocator, void* ptr) @extern("CFAllocatorDeallocate") @builtin;
extern fn CFAllocatorRef macos_CFAllocatorGetDefault() @extern("CFAllocatorGetDefault") @builtin;
extern fn void macos_CFAllocatorSetDefault(CFAllocatorRef allocator) @extern("CFAllocatorSetDefault") @builtin;
extern fn void* macos_CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extern("CFAllocatorAllocate") @builtin;
extern fn CFIndex macos_CFAllocatorGetPreferredSizeForSize(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extern("CFAllocatorGetPreferredSizeForSize") @builtin;
extern fn CFAllocatorRef _macos_CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContextRef context) @extern("CFAllocatorCreate");
extern fn void _macos_CFAllocatorDeallocate(CFAllocatorRef allocator, void* ptr) @extern("CFAllocatorDeallocate");
extern fn CFAllocatorRef _macos_CFAllocatorGetDefault() @extern("CFAllocatorGetDefault");
extern fn void _macos_CFAllocatorSetDefault(CFAllocatorRef allocator) @extern("CFAllocatorSetDefault");
extern fn void* _macos_CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extern("CFAllocatorAllocate");
extern fn CFIndex _macos_CFAllocatorGetPreferredSizeForSize(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extern("CFAllocatorGetPreferredSizeForSize");

View File

@@ -1,11 +1,11 @@
module std::os::macos::cf @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework");
module std::os::macos::cf @if(env::DARWIN);
distinct CFArrayRef = void*;
distinct CFArrayCallBacksRef = void*;
distinct CFMutableArrayRef = void*;
extern fn CFArrayRef macos_CFArrayCreate(CFAllocatorRef allocator, void** values, CFIndex num_values, CFArrayCallBacksRef callBacks) @extern("CFArrayCreate") @builtin;
extern fn CFArrayRef macos_CFArrayCopy(CFAllocatorRef allocator, CFArrayRef array) @extern("CFArrayCopy") @builtin;
extern fn CFIndex macos_CFArrayGetCount(CFArrayRef array) @extern("CFArrayGetCount") @builtin;
extern fn void macos_CFArrayAppendArray(CFMutableArrayRef theArray, CFArrayRef otherArray, CFRange otherRange) @extern("CFArrayAppendArray") @builtin;
extern fn CFMutableArrayRef macos_CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, CFArrayCallBacksRef callBacks) @extern("CFArrayCreateMutable") @builtin;
extern fn void macos_CFArrayAppendValue(CFMutableArrayRef theArray, void *value) @extern("CFArrayAppendValue") @builtin;
extern fn CFArrayRef _macos_CFArrayCreate(CFAllocatorRef allocator, void** values, CFIndex num_values, CFArrayCallBacksRef callBacks) @extern("CFArrayCreate");
extern fn CFArrayRef _macos_CFArrayCopy(CFAllocatorRef allocator, CFArrayRef array) @extern("CFArrayCopy");
extern fn CFIndex _macos_CFArrayGetCount(CFArrayRef array) @extern("CFArrayGetCount");
extern fn void _macos_CFArrayAppendArray(CFMutableArrayRef theArray, CFArrayRef otherArray, CFRange otherRange) @extern("CFArrayAppendArray");
extern fn CFMutableArrayRef _macos_CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, CFArrayCallBacksRef callBacks) @extern("CFArrayCreateMutable");
extern fn void _macos_CFArrayAppendValue(CFMutableArrayRef theArray, void *value) @extern("CFArrayAppendValue");

View File

@@ -1,4 +1,4 @@
module std::os::macos::cf @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework");
module std::os::macos::cf @if(env::DARWIN);
distinct CFTypeRef = void*;
def CFIndex = isz;
@@ -8,5 +8,5 @@ struct CFRange
CFIndex length;
}
extern fn CFTypeRef macos_CFRetain(CFTypeRef cf) @extern("CFRetain") @builtin;
extern fn void macos_CFRelease(CFTypeRef cf) @extern("CFRelease") @builtin;
extern fn CFTypeRef _macos_CFRetain(CFTypeRef cf) @extern("CFRetain");
extern fn void _macos_CFRelease(CFTypeRef cf) @extern("CFRelease");

View File

@@ -1,5 +1,5 @@
module std::os::darwin @if(env::DARWIN);
import std::collections::list, std::os;
import std::collections::list;
const CTL_UNSPEC = 0; /* unused */
const CTL_KERN = 1; /* "high kernel": proc, limits */
@@ -80,7 +80,7 @@ fn uptr! load_address() @local
{
Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT");
if (!cmd) return BacktraceFault.SEGMENT_NOT_FOUND?;
String path = env::executable_path(allocator::temp()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!;
String path = env::executable_path(mem::temp()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!;
uint dyld_count = darwin::_dyld_image_count();
for (uint i = 0; i < dyld_count; i++)
{
@@ -93,7 +93,7 @@ fn uptr! load_address() @local
}
fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_address, Allocator* allocator = allocator::heap()) @local
fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_address, Allocator* allocator = mem::heap()) @local
{
@pool(allocator)
{
@@ -136,7 +136,7 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
{
void *load_addr = (void *)load_address()!;
BacktraceList list;
list.new_init(backtrace.len, allocator);
list.init_new(backtrace.len, allocator);
defer catch
{
foreach (trace : list)
@@ -147,7 +147,7 @@ fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
}
@pool(allocator)
{
String execpath = executable_path(allocator::temp())!;
String execpath = executable_path(mem::temp())!;
foreach (addr : backtrace)
{
list.append(backtrace_load_element(execpath, addr, load_addr, allocator) ?? backtrace::BACKTRACE_UNKNOWN);

View File

@@ -1,3 +0,0 @@
module std::os::darwin @if(env::DARWIN);
extern fn usz malloc_size(void* ptr);

View File

@@ -1,4 +1,4 @@
module std::os::macos::objc @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework");
module std::os::macos::objc @if(env::DARWIN);
distinct Class = void*;
distinct Method = void*;
@@ -10,36 +10,38 @@ fault ObjcFailure
CLASS_NOT_FOUND
}
macro ZString Class.name(Class cls) => macos_class_getName(cls);
macro Class Class.superclass(Class cls) => macos_class_getSuperclass(cls);
macro bool Class.responds_to(Class cls, Selector sel) => macos_class_respondsToSelector(cls, sel);
macro Method Class.method(Class cls, Selector name) => macos_class_getClassMethod(cls, name);
macro char* Class.name(Class cls) => _macos_class_getName(cls);
macro Class Class.superclass(Class cls) => _macos_class_getSuperclass(cls);
macro bool Class.responds_to(Class cls, Selector sel) => _macos_class_respondsToSelector(cls, sel);
macro Method Class.method(Class cls, Selector name) => _macos_class_getClassMethod(cls, name);
macro bool Selector.equals(Selector a, Selector b) => a == b;
macro bool Class.equals(Class a, Class b) => a == b;
macro Class! class_by_name(ZString c)
macro Selector selector_register(char* c) => _macos_sel_registerName(c);
macro Class! class_by_name(char* c)
{
Class cls = macos_objc_lookUpClass(c);
return cls ?: ObjcFailure.CLASS_NOT_FOUND?;
Class cls = _macos_objc_lookUpClass(c);
if (!cls) return ObjcFailure.CLASS_NOT_FOUND?;
return cls;
}
macro Class[] class_get_list(Allocator *allocator = allocator::heap())
macro Class[] class_get_list(Allocator *allocator = mem::heap())
{
int num_classes = macos_objc_getClassList(null, 0);
int num_classes = _macos_objc_getClassList(null, 0);
if (!num_classes) return {};
Class[] entries = allocator.new_array(Class, num_classes);
macos_objc_getClassList(entries.ptr, entries.len);
_macos_objc_getClassList(entries.ptr, entries.len);
return entries;
}
extern fn Class macos_objc_getClass(ZString name) @extern("objc_getClass") @builtin;
extern fn int macos_objc_getClassList(Class* buffer, int buffer_count) @extern("objc_getClassList") @builtin;
extern fn ZString macos_class_getName(Class cls) @extern("class_getName") @builtin;
extern fn Class macos_class_getSuperclass(Class cls) @extern("class_getSuperclass") @builtin;
extern fn Method macos_class_getClassMethod(Class cls, Selector name) @extern("class_getClassMethod") @builtin;
extern fn bool macos_class_respondsToSelector(Class cls, Selector name) @extern("class_respondsToSelector") @builtin;
extern fn Selector macos_sel_registerName(ZString str) @extern("sel_registerName") @builtin;
extern fn Class macos_objc_lookUpClass(ZString name) @extern("objc_lookUpClass") @builtin;
extern fn Class _macos_objc_getClass(char* name) @extern("objc_getClass");
extern fn int _macos_objc_getClassList(Class* buffer, int buffer_count) @extern("objc_getClassList");
extern fn char* _macos_class_getName(Class cls) @extern("class_getName");
extern fn Class _macos_class_getSuperclass(Class cls) @extern("class_getSuperclass");
extern fn Method _macos_class_getClassMethod(Class cls, Selector name) @extern("class_getClassMethod");
extern fn bool _macos_class_respondsToSelector(Class cls, Selector name) @extern("class_respondsToSelector");
extern fn Selector _macos_sel_registerName(char* str) @extern("sel_registerName");
extern fn Class _macos_objc_lookUpClass(char* name) @extern("objc_lookUpClass");

View File

@@ -1,3 +0,0 @@
module std::os::posix @if(env::POSIX);
extern fn CInt posix_memalign(void **memptr, usz alignment, usz size);

View File

@@ -56,21 +56,10 @@ const CInt WNOHANG = 1;
const CInt WUNTRACES = 2;
JmpBuf backtrace_jmpbuf @local;
def BacktraceFn = fn CInt(void** buffer, CInt size);
fn CInt backtrace(void** buffer, CInt size)
fn CInt backtrace(void** buffer, CInt size) @extern("backtrace") @weak
{
if (size < 1) return 0;
void* handle = libc::dlopen("libc.so.6", libc::RTLD_LAZY);
if (handle)
{
BacktraceFn backtrace_fn = libc::dlsym(handle, "backtrace");
libc::dlclose(handle);
if (backtrace_fn)
{
return backtrace_fn(buffer, size);
}
}
// Loop through the return addresses until we hit a signal.
// This avoids using the frame address.
SignalFunction restore_backtrace = fn void(CInt) {

View File

@@ -1,5 +1,4 @@
module std::os::posix @if(env::POSIX);
import std::thread;
import libc;
const PTHREAD_MUTEX_NORMAL = 0;

View File

@@ -1,5 +1,6 @@
module std::os::process @if(env::WIN32 || env::POSIX);
import std::io, libc, std::os;
import std::io::file;
import libc;
// This code is based on https://github.com/sheredom/subprocess.h
@@ -246,7 +247,7 @@ fn SubProcess! create(String[] command_line, SubProcessOptions options = {}, Str
**/
fn ZString* tcopy_command_line(String[] command_line) @local @inline @if(env::POSIX)
{
ZString* copy = mem::temp_alloc_array(ZString, command_line.len + 1);
ZString* copy = mem::temp_array(ZString, command_line.len + 1);
foreach (i, str : command_line)
{
copy[i] = str.zstr_tcopy();
@@ -259,7 +260,7 @@ const ZString[1] EMPTY_ENVIRONMENT @if(env::POSIX) = { null };
fn ZString* tcopy_env(String[] environment) @local @inline @if(env::POSIX)
{
if (!environment.len) return &EMPTY_ENVIRONMENT;
ZString* copy = mem::temp_alloc_array(ZString, environment.len + 1);
ZString* copy = mem::temp_array(ZString, environment.len + 1);
copy[environment.len] = null;
foreach (i, str : environment)
{

View File

@@ -1,11 +0,0 @@
module std::os::win32 @if(env::WIN32);
extern fn void* _aligned_malloc(usz size, usz alignment);
extern fn void* _aligned_realloc(void* memblock, usz size, usz alignment);
extern fn void* _aligned_recalloc(void* memblock, usz size, usz alignment);
extern fn void _aligned_free(void* memblock);
extern fn void _aligned_msize(void* memblock, usz alignment, usz offset);
extern fn void* _aligned_offset_malloc(usz size, usz alignment, usz offset);
extern fn void* _aligned_offset_realloc(void* memblock, usz size, usz alignment, usz offset);
extern fn void* _aligned_offset_recalloc(void* memblock, usz size, usz alignment, usz offset);
extern fn usz _msize(void* memblock);

View File

@@ -1,5 +1,4 @@
module std::os::win32 @if(env::WIN32);
import std::thread, std::os::backtrace;
const Win32_DWORD STARTF_USESTDHANDLES = 0x00000100;
const Win32_DWORD CREATE_NO_WINDOW = 0x08000000;
@@ -103,8 +102,6 @@ extern fn Win32_BOOL symFromAddr(Win32_HANDLE hProcess, Win32_DWORD64 address, W
extern fn Win32_BOOL symGetLineFromAddr64(Win32_HANDLE hProcess, Win32_DWORD64 dwAddr, Win32_PDWORD pdwDisplacement, Win32_PIMAGEHLP_LINE64 line) @extern("SymGetLineFromAddr64");
extern fn Win32_WORD rtlCaptureStackBackTrace(Win32_DWORD framesToSkip, Win32_DWORD framesToCapture, Win32_PVOID *backTrace, Win32_PDWORD backTraceHash) @extern("RtlCaptureStackBackTrace");
extern fn Win32_BOOL symGetModuleInfo64(Win32_HANDLE hProcess, Win32_DWORD64 qwAddr, Win32_PIMAGEHLP_MODULE64 moduleInfo) @extern("SymGetModuleInfo64");
extern fn Win32_HANDLE getModuleHandleA(Win32_LPCSTR lpModuleName) @extern("GetModuleHandleA");
extern fn Win32_HANDLE getModuleHandleW(Win32_LPCWSTR lpModuleName) @extern("GetModuleHandleW");
fn Win32_DWORD! load_modules()
{
@@ -155,7 +152,7 @@ Win32_DWORD64 displacement;
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
{
BacktraceList list;
list.new_init(backtrace.len, allocator);
list.init_new(backtrace.len, allocator);
Win32_HANDLE process = getCurrentProcess();
symInitialize(process, null, 1);
defer symCleanup(process);

View File

@@ -1,5 +1,6 @@
module std::thread::os @if(env::POSIX);
import std::os::posix, std::time, libc;
module std::threads::os @if(env::POSIX);
import std::os::posix;
import libc;
struct NativeMutex
{
@@ -150,8 +151,8 @@ fn void* callback(void* arg) @private
fn void! NativeThread.create(&thread, ThreadFn thread_fn, void* arg)
{
PosixThreadData *thread_data = mem::new(PosixThreadData, { .thread_fn = thread_fn, .arg = arg });
PosixThreadData *thread_data = mem::new(PosixThreadData);
*thread_data = { .thread_fn = thread_fn, .arg = arg };
if (posix::pthread_create(thread, null, &callback, thread_data) != 0)
{
*thread = null;

View File

@@ -1,5 +1,5 @@
module std::thread::os @if(env::WIN32);
import std::os::win32, std::time;
import std::os::win32;
distinct NativeThread = inline Win32_HANDLE;

View File

@@ -1,6 +1,5 @@
module std::thread;
import std::thread::os;
import std::time;
distinct MutexType = int;

View File

@@ -1,12 +1,11 @@
module std::time::clock;
import std::time::os;
fn Clock now()
{
$if $defined(os::native_clock):
$if $defined(native_clock):
return os::native_clock();
$else
unreachable("Clock unsupported");
return 0;
$endif
}

View File

@@ -1,5 +1,4 @@
module std::time;
import std::io, std::time::os;
distinct Time = long;
distinct Duration = long;
@@ -78,7 +77,7 @@ fn Time now()
$endif
}
fn Time Time.add_seconds(time, long seconds) => time + (Time)(seconds * (long)SEC);
fn Time Time.add_seconds(time, long seconds) => time + (Time)(seconds * (long)MS);
fn Time Time.add_minutes(time, long minutes) => time + (Time)(minutes * (long)MIN);
fn Time Time.add_hours(time, long hours) => time + (Time)(hours * (long)HOUR);
fn Time Time.add_days(time, long days) => time + (Time)(days * (long)DAY);

View File

@@ -6,6 +6,8 @@
import platform
import io
import os
import sys
import json
import shutil
import hashlib
@@ -14,6 +16,7 @@ import tempfile
import argparse
import subprocess
import urllib.request
import re
from pathlib import Path
OUTPUT = Path("msvc_temp") # output folder
@@ -173,10 +176,10 @@ for pkg in msvc_packages:
sdk_packages = [
# Windows SDK libs
"Windows SDK for Windows Store Apps Libs-x86_en-us.msi",
"Windows SDK Desktop Libs x64-x86_en-us.msi",
f"Windows SDK for Windows Store Apps Libs-x86_en-us.msi",
f"Windows SDK Desktop Libs x64-x86_en-us.msi",
# CRT headers & libs
"Universal CRT Headers Libraries and Sources-x86_en-us.msi",
f"Universal CRT Headers Libraries and Sources-x86_en-us.msi",
]
with tempfile.TemporaryDirectory() as d:

View File

@@ -1,145 +1,5 @@
# C3C Release Notes
## 0.5.5 Change list
### Changes / improvements
- Disallow multiple `_` in a row in digits, e.g. `1__000`.
- Added `@link` attribute.
- New 'linker' build option.
- "linker" project setting updated, "system-linker" removed.
### Fixes
- Struct/union members now correctly rejects members without storage size #1147.
- `math::pow` will now correctly promote integer arguments.
- Pointer difference would fail where alignment != size (structs etc) #1150
- Fixed array calculation for npot2 vectors.
- $$memcpy_inline and $$memset_inline fixed.
- `.$Type = ...` and `.$foo = ...` now works #1156.
- `int.min` incorrect behaviour #1154.
- Bitstruct cast to other bitstruct by way of underlying type would fail #1159.
- Bug in `time.add_seconds` #1162.
- Remove initial './' in Win32 and convert '/' to '\' for paths when running a binary.
- 'output' directory for projects was incorrect in templates.
- Regression: no stacktrace.
- For MacOS, running with higher optimization would crash as initializers were removed.
- `compile-run` and `run` now returns the proper return code.
- Allow String constants -> ichar*, and allow integer pointers to explicitly convert between unsigned signed.
- Bug in unaligned return value lowering for Aarch64.
### Stdlib changes
- Added `new_aligned` and `alloc_aligned` functions to prevent accidental under-alignment when allocating simd.
- Fixes to realloc of aligned allocations
- Use native Windows calls on aligned allocations on Windows.
- mem::copy_inline, mem::clear_inline and mem::set_inline added.
- mem::copy / clear / set no longer has an `$inline` attribute.
- Native aligned libc malloc on Windows & POSIX.
- Simplification of the allocator interface.
- CoreFoundation only linked on MacOS when used.
## 0.5.4 Change list
### Changes / improvements
- Hash variables may now take a designated initializer.
- Added @safemacro to override the `@` requirement for non-function-like macros.
- More information available with debug log in non debug builds.
- Removed install_win_reqs.bat which didn't work well.
- Support `**` to mean `./**`
- MacOS init/finalizer now respects priority.
- Bitstructs supports `!=` and `==`.
- Support Windows `.def` files using `--windef`.
- Bitstructs now fold compile time constant bit ops.
- Fix issue where in some cases a constant global with a string wasn't folded (e.g. in asm stmts)
- Lateral implicit imports removed.
- Default to '.' if no libdir is specified.
- Improved error messages for `--lib`.
- Added `--linker` to set the linker #1067.
### Fixes
- Fixes to macro context evaluation with macro varargs.
- Dynamic methods registered before init functions on MacOS.
- Fixed clobber on x86 `cpuid` instruction.
- Removed invalid syntax from grammar.y.
- `output` project setting now respected.
- Aliased declarations caused errors when used in initializers.
- Aliased consts used as constant initializers caused errors.
- Exported module names replace `::` by `_`.
- Const ternary would evaluate incorrectly for ?:
- `$$MODULE` would report the incorrect module name in macros.
- Fixed debug info for globals and for/switch scopes.
- `out` now correctly detects subscript[] use.
- Ambiguous recursive imports are now correctly detected.
- Overzealous local escape check corrected #1127.
- Fixes to the matrix functions #1130.
### Stdlib changes
- Deprecated `Allocator` helper functions.
- Added `mem::allocator` functions corresponding to removed allocator functions.
- Changed `mem::new` / `mem::temp_new` to accept an optional initializer, and will clear by default.
- Mem `_clear` and `_zero` variants deprecated. "new_*" functions will clear by default.
- Mem "alloc_*" functions replace old "new_*" behaviour.
- Fixed temp memory issue with formatter.
- Added temp_push and temp_pop for pushing / popping the temp allocator manually (or from C).
- Added byte_size to `List`
- Added `GenericList`.
## 0.5.3 Change list
### Changes / improvements
- Migrate from using actual type with GEP, use i8 or i8 array instead.
- Optimize foreach for single element arrays.
- Move all calls to panic due to checks to the end of the function.
### Fixes
- Single module command line option was not respected.
- Fixed issue with compile time defined types (String in this case), which would crash the compiler in certain cases.
- Projects now correctly respect optimization directives.
- Generic modules now correctly follow the implicit import rules of regular modules.
- Passing an untyped list to a macro and then using it as a vaarg would crash the compiler.
- Extern const globals now work correctly.
### Stdlib changes
- init_new/init_temp deprecated, replaced by new_init and temp_init.
## 0.5.2 Change list
### Changes / improvements
- Allow trailing comma in calls and parameters #1092.
### Fixes
- Fixes issue where single character filenames like 'a.c3' would be rejected.
- Better errors when index type doesn't match len() when doing user defined foreach.
- Fixes to `to_int` for hexadecimal strings.
- Fixed issue when using a generic type from a generic type.
- Bug with vector parameters when the size > 2 and modified.
- Missing error on assigning to in-parameters through subscripting.
- Inference of a vector on the lhs of a binary expression would cause a crash.
- Fixes to PriorityQueue
### Stdlib changes
- Allow `to_int` family functions take a base, parsing base 2-10 and 16.
## 0.5.1 Change list
### Changes / improvements
- Improved error messages for const errors.
- Do not link with debug libraries unless using static libraries.
- Add 'print-linking' build option.
- System linker may be used even if the target arch is different from current.
- Slice -> array/vector works for constant slice lengths.
### Fixes
- On Aarch64 use the correct frame pointer type.
- On Aarch64 macOS, ensure the minimum version is 11.0 (Big Sur)
- Fixes to the yacc grammar.
- Dsym generation on macOS will correctly emit -arch.
- Stacktrace on signals on Linux when backtrace is available.
### Stdlib changes
- `delete` and `delete_range` added to DString.
- `Splitter` iterator added.
- `splitter` and `iterator` String methods.
- `load_new`, `load_buffer` and `load_temp` std::io::file functions.
## 0.5.0 Change List
### Changes / improvements
@@ -311,7 +171,7 @@
- Added `saturated` math.
- Added `@expect`, `@unlikely` and `@likely` macros.
- Temp allocator uses memory-env to determine starting size.
- Temp allocator is now accessed using `mem::temp()`, heap allocator using `allocator::heap()`.
- Temp allocator is now accessed using `mem::temp()`, heap allocator using `mem::heap()`.
- Float parsing added.
- Additions to std::net, ipv4/ipv6 parsing.
- Stream api.

View File

@@ -75,7 +75,7 @@ fn void main()
{
const String[] URLS = { "good", "title-empty", "title-missing", "head-missing", "fail" };
DynamicArenaAllocator dynamic_arena;
dynamic_arena.init(1024, allocator::heap());
dynamic_arena.init(1024, mem::heap());
OutStream* out = io::stdout();
foreach (String url : URLS)
{

View File

@@ -5,9 +5,9 @@ import libc;
fn int fannkuchredux(int n)
{
int* perm = mem::alloc_array(int, n);
int* perm1 = mem::alloc_array(int, n);
int* count = mem::alloc_array(int, n);
int* perm = mem::new_array(int, n);
int* perm1 = mem::new_array(int, n);
int* count = mem::new_array(int, n);
int max_flips_count;
int perm_count;
int checksum;

View File

@@ -4,9 +4,9 @@ import std::math;
fn int fannkuchredux(int n)
{
int[] perm = mem::alloc_array(int, n);
int[] perm1 = mem::alloc_array(int, n);
int* count = mem::alloc_array(int, n);
int[] perm = mem::new_array(int, n);
int[] perm1 = mem::new_array(int, n);
int* count = mem::new_array(int, n);
int max_flips_count;
int perm_count;
int checksum;

View File

@@ -1,6 +1,5 @@
module arkanoid;
import raylib;
import std::math;
/**
*
* raylib - classic game: arkanoid
@@ -103,7 +102,7 @@ fn void main()
// Initialize game variables
fn void init_game()
{
brick_size = { (float)raylib::get_screen_width() / BRICKS_PER_LINE, 40 };
brick_size = { raylib::get_screen_width() / BRICKS_PER_LINE, 40 };
// Initialize player
player.position = { SCREEN_WIDTH/2, SCREEN_HEIGHT * 7 / 8 };
@@ -202,7 +201,7 @@ fn void update_game()
// Hit below
if (((ball.position.y - ball.radius) <= (brick[i][j].position.y + brick_size.y / 2)) &&
((ball.position.y - ball.radius) > (brick[i][j].position.y + brick_size.y / 2 + ball.speed.y)) &&
((math::abs(ball.position.x - brick[i][j].position.x)) < (brick_size.x / 2 + ball.radius * 2.0f / 3)) && (ball.speed.y < 0))
((math::fabs((double)ball.position.x - brick[i][j].position.x)) < (double)(brick_size.x / 2 + ball.radius * 2 / 3)) && (ball.speed.y < 0))
{
brick[i][j].active = false;
ball.speed.y *= -1;
@@ -210,7 +209,7 @@ fn void update_game()
// Hit above
else if (((ball.position.y + ball.radius) >= (brick[i][j].position.y - brick_size.y/2)) &&
((ball.position.y + ball.radius) < (brick[i][j].position.y - brick_size.y/2 + ball.speed.y)) &&
((math::abs(ball.position.x - brick[i][j].position.x)) < (brick_size.x / 2 + ball.radius * 2.0f / 3)) && (ball.speed.y > 0))
((math::fabs((double)ball.position.x - brick[i][j].position.x)) < (double)(brick_size.x/2 + ball.radius*2/3)) && (ball.speed.y > 0))
{
brick[i][j].active = false;
ball.speed.y *= -1;
@@ -218,7 +217,7 @@ fn void update_game()
// Hit left
else if (((ball.position.x + ball.radius) >= (brick[i][j].position.x - brick_size.x/2)) &&
((ball.position.x + ball.radius) < (brick[i][j].position.x - brick_size.x/2 + ball.speed.x)) &&
((math::abs(ball.position.y - brick[i][j].position.y)) < (brick_size.y / 2 + ball.radius * 2.0f / 3)) && (ball.speed.x > 0))
((math::fabs((double)ball.position.y - brick[i][j].position.y)) < (double)(brick_size.y/2 + ball.radius*2/3)) && (ball.speed.x > 0))
{
brick[i][j].active = false;
ball.speed.x *= -1;
@@ -226,7 +225,7 @@ fn void update_game()
// Hit right
else if (((ball.position.x - ball.radius) <= (brick[i][j].position.x + brick_size.x/2)) &&
((ball.position.x - ball.radius) > (brick[i][j].position.x + brick_size.x/2 + ball.speed.x)) &&
((math::abs(ball.position.y - brick[i][j].position.y)) < (brick_size.y / 2 + ball.radius * 2.0f / 3)) && (ball.speed.x < 0))
((math::fabs((double)ball.position.y - brick[i][j].position.y)) < (double)(brick_size.y/2 + ball.radius*2/3)) && (ball.speed.x < 0))
{
brick[i][j].active = false;
ball.speed.x *= -1;

View File

@@ -124,12 +124,12 @@ fn void update_game()
if (raylib::is_key_pressed(keyboard::RIGHT) && allow_move)
{
snake_direction = (SnakeDirection)((snake_direction.ordinal + 1) % 4);
snake_direction = (SnakeDirection)((snake_direction + 1) % 4);
allow_move = false;
}
if (raylib::is_key_pressed(keyboard::LEFT) && allow_move)
{
snake_direction = (SnakeDirection)((snake_direction.ordinal + 3) % 4);
snake_direction = (SnakeDirection)((snake_direction + 3) % 4);
allow_move = false;
}
@@ -179,13 +179,13 @@ fn void update_game()
if (!fruit.active)
{
fruit.active = true;
fruit.position = { (float)raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x / 2, (float)raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 };
fruit.position = { raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x/2, raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 };
for (int i = 0; i < counter_tail; i++)
{
while ((fruit.position.x == snake[i].position.x) && (fruit.position.y == snake[i].position.y))
{
fruit.position = { (float)raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x/2, (float)raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 };
fruit.position = { raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x/2, raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 };
i = 0;
}
}
@@ -213,12 +213,12 @@ fn void draw_game()
// Draw grid lines
for (int i = 0; i < SCREEN_WIDTH / SQUARE_SIZE + 1; i++)
{
raylib::draw_line_v({(float)SQUARE_SIZE * i + offset.x/2, offset.y/2}, {(float)SQUARE_SIZE * i + offset.x/2, SCREEN_HEIGHT - offset.y/2}, raylib::LIGHTGRAY);
raylib::draw_line_v({SQUARE_SIZE * i + offset.x/2, offset.y/2}, {SQUARE_SIZE * i + offset.x/2, SCREEN_HEIGHT - offset.y/2}, raylib::LIGHTGRAY);
}
for (int i = 0; i < SCREEN_HEIGHT/SQUARE_SIZE + 1; i++)
{
raylib::draw_line_v({offset.x/2, (float)SQUARE_SIZE * i + offset.y / 2 }, { SCREEN_WIDTH - offset.x/2, (float)SQUARE_SIZE * i + offset.y / 2 }, raylib::LIGHTGRAY);
raylib::draw_line_v({offset.x/2, SQUARE_SIZE * i + offset.y / 2 }, { SCREEN_WIDTH - offset.x/2, SQUARE_SIZE * i + offset.y / 2 }, raylib::LIGHTGRAY);
}
// Draw snake
@@ -248,4 +248,5 @@ fn void update_draw_frame()
{
update_game();
draw_game();
}
}

View File

@@ -27,7 +27,7 @@ macro @retry(#function, int retries = 3)
}
return result;
} while (retries-- > 0);
return e?;
return e!;
}
fn void main()

View File

@@ -44,10 +44,10 @@ fn void eval_AtA_times_u(double[] u, double[] atau, double[] x)
fn void main(String[] args)
{
int n = args.len == 2 ? args[1].to_int()!! : 2000;
temparr = mem::alloc_array(double, n);
double[] u = mem::alloc_array(double, n);
double[] v = mem::alloc_array(double, n);
double[] x = mem::alloc_array(double, (usz)(n * n));
temparr = mem::new_array(double, n);
double[] u = mem::new_array(double, n);
double[] v = mem::new_array(double, n);
double[] x = mem::new_array(double, (usz)(n * n));
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)

View File

@@ -1,4 +1,3 @@
%option yylineno
D [0-9]
DU [0-9_]
@@ -19,12 +18,12 @@ E [Ee][+-]?{D}+
P [Pp][+-]?{D}+
B64 [ \t\v\n\f]?[A-Za-z0-9+/][ \t\v\n\fA-Za-z0-9+/=]+
HEX [ \t\v\n\f]?[A-Fa-f0-9][ \t\v\n\fA-Fa-f0-9]+
INTTYPE ([ui](8|16|32|64|128)|[Uu][Ll]?|[Ll])
REALTYPE ([f](8|16|32|64|128)?)
INT {D}(_?{D})*
HINT {H}(_?{H})*
OINT {O}(_?{O})*
BINT {B}(_?{B})*
INTTYPE (([ui](8|16|32|64|128))|([Uu][Ll]?|[Ll]))?
REALTYPE [f](8|16|32|64|128)?
INT {D}(_*{D})*
HINT {H}(_*{H})*
OINT {O}(_*{O})*
BINT {B}(_*{B})*
%x COMMENT RAW_STRING
@@ -42,7 +41,6 @@ int comment_level = 0;
"$alignof" { count(); return(CT_ALIGNOF); }
"$and" { count(); return(CT_AND); }
"$assert" { count(); return(CT_ASSERT); }
"$assignable" { count(); return(CT_ASSIGNABLE); }
"$case" { count(); return(CT_CASE); }
"$default" { count(); return(CT_DEFAULT); }
"$defined" { count(); return(CT_DEFINED); }
@@ -168,31 +166,29 @@ ${IDENTIFIER} { count(); return(CT_IDENT); }
0[oO]{OINT}{INTTYPE}? { count(); return(INTEGER); }
0[bB]{BINT}{INTTYPE}? { count(); return(INTEGER); }
{INT}{INTTYPE}? { count(); return(INTEGER); }
x\'{HEX}\' { count(); return(BYTES); }
x\"{HEX}\" { count(); return(BYTES); }
x\`{HEX}\` { count(); return(BYTES); }
b64\'{B64}\' { count(); return(BYTES); }
b64\"{B64}\" { count(); return(BYTES); }
b64\`{B64}\` { count(); return(BYTES); }
x\'{HEX}+\' { count(); return(BYTES); }
x\"{HEX}+\" { count(); return(BYTES); }
x\`{HEX}+\` { count(); return(BYTES); }
b64\'{B64}+\' { count(); return(BYTES); }
b64\"{B64}+\" { count(); return(BYTES); }
b64\`{B64}+\` { count(); return(BYTES); }
{INT}{REALTYPE} { count(); return(REAL); }
{INT}{E}{REALTYPE}? { count(); return(REAL); }
0[xX]{HINT}{P}{REALTYPE}? { count(); return(REAL); }
{INT}"."{INT}{E}?{REALTYPE}? { count(); return(REAL); }
0[xX]{HINT}"."{HINT}{P}{REALTYPE}? { count(); return(REAL); }
\"(\\.|[^\\"])*\" { count(); return(STRING_LITERAL); }
\'(\\[ux]{HEX}|\\.|[^\\'])\' { count(); return(CHAR_LITERAL); }
\'(\\.|[^\\'])*\' { count(); return(CHAR_LITERAL); }
"`" { count(); BEGIN(RAW_STRING); }
<RAW_STRING>{
"``" { count(); }
"`" { count(); BEGIN(INITIAL); return(STRING_LITERAL); }
"[^`]"+ { count(); }
"*"+ { count(); }
<<EOF>> { BEGIN(INITIAL); return(RAW_STRING); }
}
"!!" { count(); return(BANGBANG); }
"..." { count(); return(ELLIPSIS); }
".." { count(); return(DOTDOT); }
">>=" { count(); return(SHR_ASSIGN); }

View File

@@ -4,11 +4,10 @@
#define YYERROR_VERBOSE
int yydebug = 1;
extern char yytext[];
extern int column, yylineno;
extern int column;
int yylex(void);
void yyerror(const char *s);
void yyerror(char *s);
%}
%locations
%token IDENT HASH_IDENT CT_IDENT CONST_IDENT
%token TYPE_IDENT CT_TYPE_IDENT
@@ -34,15 +33,15 @@ void yyerror(const char *s);
%token CT_SIZEOF CT_STRINGIFY CT_QNAMEOF CT_OFFSETOF CT_VAEXPR CT_FEATURE
%token CT_EXTNAMEOF CT_EVAL CT_DEFINED CT_ALIGNOF ASSERT
%token ASM CHAR_LITERAL REAL TRUE FALSE CT_CONST_IDENT
%token LBRAPIPE RBRAPIPE HASH_CONST_IDENT CT_ASSIGNABLE CT_AND CT_IS_CONST
%token LBRAPIPE RBRAPIPE HASH_CONST_IDENT CT_CASTABLE CT_ASSIGNABLE CT_AND CT_IS_CONST
%start translation_unit
%%
path
: IDENT SCOPE
| path IDENT SCOPE
;
: IDENT SCOPE
| path IDENT SCOPE
;
path_const
: path CONST_IDENT
@@ -67,7 +66,7 @@ ident_expr
local_ident_expr
: CT_IDENT
| HASH_IDENT
| HASH_IDENT
;
ct_call
@@ -92,9 +91,9 @@ ct_analyse
ct_arg
: CT_VACONST
| CT_VAARG
| CT_VAREF
| CT_VAEXPR
| CT_VAARG
| CT_VAREF
| CT_VAEXPR
;
flat_path
@@ -127,16 +126,12 @@ base_expr
| INTEGER
| bytes_expr
| NUL
| BUILTIN CONST_IDENT
| BUILTIN IDENT
| CHAR_LITERAL
| REAL
| TRUE
| FALSE
| base_expr_assignable
;
base_expr_assignable
: BUILTIN CONST_IDENT
| BUILTIN IDENT
| path ident_expr
| ident_expr
| local_ident_expr
@@ -147,7 +142,7 @@ base_expr_assignable
| expr_block
| ct_call '(' flat_path ')'
| ct_arg '(' expr ')'
| ct_analyse '(' expression_list ')'
| ct_analyse '(' expr ')'
| CT_VACOUNT
| CT_FEATURE '(' CONST_IDENT ')'
| CT_AND '(' expression_list ')'
@@ -175,6 +170,7 @@ range_expr
| DOTDOT
;
call_inline_attributes
: AT_IDENT
| call_inline_attributes AT_IDENT
@@ -207,7 +203,7 @@ call_trailing
;
call_stmt_expr
: base_expr_assignable
: base_expr
| call_stmt_expr call_trailing
;
@@ -243,7 +239,7 @@ mult_op
: '*'
| '/'
| '%'
;
;
mult_expr
: unary_expr
@@ -270,11 +266,12 @@ shift_stmt_expr
| shift_stmt_expr shift_op mult_expr
;
bit_op
: '&'
| '^'
| '|'
;
: '&'
| '^'
| '|'
;
bit_expr
: shift_expr
@@ -289,7 +286,7 @@ bit_stmt_expr
additive_op
: '+'
| '-'
;
;
additive_expr
: bit_expr
@@ -388,20 +385,19 @@ assignment_op
;
empty
: /*empty*/
:
;
assignment_expr
: ternary_expr
| CT_TYPE_IDENT '=' type
| unary_expr assignment_op assignment_expr
;
: ternary_expr
| CT_TYPE_IDENT '=' type
| unary_expr assignment_op assignment_expr
;
assignment_stmt_expr
: ternary_stmt_expr
| CT_TYPE_IDENT '=' type
| unary_stmt_expr assignment_op assignment_expr
;
: ternary_stmt_expr
| CT_TYPE_IDENT '=' type
| unary_stmt_expr assignment_op assignment_expr
;
implies_body
: IMPLIES expr
@@ -419,6 +415,7 @@ expr
: assignment_expr
;
constant_expr
: ternary_expr
;
@@ -434,8 +431,7 @@ param_path
| param_path param_path_element
;
arg
: param_path '=' expr
arg : param_path '=' expr
| type
| param_path '=' type
| expr
@@ -447,18 +443,21 @@ arg
arg_list
: arg
| arg_list ',' arg
| arg_list ','
;
opt_arg_list
: arg_list
| empty
;
call_arg_list
: opt_arg_list
| opt_arg_list ';'
| opt_arg_list ';' parameters
: arg_list
| arg_list ';'
| arg_list ';' parameters
| ';'
| ';' parameters
| empty
;
opt_arg_list_trailing
: arg_list
| arg_list ','
| empty
;
interfaces
@@ -471,11 +470,10 @@ opt_interface_impl
| '(' ')'
| empty
;
enum_constants
: enum_constant
| enum_constants ',' enum_constant
;
: enum_constant
| enum_constants ',' enum_constant
;
enum_list
: enum_constants
@@ -485,6 +483,7 @@ enum_list
enum_constant
: CONST_IDENT opt_attributes
| CONST_IDENT '(' arg_list ')' opt_attributes
| CONST_IDENT '(' arg_list ',' ')' opt_attributes
;
identifier_list
@@ -499,53 +498,53 @@ enum_param_decl
;
base_type
: VOID
| BOOL
| CHAR
| ICHAR
| SHORT
| USHORT
| INT
| UINT
| LONG
| ULONG
| INT128
| UINT128
| FLOAT
| DOUBLE
| FLOAT16
| BFLOAT16
| FLOAT128
| IPTR
| UPTR
| ISZ
| USZ
| ANYFAULT
| ANY
| TYPEID
| TYPE_IDENT opt_generic_parameters
| path TYPE_IDENT opt_generic_parameters
| CT_TYPE_IDENT
| CT_TYPEOF '(' expr ')'
| CT_TYPEFROM '(' constant_expr ')'
| CT_VATYPE '(' constant_expr ')'
| CT_EVALTYPE '(' constant_expr ')'
;
: VOID
| BOOL
| CHAR
| ICHAR
| SHORT
| USHORT
| INT
| UINT
| LONG
| ULONG
| INT128
| UINT128
| FLOAT
| DOUBLE
| FLOAT16
| BFLOAT16
| FLOAT128
| IPTR
| UPTR
| ISZ
| USZ
| ANYFAULT
| ANY
| TYPEID
| TYPE_IDENT
| path TYPE_IDENT
| CT_TYPE_IDENT
| CT_TYPEOF '(' expr ')'
| CT_TYPEFROM '(' constant_expr ')'
| CT_VATYPE '(' constant_expr ')'
| CT_EVALTYPE '(' constant_expr ')'
;
type
: base_type
| type '*'
| type '[' constant_expr ']'
| type '[' ']'
| type '[' '*' ']'
| type LVEC constant_expr RVEC
| type LVEC '*' RVEC
;
: base_type
| type '*'
| type '[' constant_expr ']'
| type '[' ']'
| type '[' '*' ']'
| type LVEC constant_expr RVEC
| type LVEC '*' RVEC
;
optional_type
: type
| type '!'
;
: type
| type '!'
;
local_decl_after_type
: CT_IDENT
@@ -574,22 +573,22 @@ var_decl
;
initializer_list
: '{' opt_arg_list '}'
: '{' opt_arg_list_trailing '}'
;
ct_case_stmt
: CT_CASE constant_expr ':' opt_stmt_list
| CT_CASE type ':' opt_stmt_list
| CT_DEFAULT ':' opt_stmt_list
;
: CT_CASE constant_expr ':' opt_stmt_list
| CT_CASE type ':' opt_stmt_list
| CT_DEFAULT ':' opt_stmt_list
;
ct_switch_body
: ct_case_stmt
| ct_switch_body ct_case_stmt
;
| ct_switch_body ct_case_stmt
;
ct_for_stmt
: CT_FOR '(' for_cond ')' opt_stmt_list CT_ENDFOR
: CT_FOR '(' for_cond ')' opt_stmt_list CT_ENDFOR
;
ct_foreach_stmt
@@ -597,10 +596,10 @@ ct_foreach_stmt
| CT_FOREACH '(' CT_IDENT ',' CT_IDENT ':' expr ')' opt_stmt_list CT_ENDFOREACH
;
ct_switch
: CT_SWITCH '(' constant_expr ')'
| CT_SWITCH '(' type ')'
| CT_SWITCH
;
: CT_SWITCH '(' constant_expr ')'
| CT_SWITCH '(' type ')'
| CT_SWITCH
;
ct_switch_stmt
: ct_switch ct_switch_body CT_ENDSWITCH
@@ -608,7 +607,6 @@ ct_switch_stmt
var_stmt
: var_decl ';'
;
decl_stmt_after_type
: local_decl_after_type
@@ -760,7 +758,7 @@ foreach_vars
foreach_stmt
: FOREACH optional_label '(' foreach_vars ':' expr ')' statement
| FOREACH_R optional_label '(' foreach_vars ':' expr ')' statement
: FOREACH_R optional_label '(' foreach_vars ':' expr ')' statement
;
defer_stmt
@@ -815,7 +813,6 @@ asm_expr
| INTEGER
| '(' expr ')'
| '[' asm_addr ']'
;
asm_exprs
: asm_expr
@@ -836,6 +833,7 @@ asm_block_stmt
| ASM AT_IDENT '{' '}'
;
/* Order here matches compiler */
statement
: compound_statement
@@ -853,15 +851,15 @@ statement
| break_stmt
| nextcase_stmt
| asm_block_stmt
| ct_echo_stmt
| ct_echo_stmt
| ct_assert_stmt
| ct_if_stmt
| ct_switch_stmt
| ct_foreach_stmt
| ct_for_stmt
| expr_no_list ';'
| assert_stmt
| ';'
| ct_if_stmt
| ct_switch_stmt
| ct_foreach_stmt
| ct_for_stmt
| expr_no_list ';'
| assert_stmt
| ';'
;
compound_statement
@@ -886,9 +884,9 @@ switch_stmt
;
expression_list
: decl_or_expr
| expression_list ',' decl_or_expr
;
: decl_or_expr
| expression_list ',' decl_or_expr
;
optional_label
: CONST_IDENT ':'
@@ -907,11 +905,9 @@ ct_include_stmt
ct_echo_stmt
: CT_ECHO constant_expr ';'
;
bitstruct_declaration
: BITSTRUCT TYPE_IDENT opt_interface_impl ':' type opt_attributes bitstruct_body
;
bitstruct_body
: '{' '}'
@@ -934,6 +930,7 @@ bitstruct_def
| base_type IDENT ':' constant_expr ';'
;
attribute_name
: AT_IDENT
| AT_TYPE_IDENT
@@ -957,9 +954,9 @@ attribute_param_list
;
attribute
: attribute_name
| attribute_name '(' attribute_param_list ')'
;
: attribute_name
| attribute_name '(' attribute_param_list ')'
;
attribute_list
: attribute
@@ -967,9 +964,9 @@ attribute_list
;
opt_attributes
: attribute_list
| empty
;
: attribute_list
| empty
;
trailing_block_param
: AT_IDENT
@@ -990,7 +987,7 @@ macro_func_body
;
macro_declaration
: MACRO macro_header '(' macro_params ')' opt_attributes macro_func_body
: MACRO macro_header '(' macro_params ')' opt_attributes macro_func_body
;
struct_or_union
@@ -1000,16 +997,16 @@ struct_or_union
struct_declaration
: struct_or_union TYPE_IDENT opt_interface_impl opt_attributes struct_body
;
;
struct_body
: '{' struct_declaration_list '}'
: '{' struct_declaration_list '}'
;
struct_declaration_list
: struct_member_decl
| struct_declaration_list struct_member_decl
;
| struct_declaration_list struct_member_decl
;
enum_params
: enum_param_decl
@@ -1023,15 +1020,16 @@ enum_param_list
;
struct_member_decl
: type identifier_list opt_attributes ';'
| struct_or_union IDENT opt_attributes struct_body
| struct_or_union opt_attributes struct_body
| BITSTRUCT ':' type opt_attributes bitstruct_body
| BITSTRUCT IDENT ':' type opt_attributes bitstruct_body
| INLINE type IDENT opt_attributes ';'
| INLINE type opt_attributes ';'
: type identifier_list opt_attributes ';'
| struct_or_union IDENT opt_attributes struct_body
| struct_or_union opt_attributes struct_body
| BITSTRUCT ':' type opt_attributes bitstruct_body
| BITSTRUCT IDENT ':' type opt_attributes bitstruct_body
| INLINE type IDENT opt_attributes ';'
| INLINE type opt_attributes ';'
;
enum_spec
: ':' type enum_param_list
| empty
@@ -1042,14 +1040,14 @@ enum_declaration
;
faults
: CONST_IDENT
| faults ',' CONST_IDENT
;
: CONST_IDENT
| faults ',' CONST_IDENT
;
fault_declaration
: FAULT TYPE_IDENT opt_interface_impl opt_attributes '{' faults '}'
| FAULT TYPE_IDENT opt_interface_impl opt_attributes '{' faults ',' '}'
;
: FAULT TYPE_IDENT opt_interface_impl opt_attributes '{' faults '}'
| FAULT TYPE_IDENT opt_interface_impl opt_attributes '{' faults ',' '}'
;
func_macro_name
: IDENT
@@ -1072,15 +1070,11 @@ fn_parameter_list
| '(' ')'
;
parameter_default
: parameter
| parameter '=' expr
;
parameters
: parameter_default
| parameters ',' parameter_default
| parameters ','
: parameter '=' expr
| parameter
| parameters ',' parameter
| parameters ',' parameter '=' expr
;
parameter
@@ -1088,7 +1082,7 @@ parameter
| type ELLIPSIS IDENT opt_attributes
| type ELLIPSIS CT_IDENT
| type CT_IDENT
| type ELLIPSIS opt_attributes
| type ELLIPSIS opt_attributes
| type HASH_IDENT opt_attributes
| type '&' IDENT opt_attributes
| type opt_attributes
@@ -1101,24 +1095,19 @@ parameter
| CT_IDENT ELLIPSIS
;
func_defintion_decl
: FN func_header fn_parameter_list opt_attributes ';'
;
func_definition
: func_defintion_decl
: FN func_header fn_parameter_list opt_attributes ';'
| FN func_header fn_parameter_list opt_attributes macro_func_body
;
const_declaration
: CONST CONST_IDENT opt_attributes '=' expr ';'
| CONST type CONST_IDENT opt_attributes '=' expr ';'
| CONST type CONST_IDENT opt_attributes ';'
;
func_typedef
: FN optional_type fn_parameter_list
;
: FN optional_type fn_parameter_list
;
opt_inline
: INLINE
@@ -1134,9 +1123,11 @@ generic_parameters
typedef_type
: func_typedef
| type
| type opt_generic_parameters
;
multi_declaration
: ',' IDENT
| multi_declaration ',' IDENT
@@ -1148,11 +1139,27 @@ global_storage
;
global_declaration
: global_storage optional_type IDENT opt_attributes ';'
| global_storage optional_type IDENT multi_declaration opt_attributes ';'
| global_storage optional_type IDENT opt_attributes '=' expr ';'
: global_storage optional_type IDENT opt_attributes ';'
| global_storage optional_type IDENT multi_declaration opt_attributes ';'
| global_storage optional_type IDENT opt_attributes '=' expr ';'
;
opt_tl_stmts
: top_level_statements
| empty
;
tl_ct_case
: CT_CASE constant_expr ':' opt_tl_stmts
| CT_CASE type ':' opt_tl_stmts
| CT_DEFAULT ':' opt_tl_stmts
;
tl_ct_switch_body
: tl_ct_case
| tl_ct_switch_body tl_ct_case
;
define_attribute
: AT_TYPE_IDENT '(' parameters ')' opt_attributes '=' '{' opt_attributes '}'
| AT_TYPE_IDENT opt_attributes '=' '{' opt_attributes '}'
@@ -1167,21 +1174,23 @@ opt_generic_parameters
| empty
;
define_ident
: IDENT '=' path_ident opt_generic_parameters
| CONST_IDENT '=' path_const opt_generic_parameters
| AT_IDENT '=' path_at_ident opt_generic_parameters
;
;
define_declaration
: DEF define_ident opt_attributes ';'
| DEF define_attribute opt_attributes ';'
| DEF define_attribute opt_attributes';'
| DEF TYPE_IDENT opt_attributes '=' typedef_type opt_attributes ';'
;
interface_body
: func_defintion_decl
| interface_body func_defintion_decl
: func_typedef
| interface_body func_typedef
;
interface_declaration
@@ -1190,18 +1199,31 @@ interface_declaration
;
distinct_declaration
: DISTINCT TYPE_IDENT opt_interface_impl opt_attributes '=' opt_inline type ';'
: DISTINCT TYPE_IDENT opt_interface_impl opt_attributes '=' opt_inline type opt_generic_parameters ';'
;
tl_ct_if
: CT_IF constant_expr ':' opt_tl_stmts tl_ct_if_tail
;
tl_ct_if_tail
: CT_ENDIF
| CT_ELSE opt_tl_stmts CT_ENDIF
;
tl_ct_switch
: ct_switch tl_ct_switch_body CT_ENDSWITCH
;
module_param
: CONST_IDENT
| TYPE_IDENT
;
: CONST_IDENT
| TYPE_IDENT
;
module_params
: module_param
| module_params ',' module_param
;
| module_params ',' module_param
;
module
: MODULE path_ident opt_attributes ';'
@@ -1214,18 +1236,18 @@ import_paths
;
import_decl
: IMPORT import_paths opt_attributes ';'
;
: IMPORT import_paths opt_attributes ';'
;
translation_unit
: top_level_statements
| empty
;
: top_level_statements
| empty
;
top_level_statements
: top_level
| top_level_statements top_level
;
: top_level
| top_level_statements top_level
;
opt_extern
: EXTERN
@@ -1241,6 +1263,8 @@ top_level
| ct_assert_stmt
| ct_echo_stmt
| ct_include_stmt
| tl_ct_if
| tl_ct_switch
| struct_declaration
| fault_declaration
| enum_declaration
@@ -1251,17 +1275,17 @@ top_level
| interface_declaration
;
%%
void yyerror(const char *s)
void yyerror(char *s)
{
fflush(stdout);
printf(":%d:%d:\n%*s\n%*s\n", yylineno, column, column, "^", column, s);
printf("\n%*s\n%*s\n", column, "^", column, s);
}
int main(int argc, char *argv[])
{
int rc = yyparse();
printf(" -> yyparse return %d\n", rc);
return rc;
}
yyparse();
return 0;
}

View File

@@ -9,7 +9,7 @@ enum Foo
fn void print_pages()
{
allocator::temp().print_pages(io::stdout())!!;
mem::temp().print_pages(io::stdout())!!;
}
fn void setstring(char* dst, String str)
@@ -24,16 +24,16 @@ fn void setstring(char* dst, String str)
fn void testAllocator(Allocator* a, int val)
{
io::printn("Test");
void* data = allocator::malloc_aligned(a, val, 128, 16)!!;
void* data = a.alloc_aligned(val, 128, 16)!!;
io::printf("Aligned with offset %p, align 16: %s offset align 128: %s\n", data, mem::ptr_is_aligned(data, 16), mem::ptr_is_aligned(data + 16, 128));
data = allocator::calloc_aligned(a, val, 128, 16)!!;
data = a.calloc_aligned(val, 128, 16)!!;
io::printf("Aligned with offset %p, align 16: %s offset align 128: %s\n", data, mem::ptr_is_aligned(data, 16), mem::ptr_is_aligned(data + 16, 128));
data = allocator::realloc_aligned(a, data, (usz)val + 1, 128, 16)!!;
data = a.realloc_aligned(data, (usz)val + 1, 128, 16)!!;
io::printf("Aligned with offset %p, align 16: %s offset align 128: %s\n", data, mem::ptr_is_aligned(data, 16), mem::ptr_is_aligned(data + 16, 128));
data = allocator::realloc_aligned(a, data, (usz)val + 1, 128, 0)!!;
data = a.realloc_aligned(data, (usz)val + 1, 128, 0)!!;
io::printf("No offset %p, align 16: %s offset align 128: %s\n", data, mem::ptr_is_aligned(data, 16), mem::ptr_is_aligned(data + 16, 128));
io::printfn("Freeing %p", data);
allocator::free_aligned(a, data);
a.free_aligned(data);
}
fn void main()
{
@@ -63,7 +63,7 @@ fn void main()
io::printf("First big: %p\n", first_big);
print_pages();
};
mem::@scoped(allocator::temp())
mem::@scoped(mem::temp())
{
io::printf("Malloc: %p\n", (void*)malloc(23));
io::printf("Malloc: %p\n", (void*)malloc(23));
@@ -73,14 +73,14 @@ fn void main()
{
io::printf("Talloc: %p\n", (void*)tmalloc(22));
};
testAllocator(allocator::temp(), 126);
testAllocator(allocator::temp(), 12600);
testAllocator(mem::temp(), 126);
testAllocator(mem::temp(), 12600);
ArenaAllocator aa;
aa.init(&&char[1024] {});
testAllocator(&aa, 126);
io::printn("Test dynamic arena");
DynamicArenaAllocator dynamic_arena;
dynamic_arena.init(1024, allocator::heap());
dynamic_arena.init(1024, mem::heap());
testAllocator(&dynamic_arena, 112);
testAllocator(&dynamic_arena, 712);
first_big[3] = 123;

Some files were not shown because too many files have changed in this diff Show More