mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Compare commits
268 Commits
latest-0.7
...
v0.7.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e80f1b26c | ||
|
|
9299c78747 | ||
|
|
e1a125e326 | ||
|
|
a13eb99962 | ||
|
|
d46733e11a | ||
|
|
ce569462f6 | ||
|
|
9285dfefad | ||
|
|
5246ef83e7 | ||
|
|
0448e50b3d | ||
|
|
2d535aaa25 | ||
|
|
dc1e5323ab | ||
|
|
63abf1c2f8 | ||
|
|
df8904909b | ||
|
|
e986e3a8c0 | ||
|
|
f67da4f315 | ||
|
|
8a4e7b6ce8 | ||
|
|
2b0fb52f65 | ||
|
|
faf073885f | ||
|
|
11b8a9808d | ||
|
|
1ef9c73342 | ||
|
|
92d56b7a35 | ||
|
|
a894adbdd6 | ||
|
|
605a7c4091 | ||
|
|
adabae2a24 | ||
|
|
affb722b23 | ||
|
|
1b2f5989e1 | ||
|
|
e0b6c83a62 | ||
|
|
839f835845 | ||
|
|
2636a855c4 | ||
|
|
0d147a48b2 | ||
|
|
aff3a3f746 | ||
|
|
c95204c3f7 | ||
|
|
794e8371c8 | ||
|
|
2bbc6cbbca | ||
|
|
07bd37da43 | ||
|
|
a0497e9274 | ||
|
|
fa730e7ec2 | ||
|
|
b4a6e3704f | ||
|
|
dd80e8b799 | ||
|
|
5efc721b0c | ||
|
|
20c13c0bb4 | ||
|
|
cd3e924d1e | ||
|
|
2e42868467 | ||
|
|
8d698b5e40 | ||
|
|
2f45beecbe | ||
|
|
1b4b9bca94 | ||
|
|
40ae9d2e55 | ||
|
|
0df538d0e2 | ||
|
|
aa425a0886 | ||
|
|
842788e59d | ||
|
|
2b97d7d59c | ||
|
|
75f78551cf | ||
|
|
01ef53a090 | ||
|
|
a55f56a88f | ||
|
|
eb75d8f82a | ||
|
|
f07bd3cbc6 | ||
|
|
93640699be | ||
|
|
99e29bff8d | ||
|
|
95137db64b | ||
|
|
e7ce79e731 | ||
|
|
779f548a00 | ||
|
|
f0bd93d1f0 | ||
|
|
3ce15bd7af | ||
|
|
07eee04e94 | ||
|
|
1f7b62b248 | ||
|
|
b2c994618f | ||
|
|
2afa544d7d | ||
|
|
dda2d2ecbe | ||
|
|
f79f6d4001 | ||
|
|
cf167c9446 | ||
|
|
f0201f971e | ||
|
|
a3abea1a33 | ||
|
|
5f6f52838c | ||
|
|
e0237096d6 | ||
|
|
82491a6f85 | ||
|
|
1aacb1fa60 | ||
|
|
bbd9f6dc96 | ||
|
|
496d23e93f | ||
|
|
e936b999d2 | ||
|
|
becda6ea1d | ||
|
|
2ad17a04d4 | ||
|
|
1617792a35 | ||
|
|
c7b3ae0cf9 | ||
|
|
1dcd40aa5f | ||
|
|
40554192b1 | ||
|
|
9bc5e259d2 | ||
|
|
f66cadccd2 | ||
|
|
be511b26cd | ||
|
|
4cfa5441d2 | ||
|
|
5e45c34f21 | ||
|
|
d7a11903c7 | ||
|
|
b893697a87 | ||
|
|
f2daf2e11e | ||
|
|
9baeca3a8e | ||
|
|
ef649050c4 | ||
|
|
d6d0e08906 | ||
|
|
c9d9127da6 | ||
|
|
7f85534414 | ||
|
|
ba1332dc2a | ||
|
|
45a0895c39 | ||
|
|
72cc8e430a | ||
|
|
9645bd3289 | ||
|
|
8fc01d4e1a | ||
|
|
a48e2274e5 | ||
|
|
786e47408a | ||
|
|
6e348d1e71 | ||
|
|
d697b910ba | ||
|
|
4d848f1707 | ||
|
|
6377f0573d | ||
|
|
c3d2b2824c | ||
|
|
18e408ead4 | ||
|
|
08c63108a1 | ||
|
|
da25a411f9 | ||
|
|
e685414829 | ||
|
|
ae5a74bc41 | ||
|
|
76374d31c4 | ||
|
|
ffd7a5e483 | ||
|
|
d143ec227c | ||
|
|
f2703508f2 | ||
|
|
bb96dc931e | ||
|
|
a5a2b00ec8 | ||
|
|
00f1206f3c | ||
|
|
349d9ef3cf | ||
|
|
9f30b56e13 | ||
|
|
83d6b35afe | ||
|
|
f4b9f375e0 | ||
|
|
be3f9007c9 | ||
|
|
b665e2cbe5 | ||
|
|
0ed68f94cf | ||
|
|
966e8107f8 | ||
|
|
61a4dcc807 | ||
|
|
52541a03eb | ||
|
|
972c84b65b | ||
|
|
f668b96cc9 | ||
|
|
9461873b4c | ||
|
|
8d563eba7a | ||
|
|
fe98225f0a | ||
|
|
bae3e59217 | ||
|
|
b5ddc36d7f | ||
|
|
c2c0ecded8 | ||
|
|
9d5b31dad5 | ||
|
|
6c0e94cad9 | ||
|
|
84aee6a25b | ||
|
|
71a765c66e | ||
|
|
5c3b637cf6 | ||
|
|
bd1de1e7dc | ||
|
|
3cd2267b0a | ||
|
|
7fcc91edc8 | ||
|
|
9052f07c19 | ||
|
|
c7f0d54328 | ||
|
|
498803e9ba | ||
|
|
082457c5fb | ||
|
|
23897bc9a4 | ||
|
|
8ada2a70d9 | ||
|
|
a91330b7d1 | ||
|
|
2f3954a7d9 | ||
|
|
b7ae5dce8b | ||
|
|
91db6ceeda | ||
|
|
fc2f718d9e | ||
|
|
64ef3fc756 | ||
|
|
93dd432b62 | ||
|
|
6c822e5aa3 | ||
|
|
8c741c617c | ||
|
|
b83e57b952 | ||
|
|
24ebe975d8 | ||
|
|
511ae0da00 | ||
|
|
36eb650228 | ||
|
|
50b4d7aa35 | ||
|
|
abe4727c3a | ||
|
|
c528f53d58 | ||
|
|
83955ea5b5 | ||
|
|
fc5c70a628 | ||
|
|
5287640140 | ||
|
|
634438eb82 | ||
|
|
164c901ae6 | ||
|
|
54e70cae0f | ||
|
|
30ec200492 | ||
|
|
584a8a2e60 | ||
|
|
3f07d1c7b8 | ||
|
|
125436d23e | ||
|
|
900365c25e | ||
|
|
d313afa487 | ||
|
|
a411f20762 | ||
|
|
8a0907cb70 | ||
|
|
8a09b2e5f7 | ||
|
|
bfccc303d1 | ||
|
|
0d3299f267 | ||
|
|
11bb8b49da | ||
|
|
f0d2b0eff0 | ||
|
|
005cc08118 | ||
|
|
c5494a23ce | ||
|
|
5dcc67aa1b | ||
|
|
335f53fb64 | ||
|
|
3636898ac0 | ||
|
|
5ba24e05d0 | ||
|
|
0ada5504af | ||
|
|
8ac02a28cc | ||
|
|
246957b8bd | ||
|
|
0595270d9a | ||
|
|
8b86d1461d | ||
|
|
0129308bf3 | ||
|
|
05094b4f47 | ||
|
|
9a59cd164d | ||
|
|
3eecaf9e29 | ||
|
|
8b47673524 | ||
|
|
8b29e4780d | ||
|
|
fd2a81afb1 | ||
|
|
a0d4df2272 | ||
|
|
e39c7cae8d | ||
|
|
8a2907806b | ||
|
|
f778e75757 | ||
|
|
6ab7953706 | ||
|
|
42e4370994 | ||
|
|
9a1fdbbca0 | ||
|
|
434a0e8e4b | ||
|
|
946c167bf1 | ||
|
|
ba10c8953d | ||
|
|
72d7813c20 | ||
|
|
1083de1f81 | ||
|
|
b4b6cba301 | ||
|
|
3244898610 | ||
|
|
6454856fdb | ||
|
|
5cf48ad730 | ||
|
|
b5d0739de0 | ||
|
|
f6e130ad3c | ||
|
|
f9e62b80ea | ||
|
|
debbae594c | ||
|
|
37ffd92f7b | ||
|
|
a44e932806 | ||
|
|
668175851b | ||
|
|
e7c9ec0938 | ||
|
|
d6fa9cd50b | ||
|
|
41e173d255 | ||
|
|
fde2bb2a7e | ||
|
|
0a9bb2e8e0 | ||
|
|
b64dcde21d | ||
|
|
eade5fa57a | ||
|
|
f85198e3ee | ||
|
|
dca805bd8a | ||
|
|
3888fcb182 | ||
|
|
de73265d28 | ||
|
|
cb895754c8 | ||
|
|
6e42bfef3b | ||
|
|
01357ef6d7 | ||
|
|
89d205258e | ||
|
|
0f2d425297 | ||
|
|
28fc03c376 | ||
|
|
1290906d66 | ||
|
|
25d416aca1 | ||
|
|
8cce7f6836 | ||
|
|
4c26adb376 | ||
|
|
94b8330ac5 | ||
|
|
3cb5df5639 | ||
|
|
ded5fde2d5 | ||
|
|
65fb977e89 | ||
|
|
e3f3b6f5f1 | ||
|
|
ab4ed9472a | ||
|
|
1668999f90 | ||
|
|
e828d9a05a | ||
|
|
47447dc069 | ||
|
|
39a59c929f | ||
|
|
f355738dda | ||
|
|
87e254e4b1 | ||
|
|
561a683230 | ||
|
|
63e5aa58c5 | ||
|
|
2be3071bdb | ||
|
|
d3e81b193a | ||
|
|
586d191585 |
48
.github/workflows/main.yml
vendored
48
.github/workflows/main.yml
vendored
@@ -10,7 +10,7 @@ env:
|
||||
LLVM_RELEASE_VERSION_WINDOWS: 18
|
||||
LLVM_RELEASE_VERSION_MAC: 17
|
||||
LLVM_RELEASE_VERSION_LINUX: 17
|
||||
LLVM_RELEASE_VERSION_UBUNTU20: 17
|
||||
LLVM_RELEASE_VERSION_UBUNTU22: 17
|
||||
LLVM_DEV_VERSION: 21
|
||||
jobs:
|
||||
|
||||
@@ -77,15 +77,15 @@ jobs:
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55_v7
|
||||
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55
|
||||
|
||||
- name: Try raylib5
|
||||
run: |
|
||||
cd resources
|
||||
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55_v7
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55_v7 --print-linking examples\raylib\raylib_arkanoid.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55_v7 --print-linking examples\raylib\raylib_snake.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55_v7 --print-linking examples\raylib\raylib_tetris.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_arkanoid.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_snake.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_tetris.c3
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
@@ -158,7 +158,7 @@ jobs:
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
./build/c3c vendor-fetch raylib55_v7
|
||||
./build/c3c vendor-fetch raylib55
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
@@ -401,8 +401,8 @@ jobs:
|
||||
name: c3-linux-${{matrix.build_type}}
|
||||
path: c3-linux-${{matrix.build_type}}.tar.gz
|
||||
|
||||
build-linux-ubuntu20:
|
||||
runs-on: ubuntu-20.04
|
||||
build-linux-ubuntu22:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
@@ -507,7 +507,7 @@ jobs:
|
||||
../build/c3c compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/
|
||||
|
||||
- name: bundle_output
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU22
|
||||
run: |
|
||||
mkdir c3
|
||||
cp -r lib c3
|
||||
@@ -515,15 +515,15 @@ jobs:
|
||||
cp releasenotes.md c3
|
||||
cp msvc_build_libraries.py c3
|
||||
cp build/c3c c3
|
||||
tar czf c3-ubuntu-20-${{matrix.build_type}}.tar.gz c3
|
||||
tar czf c3-ubuntu-22-${{matrix.build_type}}.tar.gz c3
|
||||
|
||||
- name: upload artifacts
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU22
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: c3-ubuntu-20-${{matrix.build_type}}
|
||||
path: c3-ubuntu-20-${{matrix.build_type}}.tar.gz
|
||||
|
||||
name: c3-ubuntu-22-${{matrix.build_type}}
|
||||
path: c3-ubuntu-22-${{matrix.build_type}}.tar.gz
|
||||
|
||||
build-with-docker:
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
@@ -642,7 +642,7 @@ jobs:
|
||||
|
||||
- name: Vendor-fetch
|
||||
run: |
|
||||
./build/c3c vendor-fetch raylib55_v7
|
||||
./build/c3c vendor-fetch raylib55
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
@@ -749,7 +749,7 @@ jobs:
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu20]
|
||||
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu22]
|
||||
if: github.ref == 'refs/heads/master'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -769,19 +769,19 @@ jobs:
|
||||
- run: zip -r c3-windows-debug.zip c3-windows-Debug
|
||||
- run: mv c3-linux-Release/c3-linux-Release.tar.gz c3-linux-Release/c3-linux.tar.gz
|
||||
- run: mv c3-linux-Debug/c3-linux-Debug.tar.gz c3-linux-Debug/c3-linux-debug.tar.gz
|
||||
- run: mv c3-ubuntu-20-Release/c3-ubuntu-20-Release.tar.gz c3-ubuntu-20-Release/c3-ubuntu-20.tar.gz
|
||||
- run: mv c3-ubuntu-20-Debug/c3-ubuntu-20-Debug.tar.gz c3-ubuntu-20-Debug/c3-ubuntu-20-debug.tar.gz
|
||||
- run: mv c3-ubuntu-22-Release/c3-ubuntu-22-Release.tar.gz c3-ubuntu-22-Release/c3-ubuntu-22.tar.gz
|
||||
- run: mv c3-ubuntu-22-Debug/c3-ubuntu-22-Debug.tar.gz c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz
|
||||
- run: mv c3-macos-Release/c3-macos-Release.zip c3-macos-Release/c3-macos.zip
|
||||
- run: mv c3-macos-Debug/c3-macos-Debug.zip c3-macos-Debug/c3-macos-debug.zip
|
||||
- run: gh release delete latest-0.7.0-prerelease --cleanup-tag -y || true
|
||||
- run: echo "RELEASE_NAME=latest-0.7.0-rc-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV
|
||||
- run: gh release delete latest-prerelease --cleanup-tag -y || true
|
||||
- run: echo "RELEASE_NAME=latest-prerelease-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV
|
||||
|
||||
- id: create_release
|
||||
uses: softprops/action-gh-release@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: latest-0.7.0-prerelease
|
||||
tag_name: latest-prerelease
|
||||
name: ${{ env.RELEASE_NAME }}
|
||||
draft: false
|
||||
prerelease: true
|
||||
@@ -790,7 +790,7 @@ jobs:
|
||||
c3-windows-debug.zip
|
||||
c3-linux-Release/c3-linux.tar.gz
|
||||
c3-linux-Debug/c3-linux-debug.tar.gz
|
||||
c3-ubuntu-20-Release/c3-ubuntu-20.tar.gz
|
||||
c3-ubuntu-20-Debug/c3-ubuntu-20-debug.tar.gz
|
||||
c3-ubuntu-22-Release/c3-ubuntu-22.tar.gz
|
||||
c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz
|
||||
c3-macos-Release/c3-macos.zip
|
||||
c3-macos-Debug/c3-macos-debug.zip
|
||||
|
||||
263
CMakeLists.txt
263
CMakeLists.txt
@@ -1,5 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(C3_LLVM_MIN_VERSION 17)
|
||||
set(C3_LLVM_MAX_VERSION 21)
|
||||
set(C3_LLVM_DEFAULT_VERSION 19)
|
||||
|
||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
|
||||
message(FATAL_ERROR "In-tree build detected, please build in a separate directory")
|
||||
endif()
|
||||
|
||||
# Grab the version
|
||||
file(READ "src/version.h" ver)
|
||||
if (NOT ${ver} MATCHES "COMPILER_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"")
|
||||
@@ -7,8 +15,20 @@ if (NOT ${ver} MATCHES "COMPILER_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"")
|
||||
endif()
|
||||
|
||||
# Set the project and version
|
||||
project(c3c VERSION ${CMAKE_MATCH_1})
|
||||
message("C3C version: ${CMAKE_PROJECT_VERSION}")
|
||||
project(c3c VERSION ${CMAKE_MATCH_1} LANGUAGES C CXX)
|
||||
message("Configuring C3C ${CMAKE_PROJECT_VERSION} for ${CMAKE_SYSTEM_NAME}")
|
||||
|
||||
# Helper functions
|
||||
function(c3_print_variables)
|
||||
set(msg "")
|
||||
foreach(var ${ARGN})
|
||||
if(msg)
|
||||
string(APPEND msg " ; ")
|
||||
endif()
|
||||
string(APPEND msg "${c3_print_prefix}${var}=\"${${var}}\"")
|
||||
endforeach()
|
||||
message(STATUS "${msg}")
|
||||
endfunction()
|
||||
|
||||
# Avoid warning for FetchContent
|
||||
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
|
||||
@@ -16,7 +36,7 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
|
||||
if (MSVC)
|
||||
if (WIN32)
|
||||
set(CMAKE_INSTALL_LIBDIR "c:\\c3c\\lib")
|
||||
set(CMAKE_INSTALL_BINDIR "c:\\c3c")
|
||||
else ()
|
||||
@@ -36,36 +56,41 @@ set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# Use /MT or /MTd
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
|
||||
if(MSVC)
|
||||
message(STATUS "MSVC version ${MSVC_VERSION}")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /EHsc /utf-8")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2 /EHsc /utf-8")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi /EHa /utf-8")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi /EHa /utf-8")
|
||||
add_compile_options(/utf-8)
|
||||
else()
|
||||
if (true)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O0 -fno-exceptions")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -O0 -fno-exceptions")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fno-exceptions")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -fno-exceptions")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined,address -fno-exceptions")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -O1 -fsanitize=undefined,address -fno-exceptions")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined,address -fno-exceptions")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -O1 -fsanitize=undefined,address -fno-exceptions")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined,address -fno-exceptions")
|
||||
endif()
|
||||
add_compile_options(-gdwarf-3 -fno-exceptions)
|
||||
|
||||
# add_compile_options(-fsanitize=address,undefined)
|
||||
# add_link_options(-fsanitize=address,undefined)
|
||||
endif()
|
||||
|
||||
option(C3_LINK_DYNAMIC "link dynamically with LLVM/LLD libs")
|
||||
# Options
|
||||
set(C3_LINK_DYNAMIC OFF CACHE BOOL "Link dynamically with LLVM/LLD libs")
|
||||
set(C3_WITH_LLVM ON CACHE BOOL "Build with LLVM")
|
||||
set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
|
||||
set(C3_USE_MIMALLOC OFF CACHE BOOL "Use built-in mimalloc")
|
||||
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
|
||||
set(C3_USE_TB OFF CACHE BOOL "Use TB")
|
||||
set(C3_LLD_DIR "" CACHE STRING "Use custom LLD directory")
|
||||
set(C3_ENABLE_CLANGD_LSP OFF CACHE BOOL "Enable/Disable output of compile commands during generation")
|
||||
set(LLVM_CRT_LIBRARY_DIR "" CACHE STRING "Use custom llvm's compiler-rt directory")
|
||||
|
||||
set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
|
||||
option(C3_USE_MIMALLOC "Use built-in mimalloc" OFF)
|
||||
option(C3_USE_TB "Use TB" OFF)
|
||||
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
|
||||
option(C3_WITH_LLVM "Build with LLVM" ON)
|
||||
option(C3_LLD_DIR "Use custom LLD directory" "")
|
||||
option(LLVM_CRT_LIBRARY_DIR "Use custom llvm's compiler-rt directory" "")
|
||||
set(C3_OPTIONS
|
||||
C3_LINK_DYNAMIC
|
||||
C3_WITH_LLVM
|
||||
C3_LLVM_VERSION
|
||||
C3_USE_MIMALLOC
|
||||
C3_MIMALLOC_TAG
|
||||
C3_USE_TB
|
||||
C3_LLD_DIR
|
||||
C3_ENABLE_CLANGD_LSP
|
||||
LLVM_CRT_LIBRARY_DIR
|
||||
)
|
||||
|
||||
set(C3_USE_MIMALLOC OFF)
|
||||
if(C3_USE_MIMALLOC)
|
||||
@@ -83,13 +108,6 @@ endif()
|
||||
if (NOT WIN32)
|
||||
find_package(CURL)
|
||||
endif()
|
||||
if(C3_WITH_LLVM)
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
if (${C3_LLVM_VERSION} VERSION_LESS 17 OR ${C3_LLVM_VERSION} VERSION_GREATER 21)
|
||||
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Git QUIET)
|
||||
if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
@@ -107,7 +125,6 @@ if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
endif()
|
||||
|
||||
# Clangd LSP support
|
||||
option(C3_ENABLE_CLANGD_LSP "Enable/Disable output of compile commands during generation." OFF)
|
||||
if(C3_ENABLE_CLANGD_LSP)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
execute_process(
|
||||
@@ -120,7 +137,7 @@ endif(C3_ENABLE_CLANGD_LSP)
|
||||
if(C3_WITH_LLVM)
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
if (C3_LLVM_VERSION STREQUAL "auto")
|
||||
set(C3_LLVM_VERSION "19")
|
||||
set(C3_LLVM_VERSION ${C3_LLVM_DEFAULT_VERSION})
|
||||
endif()
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows
|
||||
@@ -139,11 +156,26 @@ if(C3_WITH_LLVM)
|
||||
FetchContent_MakeAvailable(LLVM_Windows)
|
||||
set(llvm_dir ${llvm_windows_SOURCE_DIR})
|
||||
endif()
|
||||
message("Loaded Windows LLVM libraries into ${llvm_dir}")
|
||||
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
|
||||
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
find_package(LLD REQUIRED CONFIG)
|
||||
else()
|
||||
# Add paths for LLVM CMake files of version 19 and higher as they follow a new installation
|
||||
# layout and are now in /usr/lib/llvm/*/lib/cmake/llvm/ rather than /usr/lib/cmake/llvm/
|
||||
#
|
||||
# Because of CMAKE_FIND_PACKAGE_SORT_ORDER CMAKE_FIND_PACKAGE_SORT_DIRECTION,
|
||||
# the newest version will always be found first.
|
||||
c3_print_variables(CMAKE_PREFIX_PATH)
|
||||
if (DEFINED LLVM_DIR)
|
||||
message(STATUS "Looking for LLVM CMake files in user-specified directory ${LLVM_DIR}")
|
||||
else()
|
||||
file (GLOB LLVM_CMAKE_PATHS "/usr/lib/llvm/*/lib/cmake/llvm/")
|
||||
list (APPEND CMAKE_PREFIX_PATH ${LLVM_CMAKE_PATHS} "/usr/lib/")
|
||||
message(STATUS "No LLVM_DIR specified, searching default directories ${CMAKE_PREFIX_PATH}")
|
||||
endif()
|
||||
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
|
||||
else()
|
||||
@@ -151,6 +183,10 @@ if(C3_WITH_LLVM)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (EXISTS /opt/homebrew/lib)
|
||||
list(APPEND LLVM_LIBRARY_DIRS /opt/homebrew/lib)
|
||||
endif()
|
||||
|
||||
if (EXISTS /usr/lib)
|
||||
# Some systems (such as Alpine Linux) seem to put some of the relevant
|
||||
# LLVM files in /usr/lib, but this doesn't seem to be included in the
|
||||
@@ -158,12 +194,15 @@ if(C3_WITH_LLVM)
|
||||
list(APPEND LLVM_LIBRARY_DIRS /usr/lib)
|
||||
endif()
|
||||
|
||||
list(REMOVE_DUPLICATES LLVM_LIBRARY_DIRS)
|
||||
|
||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
|
||||
message(STATUS "LLVM libraries located in: ${LLVM_LIBRARY_DIRS}")
|
||||
|
||||
if (NOT LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL 15.0)
|
||||
message(FATAL_ERROR "LLVM version 15.0 or later is required.")
|
||||
if (${LLVM_PACKAGE_VERSION} VERSION_LESS C3_LLVM_MIN_VERSION OR
|
||||
${LLVM_PACKAGE_VERSION} VERSION_GREATER C3_LLVM_MAX_VERSION)
|
||||
message(FATAL_ERROR "LLVM ${LLVM_PACKAGE_VERSION} is not supported! LLVM version between ${C3_LLVM_MIN_VERSION} and ${C3_LLVM_MAX_VERSION} is required.")
|
||||
endif()
|
||||
|
||||
if(LLVM_ENABLE_RTTI)
|
||||
@@ -213,44 +252,35 @@ if(C3_WITH_LLVM)
|
||||
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
|
||||
|
||||
if(NOT ${C3_LLD_DIR} EQUAL "" AND EXISTS ${C3_LLD_DIR})
|
||||
message("C3_LLD_DIR: " ${C3_LLD_DIR})
|
||||
set(LLVM_LIBRARY_DIRS
|
||||
"${LLVM_LIBRARY_DIRS}"
|
||||
"${C3_LLD_DIR}"
|
||||
)
|
||||
list(APPEND LLVM_LIBRARY_DIRS ${C3_LLD_DIR})
|
||||
list(REMOVE_DUPLICATES LLVM_LIBRARY_DIRS)
|
||||
endif()
|
||||
|
||||
message(STATUS "Looking for static lld libraries in ${LLVM_LIBRARY_DIRS}")
|
||||
|
||||
# These don't seem to be reliable on windows.
|
||||
message(STATUS "using find_library")
|
||||
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
else()
|
||||
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
message(STATUS "Looking for shared lld libraries in ${LLVM_LIBRARY_DIRS}")
|
||||
|
||||
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
set(llvm_libs ${LLVM})
|
||||
|
||||
# These don't seem to be reliable on windows.
|
||||
message(STATUS "using find_library")
|
||||
find_library(LLD_COFF NAMES liblldCOFF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_ELF NAMES liblldELF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MACHO NAMES liblldMachO.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_WASM NAMES liblldWasm.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COFF NAMES liblldCOFF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_ELF NAMES liblldELF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_MACHO NAMES liblldMachO.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_WASM NAMES liblldWasm.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT(${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR}))
|
||||
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
|
||||
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
# find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
set(lld_libs
|
||||
${LLD_COFF}
|
||||
${LLD_WASM}
|
||||
@@ -275,8 +305,8 @@ if(C3_WITH_LLVM)
|
||||
)
|
||||
endif()
|
||||
|
||||
message(STATUS "linking to llvm libs ${lld_libs}")
|
||||
message(STATUS "Found lld libs ${lld_libs}")
|
||||
message(STATUS "Linking to llvm libs ${llvm_libs}")
|
||||
message(STATUS "Linking to lld libs ${lld_libs}")
|
||||
endif()
|
||||
|
||||
add_library(miniz STATIC dependencies/miniz/miniz.c)
|
||||
@@ -394,6 +424,11 @@ if(C3_WITH_LLVM)
|
||||
|
||||
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1)
|
||||
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
|
||||
if (MSVC)
|
||||
target_compile_options(c3c PRIVATE
|
||||
"$<$<CONFIG:Debug>:/EHa>"
|
||||
"$<$<CONFIG:Release>:/EHsc>")
|
||||
endif()
|
||||
else()
|
||||
target_sources(c3c PRIVATE src/utils/hostinfo.c)
|
||||
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=0)
|
||||
@@ -485,34 +520,27 @@ endif()
|
||||
|
||||
|
||||
if(MSVC)
|
||||
message("Adding MSVC options")
|
||||
target_compile_options(c3c PRIVATE /wd4068 /wd4090 /WX /Wv:18)
|
||||
target_compile_options(c3c PRIVATE
|
||||
/wd4068
|
||||
/wd4090
|
||||
/WX
|
||||
/Wv:18
|
||||
)
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
target_compile_options(c3c_wrappers PUBLIC /wd4624 /wd4267 /wd4244 /WX /Wv:18)
|
||||
target_compile_options(c3c_wrappers PUBLIC
|
||||
/wd4624
|
||||
/wd4267
|
||||
/wd4244
|
||||
/WX
|
||||
/Wv:18
|
||||
)
|
||||
if(NOT LLVM_ENABLE_RTTI)
|
||||
target_compile_options(c3c_wrappers PUBLIC /GR-)
|
||||
endif()
|
||||
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_options(c3c PUBLIC /MTd)
|
||||
if (C3_WITH_LLVM)
|
||||
target_compile_options(c3c_wrappers PUBLIC /MTd)
|
||||
endif()
|
||||
target_compile_options(miniz PUBLIC /MTd)
|
||||
if (C3_USE_TB)
|
||||
target_compile_options(tilde-backend PUBLIC /MTd)
|
||||
endif()
|
||||
else()
|
||||
target_compile_options(c3c PUBLIC /MT)
|
||||
if (C3_WITH_LLVM)
|
||||
target_compile_options(c3c_wrappers PUBLIC /MT)
|
||||
endif()
|
||||
target_compile_options(miniz PUBLIC /MT)
|
||||
if (C3_USE_TB)
|
||||
target_compile_options(tilde-backend PUBLIC /MT)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
|
||||
set(sanitizer_runtime_libraries
|
||||
@@ -522,13 +550,20 @@ if(MSVC)
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "using gcc/clang warning switches")
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
if (C3_WITH_LLVM AND NOT LLVM_ENABLE_RTTI)
|
||||
target_compile_options(c3c_wrappers PRIVATE -fno-rtti)
|
||||
endif()
|
||||
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
|
||||
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
||||
target_compile_options(c3c PRIVATE
|
||||
-pthread
|
||||
-Wall
|
||||
-Werror
|
||||
-Wno-unknown-pragmas
|
||||
-Wno-unused-result
|
||||
-Wno-unused-function
|
||||
-Wno-unused-variable
|
||||
-Wno-unused-parameter
|
||||
)
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
endif()
|
||||
|
||||
install(TARGETS c3c DESTINATION bin)
|
||||
@@ -539,6 +574,12 @@ if (NOT WIN32)
|
||||
install(FILES c3c.1 DESTINATION "share/man/man1")
|
||||
endif()
|
||||
|
||||
# Copy stdlib
|
||||
if (NOT ${CMAKE_BINARY_DIR} EQUAL ${CMAKE_SOURCE_DIR})
|
||||
file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/lib)
|
||||
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
|
||||
add_custom_command(TARGET c3c POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E rm -rf -- $<TARGET_FILE_DIR:c3c>/c3c_rt
|
||||
@@ -558,3 +599,35 @@ if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
|
||||
endif()
|
||||
|
||||
feature_summary(WHAT ALL)
|
||||
|
||||
message(STATUS "Building ${CMAKE_PROJECT_NAME} with the following configuration:")
|
||||
|
||||
set(c3_print_prefix " ")
|
||||
|
||||
foreach(option IN LISTS C3_OPTIONS)
|
||||
if (DEFINED ${option})
|
||||
c3_print_variables(${option})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(flag_var
|
||||
CMAKE_BUILD_TYPE
|
||||
CMAKE_C_COMPILER
|
||||
CMAKE_CXX_COMPILER
|
||||
CMAKE_LINKER
|
||||
CMAKE_OBJCOPY
|
||||
CMAKE_STRIP
|
||||
CMAKE_DLLTOOL)
|
||||
c3_print_variables(${flag_var})
|
||||
endforeach()
|
||||
|
||||
message(STATUS "Build flags:")
|
||||
foreach(flag_var
|
||||
CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE
|
||||
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO
|
||||
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
|
||||
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
c3_print_variables(${flag_var})
|
||||
endforeach()
|
||||
|
||||
message(STATUS "Output to: \"${CMAKE_BINARY_DIR}\"")
|
||||
|
||||
57
CMakePresets.json
Normal file
57
CMakePresets.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"version": 3,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "windows-base",
|
||||
"hidden": true,
|
||||
"architecture": {
|
||||
"value": "x64"
|
||||
},
|
||||
"toolset": {
|
||||
"value": "host=x64"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-vs-2022-release",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"displayName": "Windows x64 Visual Studio 17 2022",
|
||||
"inherits": "windows-base",
|
||||
"binaryDir": "build",
|
||||
"cacheVariables": {
|
||||
"CMAKE_CONFIGURATION_TYPES": "Release;RelWithDebInfo",
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "windows-vs-2022-debug",
|
||||
"generator": "Visual Studio 17 2022",
|
||||
"displayName": "Windows x64 Visual Studio 17 2022 (Debug)",
|
||||
"inherits": "windows-base",
|
||||
"binaryDir": "build-debug",
|
||||
"cacheVariables": {
|
||||
"CMAKE_CONFIGURATION_TYPES": "Debug",
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
{
|
||||
"name": "windows-vs-2022-debug",
|
||||
"displayName": "Debug",
|
||||
"configurePreset": "windows-vs-2022-debug",
|
||||
"configuration": "Debug"
|
||||
},
|
||||
{
|
||||
"name": "windows-vs-2022-release",
|
||||
"displayName": "Release",
|
||||
"configurePreset": "windows-vs-2022-release",
|
||||
"configuration": "Release"
|
||||
},
|
||||
{
|
||||
"name": "windows-vs-2022-release-with-debug-info",
|
||||
"displayName": "RelWithDebInfo",
|
||||
"configurePreset": "windows-vs-2022-release",
|
||||
"configuration": "RelWithDebInfo"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -151,8 +151,9 @@ one with related functions when working on temporary strings.
|
||||
|
||||
# C3 Standard library style guide.
|
||||
|
||||
When contributing to the standard librairy please to your best to follow the following style requirements
|
||||
as to ensure a consistent style in the stdlib and also make accepting PRs more quickly.
|
||||
When contributing to the standard library please try your best to adhere to the
|
||||
following style requirements to ensure a consistent style in the stdlib and to
|
||||
facilitate accepting PRs more quickly.
|
||||
|
||||
### Braces are placed on the next line
|
||||
|
||||
@@ -256,4 +257,4 @@ argument.
|
||||
## Add tests to your changes
|
||||
|
||||
If you add or fix things, then there should always be tests in `test/unit/stdlib` to verify
|
||||
the functionality.
|
||||
the functionality.
|
||||
|
||||
70
CONTRIBUTING.md
Normal file
70
CONTRIBUTING.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# How to contribute to C3
|
||||
|
||||
The C3 project consists of
|
||||
|
||||
1. The C3 language itself.
|
||||
2. The C3 compiler, called c3c.
|
||||
3. The C3 standard library
|
||||
4. Various tools, such as the editor plugins
|
||||
|
||||
## 1. How to contribute to the C3 language
|
||||
|
||||
The C3 language is essentially the language specification. You can contribute to the language by:
|
||||
|
||||
1. Filing enhancement requests for changes to the language.
|
||||
2. Offering feedback on existing features, on Discord or by filing issues.
|
||||
3. Help working on the language specification.
|
||||
4. Help working on the grammar.
|
||||
|
||||
## 2. How to contribute to the C3 compiler
|
||||
|
||||
The C3 compiler consists for the compiler itself + test suites for testing the compiler.
|
||||
You can contribute by:
|
||||
|
||||
1. File bugs (by far the most important thing).
|
||||
2. Suggest improved diagnostics / error messages.
|
||||
3. Refactoring existing code (needs deep understanding of the compiler).
|
||||
4. Add support for more architectures.
|
||||
5. Add support for more backends.
|
||||
|
||||
## 3. How to contribute to the standard library
|
||||
|
||||
The standard library is the library itself + test suites for testing the standard library.
|
||||
You can contribute by:
|
||||
|
||||
1. Filing bugs on the standard library.
|
||||
2. Write additional unit tests.
|
||||
3. Suggest new functionality by filing an issue.
|
||||
4. Work on stdlib additions.
|
||||
5. Fix bugs in the stdlib
|
||||
6. Maintain a section of the standard library
|
||||
|
||||
### How to work on small stdlib additions
|
||||
|
||||
If there is just a matter of adding a function or two to an existing module, a pull request
|
||||
is sufficient. However, please make sure that:
|
||||
|
||||
1. It follows the guidelines for the code to ensure a uniform experience (naming standard, indentation, braces etc).
|
||||
2. Add a line in the release notes about the change.
|
||||
3. Make sure it has unit tests.
|
||||
|
||||
### How to work on non-trivial additions to the stdlib
|
||||
|
||||
Regardless whether an addition is approved for inclusion or not, it needs to incubate:
|
||||
|
||||
1. First implement it standalone, showing that it’s working well and has a solid design. This has the advantage of people being able to contribute or even create competing implementations
|
||||
2. Once it is considered finished it can be proposed for inclusion.
|
||||
|
||||
This will greatly help improving the quality of additions.
|
||||
|
||||
Note that any new addition needs a full set of unit tests before being included into the standard library.
|
||||
|
||||
### Maintain a part of the standard library
|
||||
|
||||
A single maintainer is insufficient for a standard library, instead we need one or more maintainer
|
||||
for each module. The maintainer(s) will review pull requests and actively work on making the module
|
||||
pristine with the highest possible quality.
|
||||
|
||||
## 4. How to contribute to various tools
|
||||
|
||||
In general, file a pull request. Depending on who maintains it, rules may differ.
|
||||
77
README.md
77
README.md
@@ -8,16 +8,18 @@ for programmers who like C.
|
||||
|
||||
Precompiled binaries for the following operating systems are available:
|
||||
|
||||
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
|
||||
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
|
||||
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
|
||||
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip), [install instructions](#installing-on-macos-with-precompiled-binaries).
|
||||
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
|
||||
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
|
||||
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
|
||||
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip), [install instructions](#installing-on-macos-with-precompiled-binaries).
|
||||
|
||||
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
|
||||
|
||||

|
||||
|
||||
Thanks to full ABI compatibility with C, it's possible to mix C and C3 in the same project with no effort. As a demonstration, vkQuake was compiled with a small portion of the code converted to C3 and compiled with the c3c compiler. (The fork can be found at https://github.com/c3lang/vkQuake)
|
||||
Thanks to full ABI compatibility with C, it's possible to mix C and C3 in the same project with no effort. As a demonstration, vkQuake was compiled with a small portion of the code converted to C3 and compiled with the c3c compiler. (The aging fork can be found at https://github.com/c3lang/vkQuake)
|
||||
|
||||
A non-curated list of user written projects and other resources can be found [here](https://github.com/c3lang/c3-showcase).
|
||||
|
||||
### Design Principles
|
||||
- Procedural "get things done"-type of language.
|
||||
@@ -33,7 +35,7 @@ whole new language.
|
||||
|
||||
### Example code
|
||||
|
||||
The following code shows [generic modules](https://c3-lang.org/references/docs/generics/) (more examples can be found at https://c3-lang.org/references/docs/examples/).
|
||||
The following code shows [generic modules](https://c3-lang.org/generic-programming/generics/) (more examples can be found at https://c3-lang.org/language-overview/examples/).
|
||||
|
||||
```cpp
|
||||
module stack {Type};
|
||||
@@ -124,6 +126,7 @@ fn void main()
|
||||
- New semantic macro system
|
||||
- Module based name spacing
|
||||
- Slices
|
||||
- Operator overloading
|
||||
- Compile time reflection
|
||||
- Enhanced compile time execution
|
||||
- Generics based on generic modules
|
||||
@@ -138,9 +141,10 @@ fn void main()
|
||||
|
||||
### Current status
|
||||
|
||||
The current stable version of the compiler is **version 0.6.8**.
|
||||
The current stable version of the compiler is **version 0.7.3**.
|
||||
|
||||
The the next version is 0.7.0 which will be a breaking release.
|
||||
The upcoming 0.7.x releases will focus on expanding the standard library,
|
||||
fixing bugs and improving compile time analysis.
|
||||
Follow the issues [here](https://github.com/c3lang/c3c/issues).
|
||||
|
||||
If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues)
|
||||
@@ -193,29 +197,31 @@ More platforms will be supported in the future.
|
||||
|
||||
### Installing
|
||||
|
||||
This installs the latest prerelease build, as opposed to the latest released version.
|
||||
|
||||
#### Installing on Windows with precompiled binaries
|
||||
1. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-windows-debug.zip))
|
||||
1. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows-debug.zip))
|
||||
2. Unzip exe and standard lib.
|
||||
3. If you don't have Visual Studio 17 installed you can either do so, or run the `msvc_build_libraries.py` Python script which will download the necessary files to compile on Windows.
|
||||
4. Run `c3c.exe`.
|
||||
|
||||
#### Installing on Debian with precompiled binaries
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-linux-debug.tar.gz))
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux-debug.tar.gz))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
#### Installing on Ubuntu with precompiled binaries
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20-debug.tar.gz))
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20-debug.tar.gz))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
#### Installing on MacOS with precompiled binaries
|
||||
1. Make sure you have XCode with command line tools installed.
|
||||
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-macos-debug.zip))
|
||||
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos-debug.zip))
|
||||
3. Unzip executable and standard lib.
|
||||
4. Run `./c3c`.
|
||||
|
||||
@@ -307,17 +313,25 @@ called `hello_world` or `hello_world.exe`depending on platform.
|
||||
1. Make sure you have Visual Studio 17 2022 installed or alternatively install the "Buildtools for Visual Studio" (https://aka.ms/vs/17/release/vs_BuildTools.exe) and then select "Desktop development with C++"
|
||||
2. Install CMake
|
||||
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
4. Enter the C3C directory `cd c3c`.
|
||||
5. Set up the CMake build `cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release`
|
||||
6. Build: `cmake --build build --config Release`
|
||||
7. You should now have the c3c.exe
|
||||
4. Enter the C3C directory: `cd c3c`.
|
||||
5. Set up the CMake build: `cmake --preset windows-vs-2022-release`
|
||||
6. Build: `cmake --build --preset windows-vs-2022-release`
|
||||
|
||||
You should now have a `c3c` executable.
|
||||
You should now have a `c3c` executable in `build\Release`.
|
||||
|
||||
You can try it out by running some sample code: `c3c.exe compile ../resources/examples/hash.c3`
|
||||
You can try it out by running some sample code: `c3c.exe compile ../../resources/examples/hash.c3`
|
||||
|
||||
Building `c3c` using Visual Studio Code is also supported when using the `CMake Tools` extension. Simply select the `Windows x64 Visual Studio 17 2022` configure preset and build.
|
||||
|
||||
*Note that if you run into linking issues when building, make sure that you are using the latest version of VS17.*
|
||||
|
||||
#### Compiling on Windows (Debug)
|
||||
|
||||
Debug build requires a different set of LLVM libraries to be loaded for which a separate CMake configuration is used to avoid conflicts.
|
||||
1. Configure: `cmake --preset windows-vs-2022-debug`
|
||||
2. Build: `cmake --build --preset windows-vs-2022-debug`
|
||||
|
||||
You should now have a `c3c` executable in `build-debug\Debug`.
|
||||
|
||||
#### Compiling on Ubuntu 24.04 LTS
|
||||
|
||||
@@ -363,6 +377,23 @@ For a sytem-wide installation, run the following as root: `cmake --install .`
|
||||
|
||||
The c3c binary should be created in the build directory. You can try it out by running some sample code: `./c3c compile ../resources/examples/hash.c3`
|
||||
|
||||
#### Compiling on Arch Linux
|
||||
|
||||
1. Install required project dependencies: `sudo pacman -S curl lld llvm-libs clang cmake git libedit llvm`
|
||||
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: `git clone https://github.com/c3lang/c3c.git --depth=1`
|
||||
3. Enter the C3C directory: `cd c3c`
|
||||
4. Create the CMake build cache:
|
||||
```bash
|
||||
cmake -B build \
|
||||
-D C3_LINK_DYNAMIC=ON \
|
||||
-D CMAKE_BUILD_TYPE=Release
|
||||
```
|
||||
5. Build the project: `make -C build`.
|
||||
|
||||
After compilation, the `c3c` binary will be located in the `build` directory. You can test it by compiling an example: `./build/c3c compile resources/examples/ls.c3`.
|
||||
|
||||
6. To install the compiler globally: `sudo cmake --install build`
|
||||
|
||||
#### Compiling on other Linux / Unix variants
|
||||
|
||||
@@ -406,4 +437,4 @@ A special thank you to sponsors [Caleb-o](https://github.com/Caleb-o) and [devda
|
||||
|
||||
## Star History
|
||||
|
||||
[](https://www.star-history.com/#c3lang/c3c&Date)
|
||||
[](https://www.star-history.com/#c3lang/c3c&Date)
|
||||
|
||||
@@ -1,77 +1,42 @@
|
||||
<* This module is scheduled for removal, use std::core::ascii *>
|
||||
module std::ascii;
|
||||
|
||||
macro bool in_range_m(c, start, len) => (uint)(c - start) < len;
|
||||
macro bool is_lower_m(c) => in_range_m(c, 0x61, 26);
|
||||
macro bool is_upper_m(c) => in_range_m(c, 0x41, 26);
|
||||
macro bool is_digit_m(c) => in_range_m(c, 0x30, 10);
|
||||
macro bool is_lower_m(c) => in_range_m(c, 0x61, 26);
|
||||
macro bool is_upper_m(c) => in_range_m(c, 0x41, 26);
|
||||
macro bool is_digit_m(c) => in_range_m(c, 0x30, 10);
|
||||
macro bool is_bdigit_m(c) => in_range_m(c, 0x30, 2);
|
||||
macro bool is_odigit_m(c) => in_range_m(c, 0x30, 8);
|
||||
macro bool is_xdigit_m(c) => in_range_m(c | 32, 0x61, 6) || is_digit_m(c);
|
||||
macro bool is_alpha_m(c) => in_range_m(c | 32, 0x61, 26);
|
||||
macro bool is_print_m(c) => in_range_m(c, 0x20, 95);
|
||||
macro bool is_graph_m(c) => in_range_m(c, 0x21, 94);
|
||||
macro bool is_space_m(c) => in_range_m(c, 0x9, 5) || c == 0x20;
|
||||
macro bool is_alnum_m(c) => is_alpha_m(c) || is_digit_m(c);
|
||||
macro bool is_punct_m(c) => !is_alnum_m(c) && is_graph_m(c);
|
||||
macro bool is_blank_m(c) => c == 0x20 || c == 0x9;
|
||||
macro bool is_cntrl_m(c) => c < 0x20 || c == 0x7f;
|
||||
macro bool is_alpha_m(c) => in_range_m(c | 32, 0x61, 26);
|
||||
macro bool is_print_m(c) => in_range_m(c, 0x20, 95);
|
||||
macro bool is_graph_m(c) => in_range_m(c, 0x21, 94);
|
||||
macro bool is_space_m(c) => in_range_m(c, 0x9, 5) || c == 0x20;
|
||||
macro bool is_alnum_m(c) => is_alpha_m(c) || is_digit_m(c);
|
||||
macro bool is_punct_m(c) => !is_alnum_m(c) && is_graph_m(c);
|
||||
macro bool is_blank_m(c) => c == 0x20 || c == 0x9;
|
||||
macro bool is_cntrl_m(c) => c < 0x20 || c == 0x7f;
|
||||
macro to_lower_m(c) => is_upper_m(c) ? c + 0x20 : c;
|
||||
macro to_upper_m(c) => is_lower_m(c) ? c - 0x20 : c;
|
||||
|
||||
fn bool in_range(char c, char start, char len) => in_range_m(c, start, len);
|
||||
fn bool is_lower(char c) => is_lower_m(c);
|
||||
fn bool is_upper(char c) => is_upper_m(c);
|
||||
fn bool is_digit(char c) => is_digit_m(c);
|
||||
fn bool is_bdigit(char c) => is_bdigit_m(c);
|
||||
fn bool is_odigit(char c) => is_odigit_m(c);
|
||||
fn bool is_xdigit(char c) => is_xdigit_m(c);
|
||||
fn bool is_alpha(char c) => is_alpha_m(c);
|
||||
fn bool is_print(char c) => is_print_m(c);
|
||||
fn bool is_graph(char c) => is_graph_m(c);
|
||||
fn bool is_space(char c) => is_space_m(c);
|
||||
fn bool is_alnum(char c) => is_alnum_m(c);
|
||||
fn bool is_punct(char c) => is_punct_m(c);
|
||||
fn bool is_blank(char c) => is_blank_m(c);
|
||||
fn bool is_cntrl(char c) => is_cntrl_m(c);
|
||||
fn char to_lower(char c) => (char)to_lower_m(c);
|
||||
fn char to_upper(char c) => (char)to_upper_m(c);
|
||||
|
||||
fn bool char.in_range(char c, char start, char len) => in_range_m(c, start, len);
|
||||
fn bool char.is_lower(char c) => is_lower_m(c);
|
||||
fn bool char.is_upper(char c) => is_upper_m(c);
|
||||
fn bool char.is_digit(char c) => is_digit_m(c);
|
||||
fn bool char.is_bdigit(char c) => is_bdigit_m(c);
|
||||
fn bool char.is_odigit(char c) => is_odigit_m(c);
|
||||
fn bool char.is_xdigit(char c) => is_xdigit_m(c);
|
||||
fn bool char.is_alpha(char c) => is_alpha_m(c);
|
||||
fn bool char.is_print(char c) => is_print_m(c);
|
||||
fn bool char.is_graph(char c) => is_graph_m(c);
|
||||
fn bool char.is_space(char c) => is_space_m(c);
|
||||
fn bool char.is_alnum(char c) => is_alnum_m(c);
|
||||
fn bool char.is_punct(char c) => is_punct_m(c);
|
||||
fn bool char.is_blank(char c) => is_blank_m(c);
|
||||
fn bool char.is_cntrl(char c) => is_cntrl_m(c);
|
||||
fn char char.to_lower(char c) => (char)to_lower_m(c);
|
||||
fn char char.to_upper(char c) => (char)to_upper_m(c);
|
||||
<*
|
||||
@require c.is_xdigit()
|
||||
*>
|
||||
fn char char.from_hex(char c) => c.is_digit() ? c - '0' : 10 + (c | 0x20) - 'a';
|
||||
|
||||
fn bool uint.in_range(uint c, uint start, uint len) => in_range_m(c, start, len);
|
||||
fn bool uint.is_lower(uint c) => is_lower_m(c);
|
||||
fn bool uint.is_upper(uint c) => is_upper_m(c);
|
||||
fn bool uint.is_digit(uint c) => is_digit_m(c);
|
||||
fn bool uint.is_bdigit(uint c) => is_bdigit_m(c);
|
||||
fn bool uint.is_odigit(uint c) => is_odigit_m(c);
|
||||
fn bool uint.is_xdigit(uint c) => is_xdigit_m(c);
|
||||
fn bool uint.is_alpha(uint c) => is_alpha_m(c);
|
||||
fn bool uint.is_print(uint c) => is_print_m(c);
|
||||
fn bool uint.is_graph(uint c) => is_graph_m(c);
|
||||
fn bool uint.is_space(uint c) => is_space_m(c);
|
||||
fn bool uint.is_alnum(uint c) => is_alnum_m(c);
|
||||
fn bool uint.is_punct(uint c) => is_punct_m(c);
|
||||
fn bool uint.is_blank(uint c) => is_blank_m(c);
|
||||
fn bool uint.is_cntrl(uint c) => is_cntrl_m(c);
|
||||
fn uint uint.to_lower(uint c) => (uint)to_lower_m(c);
|
||||
fn uint uint.to_upper(uint c) => (uint)to_upper_m(c);
|
||||
fn bool uint.is_lower(uint c) @deprecated => is_lower_m(c);
|
||||
fn bool uint.is_upper(uint c) @deprecated => is_upper_m(c);
|
||||
fn bool uint.is_digit(uint c) @deprecated => is_digit_m(c);
|
||||
fn bool uint.is_bdigit(uint c) @deprecated => is_bdigit_m(c);
|
||||
fn bool uint.is_odigit(uint c) @deprecated => is_odigit_m(c);
|
||||
fn bool uint.is_xdigit(uint c) @deprecated => is_xdigit_m(c);
|
||||
fn bool uint.is_alpha(uint c) @deprecated => is_alpha_m(c);
|
||||
fn bool uint.is_print(uint c) @deprecated => is_print_m(c);
|
||||
fn bool uint.is_graph(uint c) @deprecated => is_graph_m(c);
|
||||
fn bool uint.is_space(uint c) @deprecated => is_space_m(c);
|
||||
fn bool uint.is_alnum(uint c) @deprecated => is_alnum_m(c);
|
||||
fn bool uint.is_punct(uint c) @deprecated => is_punct_m(c);
|
||||
fn bool uint.is_blank(uint c) @deprecated => is_blank_m(c);
|
||||
fn bool uint.is_cntrl(uint c) @deprecated => is_cntrl_m(c);
|
||||
fn uint uint.to_lower(uint c) @deprecated => (uint)to_lower_m(c);
|
||||
fn uint uint.to_upper(uint c) @deprecated => (uint)to_upper_m(c);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2023 Eduardo José Gómez Hernández. All rights reserved.
|
||||
// Copyright (c) 2023-2025 Eduardo José Gómez Hernández. 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::atomic::types{Type};
|
||||
@@ -175,7 +175,7 @@ macro bool is_native_atomic_type($Type)
|
||||
$case BOOL:
|
||||
return true;
|
||||
$case DISTINCT:
|
||||
return is_native_atomic_type($typefrom($Type.inner));
|
||||
return is_native_atomic_type($Type.inner);
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
@@ -183,7 +183,7 @@ macro bool is_native_atomic_type($Type)
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to be added to ptr."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -203,7 +203,7 @@ macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to be subtracted from ptr."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -223,7 +223,7 @@ macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to be multiplied with ptr."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -240,7 +240,7 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
|
||||
$endif
|
||||
|
||||
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
|
||||
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
|
||||
|
||||
$StorageType* storage_ptr = ($StorageType*)ptr;
|
||||
|
||||
@@ -263,7 +263,7 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to divide ptr by."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -280,7 +280,7 @@ macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
|
||||
$endif
|
||||
|
||||
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
|
||||
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
|
||||
|
||||
$StorageType* storage_ptr = ($StorageType*)ptr;
|
||||
|
||||
@@ -303,7 +303,7 @@ macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to perform a bitwise or with."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -320,7 +320,7 @@ macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to perform a bitwise xor with."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -337,7 +337,7 @@ macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to perform a bitwise and with."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -354,7 +354,7 @@ macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to shift ptr by."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -372,7 +372,7 @@ macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
|
||||
$endif
|
||||
|
||||
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
|
||||
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
|
||||
|
||||
$StorageType* storage_ptr = ($StorageType*)ptr;
|
||||
|
||||
@@ -396,7 +396,7 @@ macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [in] y : "the value to shift ptr by."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
@@ -414,7 +414,7 @@ macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
|
||||
$endif
|
||||
|
||||
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
|
||||
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
|
||||
|
||||
$StorageType* storage_ptr = ($StorageType*)ptr;
|
||||
|
||||
@@ -438,7 +438,7 @@ macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
@@ -466,7 +466,7 @@ macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&in] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param [&inout] ptr : "the variable or dereferenced pointer to the data."
|
||||
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
|
||||
@return "returns the old value of ptr"
|
||||
|
||||
|
||||
@@ -7,6 +7,17 @@ import std::io,std::math;
|
||||
alias AnyPredicate = fn bool(any value);
|
||||
alias AnyTest = fn bool(any type, any context);
|
||||
|
||||
<*
|
||||
The AnyList contains a heterogenous set of types. Anything placed in the
|
||||
list will shallowly copied in order to be stored as an `any`. This means
|
||||
that the list will copy and free its elements.
|
||||
|
||||
However, because we're getting `any` values back when we pop, those operations
|
||||
need to take an allocator, as we can only copy then pop then return the copy.
|
||||
|
||||
If we're not doing pop, then things are easier, since we can just hand over
|
||||
the existing any.
|
||||
*>
|
||||
struct AnyList (Printable)
|
||||
{
|
||||
usz size;
|
||||
@@ -17,8 +28,11 @@ struct AnyList (Printable)
|
||||
|
||||
|
||||
<*
|
||||
Initialize the list. If not initialized then it will use the temp allocator
|
||||
when something is pushed to it.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@param initial_capacity : "The initial capacity to reserve"
|
||||
@param initial_capacity : "The initial capacity to reserve, defaults to 16"
|
||||
*>
|
||||
fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16)
|
||||
{
|
||||
@@ -49,6 +63,361 @@ fn AnyList* AnyList.tinit(&self, usz initial_capacity = 16)
|
||||
|
||||
fn bool AnyList.is_initialized(&self) @inline => self.allocator != null;
|
||||
|
||||
<*
|
||||
Push an element on the list by cloning it.
|
||||
*>
|
||||
macro void AnyList.push(&self, element)
|
||||
{
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
self._append(allocator::clone(self.allocator, element));
|
||||
}
|
||||
|
||||
<*
|
||||
Free a retained element removed using *_retained.
|
||||
*>
|
||||
fn void AnyList.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.
|
||||
|
||||
@param $Type : "The type we assume the value has"
|
||||
@return "The last value as the type given"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.pop(&self, $Type)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
return *anycast(self.entries[--self.size], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Copy the last value, pop it and return the copy of it.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use for copying"
|
||||
@return "A copy of the last value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.copy_pop(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
return allocator::clone_any(allocator, self.entries[--self.size]);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Copy the last value, pop it and return the copy of it.
|
||||
|
||||
@return "A temp copy of the last value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.tcopy_pop(&self) => self.copy_pop(tmem);
|
||||
|
||||
|
||||
<*
|
||||
Pop the last value. It must later be released using `list.free_element()`.
|
||||
|
||||
@return "The last value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.pop_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
return self.entries[--self.size];
|
||||
}
|
||||
|
||||
<*
|
||||
Remove all elements in the list.
|
||||
*>
|
||||
fn void AnyList.clear(&self)
|
||||
{
|
||||
for (usz i = 0; i < self.size; i++)
|
||||
{
|
||||
self.free_element(self.entries[i]);
|
||||
}
|
||||
self.size = 0;
|
||||
}
|
||||
|
||||
<*
|
||||
Pop a value who's type is known. If the type is incorrect, this
|
||||
will still pop the element.
|
||||
|
||||
@param $Type : "The type we assume the value has"
|
||||
@return "The first value as the type given"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.pop_first(&self, $Type)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.remove_at(0);
|
||||
return *anycast(self.entries[0], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Pop the first value. It must later be released using `list.free_element()`.
|
||||
|
||||
@return "The first value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.pop_first_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.remove_at(0);
|
||||
return self.entries[0];
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Copy the first value, pop it and return the copy of it.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use for copying"
|
||||
@return "A copy of the first value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.copy_pop_first(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
defer self.remove_at(0);
|
||||
return allocator::clone_any(allocator, self.entries[0]);
|
||||
}
|
||||
|
||||
<*
|
||||
Copy the first value, pop it and return the temp copy of it.
|
||||
|
||||
@return "A temp copy of the first value if it exists"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem);
|
||||
|
||||
<*
|
||||
Remove the element at the particular index.
|
||||
|
||||
@param index : "The index of the element to remove"
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void AnyList.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];
|
||||
}
|
||||
|
||||
<*
|
||||
Add all the elements in another AnyList.
|
||||
|
||||
@param [&in] other_list : "The list to add"
|
||||
*>
|
||||
fn void AnyList.add_all(&self, AnyList* 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 order of the elements in the list.
|
||||
*>
|
||||
fn void AnyList.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);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Return a view of the data as a slice.
|
||||
|
||||
@return "The slice view"
|
||||
*>
|
||||
fn any[] AnyList.array_view(&self)
|
||||
{
|
||||
return self.entries[:self.size];
|
||||
}
|
||||
|
||||
<*
|
||||
Push an element to the front of the list.
|
||||
|
||||
@param value : "The value to push to the list"
|
||||
*>
|
||||
macro void AnyList.push_front(&self, value)
|
||||
{
|
||||
self.insert_at(0, value);
|
||||
}
|
||||
|
||||
<*
|
||||
Insert an element at a particular index.
|
||||
|
||||
@param index : "the index where the element should be inserted"
|
||||
@param type : "the value to insert"
|
||||
@require index <= self.size : "The index is out of bounds"
|
||||
*>
|
||||
macro void AnyList.insert_at(&self, usz index, type)
|
||||
{
|
||||
if (index == self.size)
|
||||
{
|
||||
self.push(type);
|
||||
return;
|
||||
}
|
||||
any value = allocator::copy(self.allocator, type);
|
||||
self._insert_at(self, index, value);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove the last element in the list. The list may not be empty.
|
||||
|
||||
@require self.size > 0 : "The list was already empty"
|
||||
*>
|
||||
fn void AnyList.remove_last(&self)
|
||||
{
|
||||
self.free_element(self.entries[--self.size]);
|
||||
}
|
||||
|
||||
<*
|
||||
Remove the first element in the list, the list may not be empty.
|
||||
|
||||
@require self.size > 0
|
||||
*>
|
||||
fn void AnyList.remove_first(&self)
|
||||
{
|
||||
self.remove_at(0);
|
||||
}
|
||||
|
||||
<*
|
||||
Return the first element by value, assuming it is the given type.
|
||||
|
||||
@param $Type : "The type of the first element"
|
||||
@return "The first element"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.first(&self, $Type)
|
||||
{
|
||||
return *anycast(self.first_any(), $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Return the first element
|
||||
|
||||
@return "The first element"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.first_any(&self) @inline
|
||||
{
|
||||
return self.size ? self.entries[0] : NO_MORE_ELEMENT?;
|
||||
}
|
||||
|
||||
<*
|
||||
Return the last element by value, assuming it is the given type.
|
||||
|
||||
@param $Type : "The type of the last element"
|
||||
@return "The last element"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.last(&self, $Type)
|
||||
{
|
||||
return *anycast(self.last_any(), $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Return the last element
|
||||
|
||||
@return "The last element"
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.last_any(&self) @inline
|
||||
{
|
||||
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?;
|
||||
}
|
||||
|
||||
<*
|
||||
Return whether the list is empty.
|
||||
|
||||
@return "True if the list is empty"
|
||||
*>
|
||||
fn bool AnyList.is_empty(&self) @inline
|
||||
{
|
||||
return !self.size;
|
||||
}
|
||||
|
||||
<*
|
||||
Return the length of the list.
|
||||
|
||||
@return "The number of elements in the list"
|
||||
*>
|
||||
fn usz AnyList.len(&self) @operator(len) @inline
|
||||
{
|
||||
return self.size;
|
||||
}
|
||||
|
||||
<*
|
||||
Return an element in the list by value, assuming it is the given type.
|
||||
|
||||
@param index : "The index of the element to retrieve"
|
||||
@param $Type : "The type of the element"
|
||||
@return "The element at the index"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
@require index < self.size : "Index out of range"
|
||||
*>
|
||||
macro AnyList.get(&self, usz index, $Type)
|
||||
{
|
||||
return *anycast(self.entries[index], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
Return an element in the list.
|
||||
|
||||
@param index : "The index of the element to retrieve"
|
||||
@return "The element at the index"
|
||||
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
@require index < self.size : "Index out of range"
|
||||
*>
|
||||
fn any AnyList.get_any(&self, usz index) @inline @operator([])
|
||||
{
|
||||
return self.entries[index];
|
||||
}
|
||||
|
||||
<*
|
||||
Completely free and clear a list.
|
||||
*>
|
||||
fn void AnyList.free(&self)
|
||||
{
|
||||
if (!self.allocator) return;
|
||||
self.clear();
|
||||
allocator::free(self.allocator, self.entries);
|
||||
self.capacity = 0;
|
||||
self.entries = null;
|
||||
}
|
||||
|
||||
<*
|
||||
Swap two elements in a list.
|
||||
|
||||
@param i : "Index of one of the elements"
|
||||
@param j : "Index of the other element"
|
||||
@require i < self.size : "The first index is out of range"
|
||||
@require j < self.size : "The second index is out of range"
|
||||
*>
|
||||
fn void AnyList.swap(&self, usz i, usz j)
|
||||
{
|
||||
any temp = self.entries[i];
|
||||
self.entries[i] = self.entries[j];
|
||||
self.entries[j] = temp;
|
||||
}
|
||||
|
||||
<*
|
||||
Print the list to a formatter.
|
||||
*>
|
||||
fn usz? AnyList.to_format(&self, Formatter* formatter) @dynamic
|
||||
{
|
||||
switch (self.size)
|
||||
@@ -70,265 +439,8 @@ fn usz? AnyList.to_format(&self, Formatter* formatter) @dynamic
|
||||
}
|
||||
|
||||
<*
|
||||
Push an element on the list by cloning it.
|
||||
*>
|
||||
macro void AnyList.push(&self, element)
|
||||
{
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
self.append_internal(allocator::clone(self.allocator, element));
|
||||
}
|
||||
Remove any elements matching the predicate.
|
||||
|
||||
fn void AnyList.append_internal(&self, any element) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
self.entries[self.size++] = element;
|
||||
}
|
||||
|
||||
<*
|
||||
Free a retained element removed using *_retained.
|
||||
*>
|
||||
fn void AnyList.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? TYPE_MISMATCH, NO_MORE_ELEMENT
|
||||
*>
|
||||
macro AnyList.pop(&self, $Type)
|
||||
{
|
||||
if (!self.size) return 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? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.copy_pop(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return 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? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.tcopy_pop(&self) => self.copy_pop(tmem);
|
||||
|
||||
<*
|
||||
Pop the last value. It must later be released using list.free_element()
|
||||
@return? NO_MORE_ELEMENT
|
||||
*>
|
||||
fn any? AnyList.pop_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
return self.entries[--self.size];
|
||||
}
|
||||
|
||||
fn void AnyList.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 AnyList.pop_first(&self, $Type)
|
||||
{
|
||||
if (!self.size) return 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? AnyList.pop_first_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.remove_at(0);
|
||||
return self.entries[0];
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Same as copy_pop() but pops the first value instead.
|
||||
*>
|
||||
fn any? AnyList.copy_pop_first(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return 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? AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem);
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void AnyList.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 AnyList.add_all(&self, AnyList* 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 AnyList.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[] AnyList.array_view(&self)
|
||||
{
|
||||
return self.entries[:self.size];
|
||||
}
|
||||
|
||||
<*
|
||||
Push an element to the front of the list.
|
||||
*>
|
||||
macro void AnyList.push_front(&self, type)
|
||||
{
|
||||
self.insert_at(0, type);
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
macro void AnyList.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 AnyList.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 AnyList.remove_last(&self)
|
||||
{
|
||||
self.free_element(self.entries[--self.size]);
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.size > 0
|
||||
*>
|
||||
fn void AnyList.remove_first(&self)
|
||||
{
|
||||
self.remove_at(0);
|
||||
}
|
||||
|
||||
macro AnyList.first(&self, $Type)
|
||||
{
|
||||
return *anycast(self.first_any(), $Type);
|
||||
}
|
||||
|
||||
fn any? AnyList.first_any(&self) @inline
|
||||
{
|
||||
return self.size ? self.entries[0] : NO_MORE_ELEMENT?;
|
||||
}
|
||||
|
||||
macro AnyList.last(&self, $Type)
|
||||
{
|
||||
return *anycast(self.last_any(), $Type);
|
||||
}
|
||||
|
||||
fn any? AnyList.last_any(&self) @inline
|
||||
{
|
||||
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?;
|
||||
}
|
||||
|
||||
fn bool AnyList.is_empty(&self) @inline
|
||||
{
|
||||
return !self.size;
|
||||
}
|
||||
|
||||
fn usz AnyList.len(&self) @operator(len) @inline
|
||||
{
|
||||
return self.size;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size : "Index out of range"
|
||||
*>
|
||||
macro AnyList.get(&self, usz index, $Type)
|
||||
{
|
||||
return *anycast(self.entries[index], $Type);
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size : "Index out of range"
|
||||
*>
|
||||
fn any AnyList.get_any(&self, usz index) @inline
|
||||
{
|
||||
return self.entries[index];
|
||||
}
|
||||
|
||||
fn void AnyList.free(&self)
|
||||
{
|
||||
if (!self.allocator) return;
|
||||
self.clear();
|
||||
allocator::free(self.allocator, self.entries);
|
||||
self.capacity = 0;
|
||||
self.entries = null;
|
||||
}
|
||||
|
||||
fn void AnyList.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"
|
||||
*>
|
||||
@@ -338,6 +450,8 @@ fn usz AnyList.remove_if(&self, AnyPredicate filter)
|
||||
}
|
||||
|
||||
<*
|
||||
Retain the elements matching the predicate.
|
||||
|
||||
@param selection : "The function to determine if it should be kept or not"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
@@ -346,40 +460,95 @@ fn usz AnyList.retain_if(&self, AnyPredicate selection)
|
||||
return self._remove_if(selection, true);
|
||||
}
|
||||
|
||||
macro usz AnyList._remove_if(&self, AnyPredicate 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;
|
||||
}
|
||||
<*
|
||||
Remove any elements matching the predicate.
|
||||
|
||||
@param filter : "The function to determine if it should be removed or not"
|
||||
@param context : "The context to the function"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz AnyList.remove_using_test(&self, AnyTest filter, any context)
|
||||
{
|
||||
return self._remove_using_test(filter, false, context);
|
||||
}
|
||||
|
||||
fn usz AnyList.retain_using_test(&self, AnyTest filter, any context)
|
||||
<*
|
||||
Retain any elements matching the predicate.
|
||||
|
||||
@param selection : "The function to determine if it should be retained or not"
|
||||
@param context : "The context to the function"
|
||||
@return "the number of deleted elements"
|
||||
*>
|
||||
fn usz AnyList.retain_using_test(&self, AnyTest selection, any context)
|
||||
{
|
||||
return self._remove_using_test(filter, true, context);
|
||||
return self._remove_using_test(selection, true, context);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Reserve memory so that at least the `min_capacity` exists.
|
||||
|
||||
@param min_capacity : "The min capacity to hold"
|
||||
*>
|
||||
fn void AnyList.reserve(&self, usz min_capacity)
|
||||
{
|
||||
if (!min_capacity) return;
|
||||
if (self.capacity >= min_capacity) return;
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
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;
|
||||
}
|
||||
|
||||
<*
|
||||
Set the element at any index.
|
||||
|
||||
@param index : "The index where to set the value."
|
||||
@param value : "The value to set"
|
||||
@require index <= self.size : "Index out of range"
|
||||
*>
|
||||
macro void AnyList.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);
|
||||
}
|
||||
|
||||
// -- private
|
||||
|
||||
fn void AnyList.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);
|
||||
}
|
||||
|
||||
fn void AnyList._append(&self, any element) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
self.entries[self.size++] = element;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void AnyList._insert_at(&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;
|
||||
}
|
||||
|
||||
macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @local
|
||||
@@ -408,45 +577,28 @@ macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @
|
||||
return size - self.size;
|
||||
}
|
||||
|
||||
<*
|
||||
Reserve at least min_capacity
|
||||
*>
|
||||
fn void AnyList.reserve(&self, usz min_capacity)
|
||||
macro usz AnyList._remove_if(&self, AnyPredicate filter, bool $invert) @local
|
||||
{
|
||||
if (!min_capacity) return;
|
||||
if (self.capacity >= min_capacity) return;
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
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 AnyList.@item_at(&self, usz index) @operator([])
|
||||
{
|
||||
return self.entries[index];
|
||||
}
|
||||
|
||||
<*
|
||||
@require index <= self.size : "Index out of range"
|
||||
*>
|
||||
macro void AnyList.set(&self, usz index, value)
|
||||
{
|
||||
if (index == self.size)
|
||||
usz size = self.size;
|
||||
for (usz i = size, usz k = size; k > 0; k = i)
|
||||
{
|
||||
self.push(value);
|
||||
return;
|
||||
// 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
|
||||
}
|
||||
self.free_element(self.entries[index]);
|
||||
self.entries[index] = allocator::copy(self.allocator, value);
|
||||
}
|
||||
|
||||
fn void AnyList.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);
|
||||
return size - self.size;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
// Copyright (c) 2023-2025 C3 team. 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.
|
||||
<*
|
||||
@require SIZE > 0
|
||||
@require SIZE > 0 : "The size of the bitset in bits must be at least 1"
|
||||
*>
|
||||
module std::collections::bitset{SIZE};
|
||||
module std::collections::bitset {SIZE};
|
||||
|
||||
alias Type = uint;
|
||||
|
||||
const BITS = Type.sizeof * 8;
|
||||
const BITS = uint.sizeof * 8;
|
||||
const SZ = (SIZE + BITS - 1) / BITS;
|
||||
|
||||
struct BitSet
|
||||
{
|
||||
Type[SZ] data;
|
||||
uint[SZ] data;
|
||||
}
|
||||
|
||||
<*
|
||||
@return "The number of bits set"
|
||||
*>
|
||||
fn usz BitSet.cardinality(&self)
|
||||
{
|
||||
usz n;
|
||||
@@ -24,7 +28,11 @@ fn usz BitSet.cardinality(&self)
|
||||
}
|
||||
|
||||
<*
|
||||
@require i < SIZE
|
||||
Set a bit in the bitset.
|
||||
|
||||
@param i : "The index to set"
|
||||
|
||||
@require i < SIZE : "Index was out of range"
|
||||
*>
|
||||
fn void BitSet.set(&self, usz i)
|
||||
{
|
||||
@@ -34,7 +42,86 @@ fn void BitSet.set(&self, usz i)
|
||||
}
|
||||
|
||||
<*
|
||||
@require i < SIZE
|
||||
Perform xor over all bits, mutating itself
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
macro BitSet BitSet.xor_self(&self, BitSet set) @operator(^=)
|
||||
{
|
||||
foreach (i, &x : self.data) *x ^= set.data[i];
|
||||
return *self;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform xor over all bits, returning a new bit set.
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
fn BitSet BitSet.xor(&self, BitSet set) @operator(^)
|
||||
{
|
||||
BitSet new_set @noinit;
|
||||
foreach (i, x : self.data) new_set.data[i] = x ^ set.data[i];
|
||||
return new_set;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform or over all bits, returning a new bit set.
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
fn BitSet BitSet.or(&self, BitSet set) @operator(|)
|
||||
{
|
||||
BitSet new_set @noinit;
|
||||
foreach (i, x : self.data) new_set.data[i] = x | set.data[i];
|
||||
return new_set;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform or over all bits, mutating itself
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
macro BitSet BitSet.or_self(&self, BitSet set) @operator(|=)
|
||||
{
|
||||
foreach (i, &x : self.data) *x |= set.data[i];
|
||||
return *self;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform & over all bits, returning a new bit set.
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
fn BitSet BitSet.and(&self, BitSet set) @operator(&)
|
||||
{
|
||||
BitSet new_set @noinit;
|
||||
foreach (i, x : self.data) new_set.data[i] = x & set.data[i];
|
||||
return new_set;
|
||||
}
|
||||
|
||||
<*
|
||||
Perform & over all bits, mutating itself.
|
||||
|
||||
@param set : "The bit set to xor with"
|
||||
@return "The resulting bit set"
|
||||
*>
|
||||
macro BitSet BitSet.and_self(&self, BitSet set) @operator(&=)
|
||||
{
|
||||
foreach (i, &x : self.data) *x &= set.data[i];
|
||||
return *self;
|
||||
}
|
||||
|
||||
<*
|
||||
Unset (clear) a bit in the bitset.
|
||||
|
||||
@param i : "The index to set"
|
||||
|
||||
@require i < SIZE : "Index was out of range"
|
||||
*>
|
||||
fn void BitSet.unset(&self, usz i)
|
||||
{
|
||||
@@ -44,7 +131,11 @@ fn void BitSet.unset(&self, usz i)
|
||||
}
|
||||
|
||||
<*
|
||||
@require i < SIZE
|
||||
Get a particular bit in the bitset
|
||||
|
||||
@param i : "The index of the bit"
|
||||
|
||||
@require i < SIZE : "Index was out of range"
|
||||
*>
|
||||
fn bool BitSet.get(&self, usz i) @operator([]) @inline
|
||||
{
|
||||
@@ -59,7 +150,12 @@ fn usz BitSet.len(&self) @operator(len) @inline
|
||||
}
|
||||
|
||||
<*
|
||||
@require i < SIZE
|
||||
Change a particular bit in the bitset
|
||||
|
||||
@param i : "The index of the bit"
|
||||
@param value : "The value to set the bit to"
|
||||
|
||||
@require i < SIZE : "Index was out of range"
|
||||
*>
|
||||
fn void BitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline
|
||||
{
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
*>
|
||||
module std::collections::enummap{Enum, ValueType};
|
||||
import std::io;
|
||||
|
||||
struct EnumMap (Printable)
|
||||
{
|
||||
ValueType[Enum.len] values;
|
||||
ValueType[Enum.values.len] values;
|
||||
}
|
||||
|
||||
fn void EnumMap.init(&self, ValueType init_value)
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
module std::collections::enumset{Enum};
|
||||
import std::io;
|
||||
|
||||
alias EnumSetType @private = $typefrom(type_for_enum_elements(Enum.elements));
|
||||
const ENUM_COUNT @private = Enum.values.len;
|
||||
alias EnumSetType @private = $typefrom(type_for_enum_elements(ENUM_COUNT));
|
||||
|
||||
const IS_CHAR_ARRAY = Enum.elements > 128;
|
||||
const IS_CHAR_ARRAY = ENUM_COUNT > 128;
|
||||
typedef EnumSet (Printable) = EnumSetType;
|
||||
|
||||
fn void EnumSet.add(&self, Enum v)
|
||||
|
||||
@@ -34,3 +34,12 @@ macro Type? Maybe.get(self)
|
||||
{
|
||||
return self.has_value ? self.value : NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn bool Maybe.equals(self, Maybe other) @operator(==) @if(types::is_equatable_type(Type))
|
||||
{
|
||||
if (self.has_value)
|
||||
{
|
||||
return other.has_value && equals(self.value, other.value);
|
||||
}
|
||||
return !other.has_value;
|
||||
}
|
||||
|
||||
@@ -186,6 +186,10 @@ fn void Object.set_object(&self, String key, Object* new_object) @private
|
||||
self.map.set(key, new_object);
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.allocator != null : "This object is not properly initialized, was it really created using 'new'"
|
||||
@require !@typeis(value, void*) ||| value == null : "void pointers cannot be stored in an object"
|
||||
*>
|
||||
macro Object* Object.object_from_value(&self, value) @private
|
||||
{
|
||||
var $Type = $typeof(value);
|
||||
@@ -201,7 +205,6 @@ macro Object* Object.object_from_value(&self, value) @private
|
||||
$case $Type.typeid == Object*.typeid:
|
||||
return value;
|
||||
$case $Type.typeid == void*.typeid:
|
||||
if (value != null) return TYPE_MISMATCH?;
|
||||
return &NULL_OBJECT;
|
||||
$case $assignable(value, String):
|
||||
return new_string(value, self.allocator);
|
||||
|
||||
@@ -248,7 +248,7 @@ fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
|
||||
}
|
||||
|
||||
// write end of stream
|
||||
output[pos:END_OF_STREAM.len] = END_OF_STREAM;
|
||||
output[pos:END_OF_STREAM.len] = END_OF_STREAM[..];
|
||||
pos += END_OF_STREAM.len;
|
||||
|
||||
return output[:pos];
|
||||
@@ -364,7 +364,7 @@ fn char[]? decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels c
|
||||
}
|
||||
|
||||
// draw the pixel
|
||||
if (channels == RGBA) { image[loc:4] = p.rgba; } else { image[loc:3] = p.rgb; }
|
||||
if (channels == RGBA) { image[loc:4] = p.rgba[..]; } else { image[loc:3] = p.rgb[..]; }
|
||||
}
|
||||
|
||||
return image;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
|
||||
// Copyright (c) 2023-2025 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;
|
||||
|
||||
// The arena allocator allocates up to its maximum data
|
||||
// and then fails to allocate more, returning out of memory.
|
||||
// It supports mark and reset to mark.
|
||||
|
||||
struct ArenaAllocator (Allocator)
|
||||
{
|
||||
char[] data;
|
||||
@@ -12,6 +16,8 @@ struct ArenaAllocator (Allocator)
|
||||
|
||||
<*
|
||||
Initialize a memory arena for use using the provided bytes.
|
||||
|
||||
@param [inout] data : "The memory to use for the arena."
|
||||
*>
|
||||
fn ArenaAllocator* ArenaAllocator.init(&self, char[] data)
|
||||
{
|
||||
@@ -20,23 +26,44 @@ fn ArenaAllocator* ArenaAllocator.init(&self, char[] data)
|
||||
return self;
|
||||
}
|
||||
|
||||
<*
|
||||
Reset the usage completely.
|
||||
*>
|
||||
fn void ArenaAllocator.clear(&self)
|
||||
{
|
||||
self.used = 0;
|
||||
}
|
||||
|
||||
struct ArenaAllocatorHeader @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
<*
|
||||
Given some memory, create an arena allocator on the stack for it.
|
||||
|
||||
@param [inout] bytes : `The bytes to use, may be empty.`
|
||||
|
||||
@return `An arena allocator using the bytes`
|
||||
*>
|
||||
macro ArenaAllocator* wrap(char[] bytes)
|
||||
{
|
||||
return (ArenaAllocator){}.init(bytes);
|
||||
}
|
||||
|
||||
<*
|
||||
"Mark" the current state of the arena allocator by returning the use count.
|
||||
|
||||
@return `The value to pass to 'reset' in order to reset to the current use.`
|
||||
*>
|
||||
fn usz ArenaAllocator.mark(&self) => self.used;
|
||||
|
||||
<*
|
||||
Reset to a previous mark.
|
||||
|
||||
@param mark : `The previous mark.`
|
||||
@require mark <= self.used : "Invalid mark - out of range"
|
||||
*>
|
||||
fn void ArenaAllocator.reset(&self, usz mark) => self.used = mark;
|
||||
|
||||
<*
|
||||
Implements the Allocator interface method.
|
||||
|
||||
@require ptr != null
|
||||
*>
|
||||
fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||
@@ -50,10 +77,10 @@ fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
|
||||
}
|
||||
}
|
||||
|
||||
fn usz ArenaAllocator.mark(&self) => self.used;
|
||||
fn void ArenaAllocator.reset(&self, usz mark) => self.used = mark;
|
||||
|
||||
<*
|
||||
Implements the Allocator interface method.
|
||||
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
|
||||
@require size > 0
|
||||
@@ -77,6 +104,8 @@ fn void*? ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz a
|
||||
}
|
||||
|
||||
<*
|
||||
Implements the Allocator interface method.
|
||||
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
|
||||
@require old_pointer != null
|
||||
@@ -111,4 +140,12 @@ fn void*? ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
|
||||
void* mem = self.acquire(size, NO_ZERO, alignment)!;
|
||||
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
}
|
||||
|
||||
// Internal data
|
||||
|
||||
struct ArenaAllocatorHeader @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
module std::core::mem::allocator;
|
||||
import std::io, std::math;
|
||||
|
||||
struct AllocChunk @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
<*
|
||||
The backed arena allocator provides an allocator that will allocate from a pre-allocated chunk of memory
|
||||
provided by it's backing allocator. The allocator supports mark / reset operations, so it can be used
|
||||
as a stack (push-pop) allocator. If the initial memory is used up, it will fall back to regular allocations,
|
||||
that will be safely freed on `reset`.
|
||||
|
||||
While this allocator is similar to the dynamic arena, it supports multiple "save points", which the dynamic arena
|
||||
doesn't.
|
||||
*>
|
||||
struct BackedArenaAllocator (Allocator)
|
||||
{
|
||||
Allocator backing_allocator;
|
||||
@@ -16,6 +19,12 @@ struct BackedArenaAllocator (Allocator)
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
struct AllocChunk @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
const usz PAGE_IS_ALIGNED @local = (usz)isz.max + 1u;
|
||||
|
||||
struct ExtraPage @local
|
||||
|
||||
@@ -4,6 +4,17 @@
|
||||
module std::core::mem::allocator;
|
||||
import std::math;
|
||||
|
||||
<*
|
||||
The dynamic arena allocator is an arena allocator that can grow by adding additional arena "pages".
|
||||
It only supports reset, at which point all pages except the first one is released to the backing
|
||||
allocator.
|
||||
|
||||
If you want multiple save points, use the BackedArenaAllocator instead.
|
||||
|
||||
The advantage over the BackedArenaAllocator, is that when allocating beyond the first "page", it will
|
||||
retain the characteristics of an arena allocator (allocating a large piece of memory then handing off
|
||||
memory from that memory), wheras the BackedArenaAllocator will have heap allocator characteristics.
|
||||
*>
|
||||
struct DynamicArenaAllocator (Allocator)
|
||||
{
|
||||
Allocator backing_allocator;
|
||||
|
||||
@@ -5,6 +5,13 @@
|
||||
module std::core::mem::allocator;
|
||||
import std::math;
|
||||
|
||||
<*
|
||||
The SimpleHeapAllocator implements a simple heap allocator on top of an allocator function.
|
||||
|
||||
It uses the given allocator function to allocate memory from some source, but never frees it.
|
||||
This allocator is intended to be used in environments where there isn't any native libc malloc,
|
||||
and it has to be emulated from a memory region, or wrapping linear memory as is the case for plain WASM.
|
||||
*>
|
||||
struct SimpleHeapAllocator (Allocator)
|
||||
{
|
||||
MemoryAllocFn alloc_fn;
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
|
||||
// Copyright (c) 2021-2025 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 @if(env::LIBC);
|
||||
import std::io;
|
||||
import libc;
|
||||
|
||||
const LibcAllocator LIBC_ALLOCATOR = {};
|
||||
<*
|
||||
The LibcAllocator is a wrapper around malloc to conform to the Allocator interface.
|
||||
*>
|
||||
typedef LibcAllocator (Allocator, Printable) = uptr;
|
||||
const LibcAllocator LIBC_ALLOCATOR = {};
|
||||
|
||||
fn String LibcAllocator.to_string(&self, Allocator allocator) @dynamic => "Libc allocator".copy(allocator);
|
||||
fn usz? LibcAllocator.to_format(&self, Formatter *format) @dynamic => format.print("Libc allocator");
|
||||
@@ -123,7 +125,7 @@ fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
|
||||
if (init_type == ZERO)
|
||||
{
|
||||
void* data = alignment ? @aligned_alloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment)!! : libc::calloc(bytes, 1);
|
||||
return data ?: mem::OUT_OF_MEMORYY?;
|
||||
return data ?: mem::OUT_OF_MEMORY?;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
module std::core::mem::allocator;
|
||||
|
||||
<*
|
||||
The OnStackAllocator is similar to the ArenaAllocator: it allocates from a chunk of memory
|
||||
given to it.
|
||||
|
||||
The difference is that when it runs out of memory it will go directly to its backing allocator
|
||||
rather than failing.
|
||||
|
||||
It is utilized by the @stack_mem macro as an alternative to the temp allocator.
|
||||
*>
|
||||
struct OnStackAllocator (Allocator)
|
||||
{
|
||||
Allocator backing_allocator;
|
||||
@@ -8,7 +17,6 @@ struct OnStackAllocator (Allocator)
|
||||
OnStackAllocatorExtraChunk* chunk;
|
||||
}
|
||||
|
||||
|
||||
struct OnStackAllocatorExtraChunk @local
|
||||
{
|
||||
bool is_aligned;
|
||||
|
||||
@@ -2,12 +2,32 @@ module std::core::mem::allocator;
|
||||
import std::io, std::math;
|
||||
import std::core::sanitizer::asan;
|
||||
|
||||
|
||||
struct TempAllocatorChunk @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
// This implements the temp allocator.
|
||||
// The temp allocator is a specialized allocator only intended for use where
|
||||
// the allocation is strictly stack-like.
|
||||
//
|
||||
// It is *not* thread-safe: you cannot safely use the
|
||||
// temp allocator on a thread and pass it to another thread.
|
||||
//
|
||||
// Fundamentally the temp allocator is a thread local arena allocator
|
||||
// but the stack-like behaviour puts additional constraints to it.
|
||||
//
|
||||
// It works great for allocating temporary data in a scope then dropping
|
||||
// that data, however you should not be storing temporary data in globals
|
||||
// or locals that have a lifetime outside of the current temp allocator scope.
|
||||
//
|
||||
// Furthermore, note that the temp allocator is bounded, with additional
|
||||
// allocations on top of that causing heap allocations. Such heap allocations
|
||||
// will be cleaned up but is undesirable from a performance standpoint.
|
||||
//
|
||||
// If you want customizable behaviour similar to the temp allocator, consider
|
||||
// the ArenaAllocator, BackedArenaAllocator or the DynamicArenaAllocator.
|
||||
//
|
||||
// Experimenting with the temp allocator to make it work outside of its
|
||||
// standard usage patterns will invariably end in tears and frustrated
|
||||
// hair pulling.
|
||||
//
|
||||
// Use one of the ArenaAllocators instead.
|
||||
|
||||
struct TempAllocator (Allocator)
|
||||
{
|
||||
@@ -15,14 +35,22 @@ struct TempAllocator (Allocator)
|
||||
TempAllocatorPage* last_page;
|
||||
TempAllocator* derived;
|
||||
bool allocated;
|
||||
usz reserve_size;
|
||||
usz realloc_size;
|
||||
usz min_size;
|
||||
usz used;
|
||||
usz capacity;
|
||||
usz original_capacity;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u;
|
||||
struct TempAllocatorChunk @local
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
const usz PAGE_IS_ALIGNED @local = (usz)isz.max + 1u;
|
||||
|
||||
struct TempAllocatorPage
|
||||
{
|
||||
@@ -37,15 +65,20 @@ macro usz TempAllocatorPage.pagesize(&self) => self.size & ~PAGE_IS_ALIGNED;
|
||||
macro bool TempAllocatorPage.is_aligned(&self) => self.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED;
|
||||
|
||||
<*
|
||||
@require size >= 16
|
||||
@require size >= 64
|
||||
@require realloc_size >= 64
|
||||
@require allocator.type != TempAllocator.typeid : "You may not create a temp allocator with a TempAllocator as the backing allocator."
|
||||
@require min_size > TempAllocator.sizeof + 64 : "Min size must meaningfully hold the data + some bytes"
|
||||
*>
|
||||
fn TempAllocator*? new_temp_allocator(Allocator allocator, usz size)
|
||||
fn TempAllocator*? new_temp_allocator(Allocator allocator, usz size, usz reserve = temp_allocator_reserve_size, usz min_size = temp_allocator_min_size, usz realloc_size = temp_allocator_realloc_size)
|
||||
{
|
||||
TempAllocator* temp = allocator::alloc_with_padding(allocator, TempAllocator, size)!;
|
||||
temp.last_page = null;
|
||||
temp.backing_allocator = allocator;
|
||||
temp.used = 0;
|
||||
temp.min_size = min_size;
|
||||
temp.realloc_size = realloc_size;
|
||||
temp.reserve_size = reserve;
|
||||
temp.allocated = true;
|
||||
temp.derived = null;
|
||||
temp.original_capacity = temp.capacity = size;
|
||||
@@ -53,19 +86,18 @@ fn TempAllocator*? new_temp_allocator(Allocator allocator, usz size)
|
||||
}
|
||||
<*
|
||||
@require !self.derived
|
||||
@require min_size > TempAllocator.sizeof + 64 : "Min size must meaningfully hold the data + some bytes"
|
||||
@require mult > 0 : "The multiple can never be zero"
|
||||
*>
|
||||
fn TempAllocator*? TempAllocator.derive_allocator(&self, usz min_size, usz buffer, usz mult)
|
||||
fn TempAllocator*? TempAllocator.derive_allocator(&self, usz reserve = 0)
|
||||
{
|
||||
if (!reserve) reserve = self.reserve_size;
|
||||
usz remaining = self.capacity - self.used;
|
||||
void* mem @noinit;
|
||||
usz size @noinit;
|
||||
if (min_size + buffer > remaining)
|
||||
if (self.min_size + reserve > remaining)
|
||||
{
|
||||
return self.derived = new_temp_allocator(self.backing_allocator, min_size * mult)!;
|
||||
return self.derived = new_temp_allocator(self.backing_allocator, self.realloc_size, self.reserve_size, self.min_size, self.realloc_size)!;
|
||||
}
|
||||
usz start = mem::aligned_offset(self.used + buffer, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
usz start = mem::aligned_offset(self.used + reserve, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
void* ptr = &self.data[start];
|
||||
TempAllocator* temp = (TempAllocator*)ptr;
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
@@ -74,6 +106,9 @@ fn TempAllocator*? TempAllocator.derive_allocator(&self, usz min_size, usz buffe
|
||||
temp.last_page = null;
|
||||
temp.backing_allocator = self.backing_allocator;
|
||||
temp.used = 0;
|
||||
temp.min_size = self.min_size;
|
||||
temp.reserve_size = self.reserve_size;
|
||||
temp.realloc_size = self.realloc_size;
|
||||
temp.allocated = false;
|
||||
temp.derived = null;
|
||||
temp.original_capacity = temp.capacity = self.capacity - start - TempAllocator.sizeof;
|
||||
@@ -82,6 +117,9 @@ fn TempAllocator*? TempAllocator.derive_allocator(&self, usz min_size, usz buffe
|
||||
return temp;
|
||||
}
|
||||
|
||||
<*
|
||||
Reset the entire temp allocator, which will merge all the children into it.
|
||||
*>
|
||||
fn void TempAllocator.reset(&self)
|
||||
{
|
||||
TempAllocator* child = self.derived;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
|
||||
// Copyright (c) 2021-2025 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.
|
||||
|
||||
@@ -18,6 +18,10 @@ alias AllocMap = HashMap { uptr, Allocation };
|
||||
// A simple tracking allocator.
|
||||
// It tracks allocations using a hash map but
|
||||
// is not compatible with allocators that uses mark()
|
||||
//
|
||||
// It is also embarassingly single-threaded, so
|
||||
// do not use it to track allocations that cross threads.
|
||||
|
||||
struct TrackingAllocator (Allocator)
|
||||
{
|
||||
Allocator inner_allocator;
|
||||
|
||||
@@ -2,6 +2,25 @@ module std::core::array;
|
||||
import std::core::array::slice;
|
||||
|
||||
<*
|
||||
Returns true if the array contains at least one element, else false
|
||||
|
||||
@param [in] array
|
||||
@param [in] element
|
||||
@require @typekind(array) == SLICE || @typekind(array) == ARRAY
|
||||
@require @typeis(array[0], $typeof(element)) : "array and element must have the same type"
|
||||
*>
|
||||
macro bool contains(array, element)
|
||||
{
|
||||
foreach (&item : array)
|
||||
{
|
||||
if (*item == element) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
<*
|
||||
Return the first index of element found in the array, searching from the start.
|
||||
|
||||
@param [in] array
|
||||
@param [in] element
|
||||
@return "the first index of the element"
|
||||
@@ -17,6 +36,14 @@ macro index_of(array, element)
|
||||
}
|
||||
|
||||
<*
|
||||
Slice a 2d array and create a Slice2d from it.
|
||||
|
||||
@param array_ptr : "the pointer to create a slice from"
|
||||
@param x : "The starting position of the slice x, optional"
|
||||
@param y : "The starting position of the slice y, optional"
|
||||
@param xlen : "The length of the slice in x, defaults to the length of the array"
|
||||
@param ylen : "The length of the slice in y, defaults to the length of the array"
|
||||
@return "A Slice2d from the array"
|
||||
@require @typekind(array_ptr) == POINTER
|
||||
@require @typekind(*array_ptr) == VECTOR || @typekind(*array_ptr) == ARRAY
|
||||
@require @typekind((*array_ptr)[0]) == VECTOR || @typekind((*array_ptr)[0]) == ARRAY
|
||||
@@ -26,11 +53,13 @@ macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0)
|
||||
if (xlen < 1) xlen = $typeof((*array_ptr)[0]).len + xlen;
|
||||
if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen;
|
||||
var $ElementType = $typeof((*array_ptr)[0][0]);
|
||||
return Slice2d{$ElementType} { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
|
||||
return (Slice2d{$ElementType}) { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Return the first index of element found in the array, searching in reverse from the end.
|
||||
|
||||
@param [in] array
|
||||
@param [in] element
|
||||
@return "the last index of the element"
|
||||
@@ -81,97 +110,4 @@ macro concat(Allocator allocator, arr1, arr2) @nodiscard
|
||||
@require @typeis(arr1[0], $typeof(arr2[0])) : "Arrays must have the same type"
|
||||
@ensure return.len == arr1.len + arr2.len
|
||||
*>
|
||||
macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);
|
||||
|
||||
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_row(self, usz idy) @operator([])
|
||||
{
|
||||
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
|
||||
}
|
||||
|
||||
macro Type Slice2d.get_coord(self, usz[<2>] coord)
|
||||
{
|
||||
return *self.get_coord_ref(coord);
|
||||
}
|
||||
|
||||
macro Type Slice2d.get_xy(self, x, y)
|
||||
{
|
||||
return *self.get_xy_ref(x, y);
|
||||
}
|
||||
|
||||
macro Type* Slice2d.get_xy_ref(self, x, y)
|
||||
{
|
||||
return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
|
||||
}
|
||||
|
||||
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
|
||||
{
|
||||
return self.get_xy_ref(coord.x, coord.y);
|
||||
}
|
||||
|
||||
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
|
||||
{
|
||||
*self.get_coord_ref(coord) = value;
|
||||
}
|
||||
|
||||
macro void Slice2d.set_xy(self, x, y, Type value)
|
||||
{
|
||||
*self.get_xy_ref(x, y) = value;
|
||||
}
|
||||
|
||||
<*
|
||||
@require y >= 0 && y < self.ylen
|
||||
@require x >= 0 && x < self.xlen
|
||||
*>
|
||||
fn Slice2d Slice2d.slice(&self, isz x = 0, isz xlen = 0, isz y = 0, isz ylen = 0)
|
||||
{
|
||||
if (xlen < 1) xlen = self.xlen + xlen;
|
||||
if (ylen < 1) ylen = self.ylen + ylen;
|
||||
return { self.ptr, self.inner_len, y + self.ystart, ylen, x + self.xstart, xlen };
|
||||
}
|
||||
macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);
|
||||
114
lib/std/core/ascii.c3
Normal file
114
lib/std/core/ascii.c3
Normal file
@@ -0,0 +1,114 @@
|
||||
<*
|
||||
This module contains utils for handling ASCII characters. They only operate on
|
||||
characters corresponding to 0-127.
|
||||
*>
|
||||
module std::core::ascii;
|
||||
|
||||
macro bool @is_lower(c) => ASCII_LOOKUP[c].lower; // Is a-z
|
||||
macro bool @is_upper(c) => ASCII_LOOKUP[c].upper; // Is A-Z
|
||||
macro bool @is_digit(c) => ASCII_LOOKUP[c].digit; // Is 0-9
|
||||
macro bool @is_bdigit(c) => ASCII_LOOKUP[c].bin_digit; // Is 0-1
|
||||
macro bool @is_odigit(c) => ASCII_LOOKUP[c].oct_digit; // Is 0-7
|
||||
macro bool @is_xdigit(c) => ASCII_LOOKUP[c].hex_digit; // Is 0-9 or a-f or A-F
|
||||
macro bool @is_alpha(c) => ASCII_LOOKUP[c].alpha; // Is a-z or A-Z
|
||||
macro bool @is_print(c) => ASCII_LOOKUP[c].printable; // Is a printable character (space or higher and < 127
|
||||
macro bool @is_graph(c) => ASCII_LOOKUP[c].graph; // Does it show any graphics (printable but not space)
|
||||
macro bool @is_space(c) => ASCII_LOOKUP[c].space; // Is it a space character: space, tab, linefeed etc
|
||||
macro bool @is_alnum(c) => ASCII_LOOKUP[c].alphanum; // Is it alpha or digit
|
||||
macro bool @is_punct(c) => ASCII_LOOKUP[c].punct; // Is it "graph" but not digit or letter
|
||||
macro bool @is_blank(c) => ASCII_LOOKUP[c].blank; // Is it a blank space: space or tab
|
||||
macro bool @is_cntrl(c) => ASCII_LOOKUP[c].control; // Is it a control character: before space or 127
|
||||
macro char @to_lower(c) => c + TO_LOWER[c]; // Convert A-Z to a-z if found
|
||||
macro char @to_upper(c) => c - TO_UPPER[c]; // Convert a-z to A-Z if found
|
||||
|
||||
fn bool is_lower(char c) => @is_lower(c); // Is a-z
|
||||
fn bool is_upper(char c) => @is_upper(c); // Is A-Z
|
||||
fn bool is_digit(char c) => @is_digit(c); // Is 0-9
|
||||
fn bool is_bdigit(char c) => @is_bdigit(c); // Is 0-1
|
||||
fn bool is_odigit(char c) => @is_odigit(c); // Is 0-7
|
||||
fn bool is_xdigit(char c) => @is_xdigit(c); // Is 0-9 or a-f or A-F
|
||||
fn bool is_alpha(char c) => @is_alpha(c); // Is a-z or A-Z
|
||||
fn bool is_print(char c) => @is_print(c); // Is a printable character (space or higher and < 127
|
||||
fn bool is_graph(char c) => @is_graph(c); // Does it show any graphics (printable but not space)
|
||||
fn bool is_space(char c) => @is_space(c); // Is it a space character: space, tab, linefeed etc
|
||||
fn bool is_alnum(char c) => @is_alnum(c); // Is it alpha or digit
|
||||
fn bool is_punct(char c) => @is_punct(c); // Is it "graph" but not digit or letter
|
||||
fn bool is_blank(char c) => @is_blank(c); // Is it a blank space: space or tab
|
||||
fn bool is_cntrl(char c) => @is_cntrl(c); // Is it a control character: before space or 127
|
||||
fn char to_lower(char c) => @to_lower(c); // Convert A-Z to a-z if found
|
||||
fn char to_upper(char c) => @to_upper(c); // Convert a-z to A-Z if found
|
||||
|
||||
// The following methods are macro methods for the same functions
|
||||
macro bool char.is_lower(char c) => @is_lower(c);
|
||||
macro bool char.is_upper(char c) => @is_upper(c);
|
||||
macro bool char.is_digit(char c) => @is_digit(c);
|
||||
macro bool char.is_bdigit(char c) => @is_bdigit(c);
|
||||
macro bool char.is_odigit(char c) => @is_odigit(c);
|
||||
macro bool char.is_xdigit(char c) => @is_xdigit(c);
|
||||
macro bool char.is_alpha(char c) => @is_alpha(c);
|
||||
macro bool char.is_print(char c) => @is_print(c);
|
||||
macro bool char.is_graph(char c) => @is_graph(c);
|
||||
macro bool char.is_space(char c) => @is_space(c);
|
||||
macro bool char.is_alnum(char c) => @is_alnum(c);
|
||||
macro bool char.is_punct(char c) => @is_punct(c);
|
||||
macro bool char.is_blank(char c) => @is_blank(c);
|
||||
macro bool char.is_cntrl(char c) => @is_cntrl(c);
|
||||
macro char char.to_lower(char c) => @to_lower(c);
|
||||
macro char char.to_upper(char c) => @to_upper(c);
|
||||
|
||||
<*
|
||||
Convert a-f/A-F/0-9 to the appropriate hex value.
|
||||
|
||||
@require c.is_xdigit()
|
||||
@ensure return >= 0 && return <= 15
|
||||
*>
|
||||
macro char char.from_hex(char c) => HEX_VALUE[c];
|
||||
|
||||
<*
|
||||
Bitstruct containing the different properties of a character
|
||||
*>
|
||||
bitstruct CharType : ushort @private
|
||||
{
|
||||
bool lower;
|
||||
bool upper;
|
||||
bool digit;
|
||||
bool bin_digit;
|
||||
bool hex_digit;
|
||||
bool oct_digit;
|
||||
bool alpha;
|
||||
bool alphanum;
|
||||
bool space;
|
||||
bool printable;
|
||||
bool blank;
|
||||
bool punct;
|
||||
bool control;
|
||||
bool graph;
|
||||
}
|
||||
|
||||
const CharType[256] ASCII_LOOKUP @private = {
|
||||
[0..31] = { .control },
|
||||
[9..13] = { .control, .space },
|
||||
['\t'] = { .control, .space, .blank },
|
||||
[' '] = { .space, .printable, .blank },
|
||||
[33..126] = { .printable, .graph, .punct },
|
||||
['0'..'9'] = { .printable, .graph, .alphanum, .hex_digit, .digit },
|
||||
['2'..'7'] = { .printable, .graph, .alphanum, .hex_digit, .digit, .oct_digit },
|
||||
['0'..'1'] = { .printable, .graph, .alphanum, .hex_digit, .digit, .oct_digit, .bin_digit },
|
||||
['A'..'Z'] = { .printable, .graph, .alphanum, .alpha, .upper },
|
||||
['A'..'F'] = { .printable, .graph, .alphanum, .alpha, .upper, .hex_digit },
|
||||
['a'..'z'] = { .printable, .graph, .alphanum, .alpha, .lower },
|
||||
['a'..'f'] = { .printable, .graph, .alphanum, .alpha, .lower, .hex_digit },
|
||||
[127] = { .control },
|
||||
};
|
||||
|
||||
const char[256] HEX_VALUE = {
|
||||
['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, ['4'] = 4,
|
||||
['5'] = 5, ['6'] = 6, ['7'] = 7, ['8'] = 8, ['9'] = 9,
|
||||
['A'] = 10, ['B'] = 11, ['C'] = 12, ['D'] = 13, ['E'] = 14,
|
||||
['F'] = 15, ['a'] = 10, ['b'] = 11, ['c'] = 12, ['d'] = 13,
|
||||
['e'] = 14, ['f'] = 15
|
||||
};
|
||||
|
||||
const char[256] TO_UPPER @private = { ['a'..'z'] = 'a' - 'A' };
|
||||
const char[256] TO_LOWER @private = { ['A'..'Z'] = 'a' - 'A' };
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2023 Christoffer Lerno and contributors. All rights reserved.
|
||||
// Copyright (c) 2023-2025 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::bitorder;
|
||||
@@ -88,33 +88,33 @@ bitstruct UInt128LE : uint128 @littleendian
|
||||
}
|
||||
|
||||
<*
|
||||
@require is_array_or_slice_of_char(bytes) : "argument must be an array, a pointer to an array or a slice of char"
|
||||
@require @is_array_or_slice_of_char(bytes) : "argument must be an array, a pointer to an array or a slice of char"
|
||||
@require is_bitorder($Type) : "type must be a bitorder integer"
|
||||
*>
|
||||
macro read(bytes, $Type)
|
||||
{
|
||||
char[] s;
|
||||
$switch @typekind(bytes):
|
||||
$case POINTER:
|
||||
s = (*bytes)[:$Type.sizeof];
|
||||
$default:
|
||||
s = bytes[:$Type.sizeof];
|
||||
$case POINTER:
|
||||
s = (*bytes)[:$Type.sizeof];
|
||||
$default:
|
||||
s = bytes[:$Type.sizeof];
|
||||
$endswitch
|
||||
return bitcast(*(char[$Type.sizeof]*)s.ptr, $Type).val;
|
||||
}
|
||||
|
||||
<*
|
||||
@require is_arrayptr_or_slice_of_char(bytes) : "argument must be a pointer to an array or a slice of char"
|
||||
@require @is_arrayptr_or_slice_of_char(bytes) : "argument must be a pointer to an array or a slice of char"
|
||||
@require is_bitorder($Type) : "type must be a bitorder integer"
|
||||
*>
|
||||
macro write(x, bytes, $Type)
|
||||
{
|
||||
char[] s;
|
||||
$switch @typekind(bytes):
|
||||
$case POINTER:
|
||||
s = (*bytes)[:$Type.sizeof];
|
||||
$default:
|
||||
s = bytes[:$Type.sizeof];
|
||||
$case POINTER:
|
||||
s = (*bytes)[:$Type.sizeof];
|
||||
$default:
|
||||
s = bytes[:$Type.sizeof];
|
||||
$endswitch
|
||||
*($typeof(x)*)s.ptr = bitcast(x, $Type).val;
|
||||
}
|
||||
@@ -122,59 +122,63 @@ macro write(x, bytes, $Type)
|
||||
macro is_bitorder($Type)
|
||||
{
|
||||
$switch $Type:
|
||||
$case UShortLE:
|
||||
$case ShortLE:
|
||||
$case UIntLE:
|
||||
$case IntLE:
|
||||
$case ULongLE:
|
||||
$case LongLE:
|
||||
$case UInt128LE:
|
||||
$case Int128LE:
|
||||
$case UShortBE:
|
||||
$case ShortBE:
|
||||
$case UIntBE:
|
||||
$case IntBE:
|
||||
$case ULongBE:
|
||||
$case LongBE:
|
||||
$case UInt128BE:
|
||||
$case Int128BE:
|
||||
return true;
|
||||
$default:
|
||||
return false;
|
||||
$case UShortLE:
|
||||
$case ShortLE:
|
||||
$case UIntLE:
|
||||
$case IntLE:
|
||||
$case ULongLE:
|
||||
$case LongLE:
|
||||
$case UInt128LE:
|
||||
$case Int128LE:
|
||||
$case UShortBE:
|
||||
$case ShortBE:
|
||||
$case UIntBE:
|
||||
$case IntBE:
|
||||
$case ULongBE:
|
||||
$case LongBE:
|
||||
$case UInt128BE:
|
||||
$case Int128BE:
|
||||
return true;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro bool is_array_or_slice_of_char(bytes)
|
||||
macro bool is_array_or_slice_of_char(bytes) @deprecated("Use @is_array_or_slice_of_char")
|
||||
{
|
||||
$switch @typekind(bytes):
|
||||
$case POINTER:
|
||||
var $Inner = $typefrom($typeof(bytes).inner);
|
||||
$if $Inner.kindof == ARRAY:
|
||||
var $Inner2 = $typefrom($Inner.inner);
|
||||
return $Inner2.typeid == char.typeid;
|
||||
$endif
|
||||
$case ARRAY:
|
||||
$case SLICE:
|
||||
var $Inner = $typefrom($typeof(bytes).inner);
|
||||
return $Inner.typeid == char.typeid;
|
||||
$default:
|
||||
return false;
|
||||
return @is_array_or_slice_of_char(bytes);
|
||||
}
|
||||
|
||||
macro bool @is_array_or_slice_of_char(#bytes) @const
|
||||
{
|
||||
var $Type = $typeof(#bytes);
|
||||
$switch $Type.kindof:
|
||||
$case POINTER:
|
||||
typeid $inner = $Type.inner;
|
||||
return $inner.kindof == ARRAY &&& $inner.inner == char.typeid;
|
||||
$case ARRAY:
|
||||
$case SLICE:
|
||||
return $Type.inner == char.typeid;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro bool is_arrayptr_or_slice_of_char(bytes)
|
||||
macro bool is_arrayptr_or_slice_of_char(bytes) @deprecated("Use @is_arrayptr_or_slice_of_char")
|
||||
{
|
||||
$switch @typekind(bytes):
|
||||
$case POINTER:
|
||||
var $Inner = $typefrom($typeof(bytes).inner);
|
||||
$if $Inner.kindof == ARRAY:
|
||||
var $Inner2 = $typefrom($Inner.inner);
|
||||
return $Inner2.typeid == char.typeid;
|
||||
$endif
|
||||
$case SLICE:
|
||||
var $Inner = $typefrom($typeof(bytes).inner);
|
||||
return $Inner.typeid == char.typeid;
|
||||
$default:
|
||||
return false;
|
||||
return @is_arrayptr_or_slice_of_char(bytes);
|
||||
}
|
||||
|
||||
macro bool @is_arrayptr_or_slice_of_char(#bytes) @const
|
||||
{
|
||||
var $Type = $typeof(#bytes);
|
||||
$switch $Type.kindof:
|
||||
$case POINTER:
|
||||
typeid $inner = $Type.inner;
|
||||
return $inner.kindof == ARRAY &&& $inner.inner == char.typeid;
|
||||
$case SLICE:
|
||||
return $Type.inner == char.typeid;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
}
|
||||
@@ -30,6 +30,14 @@ typedef EmptySlot = void*;
|
||||
macro @is_empty_macro_slot(#arg) @const @builtin => @typeis(#arg, EmptySlot);
|
||||
macro @is_valid_macro_slot(#arg) @const @builtin => !@typeis(#arg, EmptySlot);
|
||||
|
||||
<*
|
||||
Returns a random value at compile time.
|
||||
|
||||
@ensure return >= 0.0 && return < 1.0
|
||||
@return "A compile time random"
|
||||
*>
|
||||
macro @rnd() @const @builtin => $$rnd();
|
||||
|
||||
/*
|
||||
Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
|
||||
*/
|
||||
@@ -196,8 +204,9 @@ macro void unreachable(String string = "Unreachable statement reached.", ...) @b
|
||||
{
|
||||
$if env::COMPILER_SAFE_MODE:
|
||||
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
|
||||
$endif;
|
||||
$else
|
||||
$$unreachable();
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -276,9 +285,8 @@ macro enum_by_name($Type, String enum_name) @builtin
|
||||
@ensure @typeis(return, $Type)
|
||||
@return? NOT_FOUND
|
||||
*>
|
||||
macro @enum_from_value($Type, #value, value) @builtin
|
||||
macro @enum_from_value($Type, #value, value) @builtin @deprecated("Use Enum.lookup_field and Enum.lookup")
|
||||
{
|
||||
usz elements = $Type.elements;
|
||||
foreach (e : $Type.values)
|
||||
{
|
||||
if (e.#value == value) return e;
|
||||
@@ -431,22 +439,63 @@ macro @generic_hash(value)
|
||||
return h;
|
||||
}
|
||||
|
||||
macro uint int.hash(int i) => @generic_hash(i);
|
||||
macro uint uint.hash(uint i) => @generic_hash(i);
|
||||
macro uint short.hash(short s) => @generic_hash(s);
|
||||
macro uint ushort.hash(ushort s) => @generic_hash(s);
|
||||
macro uint char.hash(char c) => @generic_hash(c);
|
||||
macro uint ichar.hash(ichar c) => @generic_hash(c);
|
||||
macro uint long.hash(long i) => @generic_hash(i);
|
||||
macro uint ulong.hash(ulong i) => @generic_hash(i);
|
||||
macro uint int128.hash(int128 i) => @generic_hash(i);
|
||||
macro uint uint128.hash(uint128 i) => @generic_hash(i);
|
||||
macro uint bool.hash(bool b) => @generic_hash(b);
|
||||
|
||||
macro uint int128.hash(self) => @generic_hash(self);
|
||||
macro uint uint128.hash(self) => @generic_hash(self);
|
||||
macro uint long.hash(self) => @generic_hash(self);
|
||||
macro uint ulong.hash(self) => @generic_hash(self);
|
||||
macro uint int.hash(self) => @generic_hash(self);
|
||||
macro uint uint.hash(self) => @generic_hash(self);
|
||||
macro uint short.hash(self) => @generic_hash(self);
|
||||
macro uint ushort.hash(self) => @generic_hash(self);
|
||||
macro uint ichar.hash(self) => @generic_hash(self);
|
||||
macro uint char.hash(self) => @generic_hash(self);
|
||||
macro uint bool.hash(self) => @generic_hash(self);
|
||||
|
||||
macro uint int128[*].hash(&self) => hash_array(self);
|
||||
macro uint uint128[*].hash(&self) => hash_array(self);
|
||||
macro uint long[*].hash(&self) => hash_array(self);
|
||||
macro uint ulong[*].hash(&self) => hash_array(self);
|
||||
macro uint int[*].hash(&self) => hash_array(self);
|
||||
macro uint uint[*].hash(&self) => hash_array(self);
|
||||
macro uint short[*].hash(&self) => hash_array(self);
|
||||
macro uint ushort[*].hash(&self) => hash_array(self);
|
||||
macro uint char[*].hash(&self) => hash_array(self);
|
||||
macro uint ichar[*].hash(&self) => hash_array(self);
|
||||
macro uint bool[*].hash(&self) => hash_array(self);
|
||||
|
||||
macro uint int128[<*>].hash(self) => hash_vec(self);
|
||||
macro uint uint128[<*>].hash(self) => hash_vec(self);
|
||||
macro uint long[<*>].hash(self) => hash_vec(self);
|
||||
macro uint ulong[<*>].hash(self) => hash_vec(self);
|
||||
macro uint int[<*>].hash(self) => hash_vec(self);
|
||||
macro uint uint[<*>].hash(self) => hash_vec(self);
|
||||
macro uint short[<*>].hash(self) => hash_vec(self);
|
||||
macro uint ushort[<*>].hash(self) => hash_vec(self);
|
||||
macro uint char[<*>].hash(self) => hash_vec(self);
|
||||
macro uint ichar[<*>].hash(self) => hash_vec(self);
|
||||
macro uint bool[<*>].hash(self) => hash_vec(self);
|
||||
|
||||
macro uint typeid.hash(typeid t) => @generic_hash(((ulong)(uptr)t));
|
||||
macro uint String.hash(String c) => (uint)fnv32a::hash(c);
|
||||
macro uint char[].hash(char[] c) => (uint)fnv32a::hash(c);
|
||||
macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr));
|
||||
|
||||
<*
|
||||
@require @typekind(array_ptr) == POINTER &&& @typekind(*array_ptr) == ARRAY
|
||||
*>
|
||||
macro uint hash_array(array_ptr) @local
|
||||
{
|
||||
return (uint)fnv32a::hash(((char*)array_ptr)[:$sizeof(*array_ptr)]);
|
||||
}
|
||||
|
||||
<*
|
||||
@require @typekind(vec) == VECTOR
|
||||
*>
|
||||
macro uint hash_vec(vec) @local
|
||||
{
|
||||
return (uint)fnv32a::hash(((char*)&&vec)[:$sizeof(vec.len * $typeof(vec).inner.sizeof)]);
|
||||
}
|
||||
|
||||
const MAX_FRAMEADDRESS = 128;
|
||||
<*
|
||||
@@ -731,7 +780,7 @@ macro void* get_returnaddress(int n)
|
||||
}
|
||||
}
|
||||
|
||||
module std::core::builtin @if((env::LINUX || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS);
|
||||
module std::core::builtin @if((env::LINUX || env::ANDROID || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS);
|
||||
import libc, std::io;
|
||||
|
||||
fn void sig_panic(String message)
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
module std::core::dstring;
|
||||
import std::io;
|
||||
|
||||
<*
|
||||
The DString offers a dynamic string builder.
|
||||
*>
|
||||
typedef DString (OutStream) = DStringOpaque*;
|
||||
typedef DStringOpaque = void;
|
||||
|
||||
const usz MIN_CAPACITY @private = 16;
|
||||
|
||||
<*
|
||||
Initialize the DString with a particular allocator.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@param capacity : "Starting capacity, defaults to MIN_CAPACITY and cannot be smaller"
|
||||
@return "Return the DString itself"
|
||||
@require !self.data() : "String already initialized"
|
||||
*>
|
||||
fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY)
|
||||
@@ -20,6 +28,11 @@ fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY)
|
||||
}
|
||||
|
||||
<*
|
||||
Initialize the DString with the temp allocator. Note that if the dstring is never
|
||||
initialized, this is the allocator it will default to.
|
||||
|
||||
@param capacity : "Starting capacity, defaults to MIN_CAPACITY and cannot be smaller"
|
||||
@return "Return the DString itself"
|
||||
@require !self.data() : "String already initialized"
|
||||
*>
|
||||
fn DString DString.tinit(&self, usz capacity = MIN_CAPACITY)
|
||||
@@ -99,7 +112,7 @@ fn void DString.replace(&self, String needle, String replacement)
|
||||
};
|
||||
}
|
||||
|
||||
fn DString DString.concat(self, Allocator allocator, DString b)
|
||||
fn DString DString.concat(self, Allocator allocator, DString b) @nodiscard
|
||||
{
|
||||
DString string;
|
||||
string.init(allocator, self.len() + b.len());
|
||||
@@ -220,7 +233,7 @@ fn usz DString.append_char32(&self, Char32 c)
|
||||
|
||||
fn DString DString.tcopy(&self) => self.copy(tmem);
|
||||
|
||||
fn DString DString.copy(self, Allocator allocator)
|
||||
fn DString DString.copy(self, Allocator allocator) @nodiscard
|
||||
{
|
||||
if (!self) return new(allocator);
|
||||
StringData* data = self.data();
|
||||
@@ -229,7 +242,7 @@ fn DString DString.copy(self, Allocator allocator)
|
||||
return new_string;
|
||||
}
|
||||
|
||||
fn ZString DString.copy_zstr(self, Allocator allocator)
|
||||
fn ZString DString.copy_zstr(self, Allocator allocator) @nodiscard
|
||||
{
|
||||
usz str_len = self.len();
|
||||
if (!str_len)
|
||||
@@ -243,12 +256,12 @@ fn ZString DString.copy_zstr(self, Allocator allocator)
|
||||
return (ZString)zstr;
|
||||
}
|
||||
|
||||
fn String DString.copy_str(self, Allocator allocator)
|
||||
fn String DString.copy_str(self, Allocator allocator) @nodiscard
|
||||
{
|
||||
return (String)self.copy_zstr(allocator)[:self.len()];
|
||||
}
|
||||
|
||||
fn String DString.tcopy_str(self) => self.copy_str(tmem) @inline;
|
||||
fn String DString.tcopy_str(self) @nodiscard => self.copy_str(tmem) @inline;
|
||||
|
||||
fn bool DString.equals(self, DString other_string)
|
||||
{
|
||||
@@ -558,7 +571,7 @@ fn usz? DString.appendfn(&self, String format, args...) @maydiscard
|
||||
};
|
||||
}
|
||||
|
||||
fn DString join(Allocator allocator, String[] s, String joiner)
|
||||
fn DString join(Allocator allocator, String[] s, String joiner) @nodiscard
|
||||
{
|
||||
if (!s.len) return new(allocator);
|
||||
usz total_size = joiner.len * s.len;
|
||||
@@ -568,10 +581,10 @@ fn DString join(Allocator allocator, String[] s, String joiner)
|
||||
}
|
||||
DString res = new_with_capacity(allocator, total_size);
|
||||
res.append(s[0]);
|
||||
foreach (String* &str : s[1..])
|
||||
foreach (String str : s[1..])
|
||||
{
|
||||
res.append(joiner);
|
||||
res.append(*str);
|
||||
res.append(str);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -120,6 +120,7 @@ const String COMPILER_BUILD_HASH = $$BUILD_HASH;
|
||||
const String COMPILER_BUILD_DATE = $$BUILD_DATE;
|
||||
const OsType OS_TYPE = OsType.from_ordinal($$OS_TYPE);
|
||||
const ArchType ARCH_TYPE = ArchType.from_ordinal($$ARCH_TYPE);
|
||||
const usz MAX_VECTOR_SIZE = $$MAX_VECTOR_SIZE;
|
||||
const bool ARCH_32_BIT = $$REGISTER_SIZE == 32;
|
||||
const bool ARCH_64_BIT = $$REGISTER_SIZE == 64;
|
||||
const bool LIBC = $$COMPILER_LIBC_AVAILABLE;
|
||||
@@ -158,6 +159,7 @@ const bool MEMORY_SANITIZER = $$MEMORY_SANITIZER;
|
||||
const bool THREAD_SANITIZER = $$THREAD_SANITIZER;
|
||||
const bool ANY_SANITIZER = ADDRESS_SANITIZER || MEMORY_SANITIZER || THREAD_SANITIZER;
|
||||
const int LANGUAGE_DEV_VERSION = $$LANGUAGE_DEV_VERSION;
|
||||
const bool HAS_NATIVE_ERRNO = env::LINUX || env::ANDROID || env::DARWIN || env::WIN32;
|
||||
|
||||
macro bool os_is_darwin() @const
|
||||
{
|
||||
|
||||
@@ -342,7 +342,7 @@ macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_alig
|
||||
@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 src != null || len == 0 : "Copying a null with non-zero length is invalid"
|
||||
@require src != null || $len == 0 : "Copying a null with non-zero length is invalid"
|
||||
@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)
|
||||
@@ -576,11 +576,20 @@ fn void temp_pop(PoolState old_state)
|
||||
allocator::pop_pool(old_state) @inline;
|
||||
}
|
||||
|
||||
macro void @pool_init(Allocator allocator, usz pool_size, usz buffer_size; @body) @builtin
|
||||
<*
|
||||
@require pool_size >= 64
|
||||
@require realloc_size >= 64
|
||||
@require allocator.type != TempAllocator.typeid : "You may not create a temp allocator with a TempAllocator as the backing allocator."
|
||||
@require min_size > TempAllocator.sizeof + 64 : "Min size must meaningfully hold the data + some bytes"
|
||||
*>
|
||||
macro void @pool_init(Allocator allocator, usz pool_size,
|
||||
usz reserve_size = allocator::temp_allocator_reserve_size,
|
||||
usz min_size = allocator::temp_allocator_min_size,
|
||||
usz realloc_size = allocator::temp_allocator_realloc_size; @body) @builtin
|
||||
{
|
||||
Allocator current = allocator::current_temp;
|
||||
TempAllocator* top = allocator::top_temp;
|
||||
allocator::create_temp_allocator(allocator, pool_size, buffer_size);
|
||||
allocator::create_temp_allocator(allocator, pool_size, reserve_size, min_size, realloc_size);
|
||||
defer
|
||||
{
|
||||
allocator::destroy_temp_allocators();
|
||||
@@ -589,9 +598,19 @@ macro void @pool_init(Allocator allocator, usz pool_size, usz buffer_size; @body
|
||||
}
|
||||
@body();
|
||||
}
|
||||
macro void @pool(;@body) @builtin
|
||||
|
||||
<*
|
||||
Create a new temporary allocator.
|
||||
|
||||
The `reserve` parameter allows you to determine how many bytes should be reserved for
|
||||
allocations on the current temporary allocator, if allocations are made inside of the pool scope.
|
||||
It is made available for optimization, and can usually be ignored.
|
||||
|
||||
@param reserve : "The amount of bytes to reserve for out-of-order allocations, 0 gives the default."
|
||||
*>
|
||||
macro void @pool(usz reserve = 0; @body) @builtin
|
||||
{
|
||||
PoolState state = allocator::push_pool() @inline;
|
||||
PoolState state = allocator::push_pool(reserve) @inline;
|
||||
defer
|
||||
{
|
||||
allocator::pop_pool(state) @inline;
|
||||
@@ -627,14 +646,36 @@ macro TrackingEnv* get_tracking_env()
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@param value : "The value to clone"
|
||||
@return "A pointer to the cloned value"
|
||||
@require $alignof(value) <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'clone_aligned' instead"
|
||||
*>
|
||||
macro @clone(value) @builtin @nodiscard
|
||||
{
|
||||
return allocator::clone(mem, value);
|
||||
}
|
||||
|
||||
<*
|
||||
@param value : "The value to clone"
|
||||
@return "A pointer to the cloned value, which must be released using free_aligned"
|
||||
*>
|
||||
macro @clone_aligned(value) @builtin @nodiscard
|
||||
{
|
||||
return allocator::clone_aligned(mem, value);
|
||||
}
|
||||
|
||||
<*
|
||||
@param value : "The value to clone"
|
||||
@return "A pointer to the cloned value"
|
||||
*>
|
||||
macro @tclone(value) @builtin @nodiscard
|
||||
{
|
||||
return tnew($typeof(value), value);
|
||||
$if $alignof(value) <= mem::DEFAULT_MEM_ALIGNMENT:
|
||||
return tnew($typeof(value), value);
|
||||
$else
|
||||
return allocator::clone_aligned(tmem, value);
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void* malloc(usz size) @builtin @inline @nodiscard
|
||||
@@ -738,9 +779,9 @@ macro alloc_aligned($Type) @nodiscard
|
||||
macro tnew($Type, ...) @nodiscard
|
||||
{
|
||||
$if $vacount == 0:
|
||||
return ($Type*)tcalloc($Type.sizeof) @inline;
|
||||
return ($Type*)tcalloc($Type.sizeof, $Type.alignof) @inline;
|
||||
$else
|
||||
$Type* val = tmalloc($Type.sizeof) @inline;
|
||||
$Type* val = tmalloc($Type.sizeof, $Type.alignof) @inline;
|
||||
*val = $vaexpr[0];
|
||||
return val;
|
||||
$endif
|
||||
@@ -753,9 +794,9 @@ macro tnew($Type, ...) @nodiscard
|
||||
macro temp_with_padding($Type, usz padding, ...) @nodiscard
|
||||
{
|
||||
$if $vacount == 0:
|
||||
return ($Type*)tcalloc($Type.sizeof + padding) @inline;
|
||||
return ($Type*)tcalloc($Type.sizeof + padding, $Type.alignof) @inline;
|
||||
$else
|
||||
$Type* val = tmalloc($Type.sizeof + padding) @inline;
|
||||
$Type* val = tmalloc($Type.sizeof + padding, $Type.alignof) @inline;
|
||||
*val = $vaexpr[0];
|
||||
return val;
|
||||
$endif
|
||||
@@ -763,12 +804,12 @@ macro temp_with_padding($Type, usz padding, ...) @nodiscard
|
||||
|
||||
macro talloc($Type) @nodiscard
|
||||
{
|
||||
return tmalloc($Type.sizeof);
|
||||
return tmalloc($Type.sizeof, $Type.alignof);
|
||||
}
|
||||
|
||||
macro talloc_with_padding($Type, usz padding) @nodiscard
|
||||
{
|
||||
return tmalloc($Type.sizeof + padding);
|
||||
return tmalloc($Type.sizeof + padding, $Type.alignof);
|
||||
}
|
||||
|
||||
<*
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
module std::core::mem::allocator;
|
||||
import std::math;
|
||||
|
||||
// C3 has multiple different allocators available:
|
||||
//
|
||||
// Name Arena Uses buffer OOM Fallback? Mark? Reset?
|
||||
// ArenaAllocator Yes Yes No Yes Yes
|
||||
// BackedArenaAllocator Yes No Yes Yes Yes
|
||||
// DynamicArenaAllocator Yes No Yes No Yes
|
||||
// HeapAllocator No No No No No *Note: Not for normal use
|
||||
// LibcAllocator No No No No No *Note: Wraps malloc
|
||||
// OnStackAllocator Yes Yes Yes No No *Note: Used by @stack_mem
|
||||
// TempAllocator Yes No Yes No* No* *Note: Mark/reset using @pool
|
||||
// TrackingAllocator No No N/A No No *Note: Wraps other heap allocator
|
||||
|
||||
const DEFAULT_SIZE_PREFIX = usz.sizeof;
|
||||
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
|
||||
|
||||
@@ -20,13 +32,18 @@ enum AllocInitType
|
||||
interface Allocator
|
||||
{
|
||||
<*
|
||||
Acquire memory from the allocator, with the given alignment and initialization type.
|
||||
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
|
||||
@require size > 0
|
||||
@require size > 0 : "The size must be 1 or more"
|
||||
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
|
||||
*>
|
||||
fn void*? acquire(usz size, AllocInitType init_type, usz alignment = 0);
|
||||
|
||||
<*
|
||||
Resize acquired memory from the allocator, with the given new size and alignment.
|
||||
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
|
||||
@require ptr != null
|
||||
@@ -34,8 +51,11 @@ interface Allocator
|
||||
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
|
||||
*>
|
||||
fn void*? resize(void* ptr, usz new_size, usz alignment = 0);
|
||||
|
||||
<*
|
||||
@require ptr != null
|
||||
Release memory acquired using `acquire` or `resize`.
|
||||
|
||||
@require ptr != null : "Empty pointers should never be released"
|
||||
*>
|
||||
fn void release(void* ptr, bool aligned);
|
||||
}
|
||||
@@ -283,11 +303,31 @@ macro alloc_array_try(Allocator allocator, $Type, usz elements) @nodiscard
|
||||
return (($Type*)malloc_try(allocator, $Type.sizeof * elements))[:elements];
|
||||
}
|
||||
|
||||
<*
|
||||
Clone a value.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use to clone"
|
||||
@param value : "The value to clone"
|
||||
@return "A pointer to the cloned value"
|
||||
@require $alignof(value) <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'clone_aligned' instead"
|
||||
*>
|
||||
macro clone(Allocator allocator, value) @nodiscard
|
||||
{
|
||||
return new(allocator, $typeof(value), value);
|
||||
}
|
||||
|
||||
<*
|
||||
Clone overaligned values. Must be released using free_aligned.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use to clone"
|
||||
@param value : "The value to clone"
|
||||
@return "A pointer to the cloned value"
|
||||
*>
|
||||
macro clone_aligned(Allocator allocator, value) @nodiscard
|
||||
{
|
||||
return new_aligned(allocator, $typeof(value), value)!!;
|
||||
}
|
||||
|
||||
fn any clone_any(Allocator allocator, any value) @nodiscard
|
||||
{
|
||||
usz size = value.type.sizeof;
|
||||
@@ -367,13 +407,13 @@ tlocal TempAllocator* top_temp;
|
||||
tlocal bool auto_create_temp = false;
|
||||
|
||||
usz temp_allocator_min_size = temp_allocator_default_min_size();
|
||||
usz temp_allocator_buffer_size = temp_allocator_default_buffer_size();
|
||||
usz temp_allocator_new_mult = 4;
|
||||
usz temp_allocator_reserve_size = temp_allocator_default_reserve_size();
|
||||
usz temp_allocator_realloc_size = temp_allocator_default_min_size() * 4;
|
||||
|
||||
fn PoolState push_pool()
|
||||
fn PoolState push_pool(usz reserve = 0)
|
||||
{
|
||||
Allocator old = top_temp ? current_temp : create_temp_allocator_on_demand();
|
||||
current_temp = ((TempAllocator*)old).derive_allocator(temp_allocator_min_size, temp_allocator_buffer_size, temp_allocator_new_mult)!!;
|
||||
current_temp = ((TempAllocator*)old).derive_allocator(reserve)!!;
|
||||
return (PoolState)old.ptr;
|
||||
}
|
||||
|
||||
@@ -413,7 +453,7 @@ macro usz temp_allocator_default_min_size() @local
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro usz temp_allocator_default_buffer_size() @local
|
||||
macro usz temp_allocator_default_reserve_size() @local
|
||||
{
|
||||
$switch env::MEMORY_ENV:
|
||||
$case NORMAL: return 1024;
|
||||
@@ -435,14 +475,15 @@ fn Allocator create_temp_allocator_on_demand() @private
|
||||
auto_create_temp = true;
|
||||
abort("Use '@pool_init()' to enable the temp allocator on a new thread. A temp allocator is only implicitly created on the main thread.");
|
||||
}
|
||||
return create_temp_allocator(base_allocator(), temp_allocator_size());
|
||||
return create_temp_allocator(temp_base_allocator, temp_allocator_size(), temp_allocator_reserve_size, temp_allocator_min_size, temp_allocator_realloc_size);
|
||||
}
|
||||
|
||||
<*
|
||||
@require !top_temp : "This should never be called when temp already exists"
|
||||
*>
|
||||
fn Allocator create_temp_allocator(Allocator allocator, usz size, usz buffer = temp_allocator_default_buffer_size()) @private
|
||||
fn Allocator create_temp_allocator(Allocator allocator, usz size, usz reserve, usz min_size, usz realloc_size) @private
|
||||
{
|
||||
return current_temp = top_temp = allocator::new_temp_allocator(allocator, size)!!;
|
||||
return current_temp = top_temp = allocator::new_temp_allocator(allocator, size, reserve, min_size, realloc_size)!!;
|
||||
}
|
||||
|
||||
macro Allocator temp()
|
||||
@@ -452,7 +493,7 @@ macro Allocator temp()
|
||||
|
||||
alias tmem @builtin = current_temp;
|
||||
|
||||
fn void allow_implicit_temp_allocator_on_load_thread() @init(1) @local @if(env::LIBC)
|
||||
fn void allow_implicit_temp_allocator_on_load_thread() @init(1) @local @if(env::LIBC || env::WASM_NOLIBC)
|
||||
{
|
||||
auto_create_temp = true;
|
||||
}
|
||||
|
||||
@@ -143,7 +143,7 @@ uint128 x86_features;
|
||||
|
||||
fn void add_feature_if_bit(X86Feature feature, uint register, int bit)
|
||||
{
|
||||
if (register & 1U << bit) x86_features |= 1u128 << feature.ordinal;
|
||||
if (register & 1U << bit) x86_features |= 1ULL << feature.ordinal;
|
||||
}
|
||||
|
||||
fn void x86_initialize_cpu_features()
|
||||
|
||||
@@ -22,6 +22,22 @@ struct SliceRaw
|
||||
usz len;
|
||||
}
|
||||
|
||||
macro @enum_lookup($Type, #value, value)
|
||||
{
|
||||
$foreach $val : $Type.values:
|
||||
if ($val.#value == value) return $val;
|
||||
$endforeach
|
||||
return NOT_FOUND?;
|
||||
}
|
||||
|
||||
macro @enum_lookup_new($Type, $name, value)
|
||||
{
|
||||
$foreach $val : $Type.values:
|
||||
if ($val.$eval($name) == value) return $val;
|
||||
$endforeach
|
||||
return NOT_FOUND?;
|
||||
}
|
||||
|
||||
|
||||
module std::core::runtime @if(WASM_NOLIBC);
|
||||
|
||||
|
||||
169
lib/std/core/slice2d.c3
Normal file
169
lib/std/core/slice2d.c3
Normal file
@@ -0,0 +1,169 @@
|
||||
module std::core::array::slice {Type};
|
||||
|
||||
<*
|
||||
A slice2d allows slicing an array like int[10][10] into an arbitrary "int[][]"-like counterpart
|
||||
Typically you'd use array::slice2d(...) to create one.
|
||||
*>
|
||||
struct Slice2d
|
||||
{
|
||||
Type* ptr;
|
||||
usz inner_len;
|
||||
usz ystart;
|
||||
usz ylen;
|
||||
usz xstart;
|
||||
usz xlen;
|
||||
}
|
||||
|
||||
<*
|
||||
@return `The length of the "outer" slice`
|
||||
*>
|
||||
fn usz Slice2d.len(&self) @operator(len)
|
||||
{
|
||||
return self.ylen;
|
||||
}
|
||||
|
||||
<*
|
||||
@return `The total number of elements.`
|
||||
*>
|
||||
fn usz Slice2d.count(&self)
|
||||
{
|
||||
return self.ylen * self.xlen;
|
||||
}
|
||||
|
||||
<*
|
||||
Step through each element of the slice.
|
||||
*>
|
||||
macro void Slice2d.@each(&self; @body(usz[<2>], Type))
|
||||
{
|
||||
foreach (y, line : *self)
|
||||
{
|
||||
foreach (x, val : line)
|
||||
{
|
||||
@body({ x, y }, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Step through each element of the slice *by reference*
|
||||
*>
|
||||
macro void Slice2d.@each_ref(&self; @body(usz[<2>], Type*))
|
||||
{
|
||||
foreach (y, line : *self)
|
||||
{
|
||||
foreach (x, &val : line)
|
||||
{
|
||||
@body({ x, y }, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Return a row as a slice.
|
||||
|
||||
@param idy : "The row to return"
|
||||
@return "The slice for the particular row"
|
||||
@require idy >= 0 && idy < self.ylen
|
||||
*>
|
||||
macro Type[] Slice2d.get_row(self, usz idy) @operator([])
|
||||
{
|
||||
return (self.ptr + self.inner_len * (idy + self.ystart))[self.xstart:self.xlen];
|
||||
}
|
||||
|
||||
<*
|
||||
Get the value at a particular x/y position in the slice.
|
||||
|
||||
@param coord : "The xy coordinate"
|
||||
@return "The value at that coordinate"
|
||||
@require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
|
||||
@require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro Type Slice2d.get_coord(self, usz[<2>] coord)
|
||||
{
|
||||
return *self.get_coord_ref(coord);
|
||||
}
|
||||
|
||||
<*
|
||||
Get a pointer to the value at a particular x/y position in the slice.
|
||||
|
||||
@param coord : "The xy coordinate"
|
||||
@return "A pointer to the value at that coordinate"
|
||||
@require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
|
||||
@require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro Type* Slice2d.get_coord_ref(self, usz[<2>] coord)
|
||||
{
|
||||
return self.get_xy_ref(coord.x, coord.y);
|
||||
}
|
||||
|
||||
<*
|
||||
Get the value at a particular x/y position in the slice.
|
||||
|
||||
@param x : "The x coordinate"
|
||||
@param y : "The x coordinate"
|
||||
@return "The value at that coordinate"
|
||||
@require y >= 0 && y < self.ylen : "y value out of range"
|
||||
@require x >= 0 && x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro Type Slice2d.get_xy(self, x, y)
|
||||
{
|
||||
return *self.get_xy_ref(x, y);
|
||||
}
|
||||
|
||||
<*
|
||||
Get the value at a particular x/y position in the slice by reference.
|
||||
|
||||
@param x : "The x coordinate"
|
||||
@param y : "The y coordinate"
|
||||
@return "A pointer to the value at that coordinate"
|
||||
@require y >= 0 && y < self.ylen : "y value out of range"
|
||||
@require x >= 0 && x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro Type* Slice2d.get_xy_ref(self, x, y)
|
||||
{
|
||||
return self.ptr + self.inner_len * (y + self.ystart) + self.xstart + x;
|
||||
}
|
||||
|
||||
<*
|
||||
Set the ´value at a particular x/y position in the slice.
|
||||
|
||||
@param coord : "The xy coordinate"
|
||||
@param value : "The new value"
|
||||
@require coord.y >= 0 && coord.y < self.ylen : "y value out of range"
|
||||
@require coord.x >= 0 && coord.x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro void Slice2d.set_coord(self, usz[<2>] coord, Type value)
|
||||
{
|
||||
*self.get_coord_ref(coord) = value;
|
||||
}
|
||||
|
||||
<*
|
||||
Set the value at a particular x/y position in the slice.
|
||||
|
||||
@param x : "The x coordinate"
|
||||
@param y : "The y coordinate"
|
||||
@param value : "The new value"
|
||||
@require y >= 0 && y < self.ylen : "y value out of range"
|
||||
@require x >= 0 && x < self.xlen : "x value out of range"
|
||||
*>
|
||||
macro void Slice2d.set_xy(self, x, y, Type value)
|
||||
{
|
||||
*self.get_xy_ref(x, y) = value;
|
||||
}
|
||||
|
||||
<*
|
||||
Reslice a slice2d returning a new slice.
|
||||
|
||||
@param x : "The starting x"
|
||||
@param xlen : "The length along x"
|
||||
@param y : "The starting y"
|
||||
@param ylen : "The length along y"
|
||||
@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 };
|
||||
}
|
||||
@@ -1,46 +1,87 @@
|
||||
module std::core::string;
|
||||
import std::ascii;
|
||||
import std::io;
|
||||
|
||||
typedef String @if(!$defined(String)) = inline char[];
|
||||
<*
|
||||
ZString is a pointer to a zero terminated array of chars.
|
||||
|
||||
Use ZString when you need to interop with C zero terminated strings.
|
||||
*>
|
||||
typedef ZString = inline char*;
|
||||
|
||||
<*
|
||||
WString is a pointer to a zero terminated array of Char16.
|
||||
|
||||
Depending on the platform, this may or may not correspond to wchar_t.
|
||||
For Windows, wchar_t is generally 16 bits, on MacOS it is 32 bits.
|
||||
However, for both MacOS and Linux, regular C strings (ZString)
|
||||
will be UTF-8 encoded, so there is no need to use the wchar_t versions
|
||||
of functions outside of encoding functions.
|
||||
*>
|
||||
typedef WString = inline Char16*;
|
||||
|
||||
<*
|
||||
Char32 is a UTF32 codepoint
|
||||
*>
|
||||
alias Char32 = uint;
|
||||
|
||||
<*
|
||||
Char16 is a UTF16 "character"
|
||||
*>
|
||||
alias Char16 = ushort;
|
||||
|
||||
<*
|
||||
Common faults used with strings
|
||||
*>
|
||||
faultdef INVALID_UTF8, INVALID_UTF16, CONVERSION_FAILED,
|
||||
EMPTY_STRING, NEGATIVE_VALUE, MALFORMED_INTEGER,
|
||||
INTEGER_OVERFLOW, MALFORMED_FLOAT, FLOAT_OUT_OF_RANGE;
|
||||
|
||||
const uint SURROGATE_OFFSET @private = 0x10000;
|
||||
const uint SURROGATE_GENERIC_MASK @private = 0xF800;
|
||||
const uint SURROGATE_MASK @private = 0xFC00;
|
||||
const uint SURROGATE_CODEPOINT_MASK @private = 0x03FF;
|
||||
const uint SURROGATE_BITS @private = 10;
|
||||
const uint SURROGATE_LOW_VALUE @private = 0xDC00;
|
||||
const uint SURROGATE_HIGH_VALUE @private = 0xD800;
|
||||
|
||||
<*
|
||||
Create a pointer to an UTF32 encoded string at compile time.
|
||||
|
||||
@param $string : "The string to encode"
|
||||
*>
|
||||
macro Char32* @wstring32(String $string) @builtin
|
||||
{
|
||||
return (Char32*)&&$$wstr32($string);
|
||||
}
|
||||
|
||||
<*
|
||||
Create a slice of an UTF32 encoded string at compile time.
|
||||
|
||||
@param $string : "The string to encode"
|
||||
*>
|
||||
macro Char32[] @char32(String $string) @builtin
|
||||
{
|
||||
return $$wstr32($string)[..^2];
|
||||
}
|
||||
|
||||
<*
|
||||
Create a WString (an UTF16 encoded string) at compile time.
|
||||
|
||||
@param $string : "The string to encode"
|
||||
*>
|
||||
macro WString @wstring(String $string) @builtin
|
||||
{
|
||||
return (WString)&&$$wstr16($string);
|
||||
}
|
||||
|
||||
<*
|
||||
Create a slice of an UTF32 encoded string at compile time.
|
||||
|
||||
@param $string : "The string to encode"
|
||||
*>
|
||||
macro Char16[] @char16(String $string) @builtin
|
||||
{
|
||||
return $$wstr16($string)[..^2];
|
||||
}
|
||||
|
||||
macro String @sprintf(String $format, ...) @builtin @const
|
||||
{
|
||||
return $$sprintf($format, $vasplat);
|
||||
}
|
||||
<*
|
||||
Return a temporary ZString created using the formatting function.
|
||||
|
||||
@@ -117,6 +158,39 @@ fn String join(Allocator allocator, String[] s, String joiner)
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
Replace all instances of one substring with a different string.
|
||||
|
||||
@param [in] self
|
||||
@param [in] needle : `The string to be replaced`
|
||||
@param [in] new_str : `The replacement string`
|
||||
@param [&inout] allocator : `The allocator to use for the String`
|
||||
@return "The new string with the elements replaced"
|
||||
*>
|
||||
fn String String.replace(self, Allocator allocator, String needle, String new_str) @nodiscard
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
String[] split = self.tsplit(needle);
|
||||
return dstring::join(tmem, split, new_str).copy_str(mem);
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
Replace all instances of one substring with a different string, allocating the new string on the temp allocator.
|
||||
|
||||
@param [in] self
|
||||
@param [in] needle : `The string to be replaced`
|
||||
@param [in] new_str : `The replacement string`
|
||||
@return "The new string with the elements replaced"
|
||||
*>
|
||||
fn String String.treplace(self, String needle, String new_str)
|
||||
{
|
||||
String[] split = self.tsplit(needle);
|
||||
return dstring::join(tmem, split, new_str).str_view();
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Remove characters from the front and end of a string.
|
||||
|
||||
@@ -231,7 +305,7 @@ fn String String.strip_end(self, String suffix)
|
||||
@param [&inout] allocator : "The allocator to use for the String[]"
|
||||
|
||||
@require delimiter.len > 0 : "The delimiter must be at least 1 character long"
|
||||
@ensure return.len > 0
|
||||
@ensure return.len > 0 || skip_empty
|
||||
*>
|
||||
fn String[] String.split(self, Allocator allocator, String delimiter, usz max = 0, bool skip_empty = false)
|
||||
{
|
||||
@@ -290,7 +364,7 @@ faultdef BUFFER_EXCEEDED;
|
||||
@param [inout] buffer
|
||||
@param max : "Max number of elements, 0 means no limit, defaults to 0"
|
||||
@require delimiter.len > 0 : "The delimiter must be at least 1 character long"
|
||||
@ensure return.len > 0
|
||||
@ensure return.len > 0 || skip_empty
|
||||
@return? BUFFER_EXCEEDED : `If there are more elements than would fit the buffer`
|
||||
*>
|
||||
fn String[]? String.split_to_buffer(s, String delimiter, String[] buffer, usz max = 0, bool skip_empty = false)
|
||||
@@ -338,6 +412,38 @@ fn bool String.contains(s, String substr)
|
||||
return @ok(s.index_of(substr));
|
||||
}
|
||||
|
||||
<*
|
||||
Check how many non-overlapping instances of a substring there is.
|
||||
|
||||
If the substring has zero length, the number of matches is zero.
|
||||
|
||||
@param [in] self : "The string to search"
|
||||
@param [in] substr : "The string to look for."
|
||||
@pure
|
||||
@return "The number of times matched"
|
||||
*>
|
||||
fn usz String.count(self, String substr)
|
||||
{
|
||||
usz count = 0;
|
||||
usz needed = substr.len;
|
||||
if (needed == 0) return 0;
|
||||
char first = substr[0];
|
||||
while OUTER: (self.len >= needed)
|
||||
{
|
||||
foreach (i, c: self[..^needed])
|
||||
{
|
||||
if (c == first && self[i : needed] == substr)
|
||||
{
|
||||
count++;
|
||||
self = self[i + needed..];
|
||||
continue OUTER;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
<*
|
||||
Find the index of the first incidence of a string.
|
||||
|
||||
@@ -471,6 +577,19 @@ fn usz? String.rindex_of(self, String substr)
|
||||
return NOT_FOUND?;
|
||||
}
|
||||
|
||||
fn bool ZString.eq(self, ZString other) @operator(==)
|
||||
{
|
||||
char* a = self;
|
||||
char* b = other;
|
||||
if (a == b) return true;
|
||||
for (;; a++, b++)
|
||||
{
|
||||
char c = *a;
|
||||
if (c != *b) return false;
|
||||
if (!c) return true;
|
||||
}
|
||||
}
|
||||
|
||||
fn String ZString.str_view(self)
|
||||
{
|
||||
return (String)(self[:self.len()]);
|
||||
@@ -692,16 +811,63 @@ fn usz String.utf8_codepoints(s)
|
||||
return len;
|
||||
}
|
||||
|
||||
<*
|
||||
Determine whether the current string actually points to a ZString-like string.
|
||||
|
||||
This is done by looking at the byte one step after the end of the string. If this
|
||||
is zero, it is considered zero terminated.
|
||||
|
||||
This function can safely be used with data pointing to null. However, it will not
|
||||
work correctly if the pointer is invalid, for example it is already freed.
|
||||
*>
|
||||
fn bool String.is_zstr(self) @deprecated("Unsafe, use copy instead")
|
||||
{
|
||||
return self.ptr && *(self.ptr + self.len) == 0;
|
||||
}
|
||||
|
||||
<*
|
||||
@require (base <= 10 && base > 1) || base == 16 : "Unsupported base"
|
||||
Return a pointer to the string *iff* it is a pointer
|
||||
to a zero terminated string, otherwise return a temp allocated zstring copy.
|
||||
|
||||
This function is suitable if you are converting strings to ZString on the temp
|
||||
allocator, but suspect that the String might actually already point to zero
|
||||
terminated data.
|
||||
|
||||
The function looks one step beyond the end of the slice to determine this,
|
||||
which means that if that data is then modified after this call, this function
|
||||
might behave incorrectly.
|
||||
|
||||
For this reason, try to ensure that the resulting ZString is immediately used.
|
||||
|
||||
@ensure return[self.len] == 0
|
||||
*>
|
||||
fn ZString String.quick_zstr(self) @deprecated("Unsafe, use zstr_tcopy instead")
|
||||
{
|
||||
return self.zstr_tcopy();
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Convert a number to a given base. If the base is not given, then
|
||||
it will be inferred from the number if the string starts with 0x 0o or 0b and the
|
||||
base is given as 10.
|
||||
|
||||
Furthermore it will skip any spaces before and after the number.
|
||||
|
||||
@param $Type : "The type to convert to"
|
||||
@param base : "The base to convert to"
|
||||
@require base > 0 && base <= 16 : "Unsupported base"
|
||||
@return? MALFORMED_INTEGER : "When the value has some illegal character"
|
||||
@return? INTEGER_OVERFLOW : "If the value does not fit in the given type"
|
||||
@return? EMPTY_STRING : "If the string was empty"
|
||||
@return? NEGATIVE_VALUE : "If the type was unsigned, and the value had a - prefix"
|
||||
*>
|
||||
macro String.to_integer(self, $Type, int base = 10)
|
||||
{
|
||||
usz len = self.len;
|
||||
usz index = 0;
|
||||
char* ptr = self.ptr;
|
||||
while (index < len && ascii::is_blank_m(ptr[index])) index++;
|
||||
while (index < len && ptr[index].is_blank()) index++;
|
||||
if (len == index) return EMPTY_STRING?;
|
||||
bool is_negative;
|
||||
switch (self[index])
|
||||
@@ -746,7 +912,7 @@ macro String.to_integer(self, $Type, int base = 10)
|
||||
char c = self[index++];
|
||||
switch
|
||||
{
|
||||
case base_used != 16 || c < 'A': c -= '0';
|
||||
case base_used < 10 || c < 'A': c -= '0';
|
||||
case c <= 'F': c -= 'A' - 10;
|
||||
case c < 'a' || c > 'f': return MALFORMED_INTEGER?;
|
||||
default: c -= 'a' - 10;
|
||||
@@ -784,22 +950,91 @@ fn char? String.to_uchar(self, int base = 10) => self.to_integer(char, base);
|
||||
fn double? String.to_double(self) => self.to_real(double);
|
||||
fn float? String.to_float(self) => self.to_real(float);
|
||||
|
||||
fn Splitter String.splitter(self, String split)
|
||||
{
|
||||
return { .string = self, .split = split };
|
||||
}
|
||||
|
||||
<*
|
||||
Create a Splitter to track tokenizing of a string. Tokenize will turn "foo:bar::baz" into
|
||||
"foo", "bar" and "baz", if you want the empty string to be present, use `tokenize_all`
|
||||
instead.
|
||||
|
||||
@param [in] split : "The string to use for splitting"
|
||||
@return "A Splitter to track the state"
|
||||
*>
|
||||
fn Splitter String.tokenize(self, String split)
|
||||
{
|
||||
return { .string = self, .split = split, .tokenize = true };
|
||||
return { .string = self, .split = split, .type = TOKENIZE };
|
||||
}
|
||||
|
||||
<*
|
||||
Create a Splitter to track tokenizing of a string. Tokenize will turn "foo:bar::baz" into
|
||||
"foo", "bar" and "baz", if you want the empty string to be present, use `tokenize_all`
|
||||
instead.
|
||||
|
||||
@param [in] split : "The string to use for splitting"
|
||||
@param skip_last : "Set to true to not include the last empty token if present (default: false)"
|
||||
@return "A Splitter to track the state"
|
||||
*>
|
||||
fn Splitter String.tokenize_all(self, String split, bool skip_last = false)
|
||||
{
|
||||
return {
|
||||
.string = self,
|
||||
.split = split,
|
||||
.type = skip_last ? TOKENIZE_ALL_SKIP_LAST : TOKENIZE_ALL
|
||||
};
|
||||
}
|
||||
|
||||
fn Splitter String.splitter(self, String split) @deprecated("Use tokenize_all instead")
|
||||
{
|
||||
return self.tokenize_all(split, skip_last: true);
|
||||
}
|
||||
|
||||
<*
|
||||
This macro will create a string description of a struct.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@param x : "The struct to create a description of"
|
||||
*>
|
||||
macro String from_struct(Allocator allocator, x)
|
||||
{
|
||||
DString s;
|
||||
@stack_mem(512; Allocator mem)
|
||||
{
|
||||
s.init(allocator: mem);
|
||||
io::fprint(&s, x)!!;
|
||||
return s.copy_str(allocator);
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
This macro will create a temporary string description of a struct.
|
||||
|
||||
@param x : "The struct to create a description of"
|
||||
*>
|
||||
macro String tfrom_struct(x) => from_struct(tmem, x);
|
||||
|
||||
const uint SURROGATE_OFFSET @private = 0x10000;
|
||||
const uint SURROGATE_GENERIC_MASK @private = 0xF800;
|
||||
const uint SURROGATE_MASK @private = 0xFC00;
|
||||
const uint SURROGATE_CODEPOINT_MASK @private = 0x03FF;
|
||||
const uint SURROGATE_BITS @private = 10;
|
||||
const uint SURROGATE_LOW_VALUE @private = 0xDC00;
|
||||
const uint SURROGATE_HIGH_VALUE @private = 0xD800;
|
||||
|
||||
enum SplitterType
|
||||
{
|
||||
TOKENIZE,
|
||||
TOKENIZE_ALL,
|
||||
TOKENIZE_ALL_SKIP_LAST
|
||||
}
|
||||
|
||||
<*
|
||||
Splitter is handles tokenizing strings.
|
||||
*>
|
||||
struct Splitter
|
||||
{
|
||||
String string;
|
||||
String split;
|
||||
usz current;
|
||||
bool tokenize;
|
||||
SplitterType type;
|
||||
int last_index;
|
||||
}
|
||||
|
||||
@@ -814,29 +1049,22 @@ fn String? Splitter.next(&self)
|
||||
{
|
||||
usz len = self.string.len;
|
||||
usz current = self.current;
|
||||
if (current >= len) return NO_MORE_ELEMENT?;
|
||||
if (current > len) return NO_MORE_ELEMENT?;
|
||||
if (current == len)
|
||||
{
|
||||
if (self.type != TOKENIZE_ALL) return NO_MORE_ELEMENT?;
|
||||
self.current++;
|
||||
return self.string[current - 1:0];
|
||||
}
|
||||
String remaining = self.string[current..];
|
||||
usz? next = remaining.index_of(self.split);
|
||||
if (try next)
|
||||
{
|
||||
self.current = current + next + self.split.len;
|
||||
if (!next && self.tokenize) continue;
|
||||
if (!next && self.type == TOKENIZE) continue;
|
||||
return remaining[:next];
|
||||
}
|
||||
self.current = len;
|
||||
return remaining;
|
||||
}
|
||||
}
|
||||
|
||||
macro String from_struct(Allocator allocator, x)
|
||||
{
|
||||
DString s;
|
||||
@stack_mem(512; Allocator mem)
|
||||
{
|
||||
s.init(allocator: mem);
|
||||
io::fprint(&s, x)!!;
|
||||
return s.copy_str(allocator);
|
||||
};
|
||||
}
|
||||
|
||||
macro String tfrom_struct(x) => from_struct(tmem, x);
|
||||
|
||||
233
lib/std/core/string_escape.c3
Normal file
233
lib/std/core/string_escape.c3
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright (c) 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.
|
||||
|
||||
<*
|
||||
This module provides functionality for escaping and unescaping strings
|
||||
with standard C-style escape sequences, similar to what's used in JSON
|
||||
and other string literals.
|
||||
*>
|
||||
module std::core::string;
|
||||
import std::io;
|
||||
|
||||
faultdef INVALID_ESCAPE_SEQUENCE, UNTERMINATED_STRING, INVALID_HEX_ESCAPE, INVALID_UNICODE_ESCAPE;
|
||||
|
||||
<*
|
||||
Escape a string by adding quotes and converting special characters to escape sequences.
|
||||
|
||||
@param allocator : "The allocator to use for the result"
|
||||
@param s : "The string to escape"
|
||||
@param strip_quotes : "Do not include beginning and end quotes, defaults to false"
|
||||
@return "The escaped string with surrounding quotes, can safely be cast to ZString"
|
||||
*>
|
||||
fn String String.escape(String s, Allocator allocator, bool strip_quotes = true)
|
||||
{
|
||||
// Conservative allocation: most strings need minimal escaping
|
||||
usz initial_capacity = s.len + s.len / 5 + 2; // ~1.2x + quotes
|
||||
DString result = dstring::new_with_capacity(allocator, initial_capacity);
|
||||
|
||||
if (!strip_quotes) result.append_char('"');
|
||||
|
||||
foreach (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"': result.append(`\"`);
|
||||
case '\\': result.append(`\\`);
|
||||
case '\b': result.append(`\b`);
|
||||
case '\f': result.append(`\f`);
|
||||
case '\n': result.append(`\n`);
|
||||
case '\r': result.append(`\r`);
|
||||
case '\t': result.append(`\t`);
|
||||
case '\v': result.append(`\v`);
|
||||
case '\0': result.append(`\0`);
|
||||
default:
|
||||
if (c >= 32 && c <= 126)
|
||||
{
|
||||
// Printable ASCII
|
||||
result.append_char(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-printable, use hex escape
|
||||
result.appendf("\\x%02x", (uint)c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!strip_quotes) result.append_char('"');
|
||||
return result.copy_str(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Escape a string using the temp allocator.
|
||||
|
||||
@param s : "The string to escape"
|
||||
@param strip_quotes : "Do not include beginning and end quotes, defaults to false"
|
||||
@return "The escaped string with surrounding quotes"
|
||||
*>
|
||||
fn String String.tescape(String s, bool strip_quotes = false) => s.escape(tmem, strip_quotes);
|
||||
|
||||
<*
|
||||
Calculate the length needed for an escaped string (including quotes).
|
||||
|
||||
@param s : "The string to check"
|
||||
@return "The length needed for the escaped version"
|
||||
*>
|
||||
fn usz escape_len(String s)
|
||||
{
|
||||
usz len = 2; // For quotes
|
||||
foreach (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\0':
|
||||
len += 2; // \X
|
||||
default:
|
||||
if (c >= 32 && c <= 126)
|
||||
{
|
||||
len += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
len += 4; // \xHH
|
||||
}
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
<*
|
||||
Unescape a quoted string by parsing escape sequences.
|
||||
|
||||
@param allocator : "The allocator to use for the result"
|
||||
@param s : "The quoted string to unescape"
|
||||
@param allow_unquoted : "Set to true to unescape strings not surrounded by quotes, defaults to false"
|
||||
@return "The unescaped string without quotes, safe to convert to ZString"
|
||||
@return? UNTERMINATED_STRING, INVALID_ESCAPE_SEQUENCE, INVALID_HEX_ESCAPE, INVALID_UNICODE_ESCAPE
|
||||
*>
|
||||
fn String? String.unescape(String s, Allocator allocator, bool allow_unquoted = false)
|
||||
{
|
||||
if (s.len >= 2 && s[0] == '"' && s[^1] == '"')
|
||||
{
|
||||
// Remove quotes.
|
||||
s = s[1:^2];
|
||||
}
|
||||
else if (!allow_unquoted) return UNTERMINATED_STRING?;
|
||||
|
||||
// Handle empty string case
|
||||
if (!s.len)
|
||||
{
|
||||
return "".copy(allocator);
|
||||
}
|
||||
|
||||
DString result = dstring::new_with_capacity(allocator, s.len);
|
||||
|
||||
usz len = s.len;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
char c = s[i];
|
||||
if (c != '\\')
|
||||
{
|
||||
result.append_char(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle escape sequence
|
||||
if (i + 1 >= len) return INVALID_ESCAPE_SEQUENCE?;
|
||||
|
||||
char escape_char = s[++i];
|
||||
switch (escape_char)
|
||||
{
|
||||
case '"': result.append_char('"');
|
||||
case '\\': result.append_char('\\');
|
||||
case '/': result.append_char('/');
|
||||
case 'b': result.append_char('\b');
|
||||
case 'f': result.append_char('\f');
|
||||
case 'n': result.append_char('\n');
|
||||
case 'r': result.append_char('\r');
|
||||
case 't': result.append_char('\t');
|
||||
case 'v': result.append_char('\v');
|
||||
case '0': result.append_char('\0');
|
||||
case 'x':
|
||||
// Hex escape \xHH
|
||||
if (i + 2 >= len) return INVALID_HEX_ESCAPE?;
|
||||
char h1 = s[++i];
|
||||
char h2 = s[++i];
|
||||
if (!h1.is_xdigit() || !h2.is_xdigit()) return INVALID_HEX_ESCAPE?;
|
||||
uint val = h1 > '9' ? (h1 | 32) - 'a' + 10 : h1 - '0';
|
||||
val = val << 4;
|
||||
val += h2 > '9' ? (h2 | 32) - 'a' + 10 : h2 - '0';
|
||||
result.append_char((char)val);
|
||||
case 'u':
|
||||
// Unicode escape \uHHHH
|
||||
if (i + 4 >= len) return INVALID_UNICODE_ESCAPE?;
|
||||
uint val;
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
char hex_char = s[++i];
|
||||
if (!hex_char.is_xdigit()) return INVALID_UNICODE_ESCAPE?;
|
||||
val = val << 4 + (hex_char > '9' ? (hex_char | 32) - 'a' + 10 : hex_char - '0');
|
||||
}
|
||||
result.append_char32(val);
|
||||
case 'U':
|
||||
// Unicode escape \UHHHHHHHH
|
||||
if (i + 8 >= len) return INVALID_UNICODE_ESCAPE?;
|
||||
uint val;
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
char hex_char = s[++i];
|
||||
if (!hex_char.is_xdigit()) return INVALID_UNICODE_ESCAPE?;
|
||||
val = val << 4 + (hex_char > '9' ? (hex_char | 32) - 'a' + 10 : hex_char - '0');
|
||||
}
|
||||
result.append_char32(val);
|
||||
default:
|
||||
return INVALID_ESCAPE_SEQUENCE?;
|
||||
}
|
||||
}
|
||||
|
||||
return result.copy_str(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Unescape a quoted string using the temp allocator.
|
||||
|
||||
@param s : "The quoted string to unescape"
|
||||
@param allow_unquoted : "Set to true to unescape strings not surrounded by quotes, defaults to false"
|
||||
@return "The unescaped string without quotes"
|
||||
@return? UNTERMINATED_STRING, INVALID_ESCAPE_SEQUENCE, INVALID_HEX_ESCAPE, INVALID_UNICODE_ESCAPE
|
||||
*>
|
||||
fn String? String.tunescape(String s, bool allow_unquoted = false) => s.unescape(tmem, allow_unquoted);
|
||||
|
||||
<*
|
||||
Check if a character needs to be escaped in a string literal.
|
||||
|
||||
@param c : "The character to check"
|
||||
@return "True if the character needs escaping"
|
||||
*>
|
||||
fn bool needs_escape(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\0':
|
||||
return true;
|
||||
default:
|
||||
return c < 32 || c > 126;
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ macro bool is_numerical($Type)
|
||||
{
|
||||
var $kind = $Type.kindof;
|
||||
$if $kind == TypeKind.DISTINCT:
|
||||
return is_numerical($typefrom($Type.inner));
|
||||
return is_numerical($Type.inner);
|
||||
$else
|
||||
return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT
|
||||
|| $kind == TypeKind.VECTOR;
|
||||
@@ -135,7 +135,7 @@ macro bool is_signed($Type) @const
|
||||
$case FLOAT:
|
||||
return true;
|
||||
$case VECTOR:
|
||||
return is_signed($typefrom($Type.inner));
|
||||
return is_signed($Type.inner);
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
@@ -150,7 +150,7 @@ macro bool is_unsigned($Type) @const
|
||||
$case UNSIGNED_INT:
|
||||
return true;
|
||||
$case VECTOR:
|
||||
return is_unsigned($typefrom($Type.inner));
|
||||
return is_unsigned($Type.inner);
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
@@ -159,7 +159,7 @@ macro bool is_unsigned($Type) @const
|
||||
macro typeid flat_type($Type) @const
|
||||
{
|
||||
$if $Type.kindof == DISTINCT:
|
||||
return flat_type($typefrom($Type.inner));
|
||||
return flat_type($Type.inner);
|
||||
$else
|
||||
return $Type.typeid;
|
||||
$endif
|
||||
@@ -168,7 +168,7 @@ macro typeid flat_type($Type) @const
|
||||
macro TypeKind flat_kind($Type) @const
|
||||
{
|
||||
$if $Type.kindof == DISTINCT:
|
||||
return flat_type($typefrom($Type.inner));
|
||||
return flat_type($Type.inner);
|
||||
$else
|
||||
return $Type.kindof;
|
||||
$endif
|
||||
@@ -184,6 +184,21 @@ macro bool is_ref_indexable($Type) @const
|
||||
return $defined(&($Type){}[0]);
|
||||
}
|
||||
|
||||
macro bool is_flat_intlike($Type) @const
|
||||
{
|
||||
$switch $Type.kindof:
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
return true;
|
||||
$case VECTOR:
|
||||
return is_flat_intlike($Type.inner);
|
||||
$case DISTINCT:
|
||||
return is_flat_intlike($Type.inner);
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro bool is_intlike($Type) @const
|
||||
{
|
||||
$switch $Type.kindof:
|
||||
@@ -204,7 +219,7 @@ macro bool is_underlying_int($Type) @const
|
||||
$case UNSIGNED_INT:
|
||||
return true;
|
||||
$case DISTINCT:
|
||||
return is_underlying_int($typefrom($Type.inner));
|
||||
return is_underlying_int($Type.inner);
|
||||
$default:
|
||||
return false;
|
||||
$endswitch
|
||||
@@ -232,7 +247,7 @@ macro bool is_vector($Type) @const
|
||||
macro typeid inner_type($Type) @const
|
||||
{
|
||||
$if $Type.kindof == TypeKind.DISTINCT:
|
||||
return inner_type($typefrom($Type.inner));
|
||||
return inner_type($Type.inner);
|
||||
$else
|
||||
return $Type.typeid;
|
||||
$endif
|
||||
@@ -272,6 +287,7 @@ macro bool may_load_atomic($Type) @const
|
||||
$case FLOAT:
|
||||
return true;
|
||||
$case DISTINCT:
|
||||
$case ENUM:
|
||||
return may_load_atomic($Type.inner);
|
||||
$default:
|
||||
return false;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
module std::core::values;
|
||||
import std::core::types;
|
||||
|
||||
|
||||
macro typeid @typeid(#value) @const @builtin => $typeof(#value).typeid;
|
||||
macro TypeKind @typekind(#value) @const @builtin => $typeof(#value).kindof;
|
||||
@@ -9,6 +11,7 @@ macro bool @typeis(#value, $Type) @const @builtin => $typeof(#value).typeid == $
|
||||
macro bool @is_same_type(#value1, #value2) @const => $typeof(#value1).typeid == $typeof(#value2).typeid;
|
||||
macro bool @is_bool(#value) @const => types::is_bool($typeof(#value));
|
||||
macro bool @is_int(#value) @const => types::is_int($typeof(#value));
|
||||
macro bool @is_flat_intlike(#value) @const => types::is_flat_intlike($typeof(#value));
|
||||
macro bool @is_floatlike(#value) @const => types::is_floatlike($typeof(#value));
|
||||
macro bool @is_float(#value) @const => types::is_float($typeof(#value));
|
||||
macro bool @is_promotable_to_floatlike(#value) @const => types::is_promotable_to_floatlike($typeof(#value));
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::encoding::json;
|
||||
import std::io;
|
||||
import std::ascii;
|
||||
import std::collections::object;
|
||||
|
||||
faultdef UNEXPECTED_CHARACTER, INVALID_ESCAPE_SEQUENCE, DUPLICATE_MEMBERS, INVALID_NUMBER;
|
||||
|
||||
@@ -93,7 +93,7 @@ macro @derive(Hmac *hmac_start, char[] salt, uint iterations, usz index, char[]
|
||||
UIntBE be = { (uint)index };
|
||||
hmac.update(&&bitcast(be, char[4]));
|
||||
tmp = hmac.final();
|
||||
out[..] = tmp;
|
||||
out[..] = tmp[..];
|
||||
for (int it = 1; it < iterations; it++)
|
||||
{
|
||||
hmac = *hmac_start;
|
||||
|
||||
@@ -89,10 +89,10 @@ fn char[HASH_BYTES] Md5.final(&ctx)
|
||||
body(ctx, &ctx.buffer, 64);
|
||||
|
||||
char[16] res @noinit;
|
||||
res[0:4] = bitcast(ctx.a, char[4]);
|
||||
res[4:4] = bitcast(ctx.b, char[4]);
|
||||
res[8:4] = bitcast(ctx.c, char[4]);
|
||||
res[12:4] = bitcast(ctx.d, char[4]);
|
||||
res[0:4] = bitcast(ctx.a, char[4])[..];
|
||||
res[4:4] = bitcast(ctx.b, char[4])[..];
|
||||
res[8:4] = bitcast(ctx.c, char[4])[..];
|
||||
res[12:4] = bitcast(ctx.d, char[4])[..];
|
||||
*ctx = {};
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ fn void Sha256.update(&self, char[] data) {
|
||||
uint i = 0;
|
||||
uint len = data.len;
|
||||
uint buffer_pos = (uint)(self.bitcount / 8) % BLOCK_SIZE;
|
||||
self.bitcount += (ulong)(len * 8);
|
||||
self.bitcount += ((ulong)len * 8);
|
||||
|
||||
while (len--) {
|
||||
self.buffer[buffer_pos++] = data[i++];
|
||||
@@ -173,4 +173,5 @@ fn void sha256_transform(uint* state, char* buffer) @local {
|
||||
state[7] += h;
|
||||
a = b = c = d = e = f = g = h = t1 = t2 = i = 0;
|
||||
m[:64] = buffer[:64] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
281
lib/std/hash/sha512.c3
Normal file
281
lib/std/hash/sha512.c3
Normal file
@@ -0,0 +1,281 @@
|
||||
/**
|
||||
* SHA-512 implementation for the C3 stdlib.
|
||||
*
|
||||
* The core components are based almost exclusively on the musl libc sha512 implementation:
|
||||
* https://git.musl-libc.org/cgit/musl/commit/src/misc/crypt_sha512.c?id=88bf5a8a8d7d796f63cca8589f4de67aa8345f1a
|
||||
*
|
||||
* SHA-384, 512/224, and 512/256 are very simple addenda to this module.
|
||||
*
|
||||
*/
|
||||
module std::hash::sha512;
|
||||
|
||||
import std::hash::hmac;
|
||||
|
||||
|
||||
const BLOCK_SIZE = 128;
|
||||
const HASH_SIZE = 64;
|
||||
|
||||
struct Sha512
|
||||
{
|
||||
ulong length;
|
||||
ulong[8] hash_state;
|
||||
char[BLOCK_SIZE] buffer;
|
||||
}
|
||||
|
||||
alias HmacSha512 = Hmac{Sha512, HASH_SIZE, BLOCK_SIZE};
|
||||
alias hmac = hmac::hash{Sha512, HASH_SIZE, BLOCK_SIZE};
|
||||
alias pbkdf2 = hmac::pbkdf2{Sha512, HASH_SIZE, BLOCK_SIZE};
|
||||
|
||||
macro ulong ror(ulong n, int k) @local => ((n >> k) | (n << (64 - k)));
|
||||
|
||||
macro ulong ch(ulong x, ulong y, ulong z) @local => (z ^ (x & (y ^ z)));
|
||||
macro ulong maj(ulong x, ulong y, ulong z) @local => ((x & y) | (z & (x | y)));
|
||||
macro ulong s0(ulong x) @local => (ror(x, 28) ^ ror(x, 34) ^ ror(x, 39));
|
||||
macro ulong s1(ulong x) @local => (ror(x, 14) ^ ror(x, 18) ^ ror(x, 41));
|
||||
macro ulong r0(ulong x) @local => (ror(x, 1) ^ ror(x, 8) ^ (x >> 7));
|
||||
macro ulong r1(ulong x) @local => (ror(x, 19) ^ ror(x, 61) ^ (x >> 6));
|
||||
|
||||
const ulong[80] K @local = {
|
||||
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
|
||||
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
|
||||
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
|
||||
0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
|
||||
0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
|
||||
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
|
||||
0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
|
||||
0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
|
||||
0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
|
||||
0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
|
||||
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
|
||||
0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
|
||||
0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
|
||||
0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
|
||||
0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
|
||||
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
|
||||
0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
|
||||
0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
|
||||
0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
|
||||
0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
|
||||
};
|
||||
|
||||
|
||||
// See: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
|
||||
// All truncation types are simple to add onto the base SHA512 implementation at a (near) future time.
|
||||
enum HashTruncationType : uint (inline uint truncation_width, ulong[8] initial_state)
|
||||
{
|
||||
SHA512 = {
|
||||
512,
|
||||
{
|
||||
0x6a09e667f3bcc908,
|
||||
0xbb67ae8584caa73b,
|
||||
0x3c6ef372fe94f82b,
|
||||
0xa54ff53a5f1d36f1,
|
||||
0x510e527fade682d1,
|
||||
0x9b05688c2b3e6c1f,
|
||||
0x1f83d9abfb41bd6b,
|
||||
0x5be0cd19137e2179
|
||||
}
|
||||
},
|
||||
SHA384 = {
|
||||
384,
|
||||
{
|
||||
0xcbbb9d5dc1059ed8,
|
||||
0x629a292a367cd507,
|
||||
0x9159015a3070dd17,
|
||||
0x152fecd8f70e5939,
|
||||
0x67332667ffc00b31,
|
||||
0x8eb44a8768581511,
|
||||
0xdb0c2e0d64f98fa7,
|
||||
0x47b5481dbefa4fa4
|
||||
}
|
||||
},
|
||||
SHA512_224 = {
|
||||
224,
|
||||
{
|
||||
0x8C3D37C819544DA2,
|
||||
0x73E1996689DCD4D6,
|
||||
0x1DFAB7AE32FF9C82,
|
||||
0x679DD514582F9FCF,
|
||||
0x0F6D2B697BD44DA8,
|
||||
0x77E36F7304C48942,
|
||||
0x3F9D85A86A1D36C8,
|
||||
0x1112E6AD91D692A1
|
||||
}
|
||||
},
|
||||
SHA512_256 = {
|
||||
256,
|
||||
{
|
||||
0x22312194FC2BF72C,
|
||||
0x9F555FA3C84C64C2,
|
||||
0x2393B86B6F53B151,
|
||||
0x963877195940EABD,
|
||||
0x96283EE2A88EFFE3,
|
||||
0xBE5E1E2553863992,
|
||||
0x2B0199FC2C85B8AA,
|
||||
0x0EB72DDC81C52CA2
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@param [in] data
|
||||
*>
|
||||
fn char[HASH_SIZE] hash(char[] data)
|
||||
{
|
||||
Sha512 s @noinit;
|
||||
s.init();
|
||||
s.update(data);
|
||||
return s.final();
|
||||
}
|
||||
|
||||
|
||||
fn void Sha512.init(&self)
|
||||
{
|
||||
*self = {
|
||||
.hash_state = HashTruncationType.SHA512.initial_state
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@param [in] data
|
||||
@require data.len <= ulong.max
|
||||
*>
|
||||
fn void Sha512.update(&self, char[] data)
|
||||
{
|
||||
char* p = data.ptr;
|
||||
ulong len = data.len;
|
||||
ulong l;
|
||||
ulong r = self.length % 128;
|
||||
|
||||
self.length += len;
|
||||
|
||||
if (r)
|
||||
{
|
||||
if (len < (128 - r))
|
||||
{
|
||||
for (l = 0; l < len; ++l) self.buffer[r+l] = p[l];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (l = 0; l < 128 - r; ++l) self.buffer[r+l] = p[l];
|
||||
|
||||
len -= (128 - r);
|
||||
p = &p[128 - r];
|
||||
|
||||
sha512_transform(&self.hash_state, &self.buffer);
|
||||
}
|
||||
|
||||
for (; len >= 128; len -= 128, p = &p[128]) sha512_transform(&self.hash_state, p);
|
||||
|
||||
for (l = 0; l < len; ++l) self.buffer[l] = p[l];
|
||||
}
|
||||
|
||||
|
||||
fn char[HASH_SIZE] Sha512.final(&self)
|
||||
{
|
||||
char[HASH_SIZE] hash;
|
||||
|
||||
int i;
|
||||
ulong r = self.length % 128;
|
||||
|
||||
self.buffer[r++] = 0x80;
|
||||
|
||||
if (r > 112)
|
||||
{
|
||||
for (i = 0; i < 128 - r; ++i) self.buffer[r+i] = 0;
|
||||
|
||||
r = 0;
|
||||
|
||||
sha512_transform(&self.hash_state, &self.buffer);
|
||||
}
|
||||
|
||||
for (i = 0; i < 120 - r; ++i) self.buffer[r+i] = 0;
|
||||
|
||||
self.length *= 8;
|
||||
|
||||
self.buffer[120] = (char)(self.length >> 56);
|
||||
self.buffer[121] = (char)(self.length >> 48);
|
||||
self.buffer[122] = (char)(self.length >> 40);
|
||||
self.buffer[123] = (char)(self.length >> 32);
|
||||
self.buffer[124] = (char)(self.length >> 24);
|
||||
self.buffer[125] = (char)(self.length >> 16);
|
||||
self.buffer[126] = (char)(self.length >> 8);
|
||||
self.buffer[127] = (char)(self.length);
|
||||
|
||||
sha512_transform(&self.hash_state, &self.buffer);
|
||||
|
||||
for (i = 0; i < 8; ++i)
|
||||
{
|
||||
hash[(8 * i)] = (char)(self.hash_state[i] >> 56);
|
||||
hash[(8 * i) + 1] = (char)(self.hash_state[i] >> 48);
|
||||
hash[(8 * i) + 2] = (char)(self.hash_state[i] >> 40);
|
||||
hash[(8 * i) + 3] = (char)(self.hash_state[i] >> 32);
|
||||
hash[(8 * i) + 4] = (char)(self.hash_state[i] >> 24);
|
||||
hash[(8 * i) + 5] = (char)(self.hash_state[i] >> 16);
|
||||
hash[(8 * i) + 6] = (char)(self.hash_state[i] >> 8);
|
||||
hash[(8 * i) + 7] = (char)(self.hash_state[i]);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@param [&inout] state
|
||||
@param [&in] buf
|
||||
*>
|
||||
fn void sha512_transform(ulong *state, char *buf) @local
|
||||
{
|
||||
ulong t1, t2, a, b, c, d, e, f, g, h;
|
||||
ulong[80] w;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; ++i)
|
||||
{
|
||||
w[i] = (ulong)buf[(8 * i)] << 56;
|
||||
w[i] |= (ulong)buf[(8 * i) + 1] << 48;
|
||||
w[i] |= (ulong)buf[(8 * i) + 2] << 40;
|
||||
w[i] |= (ulong)buf[(8 * i) + 3] << 32;
|
||||
w[i] |= (ulong)buf[(8 * i) + 4] << 24;
|
||||
w[i] |= (ulong)buf[(8 * i) + 5] << 16;
|
||||
w[i] |= (ulong)buf[(8 * i) + 6] << 8;
|
||||
w[i] |= buf[(8 * i) + 7];
|
||||
}
|
||||
|
||||
for (; i < 80; ++i) w[i] = r1(w[i - 2]) + w[i - 7] + r0(w[i - 15]) + w[i - 16];
|
||||
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
f = state[5];
|
||||
g = state[6];
|
||||
h = state[7];
|
||||
|
||||
for (i = 0; i < 80; ++i)
|
||||
{
|
||||
t1 = h + s1(e) + ch(e, f, g) + K[i] + w[i];
|
||||
t2 = s0(a) + maj(a, b, c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + t1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1 + t2;
|
||||
}
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
state[5] += f;
|
||||
state[6] += g;
|
||||
state[7] += h;
|
||||
}
|
||||
@@ -19,7 +19,7 @@ alias FloatType = double;
|
||||
|
||||
macro bool is_struct_with_default_print($Type)
|
||||
{
|
||||
return $Type.kindof == STRUCT
|
||||
return ($Type.kindof == STRUCT ||| $Type.kindof == BITSTRUCT)
|
||||
&&& !$defined($Type.to_format)
|
||||
&&& !$defined($Type.to_constant_string);
|
||||
}
|
||||
@@ -27,7 +27,7 @@ macro bool is_struct_with_default_print($Type)
|
||||
<*
|
||||
Introspect a struct and print it to a formatter
|
||||
|
||||
@require @typekind(value) == STRUCT : `This macro is only valid on macros`
|
||||
@require @typekind(value) == STRUCT || @typekind(value) == BITSTRUCT : `This macro is only valid on macros`
|
||||
*>
|
||||
macro usz? struct_to_format(value, Formatter* f, bool $force_dump)
|
||||
{
|
||||
@@ -40,8 +40,8 @@ macro usz? struct_to_format(value, Formatter* f, bool $force_dump)
|
||||
$if $member.nameof != "":
|
||||
total += f.printf("%s: ", $member.nameof)!;
|
||||
$endif
|
||||
$if ($force_dump &&& $member.typeid.kindof == STRUCT) |||
|
||||
is_struct_with_default_print($typefrom($member.typeid)):
|
||||
$if ($force_dump &&& ($member.typeid.kindof == STRUCT || $member.typeid.kindof == BITSTRUCT)) |||
|
||||
is_struct_with_default_print($member.typeid):
|
||||
total += struct_to_format($member.get(value), f, $force_dump)!;
|
||||
$else
|
||||
total += f.printf("%s", $member.get(value))!;
|
||||
@@ -136,7 +136,7 @@ fn usz? Formatter.print_with_function(&self, Printable arg)
|
||||
|
||||
fn usz? Formatter.out_unknown(&self, String category, any arg) @private
|
||||
{
|
||||
return self.out_substr("[") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr("]");
|
||||
return self.out_substr("<") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr(">");
|
||||
}
|
||||
fn usz? Formatter.out_str(&self, any arg) @private
|
||||
{
|
||||
@@ -196,7 +196,15 @@ fn usz? Formatter.out_str(&self, any arg) @private
|
||||
case BITSTRUCT:
|
||||
return self.out_unknown("bitstruct", arg);
|
||||
case FUNC:
|
||||
return self.out_unknown("function", arg);
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.width = 0;
|
||||
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
|
||||
case DISTINCT:
|
||||
if (arg.type == String.typeid)
|
||||
{
|
||||
|
||||
@@ -39,10 +39,11 @@ fn uint128? int_from_any(any arg, bool *is_neg) @private
|
||||
{
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case TypeKind.POINTER:
|
||||
case FUNC:
|
||||
case POINTER:
|
||||
*is_neg = false;
|
||||
return (uint128)(uptr)*(void**)arg.ptr;
|
||||
case TypeKind.DISTINCT:
|
||||
case DISTINCT:
|
||||
return int_from_any(arg.as_inner(), is_neg);
|
||||
default:
|
||||
break;
|
||||
@@ -622,9 +623,12 @@ fn usz? Formatter.out_char(&self, any arg) @private
|
||||
return self.out_substr("<NOT CHAR>");
|
||||
}
|
||||
usz len = 1;
|
||||
uint l = 1;
|
||||
// pre padding
|
||||
len += self.adjust(l)!;
|
||||
if (!self.flags.left)
|
||||
{
|
||||
len += self.pad(' ', self.width, len)!;
|
||||
}
|
||||
|
||||
// char output
|
||||
Char32 c = types::any_to_int(arg, uint) ?? 0xFFFD;
|
||||
switch (true)
|
||||
@@ -644,7 +648,10 @@ fn usz? Formatter.out_char(&self, any arg) @private
|
||||
self.out((char)(0x80 | (c >> 6 & 0x3F)))!;
|
||||
self.out((char)(0x80 | (c & 0x3F)))!;
|
||||
}
|
||||
len += self.adjust(l)!;
|
||||
if (self.flags.left)
|
||||
{
|
||||
len += self.pad(' ', self.width, len)!;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
module std::io::os;
|
||||
import libc, std::os, std::io;
|
||||
|
||||
fn void? native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || env::BSD_FAMILY) => @pool()
|
||||
fn void? native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY) => @pool()
|
||||
{
|
||||
$if env::DARWIN || env::LINUX || env::BSD_FAMILY:
|
||||
$if env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY:
|
||||
int res = libc::stat(path.zstr_tcopy(), stat);
|
||||
$else
|
||||
unreachable("Stat unimplemented");
|
||||
@@ -69,6 +69,7 @@ fn bool native_file_or_dir_exists(String path)
|
||||
$case env::NETBSD:
|
||||
$case env::OPENBSD:
|
||||
$case env::LINUX:
|
||||
$case env::ANDROID:
|
||||
Stat stat;
|
||||
return @ok(native_stat(&stat, path));
|
||||
$case env::WIN32:
|
||||
@@ -94,6 +95,7 @@ fn bool native_is_file(String path)
|
||||
$case env::NETBSD:
|
||||
$case env::OPENBSD:
|
||||
$case env::LINUX:
|
||||
$case env::ANDROID:
|
||||
Stat stat;
|
||||
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFREG);
|
||||
$default:
|
||||
@@ -105,7 +107,7 @@ fn bool native_is_file(String path)
|
||||
|
||||
fn bool native_is_dir(String path)
|
||||
{
|
||||
$if env::DARWIN || env::LINUX || env::BSD_FAMILY:
|
||||
$if env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY:
|
||||
Stat stat;
|
||||
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFDIR);
|
||||
$else
|
||||
|
||||
@@ -49,7 +49,7 @@ macro bool @is_outstream(#expr)
|
||||
|
||||
<*
|
||||
@param [&out] ref
|
||||
@require @is_instream(stream)
|
||||
@require @is_instream(stream) : "Expected a stream"
|
||||
*>
|
||||
macro usz? read_any(stream, any ref)
|
||||
{
|
||||
@@ -191,7 +191,7 @@ const char[*] MAX_VARS @private = { [2] = 3, [4] = 5, [8] = 10 };
|
||||
*>
|
||||
macro usz? read_varint(stream, x_ptr)
|
||||
{
|
||||
var $Type = $typefrom($typeof(x_ptr).inner);
|
||||
var $Type = $typeof(x_ptr).inner;
|
||||
const MAX = MAX_VARS[$Type.sizeof];
|
||||
$Type x;
|
||||
uint shift;
|
||||
|
||||
@@ -125,6 +125,7 @@ extern fn ZString getenv(ZString name);
|
||||
extern fn ZString gets(char* buffer);
|
||||
extern fn Tm* gmtime(Time_t* timer);
|
||||
extern fn Tm* gmtime_r(Time_t* timer, Tm* buf) @if(!env::WIN32);
|
||||
extern fn CInt ioctl(CInt fd, ulong request, ...);
|
||||
extern fn CInt isatty(Fd fd) @if(!env::WIN32);
|
||||
extern fn CLong labs(CLong x);
|
||||
extern fn LongDivResult ldiv(CLong number, CLong denom);
|
||||
@@ -167,6 +168,7 @@ extern fn CInt strcmp(ZString str1, ZString str2);
|
||||
extern fn CInt strcoll(ZString str1, ZString str2);
|
||||
extern fn usz strcspn(ZString str1, ZString str2);
|
||||
extern fn ZString strcpy(ZString dst, ZString src);
|
||||
extern fn ZString strdup(ZString s);
|
||||
extern fn ZString strerror(CInt errn);
|
||||
extern fn usz strftime(char* dest, usz maxsize, ZString format, Tm* timeptr);
|
||||
extern fn usz strlen(ZString str);
|
||||
@@ -373,7 +375,7 @@ module libc;
|
||||
alias CFile = void*;
|
||||
|
||||
|
||||
const HAS_MALLOC_SIZE = env::LINUX || env::WIN32 || env::DARWIN;
|
||||
const HAS_MALLOC_SIZE = env::LINUX || env::ANDROID || env::WIN32 || env::DARWIN;
|
||||
|
||||
// The following needs to be set per arch+os
|
||||
// For now I have simply pulled the defaults from MacOS
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
module libc::os @if(env::LIBC);
|
||||
import std::core::env;
|
||||
|
||||
// Linux
|
||||
extern fn int* __errno_location() @if(env::LINUX);
|
||||
macro int errno() @if(env::LINUX) => *__errno_location();
|
||||
macro void errno_set(int err) @if(env::LINUX) => *(__errno_location()) = err;
|
||||
|
||||
// Android 9
|
||||
extern fn int* __errno() @if(env::ANDROID);
|
||||
macro int errno() @if(env::ANDROID) => *__errno();
|
||||
macro void errno_set(int err) @if(env::ANDROID) => *(__errno()) = err;
|
||||
|
||||
// Darwin
|
||||
extern fn int* __error() @if(env::DARWIN);
|
||||
macro int errno() @if(env::DARWIN) => *__error();
|
||||
@@ -22,7 +28,7 @@ extern fn void _get_errno(int* result) @if(env::WIN32);
|
||||
extern fn void _set_errno(int err) @if(env::WIN32);
|
||||
|
||||
// Default
|
||||
module libc::os @if(!env::LIBC || !(env::LINUX || env::DARWIN || env::WIN32));
|
||||
module libc::os @if(!env::LIBC || !env::HAS_NATIVE_ERRNO);
|
||||
tlocal int _errno_c3 = 0;
|
||||
fn void errno_set(int err) => _errno_c3 = err;
|
||||
fn int errno() => _errno_c3;
|
||||
|
||||
@@ -127,13 +127,13 @@ fn bool BigInt.is_negative(&self)
|
||||
return self.data[MAX_LEN - 1] & 0x80000000 != 0;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.add(self, BigInt other)
|
||||
fn BigInt BigInt.add(self, BigInt other) @operator(+)
|
||||
{
|
||||
self.add_this(other);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.add_this(&self, BigInt other)
|
||||
fn void BigInt.add_this(&self, BigInt other) @operator(+=)
|
||||
{
|
||||
bool sign = self.is_negative();
|
||||
bool sign_arg = other.is_negative();
|
||||
@@ -172,13 +172,13 @@ macro uint find_length(uint* data, uint length)
|
||||
return length;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.mult(self, BigInt bi2)
|
||||
fn BigInt BigInt.mult(self, BigInt bi2) @operator(*)
|
||||
{
|
||||
self.mult_this(bi2);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.mult_this(&self, BigInt bi2)
|
||||
fn void BigInt.mult_this(&self, BigInt bi2) @operator(*=)
|
||||
{
|
||||
if (bi2.is_zero())
|
||||
{
|
||||
@@ -270,13 +270,13 @@ fn void BigInt.negate(&self)
|
||||
|
||||
macro bool BigInt.is_zero(&self) => self.len == 1 && self.data[0] == 0;
|
||||
|
||||
fn BigInt BigInt.sub(self, BigInt other)
|
||||
fn BigInt BigInt.sub(self, BigInt other) @operator(-)
|
||||
{
|
||||
self.sub_this(other);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn BigInt* BigInt.sub_this(&self, BigInt other)
|
||||
fn BigInt* BigInt.sub_this(&self, BigInt other) @operator(-=)
|
||||
{
|
||||
self.len = max(self.len, other.len);
|
||||
|
||||
@@ -325,7 +325,7 @@ fn int BigInt.bitcount(&self)
|
||||
return bits;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.unary_minus(&self)
|
||||
fn BigInt BigInt.unary_minus(&self) @operator(-)
|
||||
{
|
||||
if (self.is_zero()) return *self;
|
||||
BigInt result = *self;
|
||||
@@ -334,13 +334,13 @@ fn BigInt BigInt.unary_minus(&self)
|
||||
}
|
||||
|
||||
|
||||
macro BigInt BigInt.div(self, BigInt other)
|
||||
macro BigInt BigInt.div(self, BigInt other) @operator(/)
|
||||
{
|
||||
self.div_this(other);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.div_this(&self, BigInt other)
|
||||
fn void BigInt.div_this(&self, BigInt other) @operator(/=)
|
||||
{
|
||||
bool negate_answer = self.is_negative();
|
||||
|
||||
@@ -377,13 +377,13 @@ fn void BigInt.div_this(&self, BigInt other)
|
||||
*self = quotient;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.mod(self, BigInt bi2)
|
||||
fn BigInt BigInt.mod(self, BigInt bi2) @operator(%)
|
||||
{
|
||||
self.mod_this(bi2);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.mod_this(&self, BigInt bi2)
|
||||
fn void BigInt.mod_this(&self, BigInt bi2) @operator(%=)
|
||||
{
|
||||
if (bi2.is_negative())
|
||||
{
|
||||
@@ -428,30 +428,30 @@ fn void BigInt.bit_negate_this(&self)
|
||||
self.reduce_len();
|
||||
}
|
||||
|
||||
fn BigInt BigInt.bit_negate(self)
|
||||
fn BigInt BigInt.bit_negate(self) @operator(~)
|
||||
{
|
||||
self.bit_negate_this();
|
||||
return self;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.shr(self, int shift)
|
||||
fn BigInt BigInt.shr(self, int shift) @operator(>>)
|
||||
{
|
||||
self.shr_this(shift);
|
||||
return self;
|
||||
}
|
||||
|
||||
fn void BigInt.shr_this(self, int shift)
|
||||
fn void BigInt.shr_this(self, int shift) @operator(>>=)
|
||||
{
|
||||
self.len = shift_right(&self.data, self.len, shift);
|
||||
}
|
||||
|
||||
fn BigInt BigInt.shl(self, int shift)
|
||||
fn BigInt BigInt.shl(self, int shift) @operator(<<)
|
||||
{
|
||||
self.shl_this(shift);
|
||||
return self;
|
||||
}
|
||||
|
||||
macro bool BigInt.equals(&self, BigInt other)
|
||||
macro bool BigInt.equals(&self, BigInt other) @operator(==)
|
||||
{
|
||||
if (self.len != other.len) return false;
|
||||
return self.data[:self.len] == other.data[:self.len];
|
||||
@@ -764,7 +764,7 @@ fn BigInt BigInt.sqrt(&self)
|
||||
return result;
|
||||
}
|
||||
|
||||
fn BigInt BigInt.bit_and(self, BigInt bi2)
|
||||
fn BigInt BigInt.bit_and(self, BigInt bi2) @operator(&)
|
||||
{
|
||||
self.bit_and_this(bi2);
|
||||
return self;
|
||||
@@ -782,7 +782,7 @@ fn void BigInt.bit_and_this(&self, BigInt bi2)
|
||||
self.reduce_len();
|
||||
}
|
||||
|
||||
fn BigInt BigInt.bit_or(self, BigInt bi2)
|
||||
fn BigInt BigInt.bit_or(self, BigInt bi2) @operator(|)
|
||||
{
|
||||
self.bit_or_this(bi2);
|
||||
return self;
|
||||
@@ -800,7 +800,7 @@ fn void BigInt.bit_or_this(&self, BigInt bi2)
|
||||
self.reduce_len();
|
||||
}
|
||||
|
||||
fn BigInt BigInt.bit_xor(self, BigInt bi2)
|
||||
fn BigInt BigInt.bit_xor(self, BigInt bi2) @operator(^)
|
||||
{
|
||||
self.bit_xor_this(bi2);
|
||||
return self;
|
||||
@@ -818,7 +818,7 @@ fn void BigInt.bit_xor_this(&self, BigInt bi2)
|
||||
self.reduce_len();
|
||||
}
|
||||
|
||||
fn void BigInt.shl_this(&self, int shift)
|
||||
fn void BigInt.shl_this(&self, int shift) @operator(<<=)
|
||||
{
|
||||
self.len = shift_left(&self.data, self.len, shift);
|
||||
}
|
||||
|
||||
66
lib/std/math/complex.c3
Normal file
66
lib/std/math/complex.c3
Normal file
@@ -0,0 +1,66 @@
|
||||
module std::math;
|
||||
|
||||
// Complex number aliases.
|
||||
|
||||
alias Complexf = Complex {float};
|
||||
alias Complex = Complex {double};
|
||||
alias COMPLEX_IDENTITY @builtin = complex::IDENTITY {double};
|
||||
alias COMPLEXF_IDENTITY @builtin = complex::IDENTITY {float};
|
||||
alias IMAGINARY @builtin @deprecated("Use I") = complex::IMAGINARY { double };
|
||||
alias IMAGINARYF @builtin @deprecated("Use I_F") = complex::IMAGINARY { float };
|
||||
alias I @builtin = complex::IMAGINARY { double };
|
||||
alias I_F @builtin = complex::IMAGINARY { float };
|
||||
|
||||
<*
|
||||
The generic complex number module, for float or double based complex number definitions.
|
||||
|
||||
@require Real.kindof == FLOAT : "A complex number must use a floating type"
|
||||
*>
|
||||
module std::math::complex {Real};
|
||||
import std::io;
|
||||
|
||||
union Complex (Printable)
|
||||
{
|
||||
struct
|
||||
{
|
||||
Real r, c;
|
||||
}
|
||||
Real[<2>] v;
|
||||
}
|
||||
|
||||
const Complex IDENTITY = { 1, 0 };
|
||||
const Complex IMAGINARY = { 0, 1 };
|
||||
|
||||
macro Complex Complex.add(self, Complex b) @operator(+) => { .v = self.v + b.v };
|
||||
macro Complex Complex.add_this(&self, Complex b) @operator(+=) => { .v = self.v += b.v };
|
||||
macro Complex Complex.add_real(self, Real r) @operator_s(+) => { .v = self.v + (Real[<2>]) { r, 0 } };
|
||||
macro Complex Complex.add_each(self, Real b) => { .v = self.v + b };
|
||||
macro Complex Complex.sub(self, Complex b) @operator(-) => { .v = self.v - b.v };
|
||||
macro Complex Complex.sub_this(&self, Complex b) @operator(-=) => { .v = self.v -= b.v };
|
||||
macro Complex Complex.sub_real(self, Real r) @operator(-) => { .v = self.v - (Real[<2>]) { r, 0 } };
|
||||
macro Complex Complex.sub_real_inverse(self, Real r) @operator_r(-) => { .v = (Real[<2>]) { r, 0 } - self.v };
|
||||
macro Complex Complex.sub_each(self, Real b) => { .v = self.v - b };
|
||||
macro Complex Complex.scale(self, Real r) @operator_s(*) => { .v = self.v * r };
|
||||
macro Complex Complex.mul(self, Complex b)@operator(*) => { self.r * b.r - self.c * b.c, self.r * b.c + b.r * self.c };
|
||||
macro Complex Complex.div_real(self, Real r) @operator(/) => { .v = self.v / r };
|
||||
macro Complex Complex.div_real_inverse(Complex c, Real r) @operator_r(/) => ((Complex) { .r = self }).div(c);
|
||||
macro Complex Complex.div(self, Complex b) @operator(/)
|
||||
{
|
||||
Real div = b.v.dot(b.v);
|
||||
return { (self.r * b.r + self.c * b.c) / div, (self.c * b.r - self.r * b.c) / div };
|
||||
}
|
||||
macro Complex Complex.inverse(self)
|
||||
{
|
||||
Real sqr = self.v.dot(self.v);
|
||||
return { self.r / sqr, -self.c / sqr };
|
||||
}
|
||||
macro Complex Complex.conjugate(self) => { .r = self.r, .c = -self.c };
|
||||
macro Complex Complex.negate(self) @operator(-) => { .v = -self.v };
|
||||
macro bool Complex.equals(self, Complex b) @operator(==) => self.v == b.v;
|
||||
macro bool Complex.equals_real(self, Real r) @operator_s(==) => self.v == { r, 0 };
|
||||
macro bool Complex.not_equals(self, Complex b) @operator(!=) => self.v != b.v;
|
||||
|
||||
fn usz? Complex.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
return f.printf("%g%+gi", self.r, self.c);
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import std::math::complex;
|
||||
import std::math::matrix;
|
||||
import std::math::quaternion;
|
||||
|
||||
// TODO Define these using quad precision.
|
||||
const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466;
|
||||
const LOG2E = 1.44269504088896340735992468100189214; // log2(e)
|
||||
const LOG10E = 0.434294481903251827651128918916605082; // log10(e)
|
||||
@@ -57,21 +56,6 @@ const DOUBLE_MAX_EXP = 1024;
|
||||
const DOUBLE_MIN_EXP = -1021;
|
||||
const DOUBLE_EPSILON = 2.22044604925031308085e-16;
|
||||
|
||||
const QUAD_MANT_DIG = 113;
|
||||
|
||||
/*
|
||||
const QUAD_MAX = 1.18973149535723176508575932662800702e+4932;
|
||||
const QUAD_MIN = 3.36210314311209350626267781732175260e-4932;
|
||||
const QUAD_DENORM_MIN = 6.47517511943802511092443895822764655e-4966;
|
||||
const QUAD_DIG = 33;
|
||||
const QUAD_DEC_DIGITS = 36;
|
||||
const QUAD_MAX_10_EXP = 4932;
|
||||
const QUAD_MIN_10_EXP = -4931;
|
||||
const QUAD_MAX_EXP = 16384;
|
||||
const QUAD_MIN_EXP = -16481;
|
||||
const QUAD_EPSILON = 1.92592994438723585305597794258492732e-34;
|
||||
*/
|
||||
|
||||
enum RoundingMode : int
|
||||
{
|
||||
TOWARD_ZERO,
|
||||
@@ -82,35 +66,6 @@ enum RoundingMode : int
|
||||
|
||||
faultdef OVERFLOW, MATRIX_INVERSE_DOESNT_EXIST;
|
||||
|
||||
alias Complexf = Complex {float};
|
||||
alias Complex = Complex {double};
|
||||
alias COMPLEX_IDENTITY @builtin = complex::IDENTITY {double};
|
||||
alias COMPLEXF_IDENTITY @builtin = complex::IDENTITY {float};
|
||||
|
||||
alias Quaternionf = Quaternion {float};
|
||||
alias Quaternion = Quaternion {double};
|
||||
alias QUATERNION_IDENTITY @builtin = quaternion::IDENTITY {double};
|
||||
alias QUATERNIONF_IDENTITY @builtin = quaternion::IDENTITY {float};
|
||||
|
||||
alias Matrix2f = Matrix2x2 {float};
|
||||
alias Matrix2 = Matrix2x2 {double};
|
||||
alias Matrix3f = Matrix3x3 {float};
|
||||
alias Matrix3 = Matrix3x3 {double};
|
||||
alias Matrix4f = Matrix4x4 {float};
|
||||
alias Matrix4 = Matrix4x4 {double};
|
||||
alias matrix4_ortho @builtin = matrix::ortho {double};
|
||||
alias matrix4f_ortho @builtin = matrix::ortho {float};
|
||||
alias matrix4_perspective @builtin = matrix::perspective {double};
|
||||
alias matrix4f_perspective @builtin = matrix::perspective {float};
|
||||
|
||||
alias MATRIX2_IDENTITY @builtin = matrix::IDENTITY2 {double};
|
||||
alias MATRIX2F_IDENTITY @builtin = matrix::IDENTITY2 {float};
|
||||
alias MATRIX3_IDENTITY @builtin = matrix::IDENTITY3 {double};
|
||||
alias MATRIX3F_IDENTITY @builtin = matrix::IDENTITY3 {float};
|
||||
alias MATRIX4_IDENTITY @builtin = matrix::IDENTITY4 {double};
|
||||
alias MATRIX4F_IDENTITY @builtin = matrix::IDENTITY4 {float};
|
||||
|
||||
|
||||
<*
|
||||
@require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector`
|
||||
*>
|
||||
@@ -129,7 +84,7 @@ macro is_approx(x, y, eps)
|
||||
{
|
||||
if (x == y) return true;
|
||||
if (is_nan(x) || is_nan(y)) return false;
|
||||
return abs(x-y) <= eps;
|
||||
return abs(x - y) <= eps;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -140,7 +95,7 @@ macro is_approx_rel(x, y, eps)
|
||||
{
|
||||
if (x == y) return true;
|
||||
if (is_nan(x) || is_nan(y)) return false;
|
||||
return abs(x-y) <= eps * max(abs(x), abs(y));
|
||||
return abs(x - y) <= eps * max(abs(x), abs(y));
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -149,7 +104,7 @@ macro is_approx_rel(x, y, eps)
|
||||
macro sign(x)
|
||||
{
|
||||
var $Type = $typeof(x);
|
||||
$if $Type.kindof == TypeKind.UNSIGNED_INT:
|
||||
$if $Type.kindof == UNSIGNED_INT:
|
||||
return ($Type)(x > 0);
|
||||
$else
|
||||
return ($Type)(x > 0) - ($Type)(x < 0);
|
||||
@@ -177,7 +132,7 @@ macro atan2(x, y)
|
||||
*>
|
||||
macro sincos_ref(x, sinp, cosp)
|
||||
{
|
||||
$if @typeid(*sinp) == float.typeid:
|
||||
$if @typeis(sinp, float*.typeid):
|
||||
return _sincosf(x, sinp, cosp);
|
||||
$else
|
||||
return _sincos(x, sinp, cosp);
|
||||
@@ -192,7 +147,7 @@ macro sincos_ref(x, sinp, cosp)
|
||||
*>
|
||||
macro sincos(x)
|
||||
{
|
||||
$if @typeid(x) == float.typeid:
|
||||
$if @typeis(x, float):
|
||||
float[<2>] v @noinit;
|
||||
_sincosf(x, &v[0], &v[1]);
|
||||
$else
|
||||
@@ -279,6 +234,13 @@ macro asinh(x)
|
||||
*>
|
||||
macro ceil(x) => $$ceil(x);
|
||||
|
||||
<*
|
||||
Ceil for compile time evaluation.
|
||||
|
||||
@require @typeis($input, double) || @typeis($input, float) : "Only float and double may be used"
|
||||
*>
|
||||
macro @ceil($input) @const => $$ceil($input);
|
||||
|
||||
<*
|
||||
Constrain the value to lie within the given interval.
|
||||
|
||||
@@ -552,7 +514,7 @@ macro bool is_finite(x)
|
||||
$case float16:
|
||||
return bitcast((float)x, uint) & 0x7fffffff < 0x7f800000;
|
||||
$default:
|
||||
return bitcast((double)x, ulong) & (~0u64 >> 1) < 0x7ffu64 << 52;
|
||||
return bitcast((double)x, ulong) & (~0UL >> 1) < 0x7ffUL << 52;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
@@ -566,7 +528,7 @@ macro is_nan(x)
|
||||
$case float16:
|
||||
return bitcast((float)x, uint) & 0x7fffffff > 0x7f800000;
|
||||
$default:
|
||||
return bitcast((double)x, ulong) & (~0u64 >> 1) > 0x7ffu64 << 52;
|
||||
return bitcast((double)x, ulong) & (~0UL >> 1) > 0x7ffUL << 52;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
@@ -580,7 +542,7 @@ macro is_inf(x)
|
||||
$case float16:
|
||||
return bitcast((float)x, uint) & 0x7fffffff == 0x7f800000;
|
||||
$default:
|
||||
return bitcast((double)x, ulong) & (~0u64 >> 1) == 0x7ffu64 << 52;
|
||||
return bitcast((double)x, ulong) & (~0UL >> 1) == 0x7ffUL << 52;
|
||||
$endswitch
|
||||
}
|
||||
|
||||
@@ -1054,10 +1016,16 @@ extern fn double _atan(double x) @extern("atan");
|
||||
extern fn float _atanf(float x) @extern("atanf");
|
||||
extern fn double _atan2(double, double) @extern("atan2");
|
||||
extern fn float _atan2f(float, float) @extern("atan2f");
|
||||
|
||||
extern fn void _sincos(double, double*, double*) @extern("__sincos") @link("m") @if(env::DARWIN);
|
||||
extern fn void _sincosf(float, float*, float*) @extern("__sincosf") @link("m") @if(env::DARWIN);
|
||||
extern fn void _sincos(double, double*, double*) @extern("sincos") @link("m") @if(!env::DARWIN);
|
||||
extern fn void _sincosf(float, float*, float*) @extern("sincosf") @link("m") @if(!env::DARWIN);
|
||||
|
||||
extern fn void _sincos(double, double*, double*) @extern("sincos") @link("m") @if(!env::DARWIN && !env::WIN32);
|
||||
extern fn void _sincosf(float, float*, float*) @extern("sincosf") @link("m") @if(!env::DARWIN && !env::WIN32);
|
||||
|
||||
fn void _sincos(double a, double* s, double* c) @extern("sincos") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
|
||||
fn void _sincosf(float a, float* s, float* c) @extern("sincosf") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
|
||||
|
||||
extern fn double _tan(double x) @extern("tan");
|
||||
extern fn float _tanf(float x) @extern("tanf");
|
||||
extern fn double _scalbn(double x, int n) @extern("scalbn");
|
||||
@@ -1092,8 +1060,8 @@ fn double _frexp(double x, int* e)
|
||||
return x;
|
||||
default:
|
||||
*e = ee - 0x3fe;
|
||||
i &= 0x800fffffffffffffu64;
|
||||
i |= 0x3fe0000000000000u64;
|
||||
i &= 0x800fffffffffffffUL;
|
||||
i |= 0x3fe0000000000000UL;
|
||||
return bitcast(i, double);
|
||||
}
|
||||
}
|
||||
@@ -1118,8 +1086,8 @@ fn float _frexpf(float x, int* e)
|
||||
return x;
|
||||
default:
|
||||
*e = ee - 0x7e;
|
||||
i &= 0x807fffffu32;
|
||||
i |= 0x3f000000u32;
|
||||
i &= 0x807fffffU;
|
||||
i |= 0x3f000000U;
|
||||
return bitcast(i, float);
|
||||
}
|
||||
}
|
||||
@@ -1145,6 +1113,33 @@ macro overflow_mul_helper(x, y) @local
|
||||
return res;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [&out] out : "Where the result of the addition is stored"
|
||||
@return "Whether the addition resulted in an integer overflow"
|
||||
@require values::@is_same_type(a, b) : "a and b must be the same type"
|
||||
@require values::@is_flat_intlike(a) &&& values::@is_flat_intlike(b) : "a and b must both be integer or integer vector based"
|
||||
@require $defined(*out) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b"
|
||||
*>
|
||||
macro bool overflow_add(a, b, out) => $$overflow_add(a, b, out);
|
||||
|
||||
<*
|
||||
@param [&out] out : "Where the result of the subtraction is stored"
|
||||
@return "Whether the subtraction resulted in an integer overflow"
|
||||
@require values::@is_same_type(a, b) : "a and b must be the same type"
|
||||
@require values::@is_flat_intlike(a) &&& values::@is_flat_intlike(b) : "a and b must both be integer or integer vector based"
|
||||
@require $defined(*out) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b"
|
||||
*>
|
||||
macro bool overflow_sub(a, b, out) => $$overflow_sub(a, b, out);
|
||||
|
||||
<*
|
||||
@param [&out] out : "Where the result of the multiplication is stored"
|
||||
@return "Whether the multiplication resulted in an integer overflow"
|
||||
@require values::@is_same_type(a, b) : "a and b must be the same type"
|
||||
@require values::@is_flat_intlike(a) &&& values::@is_flat_intlike(b) : "a and b must both be integer or integer vector based"
|
||||
@require $defined(*out) &&& values::@is_same_type(*out, a) : "out must be a pointer of the same type as a and b"
|
||||
*>
|
||||
macro bool overflow_mul(a, b, out) => $$overflow_mul(a, b, out);
|
||||
|
||||
<*
|
||||
@require types::is_vector($Type) || ($Type.kindof == ARRAY &&& types::is_numerical($typefrom($Type.inner)))
|
||||
*>
|
||||
@@ -1202,31 +1197,31 @@ macro ushort[<*>] ushort[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul
|
||||
|
||||
<*
|
||||
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro int[<*>] int[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
|
||||
<*
|
||||
@require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro uint[<*>] uint[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
|
||||
<*
|
||||
@require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro long[<*>] long[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
|
||||
<*
|
||||
@require @is_same_vector_or_scalar(self, mul) `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) `div must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, mul) : `mul must be a vector of the same type as self, or be an integer scalar`
|
||||
@require @is_same_vector_or_scalar(self, div) : `div must be a vector of the same type as self, or be an integer scalar`
|
||||
*>
|
||||
macro ulong[<*>] ulong[<*>].muldiv(self, mul, div) => mul_div_helper(self, mul, div);
|
||||
|
||||
<*
|
||||
@require types::is_int($typeof(a)) `The input must be an integer`
|
||||
@require types::is_int($typeof(b)) `The input must be an integer`
|
||||
@require types::is_int($typeof(a)) : `The input must be an integer`
|
||||
@require types::is_int($typeof(b)) : `The input must be an integer`
|
||||
*>
|
||||
macro _gcd(a, b) @private
|
||||
{
|
||||
@@ -1247,9 +1242,9 @@ macro _gcd(a, b) @private
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the least common multiple for the provided arguments.
|
||||
Calculate the least common multiple for the provided arguments.
|
||||
|
||||
@require $vacount >= 2 `At least two arguments are required.`
|
||||
@require $vacount >= 2 : "At least two arguments are required."
|
||||
*>
|
||||
macro lcm(...)
|
||||
{
|
||||
@@ -1265,9 +1260,9 @@ macro lcm(...)
|
||||
}
|
||||
|
||||
<*
|
||||
Calculate the greatest common divisor for the provided arguments.
|
||||
Calculate the greatest common divisor for the provided arguments.
|
||||
|
||||
@require $vacount >= 2 `At least two arguments are required.`
|
||||
@require $vacount >= 2 : "At least two arguments are required."
|
||||
*>
|
||||
macro gcd(...)
|
||||
{
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
module std::math::complex{Real};
|
||||
|
||||
union Complex
|
||||
{
|
||||
struct
|
||||
{
|
||||
Real r, c;
|
||||
}
|
||||
Real[<2>] v;
|
||||
}
|
||||
|
||||
const Complex IDENTITY = { 1, 0 };
|
||||
const Complex IMAGINARY = { 0, 1 };
|
||||
macro Complex Complex.add(self, Complex b) => { .v = self.v + b.v };
|
||||
macro Complex Complex.add_each(self, Real b) => { .v = self.v + b };
|
||||
macro Complex Complex.sub(self, Complex b) => { .v = self.v - b.v };
|
||||
macro Complex Complex.sub_each(self, Real b) => { .v = self.v - b };
|
||||
macro Complex Complex.scale(self, Real s) => { .v = self.v * s };
|
||||
macro Complex Complex.mul(self, Complex b) => { self.r * b.r - self.c * b.c, self.r * b.c + b.r * self.c };
|
||||
macro Complex Complex.div(self, Complex b)
|
||||
{
|
||||
Real div = b.v.dot(b.v);
|
||||
return { (self.r * b.r + self.c * b.c) / div, (self.c * b.r - self.r * b.c) / div };
|
||||
}
|
||||
macro Complex Complex.inverse(self)
|
||||
{
|
||||
Real sqr = self.v.dot(self.v);
|
||||
return { self.r / sqr, -self.c / sqr };
|
||||
}
|
||||
macro Complex Complex.conjugate(self) => { .r = self.r, .c = -self.c };
|
||||
macro bool Complex.equals(self, Complex b) => self.v == b.v;
|
||||
@@ -1,47 +0,0 @@
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_atan.c
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
/* atan(x)
|
||||
* Method
|
||||
* 1. Reduce x to positive by atan(x) = -atan(-x).
|
||||
* 2. According to the integer k=4t+0.25 chopped, t=x, the argument
|
||||
* is further reduced to one of the following intervals and the
|
||||
* arctangent of t is evaluated by the corresponding formula:
|
||||
*
|
||||
* [0,7/16] atan(x) = t-t^3*(a1+t^2*(a2+...(a10+t^2*a11)...)
|
||||
* [7/16,11/16] atan(x) = atan(1/2) + atan( (t-0.5)/(1+t/2) )
|
||||
* [11/16.19/16] atan(x) = atan( 1 ) + atan( (t-1)/(1+t) )
|
||||
* [19/16,39/16] atan(x) = atan(3/2) + atan( (t-1.5)/(1+1.5t) )
|
||||
* [39/16,INF] atan(x) = atan(INF) + atan( -1/t )
|
||||
*
|
||||
* Constants:
|
||||
* The hexadecimal values are the intended ones for the following
|
||||
* constants. The decimal values may be used, provided that the
|
||||
* compiler will convert from decimal to binary accurately enough
|
||||
* to produce the hexadecimal values shown.
|
||||
*/
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/s_atanf.c */
|
||||
/*
|
||||
* Conversion to float by Ian Lance Taylor, Cygnus Support, ian@cygnus.com.
|
||||
*/
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunPro, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
module std::math;
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ fn double fmod(double x, double y) @extern("fmod") @weak @nostrip
|
||||
}
|
||||
else
|
||||
{
|
||||
uxi &= -1UL >> 12;
|
||||
uxi &= (ulong)-1 >> 12;
|
||||
uxi |= 1UL << 52;
|
||||
}
|
||||
if (!ey)
|
||||
@@ -45,7 +45,7 @@ fn double fmod(double x, double y) @extern("fmod") @weak @nostrip
|
||||
}
|
||||
else
|
||||
{
|
||||
uy.i &= -1UL >> 12;
|
||||
uy.i &= (ulong)-1 >> 12;
|
||||
uy.i |= 1UL << 52;
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ fn float fmodf(float x, float y) @extern("fmodf") @weak @nostrip
|
||||
}
|
||||
else
|
||||
{
|
||||
uxi &= -1U >> 9;
|
||||
uxi &= (uint)-1 >> 9;
|
||||
uxi |= 1U << 23;
|
||||
}
|
||||
if (!ey)
|
||||
@@ -115,7 +115,7 @@ fn float fmodf(float x, float y) @extern("fmodf") @weak @nostrip
|
||||
}
|
||||
else
|
||||
{
|
||||
uy.i &= -1U >> 9;
|
||||
uy.i &= (uint)-1 >> 9;
|
||||
uy.i |= 1U << 23;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ fn double _atanh(double x) @weak @extern("atanh") @nostrip
|
||||
}
|
||||
return double.nan;
|
||||
/* x<2**-28 */
|
||||
case ix < 0x3e300000 && (1e300 + x) > 0.:
|
||||
case ix < 0x3e300000 && (1e300 + x) > 0.0:
|
||||
return x;
|
||||
}
|
||||
x.set_high_word(ix);
|
||||
@@ -37,11 +37,11 @@ fn double _atanh(double x) @weak @extern("atanh") @nostrip
|
||||
if (ix < 0x3fe00000)
|
||||
{
|
||||
t = x + x;
|
||||
t = 0.5 * _log1p(t + t * x / (1. - x));
|
||||
t = 0.5 * _log1p(t + t * x / (1.0 - x));
|
||||
}
|
||||
else
|
||||
{
|
||||
t = 0.5 * _log1p((x + x) / (1. - x));
|
||||
t = 0.5 * _log1p((x + x) / (1.0 - x));
|
||||
}
|
||||
return sign ? -t : t;
|
||||
}
|
||||
|
||||
@@ -47,13 +47,13 @@ fn double _exp2_specialcase(double tmp, ulong sbits, ulong ki) @private
|
||||
if (ki & 0x80000000 == 0)
|
||||
{
|
||||
// k > 0, the exponent of scale might have overflowed by 1.
|
||||
sbits -= 1u64 << 52;
|
||||
sbits -= 1UL << 52;
|
||||
double scale = bitcast(sbits, double);
|
||||
double y = 2 * (scale + scale * tmp);
|
||||
return y;
|
||||
}
|
||||
// k < 0, need special care in the subnormal range.
|
||||
sbits += 1022u64 << 52;
|
||||
sbits += 1022UL << 52;
|
||||
double scale = bitcast(sbits, double);
|
||||
double y = scale + scale * tmp;
|
||||
if (y >= 1.0)
|
||||
|
||||
@@ -90,7 +90,7 @@ fn double _log1p(double x) @weak @extern("log1p") @nostrip
|
||||
/* correction term ~ log(1+x)-log(u), avoid underflow in c/u */
|
||||
if (k < 54)
|
||||
{
|
||||
c = (k >= 2) ? 1. - (u - x) : x - (u - 1.);
|
||||
c = (k >= 2) ? 1.0 - (u - x) : x - (u - 1.0);
|
||||
c /= u;
|
||||
}
|
||||
else
|
||||
@@ -100,10 +100,10 @@ fn double _log1p(double x) @weak @extern("log1p") @nostrip
|
||||
/* reduce u into [sqrt(2)/2, sqrt(2)] */
|
||||
hu = (hu & 0x000fffff) + 0x3fe6a09e;
|
||||
u = bitcast(((ulong)hu << 32) | (bitcast(u, ulong) & 0xffffffff) , double);
|
||||
f = u - 1.;
|
||||
f = u - 1.0;
|
||||
}
|
||||
double hfsq = 0.5 * f * f;
|
||||
double s = f / (2. + f);
|
||||
double s = f / (2.0 + f);
|
||||
double z = s * s;
|
||||
double w = z * z;
|
||||
double t1 = w * (LG2 + w * (LG4 + w * LG6));
|
||||
|
||||
@@ -109,7 +109,7 @@ const int[*] IPIO2 = {
|
||||
0x91615E, 0xE61B08, 0x659985, 0x5F14A0, 0x68408D, 0xFFD880,
|
||||
0x4D7327, 0x310606, 0x1556CA, 0x73A8C9, 0x60E27B, 0xC08C6B, };
|
||||
|
||||
const double[*] PIO2 = {
|
||||
const double[*] PIO2 @local = {
|
||||
1.57079625129699707031e+00, /* 0x3FF921FB, 0x40000000 */
|
||||
7.54978941586159635335e-08, /* 0x3E74442D, 0x00000000 */
|
||||
5.39030252995776476554e-15, /* 0x3CF84698, 0x80000000 */
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
module std::math::quaternion{Real};
|
||||
import std::math::vector;
|
||||
union Quaternion
|
||||
{
|
||||
struct
|
||||
{
|
||||
Real i, j, k, l;
|
||||
}
|
||||
Real[<4>] v;
|
||||
}
|
||||
|
||||
const Quaternion IDENTITY = { 0, 0, 0, 1 };
|
||||
|
||||
macro Quaternion Quaternion.add(Quaternion a, Quaternion b) => { .v = a.v + b.v };
|
||||
macro Quaternion Quaternion.add_each(Quaternion a, Real b) => { .v = a.v + b };
|
||||
macro Quaternion Quaternion.sub(Quaternion a, Quaternion b) => { .v = a.v - b.v };
|
||||
macro Quaternion Quaternion.sub_each(Quaternion a, Real b) => { .v = a.v - b };
|
||||
macro Quaternion Quaternion.scale(Quaternion a, Real s) => { .v = a.v * s };
|
||||
macro Quaternion Quaternion.normalize(Quaternion q) => { .v = q.v.normalize() };
|
||||
macro Real Quaternion.length(Quaternion q) => q.v.length();
|
||||
macro Quaternion Quaternion.lerp(Quaternion q1, Quaternion q2, Real amount) => { .v = q1.v.lerp(q2.v, amount) };
|
||||
macro Matrix4f Quaternion.to_matrixf(Quaternion* q) => into_matrix(q, Matrix4f);
|
||||
macro Matrix4 Quaternion.to_matrix(Quaternion* q) => into_matrix(q, Matrix4);
|
||||
fn Quaternion Quaternion.nlerp(Quaternion q1, Quaternion q2, Real amount) => { .v = q1.v.lerp(q2.v, amount).normalize() };
|
||||
|
||||
fn Quaternion Quaternion.invert(q)
|
||||
{
|
||||
Real length_sq = q.v.dot(q.v);
|
||||
if (length_sq <= 0) return q;
|
||||
Real inv_length = 1 / length_sq;
|
||||
return { q.v[0] * -inv_length, q.v[1] * -inv_length, q.v[2] * -inv_length, q.v[3] * inv_length };
|
||||
}
|
||||
|
||||
fn Quaternion Quaternion.slerp(q1, Quaternion q2, Real amount)
|
||||
{
|
||||
Quaternion result = {};
|
||||
|
||||
Real[<4>] q2v = q2.v;
|
||||
Real cos_half_theta = q1.v.dot(q2v);
|
||||
|
||||
if (cos_half_theta < 0)
|
||||
{
|
||||
q2v = -q2v;
|
||||
cos_half_theta = -cos_half_theta;
|
||||
}
|
||||
|
||||
if (cos_half_theta >= 1) return q1;
|
||||
|
||||
Real[<4>] q1v = q1.v;
|
||||
if (cos_half_theta > 0.95f) return { .v = q1v.lerp(q2v, amount) };
|
||||
|
||||
Real half_theta = math::cos(cos_half_theta);
|
||||
Real sin_half_theta = math::sqrt(1 - cos_half_theta * cos_half_theta);
|
||||
if (math::abs(sin_half_theta) < 0.001f)
|
||||
{
|
||||
return { .v = (q1v + q2v) * 0.5f };
|
||||
}
|
||||
Real ratio_a = math::sin((1 - amount) * half_theta) / sin_half_theta;
|
||||
Real ratio_b = math::sin(amount * half_theta) / sin_half_theta;
|
||||
return { .v = q1v * ratio_a + q2v * ratio_b };
|
||||
}
|
||||
|
||||
fn Quaternion Quaternion.mul(a, Quaternion b)
|
||||
{
|
||||
return { a.i * b.l + a.l * b.i + a.j * b.k - a.k * b.j,
|
||||
a.j * b.l + a.l * b.j + a.k * b.i - a.i * b.k,
|
||||
a.k * b.l + a.l * b.k + a.i * b.j - a.j * b.i,
|
||||
a.l * b.l - a.i * b.i - a.j * a.j - a.k * a.k };
|
||||
}
|
||||
|
||||
macro into_matrix(Quaternion* q, $Type) @private
|
||||
{
|
||||
Quaternion rotation = q.normalize();
|
||||
var x = rotation.i;
|
||||
var y = rotation.j;
|
||||
var z = rotation.k;
|
||||
var w = rotation.l;
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,32 @@
|
||||
module std::math::matrix{Real};
|
||||
module std::math;
|
||||
|
||||
// Predefined matrix types
|
||||
alias Matrix2f = Matrix2x2 {float};
|
||||
alias Matrix2 = Matrix2x2 {double};
|
||||
alias Matrix3f = Matrix3x3 {float};
|
||||
alias Matrix3 = Matrix3x3 {double};
|
||||
alias Matrix4f = Matrix4x4 {float};
|
||||
alias Matrix4 = Matrix4x4 {double};
|
||||
|
||||
// Predefined matrix functions
|
||||
alias matrix4_ortho @builtin = matrix::ortho {double};
|
||||
alias matrix4f_ortho @builtin = matrix::ortho {float};
|
||||
alias matrix4_perspective @builtin = matrix::perspective {double};
|
||||
alias matrix4f_perspective @builtin = matrix::perspective {float};
|
||||
|
||||
alias MATRIX2_IDENTITY @builtin = matrix::IDENTITY2 {double};
|
||||
alias MATRIX2F_IDENTITY @builtin = matrix::IDENTITY2 {float};
|
||||
alias MATRIX3_IDENTITY @builtin = matrix::IDENTITY3 {double};
|
||||
alias MATRIX3F_IDENTITY @builtin = matrix::IDENTITY3 {float};
|
||||
alias MATRIX4_IDENTITY @builtin = matrix::IDENTITY4 {double};
|
||||
alias MATRIX4F_IDENTITY @builtin = matrix::IDENTITY4 {float};
|
||||
|
||||
<*
|
||||
The generic matrix module, for float or double based matrix definitions.
|
||||
|
||||
@require Real.kindof == FLOAT : "A matrix must use a floating type"
|
||||
*>
|
||||
module std::math::matrix {Real};
|
||||
import std::math::vector;
|
||||
|
||||
struct Matrix2x2
|
||||
@@ -43,7 +71,7 @@ struct Matrix4x4
|
||||
}
|
||||
}
|
||||
|
||||
fn Real[<2>] Matrix2x2.apply(&self, Real[<2>] vec)
|
||||
fn Real[<2>] Matrix2x2.apply(&self, Real[<2>] vec) @operator(*)
|
||||
{
|
||||
return {
|
||||
self.m00 * vec[0] + self.m01 * vec[1],
|
||||
@@ -51,7 +79,7 @@ fn Real[<2>] Matrix2x2.apply(&self, Real[<2>] vec)
|
||||
};
|
||||
}
|
||||
|
||||
fn Real[<3>] Matrix3x3.apply(&self, Real[<3>] vec)
|
||||
fn Real[<3>] Matrix3x3.apply(&self, Real[<3>] vec) @operator(*)
|
||||
{
|
||||
return {
|
||||
self.m00 * vec[0] + self.m01 * vec[1] + self.m02 * vec[2],
|
||||
@@ -60,7 +88,7 @@ fn Real[<3>] Matrix3x3.apply(&self, Real[<3>] vec)
|
||||
};
|
||||
}
|
||||
|
||||
fn Real[<4>] Matrix4x4.apply(&self, Real[<4>] vec)
|
||||
fn Real[<4>] Matrix4x4.apply(&self, Real[<4>] vec) @operator(*)
|
||||
{
|
||||
return {
|
||||
self.m00 * vec[0] + self.m01 * vec[1] + self.m02 * vec[2] + self.m03 * vec[3],
|
||||
@@ -71,7 +99,7 @@ fn Real[<4>] Matrix4x4.apply(&self, Real[<4>] vec)
|
||||
}
|
||||
|
||||
|
||||
fn Matrix2x2 Matrix2x2.mul(&self, Matrix2x2 b)
|
||||
fn Matrix2x2 Matrix2x2.mul(&self, Matrix2x2 b) @operator(*)
|
||||
{
|
||||
return {
|
||||
self.m00 * b.m00 + self.m01 * b.m10, self.m00 * b.m01 + self.m01 * b.m11,
|
||||
@@ -79,7 +107,7 @@ fn Matrix2x2 Matrix2x2.mul(&self, Matrix2x2 b)
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix3x3 Matrix3x3.mul(&self, Matrix3x3 b)
|
||||
fn Matrix3x3 Matrix3x3.mul(&self, Matrix3x3 b) @operator(*)
|
||||
{
|
||||
return {
|
||||
self.m00 * b.m00 + self.m01 * b.m10 + self.m02 * b.m20,
|
||||
@@ -96,28 +124,28 @@ fn Matrix3x3 Matrix3x3.mul(&self, Matrix3x3 b)
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix4x4 Matrix4x4.mul(Matrix4x4* a, Matrix4x4 b)
|
||||
fn Matrix4x4 Matrix4x4.mul(Matrix4x4* self, Matrix4x4 b) @operator(*)
|
||||
{
|
||||
return {
|
||||
a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20 + a.m03 * b.m30,
|
||||
a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21 + a.m03 * b.m31,
|
||||
a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22 + a.m03 * b.m32,
|
||||
a.m00 * b.m03 + a.m01 * b.m13 + a.m02 * b.m23 + a.m03 * b.m33,
|
||||
self.m00 * b.m00 + self.m01 * b.m10 + self.m02 * b.m20 + self.m03 * b.m30,
|
||||
self.m00 * b.m01 + self.m01 * b.m11 + self.m02 * b.m21 + self.m03 * b.m31,
|
||||
self.m00 * b.m02 + self.m01 * b.m12 + self.m02 * b.m22 + self.m03 * b.m32,
|
||||
self.m00 * b.m03 + self.m01 * b.m13 + self.m02 * b.m23 + self.m03 * b.m33,
|
||||
|
||||
a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20 + a.m13 * b.m30,
|
||||
a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31,
|
||||
a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32,
|
||||
a.m10 * b.m03 + a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33,
|
||||
self.m10 * b.m00 + self.m11 * b.m10 + self.m12 * b.m20 + self.m13 * b.m30,
|
||||
self.m10 * b.m01 + self.m11 * b.m11 + self.m12 * b.m21 + self.m13 * b.m31,
|
||||
self.m10 * b.m02 + self.m11 * b.m12 + self.m12 * b.m22 + self.m13 * b.m32,
|
||||
self.m10 * b.m03 + self.m11 * b.m13 + self.m12 * b.m23 + self.m13 * b.m33,
|
||||
|
||||
a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20 + a.m23 * b.m30,
|
||||
a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31,
|
||||
a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32,
|
||||
a.m20 * b.m03 + a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33,
|
||||
self.m20 * b.m00 + self.m21 * b.m10 + self.m22 * b.m20 + self.m23 * b.m30,
|
||||
self.m20 * b.m01 + self.m21 * b.m11 + self.m22 * b.m21 + self.m23 * b.m31,
|
||||
self.m20 * b.m02 + self.m21 * b.m12 + self.m22 * b.m22 + self.m23 * b.m32,
|
||||
self.m20 * b.m03 + self.m21 * b.m13 + self.m22 * b.m23 + self.m23 * b.m33,
|
||||
|
||||
a.m30 * b.m00 + a.m31 * b.m10 + a.m32 * b.m20 + a.m33 * b.m30,
|
||||
a.m30 * b.m01 + a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31,
|
||||
a.m30 * b.m02 + a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32,
|
||||
a.m30 * b.m03 + a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33,
|
||||
self.m30 * b.m00 + self.m31 * b.m10 + self.m32 * b.m20 + self.m33 * b.m30,
|
||||
self.m30 * b.m01 + self.m31 * b.m11 + self.m32 * b.m21 + self.m33 * b.m31,
|
||||
self.m30 * b.m02 + self.m31 * b.m12 + self.m32 * b.m22 + self.m33 * b.m32,
|
||||
self.m30 * b.m03 + self.m31 * b.m13 + self.m32 * b.m23 + self.m33 * b.m33,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -125,13 +153,25 @@ fn Matrix2x2 Matrix2x2.component_mul(&self, Real s) => matrix_component_mul(self
|
||||
fn Matrix3x3 Matrix3x3.component_mul(&self, Real s) => matrix_component_mul(self, s);
|
||||
fn Matrix4x4 Matrix4x4.component_mul(&self, Real s) => matrix_component_mul(self, s);
|
||||
|
||||
fn Matrix2x2 Matrix2x2.add(&self, Matrix2x2 mat2) => matrix_add(self, mat2);
|
||||
fn Matrix3x3 Matrix3x3.add(&self, Matrix3x3 mat2) => matrix_add(self, mat2);
|
||||
fn Matrix4x4 Matrix4x4.add(&self, Matrix4x4 mat2) => matrix_add(self, mat2);
|
||||
fn Matrix2x2 Matrix2x2.add(&self, Matrix2x2 mat2) @operator(+) => matrix_add(self, mat2);
|
||||
fn Matrix3x3 Matrix3x3.add(&self, Matrix3x3 mat2) @operator(+) => matrix_add(self, mat2);
|
||||
fn Matrix4x4 Matrix4x4.add(&self, Matrix4x4 mat2) @operator(+) => matrix_add(self, mat2);
|
||||
|
||||
fn Matrix2x2 Matrix2x2.sub(&self, Matrix2x2 mat2) => matrix_sub(self, mat2);
|
||||
fn Matrix3x3 Matrix3x3.sub(&self, Matrix3x3 mat2) => matrix_sub(self, mat2);
|
||||
fn Matrix4x4 Matrix4x4.sub(&self, Matrix4x4 mat2) => matrix_sub(self, mat2);
|
||||
fn Matrix2x2 Matrix2x2.sub(&self, Matrix2x2 mat2) @operator(-) => matrix_sub(self, mat2);
|
||||
fn Matrix3x3 Matrix3x3.sub(&self, Matrix3x3 mat2) @operator(-) => matrix_sub(self, mat2);
|
||||
fn Matrix4x4 Matrix4x4.sub(&self, Matrix4x4 mat2) @operator(-) => matrix_sub(self, mat2);
|
||||
|
||||
fn Matrix2x2 Matrix2x2.negate(&self) @operator(-) => { .m = (Real[<4>])self.m };
|
||||
fn Matrix3x3 Matrix3x3.negate(&self) @operator(-) => { .m = (Real[<9>])self.m };
|
||||
fn Matrix4x4 Matrix4x4.negate(&self) @operator(-) => { .m = (Real[<16>])self.m };
|
||||
|
||||
fn bool Matrix2x2.eq(&self, Matrix2x2 mat2) @operator(==) => (Real[<4>])self.m == (Real[<4>])mat2.m;
|
||||
fn bool Matrix3x3.eq(&self, Matrix3x3 mat2) @operator(==) => (Real[<9>])self.m == (Real[<9>])mat2.m;
|
||||
fn bool Matrix4x4.eq(&self, Matrix4x4 mat2) @operator(==) => (Real[<16>])self.m == (Real[<16>])mat2.m;
|
||||
|
||||
fn bool Matrix2x2.neq(&self, Matrix2x2 mat2) @operator(!=) => (Real[<4>])self.m != (Real[<4>])mat2.m;
|
||||
fn bool Matrix3x3.neq(&self, Matrix3x3 mat2) @operator(!=) => (Real[<9>])self.m != (Real[<9>])mat2.m;
|
||||
fn bool Matrix4x4.neq(&self, Matrix4x4 mat2) @operator(!=) => (Real[<16>])self.m != (Real[<16>])mat2.m;
|
||||
|
||||
fn Matrix4x4 look_at(Real[<3>] eye, Real[<3>] target, Real[<3>] up) => matrix_look_at(Matrix4x4, eye, target, up);
|
||||
|
||||
101
lib/std/math/quaternion.c3
Normal file
101
lib/std/math/quaternion.c3
Normal file
@@ -0,0 +1,101 @@
|
||||
module std::math;
|
||||
|
||||
// Predefined quaternion aliases.
|
||||
|
||||
alias Quaternionf = Quaternion {float};
|
||||
alias Quaternion = Quaternion {double};
|
||||
alias QUATERNION_IDENTITY @builtin = quaternion::IDENTITY {double};
|
||||
alias QUATERNIONF_IDENTITY @builtin = quaternion::IDENTITY {float};
|
||||
|
||||
<*
|
||||
The generic quaternion module, for float or double based quaternion definitions.
|
||||
|
||||
@require Real.kindof == FLOAT : "A quaternion must use a floating type"
|
||||
*>
|
||||
|
||||
module std::math::quaternion {Real};
|
||||
import std::math::vector;
|
||||
union Quaternion
|
||||
{
|
||||
struct
|
||||
{
|
||||
Real i, j, k, l;
|
||||
}
|
||||
Real[<4>] v;
|
||||
}
|
||||
|
||||
const Quaternion IDENTITY = { 0, 0, 0, 1 };
|
||||
|
||||
macro Quaternion Quaternion.add(self, Quaternion b) @operator(+) => { .v = self.v + b.v };
|
||||
macro Quaternion Quaternion.add_each(self, Real b) => { .v = self.v + b };
|
||||
macro Quaternion Quaternion.sub(self, Quaternion b) @operator(-) => { .v = self.v - b.v };
|
||||
macro Quaternion Quaternion.negate(self) @operator(-) => { .v = -self.v };
|
||||
macro Quaternion Quaternion.sub_each(self, Real b) => { .v = self.v - b };
|
||||
macro Quaternion Quaternion.scale(self, Real s) @operator_s(*) => { .v = self.v * s };
|
||||
macro Quaternion Quaternion.normalize(self) => { .v = self.v.normalize() };
|
||||
macro Real Quaternion.length(self) => self.v.length();
|
||||
macro Quaternion Quaternion.lerp(self, Quaternion q2, Real amount) => { .v = self.v.lerp(q2.v, amount) };
|
||||
macro Matrix4f Quaternion.to_matrixf(&self) => into_matrix(self, Matrix4f);
|
||||
macro Matrix4 Quaternion.to_matrix(&self) => into_matrix(self, Matrix4);
|
||||
fn Quaternion Quaternion.nlerp(self, Quaternion q2, Real amount) => { .v = self.v.lerp(q2.v, amount).normalize() };
|
||||
|
||||
fn Quaternion Quaternion.invert(self)
|
||||
{
|
||||
Real length_sq = self.v.dot(self.v);
|
||||
if (length_sq <= 0) return self;
|
||||
Real inv_length = 1 / length_sq;
|
||||
return { self.v[0] * -inv_length, self.v[1] * -inv_length, self.v[2] * -inv_length, self.v[3] * inv_length };
|
||||
}
|
||||
|
||||
fn Quaternion Quaternion.slerp(self, Quaternion q2, Real amount)
|
||||
{
|
||||
Quaternion result = {};
|
||||
|
||||
Real[<4>] q2v = q2.v;
|
||||
Real cos_half_theta = self.v.dot(q2v);
|
||||
|
||||
if (cos_half_theta < 0)
|
||||
{
|
||||
q2v = -q2v;
|
||||
cos_half_theta = -cos_half_theta;
|
||||
}
|
||||
|
||||
if (cos_half_theta >= 1) return self;
|
||||
|
||||
Real[<4>] q1v = self.v;
|
||||
if (cos_half_theta > 0.95f) return { .v = q1v.lerp(q2v, amount) };
|
||||
|
||||
Real half_theta = math::cos(cos_half_theta);
|
||||
Real sin_half_theta = math::sqrt(1 - cos_half_theta * cos_half_theta);
|
||||
if (math::abs(sin_half_theta) < 0.001f)
|
||||
{
|
||||
return { .v = (q1v + q2v) * 0.5f };
|
||||
}
|
||||
Real ratio_a = math::sin((1 - amount) * half_theta) / sin_half_theta;
|
||||
Real ratio_b = math::sin(amount * half_theta) / sin_half_theta;
|
||||
return { .v = q1v * ratio_a + q2v * ratio_b };
|
||||
}
|
||||
|
||||
fn Quaternion Quaternion.mul(self, Quaternion b) @operator(*)
|
||||
{
|
||||
return { self.i * b.l + self.l * b.i + self.j * b.k - self.k * b.j,
|
||||
self.j * b.l + self.l * b.j + self.k * b.i - self.i * b.k,
|
||||
self.k * b.l + self.l * b.k + self.i * b.j - self.j * b.i,
|
||||
self.l * b.l - self.i * b.i - self.j * self.j - self.k * self.k };
|
||||
}
|
||||
|
||||
macro into_matrix(Quaternion* q, $Type) @private
|
||||
{
|
||||
Quaternion rotation = q.normalize();
|
||||
var x = rotation.i;
|
||||
var y = rotation.j;
|
||||
var z = rotation.k;
|
||||
var w = rotation.l;
|
||||
|
||||
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,
|
||||
};
|
||||
}
|
||||
@@ -118,7 +118,7 @@ macro bool next_bool(random)
|
||||
macro float next_float(random)
|
||||
{
|
||||
uint val = random.next_int() & (1 << 24 - 1);
|
||||
return val / (float)(1 << 24);
|
||||
return val * 0x1.0p-24f;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -31,4 +31,4 @@ fn char SimpleRandom.next_byte(&self) @dynamic => (char)self.next_int();
|
||||
|
||||
const long SIMPLE_RANDOM_MULTIPLIER @local = 0x5DEECE66D;
|
||||
const long SIMPLE_RANDOM_ADDEND @local = 0xB;
|
||||
const long SIMPLE_RANDOM_MASK @local = (1u64 << 48) - 1;
|
||||
const long SIMPLE_RANDOM_MASK @local = (1UL << 48) - 1;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::math;
|
||||
module std::math::math_rt;
|
||||
|
||||
fn int128 __divti3(int128 a, int128 b) @extern("__divti3") @weak @nostrip
|
||||
{
|
||||
@@ -312,21 +312,21 @@ macro float_from_i128($Type, a) @private
|
||||
$switch $Type:
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const MANT_DIG = DOUBLE_MANT_DIG;
|
||||
const MANT_DIG = math::DOUBLE_MANT_DIG;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
const EXP_BIAS = 1023;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
|
||||
const SIGN_BIT = 1u64 << 63;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFUL;
|
||||
const SIGN_BIT = 1UL << 63;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const MANT_DIG = FLOAT_MANT_DIG;
|
||||
const MANT_DIG = math::FLOAT_MANT_DIG;
|
||||
const EXP_BIAS = 127;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
const MANTISSA_MASK = 0x7F_FFFFu32;
|
||||
const SIGN_BIT = 1u32 << 31;
|
||||
const MANTISSA_MASK = 0x7F_FFFFU;
|
||||
const SIGN_BIT = 1U << 31;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const MANT_DIG = HALF_MANT_DIG;
|
||||
const MANT_DIG = math::HALF_MANT_DIG;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const MANT_DIG = QUAD_MANT_DIG;
|
||||
@@ -352,7 +352,7 @@ macro float_from_i128($Type, a) @private
|
||||
a |= (uint128)((a & 4) != 0);
|
||||
a++;
|
||||
a >>= 2;
|
||||
if (a & (1i128 << MANT_DIG))
|
||||
if (a & (1LL << MANT_DIG))
|
||||
{
|
||||
a >>= 1;
|
||||
e++;
|
||||
@@ -371,22 +371,22 @@ macro float_from_u128($Type, a) @private
|
||||
$switch $Type:
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const MANT_DIG = DOUBLE_MANT_DIG;
|
||||
const MANT_DIG = math::DOUBLE_MANT_DIG;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
const EXP_BIAS = 1023;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFUL;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const MANT_DIG = FLOAT_MANT_DIG;
|
||||
const MANT_DIG = math::FLOAT_MANT_DIG;
|
||||
const EXP_BIAS = 127;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
const MANTISSA_MASK = 0x7F_FFFFu32;
|
||||
const MANTISSA_MASK = 0x7F_FFFFU;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const MANT_DIG = HALF_MANT_DIG;
|
||||
const MANT_DIG = math::HALF_MANT_DIG;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const MANT_DIG = QUAD_MANT_DIG;
|
||||
const MANT_DIG = math::QUAD_MANT_DIG;
|
||||
$endswitch
|
||||
if (a == 0) return ($Type)0;
|
||||
int sd = 128 - (int)$$clz(a); // digits
|
||||
@@ -406,7 +406,7 @@ macro float_from_u128($Type, a) @private
|
||||
a |= (uint128)((a & 4) != 0);
|
||||
a++;
|
||||
a >>= 2;
|
||||
if (a & (1i128 << MANT_DIG))
|
||||
if (a & (1LL << MANT_DIG))
|
||||
{
|
||||
a >>= 1;
|
||||
e++;
|
||||
@@ -458,8 +458,8 @@ macro fixuint(a) @private
|
||||
int sign = rep & SIGN_BIT ? -1 : 1;
|
||||
int exponent = (int)((abs >> SIGNIFICANT_BITS) - EXPONENT_BIAS);
|
||||
$Rep significand = (abs & SIGNIFICANT_MASK) | IMPLICIT_BIT;
|
||||
if (sign == -1 || exponent < 0) return 0u128;
|
||||
if ((uint)exponent >= uint128.sizeof * 8) return ~0u128;
|
||||
if (sign == -1 || exponent < 0) return 0ULL;
|
||||
if ((uint)exponent >= uint128.sizeof * 8) return ~0ULL;
|
||||
if (exponent < SIGNIFICANT_BITS) return (uint128)significand >> (SIGNIFICANT_BITS - exponent);
|
||||
return (uint128)significand << (exponent - SIGNIFICANT_BITS);
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
module std::math;
|
||||
module std::math::math_rt;
|
||||
|
||||
fn float __roundevenf(float f) @extern("roundevenf") @weak @nostrip
|
||||
{
|
||||
// Slow implementation
|
||||
return round(f / 2) * 2;
|
||||
return math::round(f / 2) * 2;
|
||||
}
|
||||
|
||||
fn double __roundeven(double d) @extern("roundeven") @weak @nostrip
|
||||
{
|
||||
// Slow implementation
|
||||
return round(d / 2) * 2;
|
||||
return math::round(d / 2) * 2;
|
||||
}
|
||||
|
||||
fn double __powidf2(double a, int b) @extern("__powidf2") @weak @nostrip
|
||||
@@ -1,3 +1,5 @@
|
||||
// Vector supplemental methods
|
||||
|
||||
module std::math::vector;
|
||||
import std::math;
|
||||
|
||||
@@ -51,6 +53,8 @@ fn double[<3>] double[<3>].unproject(self, Matrix4 projection, Matrix4 view) =>
|
||||
fn void ortho_normalize(float[<3>]* v1, float[<3>]* v2) => ortho_normalize3(v1, v2);
|
||||
fn void ortho_normalized(double[<3>]* v1, double[<3>]* v2) => ortho_normalize3(v1, v2);
|
||||
|
||||
// -- private helpers
|
||||
|
||||
macro towards(v, target, max_distance) @private
|
||||
{
|
||||
var delta = target - v;
|
||||
@@ -80,7 +84,7 @@ macro rotate(v, angle) @private
|
||||
{
|
||||
var c = math::cos(angle);
|
||||
var s = math::sin(angle);
|
||||
return $typeof(v) { v[0] * c - v[1] * s, v[0] * s + v[1] * c };
|
||||
return ($typeof(v)) { v[0] * c - v[1] * s, v[0] * s + v[1] * c };
|
||||
}
|
||||
|
||||
macro perpendicular3(v) @private
|
||||
@@ -111,7 +115,7 @@ macro cross3(v1, v2) @private
|
||||
|
||||
macro transform2(v, mat) @private
|
||||
{
|
||||
return $typeof(v) {
|
||||
return ($typeof(v)) {
|
||||
mat.m00 * v[0] + mat.m10 * v[1] + mat.m30 ,
|
||||
mat.m01 * v[0] + mat.m11 * v[1] + mat.m31 };
|
||||
}
|
||||
@@ -1,406 +0,0 @@
|
||||
module std::net::http;
|
||||
/*
|
||||
enum HttpStatus
|
||||
{
|
||||
PENDING,
|
||||
COMPLETED,
|
||||
FAILED
|
||||
}
|
||||
|
||||
struct Http
|
||||
{
|
||||
HttpStatus status;
|
||||
int status_code;
|
||||
String reason;
|
||||
String content_type;
|
||||
String response_data;
|
||||
}
|
||||
|
||||
fn Http* http_get(String url, Allocator using = allocator::temp())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
fn Http* http_post(String url, Allocator using = allocator::temp())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
fn void Http.destroy(Http* this)
|
||||
{
|
||||
}
|
||||
|
||||
fn HttpStatus Http.process(Http* this)
|
||||
{
|
||||
unreachable();
|
||||
}
|
||||
|
||||
// Common across implementations
|
||||
|
||||
struct HttpInternal @private
|
||||
{
|
||||
inline Http http;
|
||||
Allocator allocator;
|
||||
int connect_pending;
|
||||
int request_sent;
|
||||
char[256] address;
|
||||
char[256] request_header;
|
||||
char* request_header_large;
|
||||
String request_data;
|
||||
char[1024] reason_phrase;
|
||||
char[256] content_type;
|
||||
|
||||
usz data_size;
|
||||
usz data_capacity;
|
||||
void* data;
|
||||
}
|
||||
|
||||
fn String? parse_url(String url, String* port, String* resource) @private
|
||||
{
|
||||
if (url[:7] != "http://") return NetError.INVALID_URL?;
|
||||
url = url[7..];
|
||||
|
||||
usz end_index = url.index_of(":") ?? url.index_of("/") ?? url.len;
|
||||
String address = url[:end_index];
|
||||
String end_part = url[end_index..];
|
||||
if (!end_part.len)
|
||||
{
|
||||
*port = "80";
|
||||
*resource = {};
|
||||
return address;
|
||||
}
|
||||
switch (end_part[0])
|
||||
{
|
||||
case ':':
|
||||
end_index = end_part.index_of("/") ?? end_part.len;
|
||||
end_part[:end_index].to_uint() ?? NetError.INVALID_URL?!;
|
||||
*port = end_part[:end_index];
|
||||
*resource = url[end_index..];
|
||||
case '/':
|
||||
*port = "80";
|
||||
*resource = end_part;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
fn Socket? http_internal_connect(String address, uint port) @private
|
||||
{
|
||||
return tcp::connect_async(address, port)!;
|
||||
}
|
||||
|
||||
fn HttpInternal* http_internal_create(usz request_data_size, Allocator allocator) @private
|
||||
{
|
||||
HttpInternal* internal = allocator.alloc(HttpInternal.sizeof + request_data_size)!!;
|
||||
internal.status = PENDING;
|
||||
internal.status_code = 0;
|
||||
internal.response_data = {};
|
||||
internal.allocator = allocator;
|
||||
internal.connect_pending = 1;
|
||||
internal.request_sent = 0;
|
||||
// internal.reason = "";
|
||||
// internal.content_type = "";
|
||||
internal.data_size = 0;
|
||||
internal.data_capacity = 64 * 1024;
|
||||
internal.data = allocator.alloc(internal.data_capacity)!!;
|
||||
internal.request_data = {};
|
||||
return internal;
|
||||
}
|
||||
|
||||
fn Http*? http_get(String url, Allocator allocator = allocator::temp())
|
||||
{
|
||||
$if env::WIN32:
|
||||
int[1024] wsa_data;
|
||||
if (_wsa_startup(1, &wsa_data) != 0) return NetError.GENERAL_ERROR?;
|
||||
$endif
|
||||
uint port;
|
||||
String resource;
|
||||
String address = parse_url(url, &port, &resource)?;
|
||||
Socket socket = tcp::connect(address, port)?;
|
||||
HttpInternal* internal = http_internal_create(0, allocator);
|
||||
internal.socket = socket;
|
||||
|
||||
char* request_header;
|
||||
usz request_header_len = 64 + resource.len + address.len + port.len;
|
||||
if (request_header_len < sizeof(internal.request_header))
|
||||
{
|
||||
internal.request_header_large = null;
|
||||
request_header = internal.request_header;
|
||||
}
|
||||
else
|
||||
{
|
||||
internal.request_header_large = (char*)allocator.malloc(request_header_len + 1);
|
||||
request_header = internal.request_header_large;
|
||||
}
|
||||
int default_http_port = port == "80";
|
||||
sprintf( request_header, "GET %s HTTP/1.0\r\nHost: %s%s%s\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port );
|
||||
|
||||
return internal;
|
||||
}
|
||||
|
||||
fn Http*? http_post(String url, Allocator allocator = allocator::temp())
|
||||
{
|
||||
$if env::OS_TYPE == OsType::WIN32:
|
||||
int[1024] wsa_data;
|
||||
if (_wsa_startup(1, &wsa_data) != 0) return NetError.GENERAL_ERROR?;
|
||||
$endif
|
||||
String port;
|
||||
String resource;
|
||||
String address = parse_url(url, &port, &resource)?;
|
||||
Socket socket = http_internal_connect(address, port)?;
|
||||
HttpInternal* internal = http_internal_create(0, allocator);
|
||||
internal.socket = socket;
|
||||
|
||||
char* request_header;
|
||||
uz request_header_len = 64 + resource.len + address.len + port.len;
|
||||
if (request_header_len < sizeof(internal.request_header))
|
||||
{
|
||||
internal.request_header_large = null;
|
||||
request_header = internal.request_header;
|
||||
}
|
||||
else
|
||||
{
|
||||
internal.request_header_large = (char*)allocator.malloc(request_header_len + 1);
|
||||
request_header = internal.request_header_large;
|
||||
}
|
||||
int default_http_port = port == "80";
|
||||
sprintf( request_header, "POST %s HTTP/1.0\r\nHost: %s%s%s\r\nContent-Length: %d\r\n\r\n", resource, address, default_http_port ? "" : ":", default_http_port ? "" : port,
|
||||
(int) size );
|
||||
|
||||
internal->request_data_size = size;
|
||||
internal->request_data = ( internal + 1 );
|
||||
memcpy( internal->request_data, data, size );
|
||||
|
||||
return internal;
|
||||
}
|
||||
|
||||
fn HttpStatus Http.process(Http* http)
|
||||
{
|
||||
HttpInternal* internal = (HttpInternal*)http;
|
||||
if (http.status == HttpStatus.FAILED) return http.status;
|
||||
if (internal.connect_pending)
|
||||
{
|
||||
fd_set sockets_to_check;
|
||||
FD_ZERO(&sockets_to_check);
|
||||
FD_SET( internal->socket, &sockets_to_check );
|
||||
struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
|
||||
// check if socket is ready for send
|
||||
if( select( (int)( internal->socket + 1 ), NULL, &sockets_to_check, NULL, &timeout ) == 1 )
|
||||
{
|
||||
int opt = -1;
|
||||
socklen_t len = sizeof( opt );
|
||||
if( getsockopt( internal->socket, SOL_SOCKET, SO_ERROR, (char*)( &opt ), &len) >= 0 && opt == 0 )
|
||||
{
|
||||
internal->connect_pending = 0; // if it is, we're connected
|
||||
}
|
||||
}
|
||||
}
|
||||
if (internal.connect_pending) retur http.status;
|
||||
if (!internal.request_sent)
|
||||
{
|
||||
char* request_header = internal->request_header_large ?
|
||||
internal.request_header_large : internal.request_header;
|
||||
if (send(internal.socket, request_header, (int) strlen( request_header ), 0) == -1)
|
||||
{
|
||||
return http.status = FAILED;
|
||||
}
|
||||
if (internal.request_data_size)
|
||||
{
|
||||
int res = send(internal.socket, (char const*)internal->request_data, (int) internal->request_data_size, 0 );
|
||||
if (res == -1)
|
||||
{
|
||||
http.status = HTTP_STATUS_FAILED;
|
||||
return http.status;
|
||||
}
|
||||
}
|
||||
internal.request_sent = 1;
|
||||
return http.status;
|
||||
}
|
||||
// check if socket is ready for recv
|
||||
fd_set sockets_to_check;
|
||||
FD_ZERO( &sockets_to_check );
|
||||
#pragma warning( push )
|
||||
#pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect
|
||||
FD_SET( internal->socket, &sockets_to_check );
|
||||
#pragma warning( pop )
|
||||
struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
|
||||
}
|
||||
http_status_t http_process( http_t* http )
|
||||
{
|
||||
|
||||
|
||||
while( select( (int)( internal->socket + 1 ), &sockets_to_check, NULL, NULL, &timeout ) == 1 )
|
||||
{
|
||||
char buffer[ 4096 ];
|
||||
int size = recv( internal->socket, buffer, sizeof( buffer ), 0 );
|
||||
if( size == -1 )
|
||||
{
|
||||
http->status = HTTP_STATUS_FAILED;
|
||||
return http->status;
|
||||
}
|
||||
else if( size > 0 )
|
||||
{
|
||||
size_t min_size = internal->data_size + size + 1;
|
||||
if( internal->data_capacity < min_size )
|
||||
{
|
||||
internal->data_capacity *= 2;
|
||||
if( internal->data_capacity < min_size ) internal->data_capacity = min_size;
|
||||
void* new_data = HTTP_MALLOC( memctx, internal->data_capacity );
|
||||
memcpy( new_data, internal->data, internal->data_size );
|
||||
HTTP_FREE( memctx, internal->data );
|
||||
internal->data = new_data;
|
||||
}
|
||||
memcpy( (void*)( ( (uintptr_t) internal->data ) + internal->data_size ), buffer, (size_t) size );
|
||||
internal->data_size += size;
|
||||
}
|
||||
else if( size == 0 )
|
||||
{
|
||||
char const* status_line = (char const*) internal->data;
|
||||
|
||||
int header_size = 0;
|
||||
char const* header_end = strstr( status_line, "\r\n\r\n" );
|
||||
if( header_end )
|
||||
{
|
||||
header_end += 4;
|
||||
header_size = (int)( header_end - status_line );
|
||||
}
|
||||
else
|
||||
{
|
||||
http->status = HTTP_STATUS_FAILED;
|
||||
return http->status;
|
||||
}
|
||||
|
||||
// skip http version
|
||||
status_line = strchr( status_line, ' ' );
|
||||
if( !status_line )
|
||||
{
|
||||
http->status = HTTP_STATUS_FAILED;
|
||||
return http->status;
|
||||
}
|
||||
++status_line;
|
||||
|
||||
// extract status code
|
||||
char status_code[ 16 ];
|
||||
char const* status_code_end = strchr( status_line, ' ' );
|
||||
if( !status_code_end )
|
||||
{
|
||||
http->status = HTTP_STATUS_FAILED;
|
||||
return http->status;
|
||||
}
|
||||
memcpy( status_code, status_line, (size_t)( status_code_end - status_line ) );
|
||||
status_code[ status_code_end - status_line ] = 0;
|
||||
status_line = status_code_end + 1;
|
||||
http->status_code = atoi( status_code );
|
||||
|
||||
// extract reason phrase
|
||||
char const* reason_phrase_end = strstr( status_line, "\r\n" );
|
||||
if( !reason_phrase_end )
|
||||
{
|
||||
http->status = HTTP_STATUS_FAILED;
|
||||
return http->status;
|
||||
}
|
||||
size_t reason_phrase_len = (size_t)( reason_phrase_end - status_line );
|
||||
if( reason_phrase_len >= sizeof( internal->reason_phrase ) )
|
||||
reason_phrase_len = sizeof( internal->reason_phrase ) - 1;
|
||||
memcpy( internal->reason_phrase, status_line, reason_phrase_len );
|
||||
internal->reason_phrase[ reason_phrase_len ] = 0;
|
||||
status_line = reason_phrase_end + 1;
|
||||
|
||||
// extract content type
|
||||
char const* content_type_start = strstr( status_line, "Content-Type: " );
|
||||
if( content_type_start )
|
||||
{
|
||||
content_type_start += strlen( "Content-Type: " );
|
||||
char const* content_type_end = strstr( content_type_start, "\r\n" );
|
||||
if( content_type_end )
|
||||
{
|
||||
size_t content_type_len = (size_t)( content_type_end - content_type_start );
|
||||
if( content_type_len >= sizeof( internal->content_type ) )
|
||||
content_type_len = sizeof( internal->content_type ) - 1;
|
||||
memcpy( internal->content_type, content_type_start, content_type_len );
|
||||
internal->content_type[ content_type_len ] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
http->status = http->status_code < 300 ? HTTP_STATUS_COMPLETED : HTTP_STATUS_FAILED;
|
||||
http->response_data = (void*)( ( (uintptr_t) internal->data ) + header_size );
|
||||
http->response_size = internal->data_size - header_size;
|
||||
|
||||
// add an extra zero after the received data, but don't modify the size, so ascii results can be used as
|
||||
// a zero terminated string. the size returned will be the string without this extra zero terminator.
|
||||
( (char*)http->response_data )[ http->response_size ] = 0;
|
||||
return http->status;
|
||||
}
|
||||
}
|
||||
|
||||
return http->status;
|
||||
}
|
||||
|
||||
|
||||
void http_release( http_t* http )
|
||||
{
|
||||
http_internal_t* internal = (http_internal_t*) http;
|
||||
#ifdef _WIN32
|
||||
closesocket( internal->socket );
|
||||
#else
|
||||
close( internal->socket );
|
||||
#endif
|
||||
|
||||
if( internal->request_header_large) HTTP_FREE( memctx, internal->request_header_large );
|
||||
HTTP_FREE( memctx, internal->data );
|
||||
HTTP_FREE( memctx, internal );
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#endif /* HTTP_IMPLEMENTATION */
|
||||
|
||||
/*
|
||||
revision history:
|
||||
1.0 first released version
|
||||
*/
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses - you may choose the one you like.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2016 Mattias Gustavsson
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/*/
|
||||
@@ -1,6 +1,5 @@
|
||||
module std::net;
|
||||
import std::io;
|
||||
import std::ascii;
|
||||
|
||||
enum IpProtocol : char (AIFamily ai_family)
|
||||
{
|
||||
|
||||
92
lib/std/net/os/android.c3
Normal file
92
lib/std/net/os/android.c3
Normal file
@@ -0,0 +1,92 @@
|
||||
module std::net::os @if(env::ANDROID);
|
||||
import libc;
|
||||
|
||||
const AIFamily PLATFORM_AF_AX25 = 3;
|
||||
const AIFamily PLATFORM_AF_IPX = 4;
|
||||
const AIFamily PLATFORM_AF_APPLETALK = 5;
|
||||
const AIFamily PLATFORM_AF_NETROM = 6;
|
||||
const AIFamily PLATFORM_AF_BRIDGE = 7;
|
||||
const AIFamily PLATFORM_AF_AAL5 = 8;
|
||||
const AIFamily PLATFORM_AF_X25 = 9;
|
||||
const AIFamily PLATFORM_AF_INET6 = 10;
|
||||
|
||||
const PLATFORM_O_NONBLOCK = 0o4000;
|
||||
|
||||
// https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/socket.h
|
||||
const int SOL_SOCKET = 1;
|
||||
|
||||
const int SO_DEBUG = 1; // turn on debugging info recording
|
||||
const int SO_REUSEADDR = 2; // allow local address reuse
|
||||
const int SO_TYPE = 3;
|
||||
const int SO_ERROR = 4;
|
||||
const int SO_DONTROUTE = 5; // just use interface addresses
|
||||
const int SO_BROADCAST = 6; // permit sending of broadcast msgs
|
||||
const int SO_SNDBUF = 7; // Send buffer size
|
||||
const int SO_RCVBUF = 8; // Receive buffer size
|
||||
const int SO_KEEPALIVE = 9; // keep connections alive
|
||||
const int SO_OOBINLINE = 10; // leave received OOB data in line
|
||||
const int SO_NO_CHECK = 11;
|
||||
const int SO_PRIORITY = 12;
|
||||
const int SO_LINGER = 13; // linger on close if data present (in ticks)
|
||||
const int SO_BSDCOMPAT = 14;
|
||||
const int SO_REUSEPORT = 15; // allow local address & port reuse
|
||||
const int SO_RCVLOWAT = 18;
|
||||
const int SO_SNDLOWAT = 19;
|
||||
const int SO_RCVTIMEO = 20; // IMPORTANT Verify before use
|
||||
const int SO_SNDTIMEO = 21; // IMPORTANT Verify before use
|
||||
const int SO_BINDTODEVICE = 25;
|
||||
const int SO_ATTACH_FILTER = 26;
|
||||
const int SO_DETACH_FILTER = 27;
|
||||
const int SO_PEERNAME = 28;
|
||||
const int SO_TIMESTAMP = 29; // IMPORTANT Verify before use timestamp received dgram traffic
|
||||
const int SO_ACCEPTCONN = 30;
|
||||
const int SO_PEERSEC = 31;
|
||||
const int SO_SNDBUFFORCE = 32;
|
||||
const int SO_RCVBUFFORCE = 33;
|
||||
const int SO_PASSSEC = 34;
|
||||
const int SO_MARK = 36;
|
||||
const int SO_PROTOCOL = 38;
|
||||
const int SO_DOMAIN = 39;
|
||||
const int SO_RXQ_OVFL = 40;
|
||||
const int SO_WIFI_STATUS = 41;
|
||||
const int SO_PEEK_OFF = 42;
|
||||
const int SO_NOFCS = 43;
|
||||
const int SO_LOCK_FILTER = 44;
|
||||
const int SO_SELECT_ERR_QUEUE = 45;
|
||||
const int SO_BUSY_POLL = 46;
|
||||
const int SO_MAX_PACING_RATE = 47;
|
||||
const int SO_BPF_EXTENSIONS = 48;
|
||||
const int SO_INCOMING_CPU = 49;
|
||||
const int SO_ATTACH_BPF = 50;
|
||||
const int SO_ATTACH_REUSEPORT_CBPF = 51;
|
||||
const int SO_ATTACH_REUSEPORT_EBPF = 52;
|
||||
const int SO_CNX_ADVICE = 53;
|
||||
const int SO_MEMINFO = 55;
|
||||
const int SO_INCOMING_NAPI_ID = 56;
|
||||
const int SO_COOKIE = 57;
|
||||
const int SO_PEERGROUPS = 59;
|
||||
const int SO_ZEROCOPY = 60;
|
||||
const int SO_TXTIME = 61;
|
||||
const int SO_BINDTOIFINDEX = 62;
|
||||
const int SO_DETACH_REUSEPORT_BPF = 68;
|
||||
const int SO_PREFER_BUSY_POLL = 69;
|
||||
const int SO_BUSY_POLL_BUDGET = 70;
|
||||
const int SO_NETNS_COOKIE = 71;
|
||||
const int SO_BUF_LOCK = 72;
|
||||
const int SO_RESERVE_MEM = 73;
|
||||
const int SO_TXREHASH = 74;
|
||||
const int SO_RCVMARK = 75;
|
||||
const int SO_PASSPIDFD = 76;
|
||||
const int SO_PEERPIDFD = 77;
|
||||
|
||||
const CUShort POLLRDNORM = 0x0040;
|
||||
const CUShort POLLRDBAND = 0x0080;
|
||||
const CUShort POLLWRNORM = 0x0100;
|
||||
const CUShort POLLWRBAND = 0x0200;
|
||||
const CUShort POLLMSG = 0x0400;
|
||||
const CUShort POLLREMOVE = 0x1000;
|
||||
const CUShort POLLRDHUP = 0x2000;
|
||||
const CUShort POLLFREE = 0x4000;
|
||||
const CUShort POLL_BUSY_LOOP = 0x8000;
|
||||
|
||||
const CInt MSG_PEEK = 0x0002;
|
||||
@@ -1,5 +1,5 @@
|
||||
module std::net::os;
|
||||
const bool SUPPORTS_INET = env::LIBC && (env::WIN32 || env::DARWIN || env::LINUX);
|
||||
const bool SUPPORTS_INET = env::LIBC && (env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID);
|
||||
|
||||
typedef AIFamily = CInt;
|
||||
typedef AIProtocol = CInt;
|
||||
@@ -18,7 +18,7 @@ struct AddrInfo
|
||||
AISockType ai_socktype;
|
||||
AIProtocol ai_protocol;
|
||||
Socklen_t ai_addrlen;
|
||||
struct @if(env::WIN32 || env::DARWIN)
|
||||
struct @if(env::WIN32 || env::DARWIN || env::ANDROID)
|
||||
{
|
||||
ZString ai_canonname;
|
||||
SockAddrPtr ai_addr;
|
||||
@@ -58,7 +58,7 @@ extern fn void freeaddrinfo(AddrInfo* res) @if(SUPPORTS_INET);
|
||||
extern fn CInt setsockopt(NativeSocket socket, CInt level, CInt optname, void* optval, Socklen_t optlen) @if(SUPPORTS_INET);
|
||||
extern fn CInt getsockopt(NativeSocket socket, CInt level, CInt optname, void* optval, Socklen_t optlen) @if(SUPPORTS_INET);
|
||||
|
||||
module std::net::os @if(!env::LIBC || !(env::WIN32 || env::DARWIN || env::LINUX));
|
||||
module std::net::os @if(!env::LIBC || !(env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID));
|
||||
|
||||
const AIFamily PLATFORM_AF_INET6 = 0;
|
||||
const AIFamily PLATFORM_AF_IPX = 0;
|
||||
|
||||
@@ -88,7 +88,7 @@ fn fault convert_error(WSAError error)
|
||||
|
||||
fn fault socket_error()
|
||||
{
|
||||
return convert_error(win32_WSAGetLastError());
|
||||
return convert_error(win32::wsaGetLastError());
|
||||
}
|
||||
|
||||
const CUShort POLLIN = win32::POLLIN;
|
||||
|
||||
@@ -69,7 +69,7 @@ fn ulong? poll_ms(Poll[] polls, long timeout_ms)
|
||||
{
|
||||
if (timeout_ms > CInt.max) timeout_ms = CInt.max;
|
||||
$if env::WIN32:
|
||||
CInt result = win32_WSAPoll((Win32_LPWSAPOLLFD)polls.ptr, (Win32_ULONG)polls.len, (CInt)timeout_ms);
|
||||
CInt result = win32::wsaPoll((Win32_LPWSAPOLLFD)polls.ptr, (Win32_ULONG)polls.len, (CInt)timeout_ms);
|
||||
$else
|
||||
CInt result = os::poll((Posix_pollfd*)polls.ptr, (Posix_nfds_t)polls.len, (CInt)timeout_ms);
|
||||
$endif
|
||||
|
||||
@@ -23,7 +23,7 @@ fn bool last_error_is_delayed_connect()
|
||||
{
|
||||
$switch:
|
||||
$case env::WIN32:
|
||||
switch (win32_WSAGetLastError())
|
||||
switch (win32::wsaGetLastError())
|
||||
{
|
||||
case wsa::EWOULDBLOCK:
|
||||
case wsa::EINPROGRESS: return true;
|
||||
|
||||
2
lib/std/os/freebsd/general.c3
Normal file
2
lib/std/os/freebsd/general.c3
Normal file
@@ -0,0 +1,2 @@
|
||||
module std::os::freebsd @if(env::FREEBSD);
|
||||
|
||||
@@ -67,6 +67,17 @@ struct Darwin_segment_command_64
|
||||
uint flags; /* flags */
|
||||
}
|
||||
|
||||
struct Darwin_mach_timebase_info
|
||||
{
|
||||
uint numer;
|
||||
uint denom;
|
||||
}
|
||||
|
||||
alias Darwin_mach_timebase_info_t = Darwin_mach_timebase_info;
|
||||
alias Darwin_mach_timebase_info_data_t = Darwin_mach_timebase_info;
|
||||
|
||||
extern fn void mach_timebase_info(Darwin_mach_timebase_info_data_t* timebase);
|
||||
extern fn ulong mach_absolute_time();
|
||||
|
||||
fn String? executable_path(Allocator allocator)
|
||||
{
|
||||
@@ -155,3 +166,4 @@ fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
|
||||
};
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
2
lib/std/os/macos/general.c3
Normal file
2
lib/std/os/macos/general.c3
Normal file
@@ -0,0 +1,2 @@
|
||||
module std::os::darwin @if(env::DARWIN);
|
||||
|
||||
1
lib/std/os/netbsd/general.c3
Normal file
1
lib/std/os/netbsd/general.c3
Normal file
@@ -0,0 +1 @@
|
||||
module std::os::netbsd @if(env::NETBSD);
|
||||
2
lib/std/os/openbsd/general.c3
Normal file
2
lib/std/os/openbsd/general.c3
Normal file
@@ -0,0 +1,2 @@
|
||||
module std::os::openbsd @if(env::OPENBSD);
|
||||
|
||||
@@ -4,7 +4,7 @@ import libc;
|
||||
<*
|
||||
Exit the process with a given exit code. This will typically call 'exit' in LibC
|
||||
*>
|
||||
fn void exit(int result, bool cleanup = true) @weak
|
||||
fn void exit(int result) @weak @noreturn
|
||||
{
|
||||
$if env::LIBC:
|
||||
libc::exit(result);
|
||||
@@ -17,7 +17,7 @@ fn void exit(int result, bool cleanup = true) @weak
|
||||
Exit the process with a given exit code. This will typically call '_Exit' in LibC
|
||||
usually bypassing '@finalizer' functions.
|
||||
*>
|
||||
fn void fastexit(int result, bool cleanup = true) @weak
|
||||
fn void fastexit(int result) @weak @noreturn
|
||||
{
|
||||
$if env::LIBC:
|
||||
libc::_exit(result);
|
||||
|
||||
68
lib/std/os/posix/clock.c3
Normal file
68
lib/std/os/posix/clock.c3
Normal file
@@ -0,0 +1,68 @@
|
||||
module std::os::posix @if(env::POSIX);
|
||||
import libc;
|
||||
|
||||
extern fn CInt clock_gettime(int type, TimeSpec *time);
|
||||
|
||||
module std::os::posix @if(env::OPENBSD);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 2;
|
||||
const CLOCK_MONOTONIC = 3;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 4;
|
||||
const CLOCK_UPTIME = 5;
|
||||
const CLOCK_BOOTTIME = 6;
|
||||
|
||||
module std::os::posix @if(env::FREEBSD);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_VIRTUAL = 1;
|
||||
const CLOCK_PROF = 2;
|
||||
const CLOCK_MONOTONIC = 4;
|
||||
const CLOCK_UPTIME = 5;
|
||||
const CLOCK_UPTIME_PRECISE = 7;
|
||||
const CLOCK_UPTIME_FAST = 8;
|
||||
const CLOCK_REALTIME_PRECISE = 9;
|
||||
const CLOCK_REALTIME_FAST = 10;
|
||||
const CLOCK_MONOTONIC_PRECISE = 11;
|
||||
const CLOCK_MONOTONIC_FAST = 12;
|
||||
const CLOCK_SECOND = 13;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 14;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 15;
|
||||
const CLOCK_BOOTTIME = CLOCK_UPTIME;
|
||||
const CLOCK_REALTIME_COARSE = CLOCK_REALTIME_FAST;
|
||||
const CLOCK_MONOTONIC_COARSE = CLOCK_MONOTONIC_FAST;
|
||||
|
||||
module std::os::posix @if(env::NETBSD);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_VIRTUAL = 1;
|
||||
const CLOCK_PROF = 2;
|
||||
const CLOCK_MONOTONIC = 3;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 0x20000000;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 0x40000000;
|
||||
|
||||
module std::os::posix @if(env::WASI);
|
||||
// Not implemented
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_MONOTONIC = 0;
|
||||
|
||||
module std::os::posix @if(env::DARWIN);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_MONOTONIC = 6;
|
||||
const CLOCK_MONOTONIC_RAW = 4;
|
||||
const CLOCK_MONOTONIC_RAW_APPROX = 5;
|
||||
const CLOCK_UPTIME_RAW = 8;
|
||||
const CLOCK_UPTIME_RAW_APPROX = 9;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 12;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 16;
|
||||
|
||||
module std::os::posix @if(env::LINUX || env::ANDROID);
|
||||
const CLOCK_REALTIME = 0;
|
||||
const CLOCK_MONOTONIC = 1;
|
||||
const CLOCK_PROCESS_CPUTIME_ID = 2;
|
||||
const CLOCK_THREAD_CPUTIME_ID = 3;
|
||||
const CLOCK_MONOTONIC_RAW = 4;
|
||||
const CLOCK_REALTIME_COARSE = 5;
|
||||
const CLOCK_MONOTONIC_COARSE = 6;
|
||||
const CLOCK_BOOTTIME = 7;
|
||||
const CLOCK_REALTIME_ALARM = 8;
|
||||
const CLOCK_BOOTTIME_ALARM = 9;
|
||||
const CLOCK_TAI = 11;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
module std::os::posix;
|
||||
module std::os::posix @if(env::POSIX);
|
||||
|
||||
extern ZString* environ;
|
||||
|
||||
|
||||
1
lib/std/os/posix/net.c3
Normal file
1
lib/std/os/posix/net.c3
Normal file
@@ -0,0 +1 @@
|
||||
module std::os::posix @if(env::POSIX);
|
||||
@@ -45,6 +45,8 @@ bitstruct SubProcessOptions : int
|
||||
// Note: this will **not** search for paths in any provided custom environment
|
||||
// and instead uses the PATH of the spawning process.
|
||||
bool search_user_path;
|
||||
// Inherit the parent's stdin, stdout, and stderr handles instead of creating pipes
|
||||
bool inherit_stdio;
|
||||
}
|
||||
|
||||
fn void? create_named_pipe_helper(void** rd, void **wr) @local @if(env::WIN32)
|
||||
@@ -115,22 +117,32 @@ fn WString convert_command_line_win32(String[] command_line) @inline @if(env::WI
|
||||
*>
|
||||
fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, String[] environment = {}) @if(env::WIN32)
|
||||
{
|
||||
void* rd, wr;
|
||||
Win32_DWORD flags = win32::CREATE_UNICODE_ENVIRONMENT;
|
||||
Win32_PROCESS_INFORMATION process_info;
|
||||
Win32_SECURITY_ATTRIBUTES sa_attr = { Win32_SECURITY_ATTRIBUTES.sizeof, null, 1 };
|
||||
Win32_STARTUPINFOW start_info = {
|
||||
.cb = Win32_STARTUPINFOW.sizeof,
|
||||
.dwFlags = win32::STARTF_USESTDHANDLES
|
||||
};
|
||||
|
||||
if (options.no_window) flags |= win32::CREATE_NO_WINDOW;
|
||||
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
// TODO defer catch
|
||||
if (!win32::setHandleInformation(wr, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
|
||||
// Only set STARTF_USESTDHANDLES if we're not inheriting stdio
|
||||
if (!options.inherit_stdio) start_info.dwFlags = win32::STARTF_USESTDHANDLES;
|
||||
|
||||
CFile stdin;
|
||||
CFile stdout;
|
||||
CFile stderr;
|
||||
void* rd = null;
|
||||
void* wr = null;
|
||||
|
||||
// Only create pipes if not inheriting stdio
|
||||
if (!options.inherit_stdio)
|
||||
{
|
||||
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
// TODO defer catch
|
||||
if (!win32::setHandleInformation(wr, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
}
|
||||
|
||||
@pool()
|
||||
{
|
||||
WString used_environment = null;
|
||||
@@ -149,39 +161,19 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
env.append("\0");
|
||||
used_environment = env.str_view().to_temp_wstring()!;
|
||||
}
|
||||
int fd = win32::_open_osfhandle((iptr)wr, 0);
|
||||
if (fd != -1)
|
||||
{
|
||||
stdin = win32::_fdopen(fd, "wb");
|
||||
if (!stdin) return FAILED_TO_OPEN_STDIN?;
|
||||
}
|
||||
start_info.hStdInput = rd;
|
||||
if (options.read_async)
|
||||
{
|
||||
create_named_pipe_helper(&rd, &wr)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
}
|
||||
if (!win32::setHandleInformation(rd, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
fd = win32::_open_osfhandle((iptr)rd, 0);
|
||||
if (fd != -1)
|
||||
{
|
||||
stdout = win32::_fdopen(fd, "rb");
|
||||
if (!stdout) return FAILED_TO_OPEN_STDOUT?;
|
||||
}
|
||||
|
||||
start_info.hStdOutput = wr;
|
||||
|
||||
do
|
||||
// Handle stdin pipe if not inheriting
|
||||
if (!options.inherit_stdio)
|
||||
{
|
||||
if (options.combined_stdout_stderr)
|
||||
int fd = win32::_open_osfhandle((iptr)wr, 0);
|
||||
if (fd != -1)
|
||||
{
|
||||
stderr = stdout;
|
||||
start_info.hStdError = start_info.hStdOutput;
|
||||
break;
|
||||
stdin = win32::_fdopen(fd, "wb");
|
||||
if (!stdin) return FAILED_TO_OPEN_STDIN?;
|
||||
}
|
||||
start_info.hStdInput = rd;
|
||||
|
||||
// Handle stdout pipe
|
||||
if (options.read_async)
|
||||
{
|
||||
create_named_pipe_helper(&rd, &wr)!;
|
||||
@@ -195,18 +187,49 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
fd = win32::_open_osfhandle((iptr)rd, 0);
|
||||
if (fd != -1)
|
||||
{
|
||||
stderr = win32::_fdopen(fd, "rb");
|
||||
if (!stderr) return FAILED_TO_OPEN_STDERR?;
|
||||
stdout = win32::_fdopen(fd, "rb");
|
||||
if (!stdout) return FAILED_TO_OPEN_STDOUT?;
|
||||
}
|
||||
start_info.hStdError = wr;
|
||||
};
|
||||
void *event_output;
|
||||
void *event_error;
|
||||
if (options.read_async)
|
||||
|
||||
start_info.hStdOutput = wr;
|
||||
|
||||
// Handle stderr pipe or combine with stdout
|
||||
do
|
||||
{
|
||||
if (options.combined_stdout_stderr)
|
||||
{
|
||||
stderr = stdout;
|
||||
start_info.hStdError = start_info.hStdOutput;
|
||||
break;
|
||||
}
|
||||
if (options.read_async)
|
||||
{
|
||||
create_named_pipe_helper(&rd, &wr)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!win32::createPipe(&rd, &wr, &sa_attr, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
}
|
||||
if (!win32::setHandleInformation(rd, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
|
||||
|
||||
fd = win32::_open_osfhandle((iptr)rd, 0);
|
||||
if (fd != -1)
|
||||
{
|
||||
stderr = win32::_fdopen(fd, "rb");
|
||||
if (!stderr) return FAILED_TO_OPEN_STDERR?;
|
||||
}
|
||||
start_info.hStdError = wr;
|
||||
};
|
||||
}
|
||||
|
||||
void *event_output = null;
|
||||
void *event_error = null;
|
||||
if (!options.inherit_stdio && options.read_async)
|
||||
{
|
||||
event_output = win32::createEventA(&sa_attr, 1, 1, null);
|
||||
event_error = win32::createEventA(&sa_attr, 1, 1, null);
|
||||
}
|
||||
|
||||
if (!win32::createProcessW(
|
||||
null,
|
||||
convert_command_line_win32(command_line),
|
||||
@@ -214,14 +237,17 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
null, // primary thread security attributes
|
||||
1, // handles are inherited
|
||||
flags, // creation flags
|
||||
used_environment, // environment
|
||||
used_environment, // environment
|
||||
null, // use parent dir
|
||||
&start_info, // startup info ptr
|
||||
&process_info)) return FAILED_TO_START_PROCESS?;
|
||||
};
|
||||
|
||||
// We don't need the handle of the primary thread in the called process.
|
||||
win32::closeHandle(process_info.hThread);
|
||||
if (start_info.hStdOutput)
|
||||
|
||||
// Close handles if not inheriting stdio
|
||||
if (!options.inherit_stdio && start_info.hStdOutput)
|
||||
{
|
||||
win32::closeHandle(start_info.hStdOutput);
|
||||
if (start_info.hStdOutput != start_info.hStdError) win32::closeHandle(start_info.hStdError);
|
||||
@@ -229,7 +255,7 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
|
||||
return {
|
||||
.hProcess = process_info.hProcess,
|
||||
.hStdInput = start_info.hStdInput,
|
||||
.hStdInput = options.inherit_stdio ? null : start_info.hStdInput,
|
||||
.stdin_file = stdin,
|
||||
.stdout_file = stdout,
|
||||
.stderr_file = stderr,
|
||||
@@ -280,27 +306,37 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
CInt[2] stdinfd;
|
||||
CInt[2] stdoutfd;
|
||||
CInt[2] stderrfd;
|
||||
|
||||
if (posix::pipe(&stdinfd)) return FAILED_TO_OPEN_STDIN?;
|
||||
if (posix::pipe(&stdoutfd)) return FAILED_TO_OPEN_STDOUT?;
|
||||
if (!options.combined_stdout_stderr && posix::pipe(&stderrfd)) return FAILED_TO_OPEN_STDERR?;
|
||||
CFile stdin = null;
|
||||
CFile stdout = null;
|
||||
CFile stderr = null;
|
||||
|
||||
Posix_spawn_file_actions_t actions;
|
||||
if (posix::spawn_file_actions_init(&actions)) return FAILED_TO_INITIALIZE_ACTIONS?;
|
||||
defer posix::spawn_file_actions_destroy(&actions);
|
||||
if (posix::spawn_file_actions_addclose(&actions, stdinfd[1])) return FAILED_TO_OPEN_STDIN?;
|
||||
if (posix::spawn_file_actions_adddup2(&actions, stdinfd[0], libc::STDIN_FD)) return FAILED_TO_OPEN_STDIN?;
|
||||
if (posix::spawn_file_actions_addclose(&actions, stdoutfd[0])) return FAILED_TO_OPEN_STDOUT?;
|
||||
if (posix::spawn_file_actions_adddup2(&actions, stdoutfd[1], libc::STDOUT_FD)) return FAILED_TO_OPEN_STDOUT?;
|
||||
if (options.combined_stdout_stderr)
|
||||
|
||||
// Only set up pipes if not inheriting stdio
|
||||
if (!options.inherit_stdio)
|
||||
{
|
||||
if (posix::spawn_file_actions_adddup2(&actions, libc::STDOUT_FD, libc::STDERR_FD)) return FAILED_TO_OPEN_STDERR?;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (posix::spawn_file_actions_addclose(&actions, stderrfd[0])) return FAILED_TO_OPEN_STDERR?;
|
||||
if (posix::spawn_file_actions_adddup2(&actions, stderrfd[1], libc::STDERR_FD)) return FAILED_TO_OPEN_STDERR?;
|
||||
if (posix::pipe(&stdinfd)) return FAILED_TO_OPEN_STDIN?;
|
||||
if (posix::pipe(&stdoutfd)) return FAILED_TO_OPEN_STDOUT?;
|
||||
if (!options.combined_stdout_stderr && posix::pipe(&stderrfd)) return FAILED_TO_OPEN_STDERR?;
|
||||
|
||||
if (posix::spawn_file_actions_addclose(&actions, stdinfd[1])) return FAILED_TO_OPEN_STDIN?;
|
||||
if (posix::spawn_file_actions_adddup2(&actions, stdinfd[0], libc::STDIN_FD)) return FAILED_TO_OPEN_STDIN?;
|
||||
if (posix::spawn_file_actions_addclose(&actions, stdoutfd[0])) return FAILED_TO_OPEN_STDOUT?;
|
||||
if (posix::spawn_file_actions_adddup2(&actions, stdoutfd[1], libc::STDOUT_FD)) return FAILED_TO_OPEN_STDOUT?;
|
||||
|
||||
if (options.combined_stdout_stderr)
|
||||
{
|
||||
if (posix::spawn_file_actions_adddup2(&actions, libc::STDOUT_FD, libc::STDERR_FD)) return FAILED_TO_OPEN_STDERR?;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (posix::spawn_file_actions_addclose(&actions, stderrfd[0])) return FAILED_TO_OPEN_STDERR?;
|
||||
if (posix::spawn_file_actions_adddup2(&actions, stderrfd[1], libc::STDERR_FD)) return FAILED_TO_OPEN_STDERR?;
|
||||
}
|
||||
}
|
||||
|
||||
Pid_t child;
|
||||
@pool()
|
||||
{
|
||||
@@ -315,21 +351,27 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
|
||||
if (posix::spawnp(&child, command_line_copy[0], &actions, null, command_line_copy, used_environment)) return FAILED_TO_START_PROCESS?;
|
||||
}
|
||||
};
|
||||
libc::close(stdinfd[0]);
|
||||
CFile stdin = libc::fdopen(stdinfd[1], "wb");
|
||||
libc::close(stdoutfd[1]);
|
||||
CFile stdout = libc::fdopen(stdoutfd[0], "rb");
|
||||
CFile stderr @noinit;
|
||||
do
|
||||
|
||||
// Only set up file handles if not inheriting stdio
|
||||
if (!options.inherit_stdio)
|
||||
{
|
||||
if (options.combined_stdout_stderr)
|
||||
libc::close(stdinfd[0]);
|
||||
stdin = libc::fdopen(stdinfd[1], "wb");
|
||||
libc::close(stdoutfd[1]);
|
||||
stdout = libc::fdopen(stdoutfd[0], "rb");
|
||||
|
||||
do
|
||||
{
|
||||
stderr = stdout;
|
||||
break;
|
||||
}
|
||||
libc::close(stderrfd[1]);
|
||||
stderr = libc::fdopen(stderrfd[0], "rb");
|
||||
};
|
||||
if (options.combined_stdout_stderr)
|
||||
{
|
||||
stderr = stdout;
|
||||
break;
|
||||
}
|
||||
libc::close(stderrfd[1]);
|
||||
stderr = libc::fdopen(stderrfd[0], "rb");
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
.stdin_file = stdin,
|
||||
.stdout_file = stdout,
|
||||
@@ -357,11 +399,13 @@ fn CInt? SubProcess.join(&self) @if(env::POSIX)
|
||||
|
||||
fn File SubProcess.stdout(&self)
|
||||
{
|
||||
if (!self.stdout_file) return (File){}; // Return an empty File struct
|
||||
return file::from_handle(self.stdout_file);
|
||||
}
|
||||
|
||||
fn File SubProcess.stderr(&self)
|
||||
{
|
||||
if (!self.stderr_file) return (File){}; // Return an empty File struct
|
||||
return file::from_handle(self.stderr_file);
|
||||
}
|
||||
|
||||
@@ -450,6 +494,8 @@ fn usz? read_from_file_posix(CFile file, char* buffer, usz size) @if(env::POSIX)
|
||||
|
||||
fn usz? SubProcess.read_stdout(&self, char* buffer, usz size)
|
||||
{
|
||||
if (!self.stdout_file) return 0; // No output available when inheriting stdio
|
||||
|
||||
$if env::WIN32:
|
||||
return read_from_file_win32(self.stdout_file, self.hEventOutput, buffer, size);
|
||||
$else
|
||||
@@ -457,8 +503,11 @@ fn usz? SubProcess.read_stdout(&self, char* buffer, usz size)
|
||||
$endif
|
||||
}
|
||||
|
||||
|
||||
fn usz? SubProcess.read_stderr(&self, char* buffer, usz size)
|
||||
{
|
||||
if (!self.stderr_file) return 0; // No output available when inheriting stdio
|
||||
|
||||
$if env::WIN32:
|
||||
return read_from_file_win32(self.stderr_file, self.hEventError, buffer, size);
|
||||
$else
|
||||
|
||||
6
lib/std/os/win32/clock.c3
Normal file
6
lib/std/os/win32/clock.c3
Normal file
@@ -0,0 +1,6 @@
|
||||
module std::os::win32 @if(env::WIN32);
|
||||
import std::math;
|
||||
|
||||
extern fn void getSystemTimeAsFileTime(Win32_FILETIME* time) @extern("GetSystemTimeAsFileTime");
|
||||
extern fn Win32_BOOL queryPerformanceFrequency(Win32_LARGE_INTEGER* lpFrequency) @extern("QueryPerformanceFrequency");
|
||||
extern fn Win32_BOOL queryPerformanceCounter(Win32_LARGE_INTEGER* lpPerformanceCount) @extern("QueryPerformanceCounter");
|
||||
@@ -1,5 +1,6 @@
|
||||
module std::os::win32 @if(env::WIN32);
|
||||
import libc;
|
||||
|
||||
enum Win32_GET_FILEEX_INFO_LEVELS
|
||||
{
|
||||
STANDARD,
|
||||
|
||||
@@ -222,4 +222,5 @@ const Win32_DWORD ERROR_MR_MID_NOT_FOUND = 0x13D;
|
||||
const Win32_DWORD ERROR_SCOPE_NOT_FOUND = 0x13E;
|
||||
const Win32_DWORD ERROR_UNDEFINED_SCOPE = 0x13F;
|
||||
const Win32_DWORD ERROR_IO_INCOMPLETE = 0x3E4;
|
||||
const Win32_DWORD ERROR_IO_PENDING = 0x3E5;
|
||||
const Win32_DWORD ERROR_IO_PENDING = 0x3E5;
|
||||
const Win32_DWORD ERROR_TIMEOUT = 0x5B4;
|
||||
@@ -46,6 +46,8 @@ const IMAGE_FILE_MACHINE_ARM64 = 0xAA64;
|
||||
const IMAGE_FILE_MACHINE_AMD64 = 0x8664;
|
||||
const UNDNAME_COMPLETE = 0x0000;
|
||||
|
||||
alias Win32_INIT_ONCE_FN = fn Win32_BOOL(Win32_INIT_ONCE* initOnce, void* parameter, void** context);
|
||||
|
||||
extern fn void initializeCriticalSection(Win32_CRITICAL_SECTION* section) @extern("InitializeCriticalSection");
|
||||
extern fn void deleteCriticalSection(Win32_CRITICAL_SECTION* section) @extern("DeleteCriticalSection");
|
||||
extern fn Win32_HANDLE createMutex(void*, Win32_BOOL, void*) @extern("CreateMutexA");
|
||||
@@ -53,6 +55,19 @@ extern fn Win32_BOOL releaseMutex(Win32_HANDLE) @extern("ReleaseMutex");
|
||||
extern fn void enterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("EnterCriticalSection");
|
||||
extern fn void leaveCriticalSection(Win32_CRITICAL_SECTION* section) @extern("LeaveCriticalSection");
|
||||
extern fn Win32_BOOL tryEnterCriticalSection(Win32_CRITICAL_SECTION* section) @extern("TryEnterCriticalSection");
|
||||
extern fn void initializeSRWLock(Win32_SRWLOCK* lock) @extern("InitializeSRWLock");
|
||||
extern fn void acquireSRWLockExclusive(Win32_SRWLOCK* lock) @extern("AcquireSRWLockExclusive");
|
||||
extern fn void acquireSRWLockShared(Win32_SRWLOCK* lock) @extern("AcquireSRWLockShared");
|
||||
extern fn void releaseSRWLockExclusive(Win32_SRWLOCK* lock) @extern("ReleaseSRWLockExclusive");
|
||||
extern fn void releaseSRWLockShared(Win32_SRWLOCK* lock) @extern("ReleaseSRWLockShared");
|
||||
extern fn Win32_BOOL tryAcquireSRWLockExclusive(Win32_SRWLOCK* lock) @extern("TryAcquireSRWLockExclusive");
|
||||
extern fn Win32_BOOL tryAcquireSRWLockShared(Win32_SRWLOCK* lock) @extern("TryAcquireSRWLockShared");
|
||||
extern fn void initializeConditionVariable(Win32_CONDITION_VARIABLE* conditionVariable) @extern("InitializeConditionVariable");
|
||||
extern fn void wakeConditionVariable(Win32_CONDITION_VARIABLE* conditionVariable) @extern("WakeConditionVariable");
|
||||
extern fn void wakeAllConditionVariable(Win32_CONDITION_VARIABLE* conditionVariable) @extern("WakeAllConditionVariable");
|
||||
extern fn Win32_BOOL sleepConditionVariableCS(Win32_CONDITION_VARIABLE* conditionVariable, Win32_CRITICAL_SECTION* section, Win32_DWORD dwMilliseconds) @extern("SleepConditionVariableCS");
|
||||
extern fn Win32_BOOL sleepConditionVariableSRW(Win32_CONDITION_VARIABLE* conditionVariable, Win32_SRWLOCK* lock, Win32_DWORD dwMilliseconds, Win32_ULONG flags) @extern("SleepConditionVariableSRW");
|
||||
extern fn Win32_BOOL initOnceExecuteOnce(Win32_INIT_ONCE* initOnce, Win32_INIT_ONCE_FN initFn, void* parameter, void** context) @extern("InitOnceExecuteOnce");
|
||||
extern fn Win32_DWORD waitForSingleObject(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds) @extern("WaitForSingleObject");
|
||||
extern fn Win32_DWORD waitForSingleObjectEx(Win32_HANDLE hHandle, Win32_DWORD dwMilliseconds, Win32_BOOL bAlertable) @extern("WaitForSingleObjectEx");
|
||||
extern fn Win32_DWORD waitForMultipleObjects(Win32_DWORD nCount, Win32_HANDLE* lpHandles, Win32_BOOL bWaitAll, Win32_DWORD dwMilliseconds) @extern("WaitForMultipleObjects");
|
||||
|
||||
@@ -190,6 +190,9 @@ union Win32_LARGE_INTEGER
|
||||
}
|
||||
|
||||
typedef Win32_CRITICAL_SECTION = ulong[5];
|
||||
typedef Win32_CONDITION_VARIABLE = void*;
|
||||
typedef Win32_SRWLOCK = void*;
|
||||
typedef Win32_INIT_ONCE = void*;
|
||||
|
||||
struct Win32_SECURITY_ATTRIBUTES
|
||||
{
|
||||
@@ -694,4 +697,3 @@ alias Win32_LPSTACKFRAME64 = Win32_STACKFRAME64*;
|
||||
alias Win32_PMODLOAD_DATA = Win32_MODLOAD_DATA*;
|
||||
alias Win32_PIMAGEHLP_LINE64 = Win32_IMAGEHLP_LINE64*;
|
||||
alias Win32_LPMODULEINFO = Win32_MODULEINFO*;
|
||||
|
||||
|
||||
@@ -122,11 +122,11 @@ const SD_RECEIVE = 0x00;
|
||||
const SD_SEND = 0x01;
|
||||
const SD_BOTH = 0x02;
|
||||
|
||||
extern fn CInt win32_WSAPoll(Win32_LPWSAPOLLFD fdArray, Win32_ULONG fds, Win32_INT timeout) @extern("WSAPoll") @builtin;
|
||||
extern fn WSAError win32_WSAGetLastError() @extern("WSAGetLastError") @builtin;
|
||||
extern fn void win32_WSASetLastError(WSAError error) @extern("WSASetLastError") @builtin;
|
||||
extern fn CInt win32_WSAStartup(Win32_WORD, void*) @extern("WSAStartup") @builtin;
|
||||
extern fn CInt win32_WSACleanup() @extern("WSACleanup") @builtin;
|
||||
extern fn CInt wsaPoll(Win32_LPWSAPOLLFD fdArray, Win32_ULONG fds, Win32_INT timeout) @extern("WSAPoll");
|
||||
extern fn WSAError wsaGetLastError() @extern("WSAGetLastError");
|
||||
extern fn void wsaSetLastError(WSAError error) @extern("WSASetLastError");
|
||||
extern fn CInt wsaStartup(Win32_WORD, void*) @extern("WSAStartup");
|
||||
extern fn CInt wsaCleanup() @extern("WSACleanup");
|
||||
|
||||
const int FIONBIO = -2147195266;
|
||||
const int FIONREAD = 1074030207;
|
||||
@@ -230,3 +230,4 @@ const WSAError QOS_EPSFILTERSPEC = 11028;
|
||||
const WSAError QOS_ESDMODEOBJ = 11029;
|
||||
const WSAError QOS_ESHAPERATEOBJ = 11030;
|
||||
const WSAError QOS_RESERVED_PETYPE = 11031;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ alias Indexs @private = char[256];
|
||||
alias ElementType = $typeof((Type){}[0]);
|
||||
|
||||
const bool NO_KEY_FN @private = types::is_same(KeyFn, EmptySlot);
|
||||
const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $assignable((Type){}[0], $typefrom(KeyFn.paramsof[0].type));
|
||||
const bool KEY_BY_VALUE @private = NO_KEY_FN ||| $assignable((Type){}[0], KeyFn.paramsof[0].type);
|
||||
const bool LIST_HAS_REF @private = $defined(&(Type){}[0]);
|
||||
|
||||
alias KeyFnReturnType @if(!NO_KEY_FN) = $typefrom(KeyFn.returns) ;
|
||||
|
||||
@@ -25,7 +25,7 @@ fn void isort(Type list, usz low, usz high, CmpFn comp, Context context)
|
||||
{
|
||||
var $has_cmp = @is_valid_macro_slot(comp);
|
||||
var $has_context = @is_valid_macro_slot(context);
|
||||
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type));
|
||||
var $cmp_by_value = $has_cmp &&& $assignable(list[0], CmpFn.paramsof[0].type);
|
||||
var $has_get_ref = $defined(&list[0]);
|
||||
for (usz i = low; i < high; ++i)
|
||||
{
|
||||
|
||||
@@ -115,7 +115,7 @@ macro @partition(Type list, isz l, isz h, CmpFn cmp, Context context)
|
||||
{
|
||||
var $has_cmp = @is_valid_macro_slot(cmp);
|
||||
var $has_context = @is_valid_macro_slot(context);
|
||||
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom(CmpFn.paramsof[0].type));
|
||||
var $cmp_by_value = $has_cmp &&& $assignable(list[0], CmpFn.paramsof[0].type);
|
||||
|
||||
ElementType pivot = list[l];
|
||||
while (l < h)
|
||||
|
||||
@@ -39,7 +39,7 @@ macro int @sort_cmp(list, pos, cmp, ctx) @local
|
||||
{
|
||||
var $has_cmp = @is_valid_macro_slot(cmp);
|
||||
var $has_context = @is_valid_macro_slot(ctx);
|
||||
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typefrom($typeof(cmp).paramsof[0].type));
|
||||
var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typeof(cmp).paramsof[0].type);
|
||||
|
||||
var a = list[pos];
|
||||
var b = list[pos+1];
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/*module std::text::i18n;
|
||||
import std::collections::map;
|
||||
import std::hash::fnv32a;
|
||||
typedef Language = char[];
|
||||
|
||||
const Language EN = "en";
|
||||
|
||||
alias TranslationMap = HashMap{String, String};
|
||||
fn uint Language.hash(self) => fnv32a::encode((char[])self);
|
||||
HashMap{Language, TranslationMap*} language_map @private;
|
||||
TranslationMap? current_map;
|
||||
|
||||
macro String @localized(String string) @builtin
|
||||
{
|
||||
return current_map[string] ?? string;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ fn void? FixedThreadPool.init(&self, usz threads, usz queue_size = 0)
|
||||
*>
|
||||
fn void? FixedThreadPool.destroy(&self)
|
||||
{
|
||||
return self.@shutdown(stop_now);
|
||||
return self.@shutdown(self.stop_now);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -70,7 +70,7 @@ fn void? FixedThreadPool.destroy(&self)
|
||||
*>
|
||||
fn void? FixedThreadPool.stop_and_destroy(&self)
|
||||
{
|
||||
return self.@shutdown(stop);
|
||||
return self.@shutdown(self.stop);
|
||||
}
|
||||
|
||||
macro void? FixedThreadPool.@shutdown(&self, #stop) @private
|
||||
@@ -78,7 +78,7 @@ macro void? FixedThreadPool.@shutdown(&self, #stop) @private
|
||||
if (self.initialized)
|
||||
{
|
||||
self.mu.lock()!;
|
||||
self.#stop = true;
|
||||
#stop = true;
|
||||
self.notify.broadcast()!;
|
||||
self.mu.unlock()!;
|
||||
// Wait for all threads to shutdown.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module std::thread::os @if (!env::POSIX && !env::WIN32);
|
||||
|
||||
typedef NativeMutex = int;
|
||||
typedef NativeTimedMutex = int;
|
||||
typedef NativeConditionVariable = int;
|
||||
typedef NativeOnceFlag = int;
|
||||
typedef NativeThread = int;
|
||||
@@ -7,6 +7,8 @@ struct NativeMutex
|
||||
bool initialized;
|
||||
}
|
||||
|
||||
alias NativeTimedMutex = NativeMutex;
|
||||
|
||||
alias NativeConditionVariable = Pthread_cond_t;
|
||||
|
||||
struct NativeThread
|
||||
@@ -28,7 +30,7 @@ fn void? NativeMutex.init(&self, MutexType type)
|
||||
if (posix::pthread_mutexattr_init(&attr)) return thread::INIT_FAILED?;
|
||||
defer posix::pthread_mutexattr_destroy(&attr);
|
||||
// TODO: make a fine grained error instead
|
||||
if (type & thread::MUTEX_RECURSIVE)
|
||||
if (type.recursive)
|
||||
{
|
||||
if (posix::pthread_mutexattr_settype(&attr, posix::PTHREAD_MUTEX_RECURSIVE)) return thread::INIT_FAILED?;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user