Compare commits

...

40 Commits

Author SHA1 Message Date
Christoffer Lerno
d8af01dc46 Update release notes and change how versions are reported. 2024-03-18 11:51:31 +01:00
David Gonzalez Martin
5bead069f2 Fix aarch64 return type ABI bug 2024-03-17 20:06:53 +01:00
shv187
63e1345780 Add getModuleHandleA + W to win32 2024-03-16 09:47:42 +01:00
Christoffer Lerno
3df988d0b8 Allow String constants -> ichar*, and allow integer pointers to explicitly convert between unsigned signed. 2024-03-15 23:08:52 +01:00
Christoffer Lerno
656202dc0d Convert paths to backslash before running on Windows. 2024-03-15 20:09:06 +01:00
Christoffer Lerno
eec3253669 Mingw -> 18.1.1-3 2024-03-15 13:23:41 +01:00
Christoffer Lerno
d9423201b8 Change mingw version. 2024-03-15 13:20:05 +01:00
Christoffer Lerno
c6087bc369 Fix underlying type of llvm.used and update section. 2024-03-15 13:02:39 +01:00
Christoffer Lerno
b7077c7967 Fix Win32 with compile-run. 2024-03-14 12:05:01 +01:00
Christoffer Lerno
4acb07f1cb compile-run and run now returns the proper return code. 2024-03-14 11:55:55 +01:00
Christoffer Lerno
5207022a4a For MacOS, running with higher optimization would crash as initializers were removed. 2024-03-14 09:33:24 +01:00
Christoffer Lerno
1a25746343 Regression: no stacktrace. 2024-03-12 17:31:06 +01:00
Christoffer Lerno
0d7ceb625b Fixed link on msvc. 2024-03-12 10:24:48 +01:00
Christoffer Lerno
95fb5f904f New linker build option. "system-linker" deprecated and removed from project settings. 2024-03-12 10:09:02 +01:00
Christoffer Lerno
a0309855d7 Added @link attribute. 2024-03-11 18:10:40 +01:00
Christoffer Lerno
546754e803 'output' directory for projects was incorrect in templates. 2024-03-08 15:34:04 +01:00
Christoffer Lerno
86461909d3 Remove initial './' in Win32 paths when running a binary. 2024-03-04 17:04:57 +01:00
Christoffer Lerno
feebd2a733 Bug in time.add_seconds #1162. 2024-03-01 12:09:47 +01:00
Christoffer Lerno
75e7176675 Bitstruct cast to other bitstruct by way of underlying type would fail #1159. 2024-02-26 23:51:14 +01:00
Christoffer Lerno
bae5d9c7f8 Improved checks of aliased "void". 2024-02-26 18:45:55 +01:00
Christoffer Lerno
4ba033fc84 Fix of int.min incorrect behaviour #1154. 2024-02-26 18:13:40 +01:00
Christoffer Lerno
f0dd0e8f92 Fix of CT named arguments #1156. 2024-02-26 17:47:50 +01:00
Christoffer Lerno
7ea3d230bb 0.5.5 features (#1151)
0.5.5 Disallow multiple `_` in a row in digits, e.g. `1__000`. #1138. Fixed toposort example. Struct/union members now correctly rejects members without storage size #1147. `math::pow` will now correctly promote integer arguments. `math::pow` will now correctly promote integer arguments. Added `new_aligned` and `alloc_aligned` functions to prevent accidental under-alignment when allocating simd. Pointer difference would fail where alignment != size (structs etc) #1150. Add test that overalignment actually works for lists. Fixed array calculation for npot2 vectors. Use native aligned alloc on Windows and POSIX. Deprecates "offset". Simplification of the Allocator interface.
2024-02-22 17:13:51 +01:00
Christoffer Lerno
b7f4fd9074 Create FUNDING.yml 2024-02-20 09:27:21 +01:00
Christoffer Lerno
9a114b38d3 Updated retry and test.c3 examples. 2024-02-17 15:19:27 +01:00
Christoffer Lerno
bec1116f86 Fixes to scoped mem report. 2024-02-17 11:52:18 +01:00
Christoffer Lerno
d7cc37b951 Important fixes to project settings. 2024-02-17 11:39:08 +01:00
Christoffer Lerno
4ce62cf221 Fix of allocator::new. 2024-02-16 21:55:30 +01:00
Christoffer Lerno
1f052da0b9 Clock.c3 fix. 2024-02-16 21:31:49 +01:00
Christoffer Lerno
812dc0c292 Update memory test code. 2024-02-16 14:19:42 +01:00
Christoffer Lerno
798fe0dce9 Updated lex file. 2024-02-16 12:01:13 +01:00
Christoffer Lerno
3f6fe55f9a Grammar fix. 2024-02-15 23:18:11 +01:00
Christoffer Lerno
aee4aecfe7 Remove install_win_reqs.bat from releases. 2024-02-15 22:41:44 +01:00
Christoffer Lerno
748c737e8f 0.5.4: Hash variables accept designated initializers. @safemacro overrides the need for @ in macro names. Fixes to macro context evaluation. Updated allocator api. Removed install_win_reqs.bat. Deterministic @init for MacOS. Fixed temp memory issue with formatter. Support LLVM 19. Add support to compare bitstructs using == and !=. Support Windows .def files. Removed invalid grammar from grammar.y. Support compile time folding of &|^~ for bitstructs. output project setting now respected. Fix issue where constants were not properly constant folded. Add temp_push/pop. Aliased declarations caused errors when used in initializers. Fix export output. Fix of const ternary #1118. Fix of $$MODULE in nested macros #1117. Fix debug info on globals. out now correctly detects subscript[] use #1116. Lateral implicit imports removed. Default to '.' if no libdir is specified. Improved error messages for --lib. Fix raylib snake example. Overzealous local escape check corrected #1127. Improved yacc grammar #1128. --linker argument #1067. Fixes to the matrix operations #1130. Added GenericList. 2024-02-15 21:39:33 +01:00
Christoffer Lerno
c673101bbb Fix incorrect code in sample. 2024-02-14 09:16:36 +01:00
Poly2it
d66674655c Update compilation instructions for Void Linux 2024-02-13 16:45:51 +01:00
Christian Clauss
da292e41bd msvc_build_libraries.py: Remove unused import an f-strings with no placeholder
% `ruff ` # https://docs.astral.sh/ruff
```
 Error: msvc_build_libraries.py:9:8: F401 `os` imported but unused
Error: msvc_build_libraries.py:10:8: F401 `sys` imported but unused
Error: msvc_build_libraries.py:19:8: F401 `re` imported but unused
Error: msvc_build_libraries.py:179:3: F541 f-string without any placeholders
Error: msvc_build_libraries.py:180:3: F541 f-string without any placeholders
Error: msvc_build_libraries.py:182:3: F541 f-string without any placeholders
Error: Process completed with exit code 1.
```
2024-01-18 11:24:03 +01:00
Christoffer Lerno
deb4cc7c4b 0.5.3: Single-module not respected. Fix issue with compiler defined types. Fix optimization levels for projects. Use GEP i8 on offsets. Optimize foreach on len 1 arrays. Move panic blocks last. Fix generic module wildcard imports. Deprecate init_temp / init_new. Fix issue with macro vaarg and untyped lists. Fix extern const globals. 2024-01-14 15:34:54 +01:00
Christoffer Lerno
e91f6e268e 0.5.2: Allow trailing comma in calls and parameter declarations #1092. Fixes issue where single character filenames like 'a.c3' would be rejected. Improve error messages for incorrect user defined foreach. Fix bug with generics in generics. Fix to error with modified vector parameters. Crash with lhs vector inference. Fixes to priority queue. 2023-12-23 23:15:51 +01:00
Poly2it
2595ed5cc9 Add compilation instructions for Void Linux 2023-12-09 15:15:17 +01:00
358 changed files with 14886 additions and 11495 deletions

14
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
# 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,6 +61,14 @@ 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
@@ -71,6 +79,11 @@ 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:
@@ -99,8 +112,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-17.0.6-1-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-17.0.6-1-any.pkg.tar.zst
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
- name: CMake
run: |
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
@@ -193,7 +206,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [15, 16, 17, 18]
llvm_version: [15, 16, 17, 18, 19]
steps:
- uses: actions/checkout@v4
@@ -207,7 +220,7 @@ jobs:
if [[ "${{matrix.llvm_version}}" < 16 ]]; then
sudo apt remove libllvm15
fi
if [[ "${{matrix.llvm_version}}" < 18 ]]; then
if [[ "${{matrix.llvm_version}}" < 19 ]]; 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 \
@@ -222,6 +235,7 @@ 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 \
@@ -234,10 +248,24 @@ 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
@@ -257,7 +285,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
@@ -418,7 +446,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [15, 16]
llvm_version: [15, 16, 17]
steps:
- uses: actions/checkout@v4
- name: Download LLVM
@@ -523,8 +551,6 @@ 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 18)
if (${C3_LLVM_VERSION} VERSION_LESS 15 OR ${C3_LLVM_VERSION} VERSION_GREATER 19)
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,6 +319,21 @@ 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.

View File

@@ -1,17 +0,0 @@
@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,15 +86,29 @@ struct GrowableBitSet
* @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 = mem::heap())
fn GrowableBitSet* GrowableBitSet.new_init(&self, usz initial_capacity = 1, Allocator* allocator = allocator::heap())
{
self.data.init_new(initial_capacity, allocator);
self.data.new_init(initial_capacity, allocator);
return self;
}
fn GrowableBitSet* GrowableBitSet.init_temp(&self, usz initial_capacity = 1)
/**
* @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")
{
return self.init_new(initial_capacity, mem::temp()) @inline;
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);
}
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 = mem::heap()) @dynamic
fn String EnumMap.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-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.
@@ -6,8 +6,9 @@
* @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;
@@ -140,7 +141,7 @@ fn usz! EnumSet.to_format(&set, Formatter* formatter) @dynamic
return n;
}
fn String EnumSet.to_new_string(&set, Allocator* allocator = mem::heap()) @dynamic
fn String EnumSet.to_new_string(&set, Allocator* allocator = allocator::heap()) @dynamic
{
return string::new_format("%s", *set, .allocator = allocator);
}

View File

@@ -0,0 +1,467 @@
// 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 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-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::linkedlist(<Type>);
@@ -32,15 +32,29 @@ 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.init_new(&self, Allocator* allocator = mem::heap())
fn LinkedList* LinkedList.new_init(&self, Allocator* allocator = allocator::heap())
{
*self = { .allocator = allocator };
return self;
}
fn LinkedList* LinkedList.init_temp(&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")
{
return self.init_new(mem::temp()) @inline;
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;
}
/**
@@ -48,12 +62,13 @@ fn LinkedList* LinkedList.init_temp(&self)
**/
macro void LinkedList.free_node(&self, Node* node) @private
{
self.allocator.free(node);
allocator::free(self.allocator, node);
}
macro Node* LinkedList.alloc_node(&self) @private
{
if (!self.allocator) self.allocator = mem::heap();
return self.allocator.new(Node);
if (!self.allocator) self.allocator = allocator::heap();
return allocator::alloc(self.allocator, Node);
}
fn void LinkedList.link_first(&self, Type value) @private

View File

@@ -1,9 +1,8 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-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::list(<Type>);
import std::io;
import std::math;
import std::io,std::math;
def ElementPredicate = fn bool(Type *type);
def ElementTest = fn bool(Type *type, any* context);
@@ -18,18 +17,19 @@ 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.init_new(&self, usz initial_capacity = 16, Allocator* allocator = mem::heap())
fn List* List.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_aligned(Type.sizeof * initial_capacity, .alignment = Type[1].alignof)!!;
self.entries = allocator::malloc_aligned(allocator, Type.sizeof * initial_capacity, .alignment = Type[1].alignof)!!;
}
else
{
@@ -39,20 +39,39 @@ fn List* List.init_new(&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.init_temp(&self, usz initial_capacity = 16)
fn List* List.temp_init(&self, usz initial_capacity = 16)
{
return self.init_new(initial_capacity, mem::temp()) @inline;
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;
}
/**
* @require self.size == 0 "The List must be empty"
**/
fn void List.init_wrapping_array(&self, Type[] types, Allocator* allocator = mem::heap())
fn void List.init_wrapping_array(&self, Type[] types, Allocator* allocator = allocator::heap())
{
self.allocator = allocator;
self.size = types.len;
@@ -80,7 +99,7 @@ fn usz! List.to_format(&self, Formatter* formatter) @dynamic
}
}
fn String List.to_new_string(&self, Allocator* allocator = mem::heap()) @dynamic
fn String List.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
{
return string::new_format("%s", *self, .allocator = allocator);
}
@@ -129,11 +148,8 @@ fn Type List.pop_first(&self)
**/
fn void List.remove_at(&self, usz index)
{
for (usz i = index + 1; i < self.size; i++)
{
self.entries[i - 1] = self.entries[i];
}
self.size--;
if (!--self.size || index == self.size) return;
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
}
fn void List.add_all(&self, List* other_list)
@@ -147,17 +163,17 @@ fn void List.add_all(&self, List* other_list)
}
fn Type[] List.to_new_array(&self, Allocator* allocator = mem::heap())
fn Type[] List.to_new_array(&self, Allocator* allocator = allocator::heap())
{
if (!self.size) return Type[] {};
Type[] result = allocator.new_array(Type, self.size);
Type[] result = allocator::alloc_array(allocator, Type, self.size);
result[..] = self.entries[:self.size];
return result;
}
fn Type[] List.to_tarray(&self)
{
return self.to_new_array(mem::temp());
return self.to_new_array(allocator::temp());
}
/**
@@ -247,6 +263,11 @@ 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;
@@ -260,7 +281,7 @@ fn Type List.get(&self, usz index) @inline
fn void List.free(&self)
{
if (!self.allocator) return;
self.allocator.free_aligned(self.entries);
allocator::free_aligned(self.allocator, self.entries);
self.capacity = 0;
self.size = 0;
self.entries = null;
@@ -356,9 +377,9 @@ fn void List.reserve(&self, usz min_capacity)
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = mem::heap();
if (!self.allocator) self.allocator = allocator::heap();
min_capacity = math::next_power_of_2(min_capacity);
self.entries = self.allocator.realloc_aligned(self.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof) ?? null;
self.entries = allocator::realloc_aligned(self.allocator, self.entries, Type.sizeof * min_capacity, .alignment = Type[1].alignof) ?? null;
self.capacity = min_capacity;
}

View File

@@ -19,6 +19,23 @@ struct HashMap
float load_factor;
}
/**
* @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 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())
{
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"
@@ -26,14 +43,20 @@ struct HashMap
* @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 = mem::heap())
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")
{
capacity = math::next_power_of_2(capacity);
map.allocator = allocator;
map.load_factor = load_factor;
map.threshold = (uint)(capacity * load_factor);
map.table = allocator.new_zero_array(Entry*, capacity);
return map;
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;
}
/**
@@ -42,9 +65,9 @@ fn HashMap* HashMap.init_new(&map, uint capacity = DEFAULT_INITIAL_CAPACITY, flo
* @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)
fn HashMap* HashMap.init_temp(&map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) @deprecated("Replaced by temp_init")
{
return map.init_new(capacity, load_factor, mem::temp());
return map.temp_init(capacity, load_factor) @inline;
}
/**
@@ -62,21 +85,40 @@ 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.init_new_from_map(&self, HashMap* other_map, Allocator* allocator = mem::heap())
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map, Allocator* allocator = allocator::heap())
{
self.init_new(other_map.table.len, other_map.load_factor, allocator);
self.new_init(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_temp_from_map(&map, HashMap* other_map)
fn HashMap* HashMap.init_new_from_map(&self, HashMap* other_map, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init_from_map")
{
return map.init_new_from_map(other_map, mem::temp()) @inline;
return self.new_init_from_map(other_map, allocator) @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;
@@ -111,6 +153,7 @@ 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)
{
@@ -146,7 +189,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.init_new();
map.new_init();
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
@@ -197,14 +240,14 @@ fn void HashMap.free(&map)
fn Key[] HashMap.key_tlist(&map)
{
return map.key_new_list(mem::temp()) @inline;
return map.key_new_list(allocator::temp()) @inline;
}
fn Key[] HashMap.key_new_list(&map, Allocator* allocator = mem::heap())
fn Key[] HashMap.key_new_list(&map, Allocator* allocator = allocator::heap())
{
if (!map.count) return {};
Key[] list = allocator.new_array(Key, map.count);
Key[] list = allocator::alloc_array(allocator, Key, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
@@ -241,13 +284,13 @@ macro HashMap.@each_entry(map; @body(entry))
fn Value[] HashMap.value_tlist(&map)
{
return map.value_new_list(mem::temp()) @inline;
return map.value_new_list(allocator::temp()) @inline;
}
fn Value[] HashMap.value_new_list(&map, Allocator* allocator = mem::heap())
fn Value[] HashMap.value_new_list(&map, Allocator* allocator = allocator::heap())
{
if (!map.count) return {};
Value[] list = allocator.new_array(Value, map.count);
Value[] list = allocator::alloc_array(allocator, Value, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
@@ -278,11 +321,10 @@ 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 = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
if (map.count++ >= map.threshold)
{
@@ -299,7 +341,7 @@ fn void HashMap.resize(&map, uint new_capacity) @private
map.threshold = uint.max;
return;
}
Entry*[] new_table = map.allocator.new_zero_array(Entry*, new_capacity);
Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity);
map.transfer(new_table);
map.table = new_table;
map.free_internal(old_table.ptr);
@@ -363,7 +405,7 @@ fn void HashMap.put_for_create(&map, Key key, Value value) @private
fn void HashMap.free_internal(&map, void* ptr) @inline @private
{
map.allocator.free(ptr);
allocator::free(map.allocator, ptr);
}
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
@@ -398,11 +440,10 @@ 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 = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
map.count++;
}
@@ -410,7 +451,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:
self.allocator.free(entry.key);
allocator::free(self.allocator, entry.key);
$endif
self.free_internal(entry);
}

View File

@@ -2,9 +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::collections::object;
import std::collections::map;
import std::collections::list;
import std::io;
import std::collections::map, std::collections::list, std::io;
const Object TRUE_OBJECT = { .b = true, .type = bool.typeid };
const Object FALSE_OBJECT = { .b = false, .type = bool.typeid };
@@ -80,9 +78,7 @@ fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
fn Object* new_obj(Allocator* allocator)
{
Object* o = allocator.new(Object);
*o = { .allocator = allocator, .type = void.typeid };
return o;
return allocator::new(allocator, Object, { .allocator = allocator, .type = void.typeid });
}
fn Object* new_null()
@@ -92,30 +88,22 @@ fn Object* new_null()
fn Object* new_int(int128 i, Allocator* allocator)
{
Object* o = allocator.new(Object);
*o = { .i = i, .allocator = allocator, .type = int128.typeid };
return o;
return allocator::new(allocator, Object, { .i = i, .allocator = allocator, .type = int128.typeid });
}
macro Object* new_enum(e, Allocator* allocator)
{
Object* o = allocator.new(Object);
*o = { .i = (int128)e, .allocator = allocator, .type = @typeid(e) };
return o;
return allocator::new(allocator, Object, { .i = (int128)e, .allocator = allocator, .type = @typeid(e) });
}
fn Object* new_float(double f, Allocator* allocator)
{
Object* o = allocator.new(Object);
*o = { .f = f, .allocator = allocator, .type = double.typeid };
return o;
return allocator::new(allocator, Object, { .f = f, .allocator = allocator, .type = double.typeid });
}
fn Object* new_string(String s, Allocator* allocator)
{
Object* o = allocator.new(Object);
*o = { .s = s.copy(allocator), .allocator = allocator, .type = String.typeid };
return o;
return allocator::new(allocator, Object, { .s = s.copy(allocator), .allocator = allocator, .type = String.typeid });
}
@@ -131,7 +119,7 @@ fn void Object.free(&self)
case void:
break;
case String:
self.allocator.free(self.s);
allocator::free(self.allocator, self.s);
case ObjectInternalList:
foreach (ol : self.array)
{
@@ -140,13 +128,13 @@ fn void Object.free(&self)
self.array.free();
case ObjectInternalMap:
self.map.@each_entry(; ObjectInternalMapEntry* entry) {
self.allocator.free(entry.key);
allocator::free(self.allocator, entry.key);
entry.value.free();
};
default:
break;
}
if (self.allocator) self.allocator.free(self);
if (self.allocator) allocator::free(self.allocator, self);
}
fn bool Object.is_null(&self) @inline => self == &NULL_OBJECT;
@@ -168,7 +156,7 @@ fn void Object.init_map_if_needed(&self) @private
if (self.is_empty())
{
self.type = ObjectInternalMap.typeid;
self.map.init_new(.allocator = self.allocator);
self.map.new_init(.allocator = self.allocator);
}
}
@@ -180,7 +168,7 @@ fn void Object.init_array_if_needed(&self) @private
if (self.is_empty())
{
self.type = ObjectInternalList.typeid;
self.array.init_new(.allocator = self.allocator);
self.array.new_init(.allocator = self.allocator);
}
}
@@ -193,7 +181,7 @@ fn void Object.set_object(&self, String key, Object* new_object) @private
ObjectInternalMapEntry*! entry = self.map.get_entry(key);
defer
{
(void)self.allocator.free(entry.key);
(void)allocator::free(self.allocator, 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;
import std::collections::list, std::io;
def Heap = List(<Type>);
@@ -36,14 +36,24 @@ struct PrivatePriorityQueue (Printable)
Heap heap;
}
fn void PrivatePriorityQueue.init_new(&self, usz initial_capacity = 16, Allocator* allocator = mem::heap()) @inline
fn void PrivatePriorityQueue.init_new(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @inline @deprecated("Replaced by new_init")
{
self.heap.init_new(initial_capacity, allocator);
return self.new_init(initial_capacity, allocator);
}
fn void PrivatePriorityQueue.init_temp(&self, usz initial_capacity = 16) @inline
fn void PrivatePriorityQueue.new_init(&self, usz initial_capacity = 16, Allocator* allocator = allocator::heap()) @inline
{
self.heap.init_new(initial_capacity, mem::temp()) @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;
}
fn void PrivatePriorityQueue.push(&self, Type element)
@@ -74,29 +84,32 @@ fn Type! PrivatePriorityQueue.pop(&self)
usz i = 0;
usz len = self.heap.len();
if (!len) return IteratorResult.NO_MORE_ELEMENT?;
usz newCount = len - 1;
self.heap.swap(0, newCount);
while ((2 * i + 1) < newCount)
usz new_count = len - 1;
self.heap.swap(0, new_count);
while OUTER: ((2 * i + 1) < new_count)
{
usz j = 2 * i + 1;
Type itemj = self.heap[j];
if ((j + 1) < newCount)
{
Type nextj = self.heap[j + 1];
$if MAX:
bool ok = greater(nextj, itemj);
$else
bool ok = less(nextj, itemj);
$endif
if (ok) j++;
}
Type left = self.heap[j];
Type item = self.heap[i];
$if MAX:
bool ok = less(item, itemj);
$else
bool ok = greater(item, itemj);
$endif
if (!ok) break;
switch
{
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
}
self.heap.swap(i, j);
i = j;
}
@@ -138,7 +151,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 = mem::heap()) @dynamic
fn String PrivatePriorityQueue.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
{
return self.heap.to_new_string(allocator);
}

View File

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

View File

@@ -2,6 +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::mem::allocator;
import std::math;
struct ArenaAllocator (Allocator)
{
@@ -29,9 +30,11 @@ 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.
@@ -40,29 +43,26 @@ 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 offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
* @require size > 0
**/
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 + 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;
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;
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,21 +72,11 @@ 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 offset <= mem::MAX_MEMORY_ALIGNMENT `offset too big`
* @require offset <= size && offset >= 0
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
* @require old_pointer != null
* @require size > 0
**/
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;
@@ -94,7 +84,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 + offset, alignment))
if (&self.data[self.used] == old_pointer + old_size && mem::ptr_is_aligned(old_pointer, alignment))
{
if (old_size >= size)
{
@@ -110,7 +100,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, offset)!;
void* mem = self.acquire(size, false, alignment, 0)!;
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}

View File

@@ -1,7 +1,8 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-2024 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)
{
@@ -29,14 +30,14 @@ fn void DynamicArenaAllocator.free(&self)
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
self.backing_allocator.free(page);
allocator::free(self.backing_allocator, page);
page = next_page;
}
page = self.unused_page;
while (page)
{
DynamicArenaPage* next_page = page.prev_arena;
self.backing_allocator.free(page);
allocator::free(self.backing_allocator, page);
page = next_page;
}
self.page = null;
@@ -58,11 +59,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)
{
@@ -72,19 +73,12 @@ 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;
@@ -108,7 +102,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, offset)!;
void* new_mem = self.acquire(size, false, alignment, 0)!;
mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
return new_mem;
}
@@ -134,21 +128,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, usz offset) @local
fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @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 + offset, alignment) - offset);
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof, alignment));
// Grab the page without alignment (we do it ourselves)
void* mem = self.backing_allocator.alloc_checked(page_size)!;
DynamicArenaPage*! page = self.backing_allocator.new(DynamicArenaPage);
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
DynamicArenaPage*! page = allocator::new_try(self.backing_allocator, DynamicArenaPage);
if (catch err = page)
{
self.backing_allocator.free(mem);
allocator::free(self.backing_allocator, mem);
return err?;
}
page.memory = mem;
void* mem_start = mem::aligned_pointer(mem + offset + DynamicArenaChunk.sizeof, alignment) - offset;
void* mem_start = mem::aligned_pointer(mem + DynamicArenaChunk.sizeof, alignment);
assert(mem_start + size < mem + page_size);
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1;
chunk.size = size;
@@ -161,11 +155,11 @@ fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment, usz o
}
/**
* @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 = {|
@@ -175,14 +169,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, offset);
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
if (!page) return self._alloc_new(size, alignment);
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
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 + offset, alignment) - offset;
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
new_used = start + size - page.memory;
if (page.total >= new_used)
{
@@ -192,7 +186,7 @@ fn void*! DynamicArenaAllocator.acquire(&self, usz size, bool clear, usz alignme
break ALLOCATE_NEW;
}
}
return self._alloc_new(size, alignment, offset);
return self._alloc_new(size, alignment);
}
page.used = new_used;
assert(start + size == page.memory + page.used);

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2021-2023 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-2024 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,27 +23,17 @@ 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_calloc(self._calloc, size, alignment, offset) : self._calloc(size);
return alignment > 0 ? @aligned_alloc(self._calloc, size, alignment) : self._calloc(size);
}
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment, offset) : self._alloc(size);
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : 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, offset)
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment)
: self._realloc(old_pointer, size);
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-2024 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,22 +6,36 @@ 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 = alignment ? @aligned_calloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment, offset)!! : libc::calloc(bytes, 1);
return data ?: AllocationFailure.OUT_OF_MEMORY?;
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 = alignment ? @aligned_alloc(libc::malloc, bytes, alignment, offset)!! : libc::malloc(bytes);
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
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
@@ -31,7 +45,6 @@ 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);
@@ -39,11 +52,101 @@ fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignmen
}
if (!old_ptr)
{
return self.acquire(new_bytes, true, alignment, offset);
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)
{
void* data = @aligned_realloc(fn void*(usz bytes) => libc::calloc(bytes, 1), libc::free, old_ptr, new_bytes, alignment, offset)!!;
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);
return data ?: AllocationFailure.OUT_OF_MEMORY?;
}
else
{
void* data = alignment ? @aligned_alloc(libc::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)
{
void* data = @aligned_realloc(fn void*(usz bytes) => libc::malloc(bytes), libc::free, old_ptr, new_bytes, alignment)!!;
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)
{
self.backing_allocator.free_aligned(chunk.data);
allocator::free_aligned(self.backing_allocator, chunk.data);
}
else
{
self.backing_allocator.free(chunk.data);
allocator::free(self.backing_allocator, chunk.data);
}
void* old = chunk;
chunk = chunk.prev;
self.backing_allocator.free(old);
allocator::free(self.backing_allocator, old);
}
self.chunk = null;
self.used = 0;
@@ -54,9 +54,11 @@ 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);
@@ -76,7 +78,7 @@ fn void on_stack_allocator_remove_chunk(OnStackAllocator* a, void* ptr) @local
if (chunk.data == ptr)
{
*addr = chunk.prev;
a.backing_allocator.free(chunk);
allocator::free(a.backing_allocator, chunk);
return;
}
addr = &chunk.prev;
@@ -98,56 +100,49 @@ 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 offset) @dynamic
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz deprecated) @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, offset)!;
return chunk.data = self.backing_allocator.resize(old_pointer, size, alignment, 0)!;
}
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
usz old_size = header.size;
void* mem = self.acquire(size, true, alignment, offset)!;
void* mem = self.acquire(size, false, alignment, 0)!;
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 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
* @require size > 0
**/
fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, usz offset) @dynamic
fn void*! OnStackAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @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 + 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;
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;
Allocator* backing_allocator = self.backing_allocator;
if (end > total_len)
{
OnStackAllocatorExtraChunk* chunk = backing_allocator.alloc_checked(OnStackAllocatorExtraChunk.sizeof)!;
defer catch backing_allocator.free(chunk);
OnStackAllocatorExtraChunk* chunk = allocator::alloc_try(backing_allocator, OnStackAllocatorExtraChunk)!;
defer catch allocator::free(backing_allocator, chunk);
defer try self.chunk = chunk;
*chunk = { .prev = self.chunk, .is_aligned = aligned };
return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, offset)!;
return chunk.data = backing_allocator.acquire(size, clear, aligned ? alignment : 0, 0)!;
}
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;
import std::io, std::math;
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(usz size, Allocator* allocator)
fn TempAllocator*! new_temp_allocator(usz size, Allocator* allocator)
{
TempAllocator* temp = allocator.alloc_checked(TempAllocator.sizeof + size)!;
TempAllocator* temp = allocator::alloc_with_padding(allocator, TempAllocator, size)!;
temp.last_page = null;
temp.backing_allocator = allocator;
temp.used = 0;
@@ -45,6 +45,11 @@ fn TempAllocator*! new_temp(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
@@ -71,11 +76,10 @@ fn void TempAllocator.reset(&self, usz mark) @dynamic
fn void! TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local
{
void* mem = page.start;
if (page.is_aligned()) return self.backing_allocator.free_aligned(mem);
return self.backing_allocator.free(mem);
return self.backing_allocator.release(mem, page.is_aligned());
}
fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment, usz offset) @inline @local
fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
@@ -90,53 +94,37 @@ 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, offset)!;
void* data = self.acquire(size, size > page_size, alignment, 0)!;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
if (page.is_aligned())
{
self.backing_allocator.free_aligned(real_pointer);
}
else
{
self.backing_allocator.free(real_pointer);
}
self.backing_allocator.release(real_pointer, page.is_aligned());
return data;
}
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, usz offset) @dynamic
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment, usz deprecated) @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, offset);
return self._realloc_page(page, size, alignment);
}
// TODO optimize last allocation
TempAllocatorChunk* data = self.acquire(size, size > chunk.size, alignment, offset)!;
TempAllocatorChunk* data = self.acquire(size, size > chunk.size, alignment, 0)!;
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 offset) @dynamic
fn void*! TempAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
{
if (!size) return null;
alignment = alignment_for_allocation(alignment);
void* start_mem = &self.data;
void* starting_ptr = start_mem + self.used;
@@ -144,7 +132,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 + offset, alignment) - offset;
mem = mem::aligned_pointer(mem, alignment);
}
usz new_usage = (usz)(mem - start_mem) + size;
@@ -162,19 +150,20 @@ 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 || offset)
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
// This is actually simpler, since it will create the offset for us.
usz total_alloc_size = TempAllocatorPage.sizeof + size;
usz total_alloc_size = mem::aligned_offset(TempAllocatorPage.sizeof + size, alignment);
if (clear)
{
page = self.backing_allocator.calloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
mem = allocator::calloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
}
else
{
page = self.backing_allocator.alloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)!;
mem = allocator::malloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
}
page.start = page;
page = (TempAllocatorPage*)mem - 1;
page.start = mem;
page.size = size | PAGE_IS_ALIGNED;
}
else

View File

@@ -1,10 +1,9 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-2024 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::map;
import std::collections::list;
import std::collections, std::io, std::os::backtrace;
const MAX_BACKTRACE = 16;
struct Allocation
@@ -35,7 +34,7 @@ struct TrackingAllocator (Allocator)
fn void TrackingAllocator.init(&self, Allocator* allocator)
{
*self = { .inner_allocator = allocator };
self.map.init_new(.allocator = allocator);
self.map.new_init(.allocator = allocator);
}
/**
@@ -80,48 +79,36 @@ 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 offset) @dynamic
fn void*! TrackingAllocator.acquire(&self, usz size, bool clear, usz alignment, usz deprecated) @dynamic
{
void* data = self.inner_allocator.acquire(size, clear, alignment, offset)!;
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;
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 offset) @dynamic
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment, usz deprecated) @dynamic
{
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++;
}
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++;
return data;
}
fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dynamic
{
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);
}
}
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);
}
@@ -168,7 +155,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], mem::temp()).get(0) ?? backtrace::BACKTRACE_UNKNOWN;
trace = backtrace::symbolize_backtrace(allocation.backtrace[3:1], allocator::temp()).get(0) ?? backtrace::BACKTRACE_UNKNOWN;
}
if (trace.function.len) leaks = true;
io::fprintfn(out, "%13s %p %s:%d", allocation.size,
@@ -207,7 +194,7 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream* out)
break;
}
}
BacktraceList list = backtrace::symbolize_backtrace(allocation.backtrace[3..(end - 1)], mem::temp())!;
BacktraceList list = backtrace::symbolize_backtrace(allocation.backtrace[3..(end - 1)], allocator::temp())!;
io::fprintfn(out, "Allocation %d (%d bytes): ", i + 1, allocation.size)!;
foreach (trace : list)
{

View File

@@ -1,4 +1,5 @@
module std::core::array;
import std::core::array::slice;
/**
* @param [in] array
@@ -15,6 +16,19 @@ 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
@@ -41,10 +55,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 = mem::heap())
macro concat_new(arr1, arr2, Allocator* allocator = allocator::heap())
{
var $Type = $typeof(arr1[0]);
$Type[] result = allocator.new_array($Type, arr1.len + arr2.len);
$Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len);
if (arr1.len > 0)
{
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $Type.alignof, $Type.alignof);
@@ -67,4 +81,67 @@ macro concat_new(arr1, arr2, Allocator* allocator = mem::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, mem::temp());
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 };
}

View File

@@ -1,9 +1,8 @@
// Copyright (c) 2021-2022 Christoffer Lerno and contributors. All rights reserved.
// Copyright (c) 2021-2024 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;
import std::hash;
import libc, std::hash, std::io, std::os::backtrace;
/**
* Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
@@ -75,7 +74,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, mem::temp());
BacktraceList! backtrace = backtrace::symbolize_backtrace(backtraces, allocator::temp());
if (catch backtrace) return false;
if (backtrace.len() <= backtraces_to_ignore) return false;
io::eprint("\nERROR: '");
@@ -130,7 +129,7 @@ fn void panicf(String fmt, String file, String function, uint line, args...)
@stack_mem(512; Allocator* allocator)
{
DString s;
s.init_new(.allocator = allocator);
s.new_init(.allocator = allocator);
s.appendf(fmt, ...args);
panic(s.str_view(), file, function, line);
};
@@ -615,7 +614,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;
import libc, std::io;
fn void sig_panic(String message)
{

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021-2022 Christoffer Lerno and contributors. All rights reserved.
// Copyright (c) 2021-2024 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 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-2024 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.init_new(&self, usz capacity = MIN_CAPACITY, Allocator* allocator = mem::heap())
fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator* allocator = allocator::heap())
{
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
StringData* data = allocator.new(StringData, .end_padding = capacity);
StringData* data = allocator::alloc_with_padding(allocator, StringData, capacity)!!;
data.allocator = allocator;
data.len = 0;
data.capacity = capacity;
@@ -21,20 +21,36 @@ fn DString DString.init_new(&self, usz capacity = MIN_CAPACITY, Allocator* alloc
/**
* @require !self.data() "String already initialized"
**/
fn DString DString.init_temp(&self, usz capacity = MIN_CAPACITY)
fn DString DString.init_new(&self, usz capacity = MIN_CAPACITY, Allocator* allocator = allocator::heap()) @deprecated("Replaced by new_init")
{
self.init_new(capacity, mem::temp()) @inline;
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;
return *self;
}
fn DString new_with_capacity(usz capacity, Allocator* allocator = mem::heap())
/**
* @require !self.data() "String already initialized"
**/
fn DString DString.init_temp(&self, usz capacity = MIN_CAPACITY) @deprecated("Replaced by temp_init")
{
return DString{}.init_new(capacity, allocator);
return self.temp_init(capacity) @inline;
}
fn DString temp_with_capacity(usz capacity) => new_with_capacity(capacity, mem::temp()) @inline;
fn DString new_with_capacity(usz capacity, Allocator* allocator = allocator::heap())
{
return DString{}.new_init(capacity, allocator);
}
fn DString new(String c = "", Allocator* allocator = mem::heap())
fn DString temp_with_capacity(usz capacity) => new_with_capacity(capacity, allocator::temp()) @inline;
fn DString new(String c = "", Allocator* allocator = allocator::heap())
{
usz len = c.len;
StringData* data = (StringData*)new_with_capacity(len, allocator);
@@ -46,18 +62,20 @@ fn DString new(String c = "", Allocator* allocator = mem::heap())
return (DString)data;
}
fn DString temp_new(String s = "") => new(s, mem::temp()) @inline;
fn DString temp_new(String s = "") => new(s, allocator::temp()) @inline;
fn DString DString.new_concat(self, DString b, Allocator* allocator = mem::heap())
fn DString DString.new_concat(self, DString b, Allocator* allocator = allocator::heap())
{
DString string;
string.init_new(self.len() + b.len(), allocator);
string.new_init(self.len() + b.len(), allocator);
string.append(self);
string.append(b);
return string;
}
fn DString DString.new_tconcat(self, DString b) => self.new_concat(b, mem::temp());
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 ZString DString.zstr_view(&self)
{
@@ -146,7 +164,7 @@ fn void DString.append_char32(&self, Char32 c)
data.len += n;
}
fn DString DString.tcopy(&self) => self.copy(mem::temp());
fn DString DString.tcopy(&self) => self.copy(allocator::temp());
fn DString DString.copy(self, Allocator* allocator = null)
{
@@ -156,32 +174,32 @@ fn DString DString.copy(self, Allocator* allocator = null)
return (DString)null;
}
StringData* data = self.data();
if (!allocator) allocator = mem::heap();
if (!allocator) allocator = allocator::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 = mem::heap())
fn ZString DString.copy_zstr(self, Allocator* allocator = allocator::heap())
{
usz str_len = self.len();
if (!str_len)
{
return (ZString)allocator.calloc(1);
return (ZString)allocator::calloc(allocator, 1);
}
char* zstr = allocator.alloc(str_len + 1);
char* zstr = allocator::malloc(allocator, 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 = mem::heap())
fn String DString.copy_str(self, Allocator* allocator = allocator::heap())
{
return (String)self.copy_zstr(allocator)[:self.len()];
}
fn String DString.tcopy_str(self) => self.copy_str(mem::temp()) @inline;
fn String DString.tcopy_str(self) => self.copy_str(allocator::temp()) @inline;
fn bool DString.equals(self, DString other_string)
{
@@ -204,7 +222,7 @@ fn void DString.free(&self)
if (!*self) return;
StringData* data = self.data();
if (!data) return;
data.allocator.free(data);
allocator::free(data.allocator, data);
*self = (DString)null;
}
@@ -240,7 +258,7 @@ fn void DString.append_chars(&self, String str)
data.len += other_len;
}
fn Char32[] DString.copy_utf32(&self, Allocator* allocator = mem::heap())
fn Char32[] DString.copy_utf32(&self, Allocator* allocator = allocator::heap())
{
return self.str_view().to_new_utf32(allocator) @inline!!;
}
@@ -385,7 +403,7 @@ fn usz! DString.appendfn(&self, String format, args...) @maydiscard
return len + 1;
}
fn DString new_join(String[] s, String joiner, Allocator* allocator = mem::heap())
fn DString new_join(String[] s, String joiner, Allocator* allocator = allocator::heap())
{
if (!s.len) return (DString)null;
usz total_size = joiner.len * s.len;
@@ -429,7 +447,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)data.allocator.realloc(data, StringData.sizeof + new_capacity);
*self = (DString)allocator::realloc(data.allocator, data, StringData.sizeof + new_capacity);
}
fn usz! DString.read_from_stream(&self, InStream* reader)

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-2024 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,6 +131,7 @@ 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,6 +2,8 @@
// 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;
@@ -251,11 +253,12 @@ 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)
{
$if $inlined:
$$memset_inline(dst, (char)0, len, $is_volatile, $dst_align);
$else
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
$endif
$$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);
}
/**
@@ -267,17 +270,30 @@ macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = fal
* @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)
{
$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
$$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);
}
/**
@@ -303,19 +319,29 @@ 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, bool $inlined = false)
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false)
{
$if $inlined:
$$memset_inline(dst, val, len, $is_volatile, $dst_align);
$else
$$memset(dst, val, len, $is_volatile, $dst_align);
$endif
$$memset(dst, val, len, $is_volatile, $dst_align);
}
/**
* 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
@@ -379,21 +405,21 @@ macro type_alloc_must_be_aligned($Type)
**/
macro void @scoped(Allocator* allocator; @body())
{
Allocator* old_allocator = thread_allocator;
thread_allocator = allocator;
defer thread_allocator = old_allocator;
Allocator* old_allocator = allocator::thread_allocator;
allocator::thread_allocator = allocator;
defer allocator::thread_allocator = old_allocator;
@body();
}
macro void @report_heap_allocs_in_scope(;@body())
{
TrackingAllocator tracker;
tracker.init(thread_allocator);
Allocator* old_allocator = thread_allocator;
thread_allocator = &tracker;
tracker.init(allocator::thread_allocator);
Allocator* old_allocator = allocator::thread_allocator;
allocator::thread_allocator = &tracker;
defer
{
thread_allocator = old_allocator;
allocator::thread_allocator = old_allocator;
tracker.print_report();
tracker.free();
}
@@ -404,7 +430,7 @@ macro void @stack_mem(usz $size; @body(Allocator* mem)) @builtin
{
char[$size] buffer;
OnStackAllocator allocator;
allocator.init(&buffer, mem::heap());
allocator.init(&buffer, allocator::heap());
defer allocator.free();
@body(&allocator);
}
@@ -413,7 +439,7 @@ macro void @stack_pool(usz $size; @body) @builtin
{
char[$size] buffer;
OnStackAllocator allocator;
allocator.init(&buffer, mem::heap());
allocator.init(&buffer, allocator::heap());
defer allocator.free();
mem::@scoped(&allocator)
{
@@ -421,76 +447,67 @@ 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 = temp();
TempAllocator* current = allocator::temp();
var $has_arg = !$is_const(#other_temp);
$if $has_arg:
TempAllocator* original = current;
if (current == (void*)#other_temp) current = temp_allocator_next();
if (current == (void*)#other_temp) current = allocator::temp_allocator_next();
$endif
usz mark = current.used;
defer
{
current.reset(mark);
$if $has_arg:
thread_temp_allocator = original;
allocator::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);
@@ -505,7 +522,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;
thread_allocator = &wasm_allocator;
allocator::thread_allocator = &wasm_allocator;
}
module std::core::mem;
@@ -520,88 +537,218 @@ macro TrackingEnv* get_tracking_env()
$endif
}
macro @clone(value) @builtin
macro @clone(value) @builtin @nodiscard
{
return mem::heap().clone(value);
return allocator::clone(allocator::heap(), value);
}
macro @tclone(value) @builtin
macro @tclone(value) @builtin @nodiscard
{
return mem::temp().clone(value);
return temp_new($typeof(value), value);
}
fn void* malloc(usz size) @builtin @inline
fn void* malloc(usz size) @builtin @inline @nodiscard
{
return mem::heap().alloc(size);
return allocator::malloc(allocator::heap(), size);
}
fn void* tmalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline
fn void* tmalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline @nodiscard
{
return temp().acquire(size, false, alignment, offset)!!;
if (!size) return null;
return allocator::temp().acquire(size, false, alignment, 0)!!;
}
macro new($Type)
/**
* @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
{
return heap().new($Type);
$if $vacount == 0:
return ($Type*)calloc($Type.sizeof);
$else
$Type* val = malloc($Type.sizeof);
*val = $vaexpr(0);
return val;
$endif
}
macro new_clear($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
{
return heap().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
}
macro new_temp($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")
{
return tmalloc($Type.sizeof);
}
macro new_temp_clear($Type)
/**
* @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")
{
return tcalloc($Type.sizeof);
}
macro new_array($Type, usz elements)
/**
* @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
{
return heap().new_array($Type, elements);
return allocator::new_array(allocator::heap(), $Type, elements);
}
macro temp_array($Type, usz 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
{
return (($Type*)tmalloc($Type.sizeof * elements, $Type.alignof))[:elements];
}
macro new_zero_array($Type, usz elements)
macro temp_array($Type, usz elements) @nodiscard @deprecated("use mem::temp_alloc_array")
{
return heap().new_zero_array($Type, elements);
return temp_alloc_array($Type, elements);
}
macro temp_zero_array($Type, usz elements)
macro temp_new_array($Type, usz elements) @nodiscard
{
return (($Type*)tcalloc($Type.sizeof * elements, $Type.alignof))[:elements];
}
fn void* calloc(usz size) @builtin @inline
macro new_zero_array($Type, usz elements) @deprecated("Use new_array")
{
return heap().calloc(size);
return new_array($Type, elements);
}
fn void* tcalloc(usz size, usz alignment = 0, usz offset = 0) @builtin @inline
macro temp_zero_array($Type, usz elements) @deprecated("Use temp_new_array")
{
return temp().acquire(size, false, alignment, offset)!!;
return temp_new_array($Type, elements);
}
fn void* realloc(void *ptr, usz new_size) @builtin @inline
fn void* calloc(usz size) @builtin @inline @nodiscard
{
return heap().realloc(ptr, new_size);
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)!!;
}
fn void free(void* ptr) @builtin @inline
{
heap().free(ptr);
return allocator::free(allocator::heap(), ptr);
}
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline
fn void free_aligned(void* ptr) @builtin @inline
{
return temp().resize(ptr, size, alignment, 0)!!;
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)!!;
}

View File

@@ -15,83 +15,27 @@ 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
@@ -105,120 +49,396 @@ fn usz alignment_for_allocation(usz alignment) @inline @private
return alignment < mem::DEFAULT_MEM_ALIGNMENT ? alignment = mem::DEFAULT_MEM_ALIGNMENT : alignment;
}
// Allocator "functions"
macro void*! Allocator.alloc_checked(&self, usz size)
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 = self.acquire(size, false, 0, 0)!;
char* data = allocator.acquire(size, false, 0, 0)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
return data;
$else
return self.acquire(size, false, 0, 0);
return allocator.acquire(size, false, 0, 0);
$endif
}
macro void*! Allocator.calloc_checked(&self, usz size)
macro void* calloc(Allocator* allocator, usz size) @nodiscard
{
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);
return calloc_try(allocator, size)!!;
}
macro Allocator.new_array(&self, $Type, usz size, usz end_padding = 0)
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")
{
return malloc_try(self, size);
}
macro void*! Allocator.calloc_checked(&self, usz size) @deprecated("Use allocator::calloc_try")
{
return calloc_try(self, size);
}
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")
{
return (($Type*)self.alloc_checked($Type.sizeof * size + end_padding))[:size]!!;
}
macro Allocator.new_array_checked(&self, $Type, usz size, usz end_padding = 0)
macro Allocator.new_array_checked(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::alloc_array_try")
{
return (($Type*)self.alloc_checked($Type.sizeof * size + end_padding))[:size];
}
macro Allocator.new_zero_array(&self, $Type, usz size, usz end_padding = 0)
macro Allocator.new_zero_array(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::new_array")
{
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)
macro Allocator.new_zero_array_checked(&self, $Type, usz size, usz end_padding = 0) @deprecated("Use allocator::new_array_try")
{
return (($Type*)self.calloc_checked($Type.sizeof * size + end_padding))[:size];
}
macro Allocator.new(&self, $Type, usz end_padding = 0) @nodiscard
macro Allocator.new(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::alloc")
{
return ($Type*)self.alloc_checked($Type.sizeof + end_padding)!!;
}
macro Allocator.new_checked(&self, $Type, usz end_padding = 0) @nodiscard
macro Allocator.new_checked(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::alloc_try")
{
return ($Type*)self.alloc_checked($Type.sizeof + end_padding);
}
macro Allocator.new_clear(&self, $Type, usz end_padding = 0) @nodiscard
macro Allocator.new_clear(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::new")
{
return ($Type*)self.calloc_checked($Type.sizeof + end_padding)!!;
}
macro Allocator.new_clear_checked(&self, $Type, usz end_padding = 0) @nodiscard
macro Allocator.new_clear_checked(&self, $Type, usz end_padding = 0) @nodiscard @deprecated("Use allocator::new_try")
{
return ($Type*)self.calloc_checked($Type.sizeof + end_padding);
}
macro Allocator.clone(&self, value)
macro Allocator.clone(&self, value) @deprecated("Use allocator::clone")
{
var x = self.alloc($typeof(value));
*x = value;
return x;
}
macro void* Allocator.alloc(&self, usz size) @nodiscard
fn void* Allocator.alloc(&self, usz size) @nodiscard @deprecated("Use allocator::malloc")
{
return self.alloc_checked(size)!!;
return malloc(self, size);
}
macro void* Allocator.calloc(&self, usz size) @nodiscard
fn void* Allocator.calloc(&self, usz size) @nodiscard @deprecated("Use allocator::calloc")
{
return self.acquire(size, true, 0, 0)!!;
return calloc(self, size);
}
macro void* Allocator.realloc(&self, void* ptr, usz new_size) @nodiscard
fn void* Allocator.realloc(&self, void* ptr, usz new_size) @nodiscard @deprecated("Use allocator::realloc")
{
return self.resize(ptr, new_size, 0, 0)!!;
return realloc(self, ptr, new_size);
}
macro void*! Allocator.alloc_aligned(&self, usz size, usz alignment, usz offset = 0)
fn void*! Allocator.alloc_aligned(&self, usz size, usz alignment, usz offset = 0) @deprecated("Use allocator::malloc_aligned")
{
$if env::TESTING:
char* data = self.acquire(size, false, alignment, offset)!;
mem::set(data, 0xAA, size, mem::DEFAULT_MEM_ALIGNMENT);
return data;
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)!;
$else
return self.acquire(size, false, alignment, offset);
void* data = #alloc_fn(alignsize);
$endif
}
macro void*! Allocator.calloc_aligned(&self, usz size, usz alignment, usz offset = 0)
{
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);
void* mem = mem::aligned_pointer(data + AlignedBlock.sizeof, alignment);
AlignedBlock* desc = (AlignedBlock*)mem - 1;
assert(mem > data);
*desc = { bytes, data };
return mem;
}
macro void Allocator.free(&self, void* ptr)
struct AlignedBlock
{
$if env::TESTING:
if (ptr) ((char*)ptr)[0] = 0xBA;
$endif
self.release(ptr, false);
usz len;
void* start;
}
macro void Allocator.free_aligned(&self, void* ptr)
macro void! @aligned_free(#free_fn, void* old_pointer)
{
$if env::TESTING:
if (ptr) ((char*)ptr)[0] = 0xBA;
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
$if @typekind(#free_fn(desc.start)) == OPTIONAL:
#free_fn(desc.start)!;
$else
#free_fn(desc.start);
$endif
self.release(ptr, true);
}
/**
* @require bytes > 0
* @require alignment > 0
**/
macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment)
{
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);
$endif
return new_data;
}
// 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

@@ -0,0 +1,258 @@
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

@@ -0,0 +1,254 @@
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::new_array(String, argc);
String[] list = mem::alloc_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::new_array(String, argc);
String[] list = mem::alloc_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;
import libc, std::time, std::io, std::sort;
struct AnyStruct
{
@@ -24,11 +24,11 @@ struct BenchmarkUnit
BenchmarkFn func;
}
fn BenchmarkUnit[] benchmark_collection_create(Allocator* allocator = mem::heap())
fn BenchmarkUnit[] benchmark_collection_create(Allocator* allocator = allocator::heap())
{
BenchmarkFn[] fns = $$BENCHMARK_FNS;
String[] names = $$BENCHMARK_NAMES;
BenchmarkUnit[] benchmarks = allocator.new_array(BenchmarkUnit, names.len);
BenchmarkUnit[] benchmarks = allocator::alloc_array(allocator, 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(mem::temp()));
return run_benchmarks(benchmark_collection_create(allocator::temp()));
};
}
@@ -142,11 +142,11 @@ struct TestUnit
TestFn func;
}
fn TestUnit[] test_collection_create(Allocator* allocator = mem::heap())
fn TestUnit[] test_collection_create(Allocator* allocator = allocator::heap())
{
TestFn[] fns = $$TEST_FNS;
String[] names = $$TEST_NAMES;
TestUnit[] tests = allocator.new_array(TestUnit, names.len);
TestUnit[] tests = allocator::alloc_array(allocator, 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(mem::temp()));
return run_tests(test_collection_create(allocator::temp()));
};
}

View File

@@ -38,7 +38,7 @@ macro String tformat(String fmt, ...)
return str.str_view();
}
macro String new_format(String fmt, ..., Allocator* allocator = mem::heap())
macro String new_format(String fmt, ..., Allocator* allocator = allocator::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 = mem::heap())
fn String join_new(String[] s, String joiner, Allocator* allocator = allocator::heap())
{
if (!s)
{
return (String)allocator.new_zero_array(char, 2)[:0];
return (String)allocator::new_array(allocator, 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 = mem::heap())
fn String[] String.split(s, String needle, usz max = 0, Allocator* allocator = allocator::heap())
{
usz capacity = 16;
usz i = 0;
String* holder = allocator.new_array(String, capacity);
String* holder = allocator::alloc_array(allocator, 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 = m
if (i == capacity)
{
capacity *= 2;
holder = allocator.realloc(holder, String.sizeof * capacity);
holder = allocator::realloc(allocator, holder, String.sizeof * capacity);
}
holder[i++] = res;
}
@@ -193,7 +193,7 @@ fn String[] String.split(s, String needle, usz max = 0, Allocator* allocator = m
**/
fn String[] String.tsplit(s, String needle, usz max = 0)
{
return s.split(needle, max, mem::temp()) @inline;
return s.split(needle, max, allocator::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 = mem::heap())
fn ZString String.zstr_copy(s, Allocator* allocator = allocator::heap())
{
usz len = s.len;
char* str = allocator.alloc(len + 1);
char* str = allocator::malloc(allocator, len + 1);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (ZString)str;
}
fn String String.concat(s1, String s2, Allocator* allocator = mem::heap())
fn String String.concat(s1, String s2, Allocator* allocator = allocator::heap())
{
usz full_len = s1.len + s2.len;
char* str = allocator.alloc(full_len + 1);
char* str = allocator::malloc(allocator, 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 = mem::heap())
return (String)str[:full_len];
}
fn String String.tconcat(s1, String s2) => s1.concat(s2, mem::temp());
fn String String.tconcat(s1, String s2) => s1.concat(s2, allocator::temp());
fn ZString String.zstr_tcopy(s) => s.zstr_copy(mem::temp()) @inline;
fn ZString String.zstr_tcopy(s) => s.zstr_copy(allocator::temp()) @inline;
fn String String.copy(s, Allocator* allocator = mem::heap())
fn String String.copy(s, Allocator* allocator = allocator::heap())
{
usz len = s.len;
char* str = allocator.alloc(len + 1);
char* str = allocator::malloc(allocator, len + 1);
mem::copy(str, s.ptr, len);
str[len] = 0;
return (String)str[:len];
}
fn void String.free(&s, Allocator* allocator = mem::heap())
fn void String.free(&s, Allocator* allocator = allocator::heap())
{
if (!s.len) return;
allocator.free(s.ptr);
allocator::free(allocator, s.ptr);
*s = "";
}
fn String String.tcopy(s) => s.copy(mem::temp()) @inline;
fn String String.tcopy(s) => s.copy(allocator::temp()) @inline;
fn String ZString.copy(z, Allocator* allocator = mem::temp())
fn String ZString.copy(z, Allocator* allocator = allocator::temp())
{
return z.str_view().copy(allocator) @inline;
}
fn String ZString.tcopy(z)
{
return z.str_view().copy(mem::temp()) @inline;
return z.str_view().copy(allocator::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 = mem::heap())
fn Char16[]! String.to_new_utf16(s, Allocator* allocator = allocator::heap())
{
usz len16 = conv::utf16len_for_utf8(s);
Char16* data = allocator.new_array_checked(Char16, len16 + 1)!;
Char16* data = allocator::alloc_array_try(allocator, 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 = mem::heap())
**/
fn Char16[]! String.to_temp_utf16(s)
{
return s.to_new_utf16(mem::temp());
return s.to_new_utf16(allocator::temp());
}
fn WString! String.to_new_wstring(s, Allocator* allocator = mem::heap())
fn WString! String.to_new_wstring(s, Allocator* allocator = allocator::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 = mem::heap())
fn Char32[]! String.to_new_utf32(s, Allocator* allocator = allocator::heap())
{
usz codepoints = conv::utf8_codepoints(s);
Char32* data = allocator.new_array(Char32, codepoints + 1);
Char32* data = allocator::alloc_array_try(allocator, 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 = mem::heap())
fn Char32[]! String.to_temp_utf32(s)
{
return s.to_new_utf32(mem::temp());
return s.to_new_utf32(allocator::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 = mem::heap())
fn String String.new_ascii_to_lower(s, Allocator* allocator = allocator::heap())
{
String copy = s.copy(allocator);
copy.convert_ascii_to_lower();
return copy;
}
fn String String.temp_ascii_to_lower(s, Allocator* allocator = mem::heap())
fn String String.temp_ascii_to_lower(s, Allocator* allocator = allocator::heap())
{
return s.new_ascii_to_lower(mem::temp());
return s.new_ascii_to_lower(allocator::temp());
}
fn void String.convert_ascii_to_upper(s)
@@ -437,7 +437,7 @@ 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 = mem::heap())
fn String String.new_ascii_to_upper(s, Allocator* allocator = allocator::heap())
{
String copy = s.copy(allocator);
copy.convert_ascii_to_upper();
@@ -451,30 +451,30 @@ fn StringIterator String.iterator(s)
fn String String.temp_ascii_to_upper(s)
{
return s.new_ascii_to_upper(mem::temp());
return s.new_ascii_to_upper(allocator::temp());
}
fn String! new_from_utf32(Char32[] utf32, Allocator* allocator = mem::heap())
fn String! new_from_utf32(Char32[] utf32, Allocator* allocator = allocator::heap())
{
usz len = conv::utf8len_for_utf32(utf32);
char* data = allocator.alloc_checked(len + 1)!;
defer catch allocator.free(data);
char* data = allocator::malloc_try(allocator, len + 1)!;
defer catch allocator::free(allocator, data);
conv::utf32to8_unsafe(utf32, data);
data[len] = 0;
return (String)data[:len];
}
fn String! new_from_utf16(Char16[] utf16, Allocator* allocator = mem::heap())
fn String! new_from_utf16(Char16[] utf16, Allocator* allocator = allocator::heap())
{
usz len = conv::utf8len_for_utf16(utf16);
char* data = allocator.alloc_checked(len + 1)!;
defer catch allocator.free(data);
char* data = allocator::malloc_try(allocator, len + 1)!;
defer catch allocator::free(allocator, data);
conv::utf16to8_unsafe(utf16, data)!;
data[len] = 0;
return (String)data[:len];
}
fn String! new_from_wstring(WString wstring, Allocator* allocator = mem::heap())
fn String! new_from_wstring(WString wstring, Allocator* allocator = allocator::heap())
{
usz utf16_len;
while (wstring[utf16_len] != 0) utf16_len++;
@@ -482,8 +482,8 @@ fn String! new_from_wstring(WString wstring, Allocator* allocator = mem::heap())
return new_from_utf16(utf16, allocator);
}
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 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 usz String.utf8_codepoints(s)
{
@@ -495,7 +495,11 @@ fn usz String.utf8_codepoints(s)
return len;
}
macro String.to_integer(string, $Type)
/**
* @require (base <= 10 && base > 1) || base == 16 : "Unsupported base"
**/
macro String.to_integer(string, $Type, int base = 10)
{
usz len = string.len;
usz index = 0;
@@ -515,8 +519,8 @@ macro String.to_integer(string, $Type)
break;
}
if (len == index) return NumberConversion.MALFORMED_INTEGER?;
$Type base = 10;
if (string[index] == '0')
$Type base_used = ($Type)base;
if (string[index] == '0' && base == 10)
{
index++;
if (index == len) return ($Type)0;
@@ -524,15 +528,15 @@ macro String.to_integer(string, $Type)
{
case 'x':
case 'X':
base = 16;
base_used = 16;
index++;
case 'b':
case 'B':
base = 2;
base_used = 2;
index++;
case 'o':
case 'O':
base = 8;
base_used = 8;
index++;
default:
break;
@@ -544,21 +548,21 @@ macro String.to_integer(string, $Type)
{
char c = {|
char ch = string[index++];
if (base != 16 || ch < 'A') return (char)(ch - '0');
if (ch <= 'F') return (char)(ch - 'A');
if (base_used != 16 || ch < 'A') return (char)(ch - '0');
if (ch <= 'F') return (char)(ch - 'A' + 10);
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER?;
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER?;
return (char)(ch - 'a');
return (char)(ch - 'a' + 10);
|}!;
if (c >= base) return NumberConversion.MALFORMED_INTEGER?;
if (c >= base_used) return NumberConversion.MALFORMED_INTEGER?;
value = {|
if (is_negative)
{
$Type new_value = value * base - c;
$Type new_value = value * base_used - c;
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW?;
return new_value;
}
$Type new_value = value * base + c;
$Type new_value = value * base_used + c;
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW?;
return new_value;
|}!;
@@ -566,17 +570,17 @@ macro String.to_integer(string, $Type)
return value;
}
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 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 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 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 double! String.to_double(s) => s.to_real(double);
fn float! String.to_float(s) => s.to_real(float);

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 = mem::heap())
fn String[]! CsvReader.read_new_row(self, Allocator* allocator = allocator::heap())
{
return self.read_new_row_with_allocator(mem::temp()) @inline;
return self.read_new_row_with_allocator(allocator::temp()) @inline;
}
fn String[]! CsvReader.read_new_row_with_allocator(self, Allocator* allocator = mem::heap())
fn String[]! CsvReader.read_new_row_with_allocator(self, Allocator* allocator = allocator::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(mem::temp()) @inline;
return self.read_new_row_with_allocator(allocator::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 = mem::heap())
fn Object*! parse(InStream* s, Allocator* allocator = allocator::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;
import libc, std::io::path, std::io::os;
fn File! open(String filename, String mode)
{
@@ -161,14 +161,14 @@ fn char[]! load_buffer(String filename, char[] buffer)
}
fn char[]! load_new(String filename, Allocator* allocator = mem::heap())
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.alloc_checked(len)!;
defer catch allocator.free(data);
char* data = allocator::malloc_try(allocator, len)!;
defer catch allocator::free(allocator, data);
usz read = 0;
while (read < len)
{
@@ -179,7 +179,7 @@ fn char[]! load_new(String filename, Allocator* allocator = mem::heap())
fn char[]! load_temp(String filename)
{
return load_new(filename, mem::temp());
return load_new(filename, allocator::temp());
}
/**

View File

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

View File

@@ -1,4 +1,5 @@
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 = mem::heap())
macro String! readline(stream = io::stdin(), Allocator* allocator = allocator::heap())
{
bool $is_stream = @typeid(stream) == InStream*.typeid;
$if $is_stream:
@@ -84,7 +84,7 @@ macro String! readline(stream = io::stdin(), Allocator* allocator = mem::heap())
};
}
macro String! treadline(stream = io::stdin()) => readline(stream, mem::temp()) @inline;
macro String! treadline(stream = io::stdin()) => readline(stream, allocator::temp()) @inline;
/**
* @require @is_outstream(out) "The output must implement OutStream"

View File

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

View File

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

View File

@@ -1,9 +1,10 @@
module std::io::file::os @if(env::POSIX);
module std::io::os @if(env::POSIX);
import std::io, std::os;
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* allocator)
{
PathList list;
list.init_new(.allocator = allocator);
list.new_init(.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)?;
@@ -21,11 +22,12 @@ 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.init_new(.allocator = allocator);
list.new_init(.allocator = allocator);
@pool(allocator)
{

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
module std::io::path;
import std::collections::list;
import std::collections::list, std::io::os;
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 = mem::heap())
fn Path! getcwd(Allocator* allocator = allocator::heap())
{
@pool(allocator)
{
return new(os::getcwd(mem::temp()), allocator);
return new(os::getcwd(allocator::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(mem::temp()) @inline;
fn Path! tgetcwd() => getcwd(allocator::temp()) @inline;
fn void! chdir(Path path) => os::native_chdir(path) @inline;
fn Path! temp_directory(Allocator* allocator = mem::heap()) => os::native_temp_directory(allocator);
fn Path! temp_directory(Allocator* allocator = allocator::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 = mem::heap())
fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator* allocator = allocator::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 = mem::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
fn Path! new(String path, Allocator* allocator = allocator::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, mem::temp(), path_env);
return new(path, allocator::temp(), path_env);
}
fn Path! new_win32_wstring(WString path, Allocator* allocator = mem::heap())
fn Path! new_win32_wstring(WString path, Allocator* allocator = allocator::heap())
{
@pool(allocator)
{
@@ -123,12 +123,12 @@ fn Path! new_win32_wstring(WString path, Allocator* allocator = mem::heap())
};
}
fn Path! new_windows(String path, Allocator* allocator = mem::heap())
fn Path! new_windows(String path, Allocator* allocator = allocator::heap())
{
return new(path, allocator, WIN32);
}
fn Path! new_posix(String path, Allocator* allocator = mem::heap())
fn Path! new_posix(String path, Allocator* allocator = allocator::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 = mem::heap())
fn Path! Path.append(self, String filename, Allocator* allocator = allocator::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 = mem::heap())
};
}
fn Path! Path.tappend(self, String filename) => self.append(filename, mem::temp());
fn Path! Path.tappend(self, String filename) => self.append(filename, allocator::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 = mem::heap())
fn Path! Path.absolute(self, Allocator* allocator = allocator::heap())
{
String path_str = self.str_view();
if (!path_str.len) path_str = ".";
if (path_str == ".")
{
String cwd = os::getcwd(mem::temp())!;
String cwd = os::getcwd(allocator::temp())!;
return new(cwd, allocator, self.env);
}
switch (self.env)
@@ -196,7 +196,7 @@ fn Path! Path.absolute(self, Allocator* allocator = mem::heap())
case POSIX:
if (path_str[0] == PREFERRED_SEPARATOR_POSIX) return self;
}
String cwd = os::getcwd(mem::temp())!;
String cwd = os::getcwd(allocator::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 = mem::heap()) @dynamic
fn String Path.to_new_string(&self, Allocator* allocator = allocator::heap()) @dynamic
{
return self.str_view().copy(allocator);
}

View File

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

View File

@@ -16,7 +16,17 @@ 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 = mem::heap())
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())
{
*self = { .allocator = allocator, .max_read = max_read };
initial_capacity = max(initial_capacity, 16);
@@ -24,9 +34,14 @@ fn ByteBuffer*! ByteBuffer.init_new(&self, usz max_read, usz initial_capacity =
return self;
}
fn ByteBuffer*! ByteBuffer.init_temp(&self, usz max_read, usz initial_capacity = 16)
fn ByteBuffer*! ByteBuffer.init_temp(&self, usz max_read, usz initial_capacity = 16) @deprecated("Replaced by temp_init")
{
return self.init_new(max_read, initial_capacity, mem::temp());
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());
}
/**
@@ -41,7 +56,7 @@ fn ByteBuffer*! ByteBuffer.init_with_buffer(&self, char[] buf)
fn void ByteBuffer.free(&self)
{
if (self.allocator) self.allocator.free(self.bytes);
if (self.allocator) allocator::free(self.allocator, self.bytes);
*self = {};
}
@@ -131,7 +146,7 @@ fn usz! ByteBuffer.available(&self) @inline @dynamic
fn void! ByteBuffer.grow(&self, usz n)
{
n = math::next_power_of_2(n);
char* p = self.allocator.realloc_aligned(self.bytes, n, .alignment = char.alignof)!;
char* p = allocator::realloc_aligned(self.allocator, self.bytes, n, .alignment = char.alignof)!;
self.bytes = p[:n];
}

View File

@@ -1,4 +1,5 @@
module std::io;
import std::math;
struct ByteWriter (OutStream)
{
@@ -13,20 +14,41 @@ 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.init_new(&self, Allocator* allocator = mem::heap())
fn ByteWriter* ByteWriter.new_init(&self, Allocator* allocator = allocator::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.init_temp(&self)
fn ByteWriter* ByteWriter.temp_init(&self)
{
return self.init_new(mem::temp());
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;
}
fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data)
@@ -38,7 +60,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) self.allocator.free(ptr);
if (void* ptr = self.bytes.ptr) allocator::free(self.allocator, ptr);
*self = { };
}
@@ -53,7 +75,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 = self.allocator.realloc_checked(self.bytes.ptr, new_capacity)!;
char* new_ptr = allocator::realloc_try(self.allocator, self.bytes.ptr, new_capacity)!;
self.bytes = new_ptr[:new_capacity];
}

View File

@@ -97,8 +97,10 @@ def complex_identity = complex::identity(<double>);
def Quaternionf = Quaternion(<float>);
def Quaternion = Quaternion(<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 quaternionf_identity = quaternion::identity(<float>);
def Matrix2f = Matrix2x2(<float>);
def Matrix2 = Matrix2x2(<double>);
@@ -118,6 +120,14 @@ 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`
**/
@@ -390,14 +400,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(x)) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
* @require $assignable(exp, $typeof(values::promote_int(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(x, ($typeof(x))exp);
return $$pow(values::promote_int(x), ($typeof(values::promote_int(x)))exp);
$else
return $$pow_int(x, exp);
return $$pow_int(values::promote_int(x), exp);
$endif
}
@@ -439,6 +449,16 @@ 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 rangeInv = (Real)1.0 / (near - far);
Real range_inv = (Real)1.0 / (near - far);
return {
f / aspect_ratio, 0, 0, 0,
0, f, 0, 0,
0, 0, (near + far) * rangeInv, -1,
0, 0, near * far * rangeInv * 2, 0,
0, 0, (near + far) * range_inv, near * far * range_inv * 2,
0, 0, -1, 0,
};
}

View File

@@ -9,7 +9,9 @@ union Quaternion
Real[<4>] v;
}
macro Quaternion identity() => { 0, 0, 0, 1 };
const Quaternion IDENTITY = { 0, 0, 0, 1 };
macro Quaternion identity() @deprecated("Replaced with QUATERNION_IDENTITY constant") => { 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 };
@@ -69,30 +71,16 @@ fn Quaternion Quaternion.mul(a, Quaternion b)
macro into_matrix(Quaternion* q, $Type) @private
{
$Type result = { .m = { [0] = 1, [5] = 1, [10] = 1, [15] = 1 } };
Quaternion norm = q.normalize();
Quaternion rotation = q.normalize();
var x = rotation.i;
var y = rotation.j;
var z = rotation.k;
var w = rotation.l;
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;
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,
};
}

View File

@@ -203,10 +203,10 @@ macro matrix_look_at($Type, eye, target, up) @private
var vy = vz.cross(vx);
return $Type {
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
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
};
}

View File

@@ -1,5 +1,5 @@
module std::math::random;
import std::hash::fnv32a;
import std::hash::fnv32a, std::time;
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_array(ulong, (out_chars + 7) / 8);
ulong[] words = mem::temp_alloc_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.init_new),
hash(mem::heap())
hash(&DString.new_init),
hash(allocator::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 = mem::heap()) @dynamic
fn String InetAddress.to_new_string(InetAddress* addr, Allocator* allocator = allocator::heap()) @dynamic
{
if (addr.is_ipv6)
{

View File

@@ -1,4 +1,5 @@
module std::net;
import std::io;
fault NetError
{
@@ -56,7 +57,7 @@ fn uint! ipv4toint(String s)
return out;
}
fn String! int_to_new_ipv4(uint val, Allocator* allocator = mem::heap())
fn String! int_to_new_ipv4(uint val, Allocator* allocator = allocator::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)!;
@@ -65,5 +66,5 @@ fn String! int_to_new_ipv4(uint val, Allocator* allocator = mem::heap())
fn String! int_to_temp_ipv4(uint val)
{
return int_to_new_ipv4(val, mem::temp());
return int_to_new_ipv4(val, allocator::temp());
}

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
module std::net @if(os::SUPPORTS_INET);
import libc;
import std::time, libc, std::os;
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 libc;
import std::time, libc;
distinct TcpSocket = inline Socket;
distinct TcpServerSocket = inline Socket;

View File

@@ -1,5 +1,5 @@
module std::os::backtrace;
import std::collections::list;
import std::collections::list, std::os, std::io;
fault BacktraceFault
{
@@ -48,9 +48,9 @@ fn usz! Backtrace.to_format(&self, Formatter* formatter) @dynamic
fn void Backtrace.free(&self)
{
if (!self.allocator) return;
self.allocator.free(self.function);
self.allocator.free(self.object_file);
self.allocator.free(self.file);
allocator::free(self.allocator, self.function);
allocator::free(self.allocator, self.object_file);
allocator::free(self.allocator, 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 libc;
import std::io::path, libc, std::os;
/**
* @param [in] name
* @require name.len > 0
* @return! SearchResult.MISSING
**/
fn String! get_var(String name, Allocator* allocator = mem::heap())
fn String! get_var(String name, Allocator* allocator = allocator::heap())
{
@pool(allocator)
{
@@ -38,7 +38,7 @@ fn String! get_var(String name, Allocator* allocator = mem::heap())
fn String! get_var_temp(String name)
{
return get_var(name, mem::temp());
return get_var(name, allocator::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 = mem::heap())
fn String! get_home_dir(Allocator* using = allocator::heap())
{
String home;
$if !env::WIN32:
@@ -86,7 +86,7 @@ fn String! get_home_dir(Allocator* using = mem::heap())
/**
* Returns the current user's config directory.
**/
fn Path! get_config_dir(Allocator* allocator = mem::heap())
fn Path! get_config_dir(Allocator* allocator = allocator::heap())
{
@pool(allocator)
{
@@ -126,7 +126,7 @@ fn bool clear_var(String name)
};
}
fn String! executable_path(Allocator *allocator = mem::heap())
fn String! executable_path(Allocator *allocator = allocator::heap())
{
$if env::DARWIN:
return darwin::executable_path(allocator);

2
lib/std/os/linux/heap.c3 Normal file
View File

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

View File

@@ -1,8 +1,5 @@
module std::os::linux @if(env::LINUX);
import libc;
import std::os::posix;
import std::io;
import std::collections::list;
import libc, std::os, std::io, std::collections::list;
extern fn isz readlink(ZString path, char* buf, usz bufsize);
@@ -133,7 +130,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_array(char, 1024);
char[] buf = mem::temp_alloc_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);
@@ -143,7 +140,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_array(char, 1024);
char[] buf = mem::temp_alloc_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;
@@ -185,7 +182,7 @@ fn Backtrace! backtrace_from_addr2line(void* addr, String addr2line, String obj_
};
}
fn Backtrace! backtrace_load_element(void* addr, Allocator* allocator = mem::heap()) @local
fn Backtrace! backtrace_load_element(void* addr, Allocator* allocator = allocator::heap()) @local
{
if (!addr) return backtrace::BACKTRACE_UNKNOWN;
@@ -203,7 +200,7 @@ fn Backtrace! backtrace_load_element(void* addr, Allocator* allocator = mem::hea
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
{
BacktraceList list;
list.init_new(backtrace.len, allocator);
list.new_init(backtrace.len, allocator);
defer catch
{
foreach (trace : list)

View File

@@ -1,18 +1,18 @@
module std::os::macos::cf @if(env::DARWIN);
module std::os::macos::cf @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework");
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");
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");
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;

View File

@@ -1,11 +1,11 @@
module std::os::macos::cf @if(env::DARWIN);
module std::os::macos::cf @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework");
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");
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");
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;

View File

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

View File

@@ -1,5 +1,5 @@
module std::os::darwin @if(env::DARWIN);
import std::collections::list;
import std::collections::list, std::os;
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(mem::temp()) ?? BacktraceFault.EXECUTABLE_PATH_NOT_FOUND?!;
String path = env::executable_path(allocator::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 = mem::heap()) @local
fn Backtrace! backtrace_load_element(String execpath, void* buffer, void* load_address, Allocator* allocator = allocator::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.init_new(backtrace.len, allocator);
list.new_init(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(mem::temp())!;
String execpath = executable_path(allocator::temp())!;
foreach (addr : backtrace)
{
list.append(backtrace_load_element(execpath, addr, load_addr, allocator) ?? backtrace::BACKTRACE_UNKNOWN);

3
lib/std/os/macos/heap.c3 Normal file
View File

@@ -0,0 +1,3 @@
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);
module std::os::macos::objc @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework");
distinct Class = void*;
distinct Method = void*;
@@ -10,38 +10,36 @@ fault ObjcFailure
CLASS_NOT_FOUND
}
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 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 bool Selector.equals(Selector a, Selector b) => a == b;
macro bool Class.equals(Class a, Class b) => a == b;
macro Selector selector_register(char* c) => _macos_sel_registerName(c);
macro Class! class_by_name(char* c)
macro Class! class_by_name(ZString c)
{
Class cls = _macos_objc_lookUpClass(c);
if (!cls) return ObjcFailure.CLASS_NOT_FOUND?;
return cls;
Class cls = macos_objc_lookUpClass(c);
return cls ?: ObjcFailure.CLASS_NOT_FOUND?;
}
macro Class[] class_get_list(Allocator *allocator = mem::heap())
macro Class[] class_get_list(Allocator *allocator = allocator::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(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");
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;

3
lib/std/os/posix/heap.c3 Normal file
View File

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

View File

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

View File

@@ -1,6 +1,5 @@
module std::os::process @if(env::WIN32 || env::POSIX);
import std::io::file;
import libc;
import std::io, libc, std::os;
// This code is based on https://github.com/sheredom/subprocess.h
@@ -247,7 +246,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_array(ZString, command_line.len + 1);
ZString* copy = mem::temp_alloc_array(ZString, command_line.len + 1);
foreach (i, str : command_line)
{
copy[i] = str.zstr_tcopy();
@@ -260,7 +259,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_array(ZString, environment.len + 1);
ZString* copy = mem::temp_alloc_array(ZString, environment.len + 1);
copy[environment.len] = null;
foreach (i, str : environment)
{

11
lib/std/os/win32/heap.c3 Normal file
View File

@@ -0,0 +1,11 @@
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,4 +1,5 @@
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;
@@ -102,6 +103,8 @@ 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()
{
@@ -152,7 +155,7 @@ Win32_DWORD64 displacement;
fn BacktraceList! symbolize_backtrace(void*[] backtrace, Allocator* allocator)
{
BacktraceList list;
list.init_new(backtrace.len, allocator);
list.new_init(backtrace.len, allocator);
Win32_HANDLE process = getCurrentProcess();
symInitialize(process, null, 1);
defer symCleanup(process);

View File

@@ -1,6 +1,5 @@
module std::threads::os @if(env::POSIX);
import std::os::posix;
import libc;
module std::thread::os @if(env::POSIX);
import std::os::posix, std::time, libc;
struct NativeMutex
{
@@ -151,8 +150,8 @@ fn void* callback(void* arg) @private
fn void! NativeThread.create(&thread, ThreadFn thread_fn, void* arg)
{
PosixThreadData *thread_data = mem::new(PosixThreadData);
*thread_data = { .thread_fn = thread_fn, .arg = arg };
PosixThreadData *thread_data = mem::new(PosixThreadData, { .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;
import std::os::win32, std::time;
distinct NativeThread = inline Win32_HANDLE;

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
module std::time;
import std::io, std::time::os;
distinct Time = long;
distinct Duration = long;
@@ -77,7 +78,7 @@ fn Time now()
$endif
}
fn Time Time.add_seconds(time, long seconds) => time + (Time)(seconds * (long)MS);
fn Time Time.add_seconds(time, long seconds) => time + (Time)(seconds * (long)SEC);
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,8 +6,6 @@
import platform
import io
import os
import sys
import json
import shutil
import hashlib
@@ -16,7 +14,6 @@ import tempfile
import argparse
import subprocess
import urllib.request
import re
from pathlib import Path
OUTPUT = Path("msvc_temp") # output folder
@@ -176,10 +173,10 @@ for pkg in msvc_packages:
sdk_packages = [
# Windows SDK libs
f"Windows SDK for Windows Store Apps Libs-x86_en-us.msi",
f"Windows SDK Desktop Libs x64-x86_en-us.msi",
"Windows SDK for Windows Store Apps Libs-x86_en-us.msi",
"Windows SDK Desktop Libs x64-x86_en-us.msi",
# CRT headers & libs
f"Universal CRT Headers Libraries and Sources-x86_en-us.msi",
"Universal CRT Headers Libraries and Sources-x86_en-us.msi",
]
with tempfile.TemporaryDirectory() as d:

View File

@@ -1,5 +1,123 @@
# 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
@@ -7,7 +125,7 @@
- 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 lenghts.
- Slice -> array/vector works for constant slice lengths.
### Fixes
- On Aarch64 use the correct frame pointer type.
@@ -193,7 +311,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 `mem::heap()`.
- Temp allocator is now accessed using `mem::temp()`, heap allocator using `allocator::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, mem::heap());
dynamic_arena.init(1024, allocator::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::new_array(int, n);
int* perm1 = mem::new_array(int, n);
int* count = mem::new_array(int, n);
int* perm = mem::alloc_array(int, n);
int* perm1 = mem::alloc_array(int, n);
int* count = mem::alloc_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::new_array(int, n);
int[] perm1 = mem::new_array(int, n);
int* count = mem::new_array(int, n);
int[] perm = mem::alloc_array(int, n);
int[] perm1 = mem::alloc_array(int, n);
int* count = mem::alloc_array(int, n);
int max_flips_count;
int perm_count;
int checksum;

View File

@@ -1,5 +1,6 @@
module arkanoid;
import raylib;
import std::math;
/**
*
* raylib - classic game: arkanoid
@@ -102,7 +103,7 @@ fn void main()
// Initialize game variables
fn void init_game()
{
brick_size = { raylib::get_screen_width() / BRICKS_PER_LINE, 40 };
brick_size = { (float)raylib::get_screen_width() / BRICKS_PER_LINE, 40 };
// Initialize player
player.position = { SCREEN_WIDTH/2, SCREEN_HEIGHT * 7 / 8 };
@@ -201,7 +202,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::fabs((double)ball.position.x - brick[i][j].position.x)) < (double)(brick_size.x / 2 + ball.radius * 2 / 3)) && (ball.speed.y < 0))
((math::abs(ball.position.x - brick[i][j].position.x)) < (brick_size.x / 2 + ball.radius * 2.0f / 3)) && (ball.speed.y < 0))
{
brick[i][j].active = false;
ball.speed.y *= -1;
@@ -209,7 +210,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::fabs((double)ball.position.x - brick[i][j].position.x)) < (double)(brick_size.x/2 + ball.radius*2/3)) && (ball.speed.y > 0))
((math::abs(ball.position.x - brick[i][j].position.x)) < (brick_size.x / 2 + ball.radius * 2.0f / 3)) && (ball.speed.y > 0))
{
brick[i][j].active = false;
ball.speed.y *= -1;
@@ -217,7 +218,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::fabs((double)ball.position.y - brick[i][j].position.y)) < (double)(brick_size.y/2 + ball.radius*2/3)) && (ball.speed.x > 0))
((math::abs(ball.position.y - brick[i][j].position.y)) < (brick_size.y / 2 + ball.radius * 2.0f / 3)) && (ball.speed.x > 0))
{
brick[i][j].active = false;
ball.speed.x *= -1;
@@ -225,7 +226,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::fabs((double)ball.position.y - brick[i][j].position.y)) < (double)(brick_size.y/2 + ball.radius*2/3)) && (ball.speed.x < 0))
((math::abs(ball.position.y - brick[i][j].position.y)) < (brick_size.y / 2 + ball.radius * 2.0f / 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 + 1) % 4);
snake_direction = (SnakeDirection)((snake_direction.ordinal + 1) % 4);
allow_move = false;
}
if (raylib::is_key_pressed(keyboard::LEFT) && allow_move)
{
snake_direction = (SnakeDirection)((snake_direction + 3) % 4);
snake_direction = (SnakeDirection)((snake_direction.ordinal + 3) % 4);
allow_move = false;
}
@@ -179,13 +179,13 @@ fn void update_game()
if (!fruit.active)
{
fruit.active = true;
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 };
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 };
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 = { 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 };
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 };
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({SQUARE_SIZE * i + offset.x/2, offset.y/2}, {SQUARE_SIZE * i + offset.x/2, SCREEN_HEIGHT - offset.y/2}, raylib::LIGHTGRAY);
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);
}
for (int i = 0; i < SCREEN_HEIGHT/SQUARE_SIZE + 1; i++)
{
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);
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);
}
// Draw snake
@@ -248,5 +248,4 @@ 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::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));
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));
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)

View File

@@ -1,3 +1,4 @@
%option yylineno
D [0-9]
DU [0-9_]
@@ -18,12 +19,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
@@ -167,29 +168,31 @@ ${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}{E}?{REALTYPE}? { count(); return(REAL); }
{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); }
\'(\\.|[^\\'])*\' { count(); return(CHAR_LITERAL); }
\'(\\[ux]{HEX}|\\.|[^\\'])\' { 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,10 +4,11 @@
#define YYERROR_VERBOSE
int yydebug = 1;
extern char yytext[];
extern int column;
extern int column, yylineno;
int yylex(void);
void yyerror(char *s);
void yyerror(const char *s);
%}
%locations
%token IDENT HASH_IDENT CT_IDENT CONST_IDENT
%token TYPE_IDENT CT_TYPE_IDENT
@@ -39,9 +40,9 @@ void yyerror(char *s);
%%
path
: IDENT SCOPE
| path IDENT SCOPE
;
: IDENT SCOPE
| path IDENT SCOPE
;
path_const
: path CONST_IDENT
@@ -66,7 +67,7 @@ ident_expr
local_ident_expr
: CT_IDENT
| HASH_IDENT
| HASH_IDENT
;
ct_call
@@ -91,9 +92,9 @@ ct_analyse
ct_arg
: CT_VACONST
| CT_VAARG
| CT_VAREF
| CT_VAEXPR
| CT_VAARG
| CT_VAREF
| CT_VAEXPR
;
flat_path
@@ -126,12 +127,16 @@ 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
@@ -170,7 +175,6 @@ range_expr
| DOTDOT
;
call_inline_attributes
: AT_IDENT
| call_inline_attributes AT_IDENT
@@ -203,7 +207,7 @@ call_trailing
;
call_stmt_expr
: base_expr
: base_expr_assignable
| call_stmt_expr call_trailing
;
@@ -239,7 +243,7 @@ mult_op
: '*'
| '/'
| '%'
;
;
mult_expr
: unary_expr
@@ -266,12 +270,11 @@ shift_stmt_expr
| shift_stmt_expr shift_op mult_expr
;
bit_op
: '&'
| '^'
| '|'
;
: '&'
| '^'
| '|'
;
bit_expr
: shift_expr
@@ -286,7 +289,7 @@ bit_stmt_expr
additive_op
: '+'
| '-'
;
;
additive_expr
: bit_expr
@@ -385,19 +388,20 @@ 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
@@ -415,7 +419,6 @@ expr
: assignment_expr
;
constant_expr
: ternary_expr
;
@@ -431,7 +434,8 @@ param_path
| param_path param_path_element
;
arg : param_path '=' expr
arg
: param_path '=' expr
| type
| param_path '=' type
| expr
@@ -443,21 +447,18 @@ arg : param_path '=' expr
arg_list
: arg
| arg_list ',' arg
| arg_list ','
;
opt_arg_list
: arg_list
| empty
;
call_arg_list
: arg_list
| arg_list ';'
| arg_list ';' parameters
| ';'
| ';' parameters
| empty
;
opt_arg_list_trailing
: arg_list
| arg_list ','
| empty
: opt_arg_list
| opt_arg_list ';'
| opt_arg_list ';' parameters
;
interfaces
@@ -470,10 +471,11 @@ opt_interface_impl
| '(' ')'
| empty
;
enum_constants
: enum_constant
| enum_constants ',' enum_constant
;
: enum_constant
| enum_constants ',' enum_constant
;
enum_list
: enum_constants
@@ -483,7 +485,6 @@ enum_list
enum_constant
: CONST_IDENT opt_attributes
| CONST_IDENT '(' arg_list ')' opt_attributes
| CONST_IDENT '(' arg_list ',' ')' opt_attributes
;
identifier_list
@@ -498,53 +499,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 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 ')'
;
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
@@ -573,22 +574,22 @@ var_decl
;
initializer_list
: '{' opt_arg_list_trailing '}'
: '{' opt_arg_list '}'
;
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
@@ -596,10 +597,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
@@ -607,6 +608,7 @@ ct_switch_stmt
var_stmt
: var_decl ';'
;
decl_stmt_after_type
: local_decl_after_type
@@ -758,7 +760,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
@@ -813,6 +815,7 @@ asm_expr
| INTEGER
| '(' expr ')'
| '[' asm_addr ']'
;
asm_exprs
: asm_expr
@@ -833,7 +836,6 @@ asm_block_stmt
| ASM AT_IDENT '{' '}'
;
/* Order here matches compiler */
statement
: compound_statement
@@ -851,15 +853,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
@@ -884,9 +886,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 ':'
@@ -905,9 +907,11 @@ 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
: '{' '}'
@@ -930,7 +934,6 @@ bitstruct_def
| base_type IDENT ':' constant_expr ';'
;
attribute_name
: AT_IDENT
| AT_TYPE_IDENT
@@ -954,9 +957,9 @@ attribute_param_list
;
attribute
: attribute_name
| attribute_name '(' attribute_param_list ')'
;
: attribute_name
| attribute_name '(' attribute_param_list ')'
;
attribute_list
: attribute
@@ -964,9 +967,9 @@ attribute_list
;
opt_attributes
: attribute_list
| empty
;
: attribute_list
| empty
;
trailing_block_param
: AT_IDENT
@@ -987,7 +990,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
@@ -997,16 +1000,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
@@ -1020,16 +1023,15 @@ 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
@@ -1040,14 +1042,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
@@ -1070,11 +1072,15 @@ fn_parameter_list
| '(' ')'
;
parameter_default
: parameter
| parameter '=' expr
;
parameters
: parameter '=' expr
| parameter
| parameters ',' parameter
| parameters ',' parameter '=' expr
: parameter_default
| parameters ',' parameter_default
| parameters ','
;
parameter
@@ -1082,7 +1088,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
@@ -1107,11 +1113,12 @@ func_definition
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
@@ -1130,8 +1137,6 @@ typedef_type
| type
;
multi_declaration
: ',' IDENT
| multi_declaration ',' IDENT
@@ -1143,27 +1148,11 @@ 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 ';'
;
opt_tl_stmts
: top_level_statements
| empty
: global_storage optional_type IDENT opt_attributes ';'
| global_storage optional_type IDENT multi_declaration opt_attributes ';'
| global_storage optional_type IDENT opt_attributes '=' expr ';'
;
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 '}'
@@ -1178,17 +1167,15 @@ 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 ';'
;
@@ -1206,28 +1193,15 @@ distinct_declaration
: DISTINCT TYPE_IDENT opt_interface_impl opt_attributes '=' opt_inline type ';'
;
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 ';'
@@ -1240,18 +1214,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
@@ -1267,8 +1241,6 @@ top_level
| ct_assert_stmt
| ct_echo_stmt
| ct_include_stmt
| tl_ct_if
| tl_ct_switch
| struct_declaration
| fault_declaration
| enum_declaration
@@ -1279,17 +1251,17 @@ top_level
| interface_declaration
;
%%
void yyerror(char *s)
void yyerror(const char *s)
{
fflush(stdout);
printf("\n%*s\n%*s\n", column, "^", column, s);
printf(":%d:%d:\n%*s\n%*s\n", yylineno, column, column, "^", column, s);
}
int main(int argc, char *argv[])
{
yyparse();
return 0;
}
int rc = yyparse();
printf(" -> yyparse return %d\n", rc);
return rc;
}

View File

@@ -9,7 +9,7 @@ enum Foo
fn void print_pages()
{
mem::temp().print_pages(io::stdout())!!;
allocator::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 = a.alloc_aligned(val, 128, 16)!!;
void* data = allocator::malloc_aligned(a, 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 = a.calloc_aligned(val, 128, 16)!!;
data = allocator::calloc_aligned(a, 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 = a.realloc_aligned(data, (usz)val + 1, 128, 16)!!;
data = allocator::realloc_aligned(a, 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 = a.realloc_aligned(data, (usz)val + 1, 128, 0)!!;
data = allocator::realloc_aligned(a, 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);
a.free_aligned(data);
allocator::free_aligned(a, data);
}
fn void main()
{
@@ -63,7 +63,7 @@ fn void main()
io::printf("First big: %p\n", first_big);
print_pages();
};
mem::@scoped(mem::temp())
mem::@scoped(allocator::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(mem::temp(), 126);
testAllocator(mem::temp(), 12600);
testAllocator(allocator::temp(), 126);
testAllocator(allocator::temp(), 12600);
ArenaAllocator aa;
aa.init(&&char[1024] {});
testAllocator(&aa, 126);
io::printn("Test dynamic arena");
DynamicArenaAllocator dynamic_arena;
dynamic_arena.init(1024, mem::heap());
dynamic_arena.init(1024, allocator::heap());
testAllocator(&dynamic_arena, 112);
testAllocator(&dynamic_arena, 712);
first_big[3] = 123;

View File

@@ -0,0 +1,17 @@
import std;
struct Overalign
{
double[<33>] x;
}
fn void main()
{
List(<Overalign>) l;
Overalign y;
for (int i = 0; i < 1000; i++)
{
io::printfn("Pushing %d", i);
l.push(y);
if (i > 3) io::printfn("Diff %d", (usz)l.get_ref(i) - (usz)l.get_ref(i - (usz)1));
}
}

View File

@@ -4,9 +4,9 @@ extern fn int printf(char* message, ...);
macro void @swap(&a, &b)
{
$typeof(a) temp = a;
a = b;
b = temp;
$typeof(*a) temp = *a;
*a = *b;
*b = temp;
}
fn void main()

View File

@@ -1,6 +1,5 @@
module topologicalsort;
extern fn void printf(char* x, ...);
import std::io;
struct InputPair
{
@@ -23,16 +22,14 @@ struct TopoList
fn void sort(InputPair[] pairs, uint elements)
{
InputPair[] result = mem::new_array(InputPair, pairs.len);
InputPair[] result = mem::alloc_array(InputPair, pairs.len);
TopoList* top = mem::new_array(TopoList, elements);
for (int i = 0; i < pairs.len; i++)
foreach (pair : pairs)
{
InputPair pair = pairs[i];
assert(pair.value >= 0 && pair.value < elements);
assert(pair.successor >= 0 && pair.successor < elements);
top[pair.successor].count++;
Entry* successor_entry = mem::new(Entry);
*successor_entry = { pair.successor, null };
Entry* successor_entry = mem::new(Entry, { pair.successor, null });
Entry** next_ref = &top[pair.value].next;
while (*next_ref)
{
@@ -40,7 +37,7 @@ fn void sort(InputPair[] pairs, uint elements)
}
*next_ref = successor_entry;
}
int[] intout = mem::new_array(int, elements);
int[] intout = mem::alloc_array(int, elements);
int count = 0;
while LOOP: (1)
{
@@ -61,10 +58,10 @@ fn void sort(InputPair[] pairs, uint elements)
}
break;
}
printf("Got %d elements.\n", count);
for (int i = 0; i < count; i++)
io::printfn("Got %d elements.", count);
foreach (val : intout[:count])
{
printf("%d\n", intout[i]);
io::printn(val);
}
}

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