Compare commits

..

1 Commits

Author SHA1 Message Date
Christoffer Lerno
ba785c3cb0 Const Enums From / to ordinal using casts is back. Add "--use-old-enums", deprecating lookup 2025-06-29 01:20:32 +02:00
295 changed files with 2848 additions and 16547 deletions

View File

@@ -10,7 +10,6 @@ env:
LLVM_RELEASE_VERSION_WINDOWS: 18 LLVM_RELEASE_VERSION_WINDOWS: 18
LLVM_RELEASE_VERSION_MAC: 17 LLVM_RELEASE_VERSION_MAC: 17
LLVM_RELEASE_VERSION_LINUX: 17 LLVM_RELEASE_VERSION_LINUX: 17
LLVM_RELEASE_VERSION_OPENBSD: 19
LLVM_RELEASE_VERSION_UBUNTU22: 17 LLVM_RELEASE_VERSION_UBUNTU22: 17
LLVM_DEV_VERSION: 21 LLVM_DEV_VERSION: 21
jobs: jobs:
@@ -53,9 +52,9 @@ jobs:
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
cd resources/testproject cd resources/testproject
..\..\build\${{ matrix.build_type }}\c3c.exe -vvv --emit-llvm run hello_world_win32 --trust=full ..\..\build\${{ matrix.build_type }}\c3c.exe -vvv --emit-llvm run hello_world_win32 --trust=full
dir out\llvm\windows-x64 dir build\llvm\windows-x64
..\..\build\${{ matrix.build_type }}\c3c.exe clean ..\..\build\${{ matrix.build_type }}\c3c.exe clean
dir out\llvm\windows-x64 dir build\llvm\windows-x64
- name: Build testproject lib - name: Build testproject lib
@@ -91,7 +90,7 @@ jobs:
- name: Compile run unit tests - name: Compile run unit tests
run: | run: |
cd test cd test
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1 -D SLOW_TESTS ..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1
- name: run compiler tests - name: run compiler tests
run: | run: |
@@ -274,7 +273,6 @@ jobs:
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \ -DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \ -DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \ -DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DLLVM_ENABLE_LIBXML2=OFF \
-DC3_LLVM_VERSION=${{matrix.llvm_version}} -DC3_LLVM_VERSION=${{matrix.llvm_version}}
cmake --build build cmake --build build
- name: CMake18 - name: CMake18
@@ -289,7 +287,6 @@ jobs:
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \ -DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \ -DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \ -DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DLLVM_ENABLE_LIBXML2=OFF \
-DC3_LLVM_VERSION=${{matrix.llvm_version}}.1 -DC3_LLVM_VERSION=${{matrix.llvm_version}}.1
cmake --build build cmake --build build
@@ -348,7 +345,7 @@ jobs:
- name: Compile run unit tests - name: Compile run unit tests
run: | run: |
cd test cd test
../build/c3c compile-test unit -D SLOW_TESTS ../build/c3c compile-test unit
- name: Build testproject - name: Build testproject
run: | run: |
@@ -442,7 +439,6 @@ jobs:
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \ -DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \ -DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \ -DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DLLVM_ENABLE_LIBXML2=OFF \
-DC3_LLVM_VERSION=${{matrix.llvm_version}} -DC3_LLVM_VERSION=${{matrix.llvm_version}}
cmake --build build cmake --build build
- name: CMake - name: CMake
@@ -457,7 +453,6 @@ jobs:
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \ -DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \ -DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \ -DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
-DLLVM_ENABLE_LIBXML2=OFF \
-DC3_LLVM_VERSION=${{matrix.llvm_version}}.1 -DC3_LLVM_VERSION=${{matrix.llvm_version}}.1
cmake --build build cmake --build build
- name: Compile and run some examples - name: Compile and run some examples
@@ -494,7 +489,7 @@ jobs:
- name: Compile run unit tests - name: Compile run unit tests
run: | run: |
cd test cd test
../build/c3c compile-test unit --sanitize=address -D SLOW_TESTS ../build/c3c compile-test unit --sanitize=address
- name: Build testproject - name: Build testproject
run: | run: |
@@ -588,7 +583,7 @@ jobs:
- name: Compile run unit tests - name: Compile run unit tests
run: | run: |
cd test cd test
../build/c3c compile-test unit -D SLOW_TESTS ../build/c3c compile-test unit
- name: Build testproject - name: Build testproject
run: | run: |
@@ -670,7 +665,7 @@ jobs:
- name: Compile run unit tests - name: Compile run unit tests
run: | run: |
cd test cd test
../build/c3c compile-test unit -O1 -D SLOW_TESTS ../build/c3c compile-test unit -O1
- name: Test WASM - name: Test WASM
run: | run: |
@@ -752,119 +747,6 @@ jobs:
nix build -L ".#c3c-checks" nix build -L ".#c3c-checks"
fi fi
build-openbsd:
runs-on: ubuntu-latest
strategy:
# Don't abort runners if a single one fails
fail-fast: false
matrix:
build_type: [Release, Debug]
steps:
- uses: actions/checkout@v4
- name: OpenBSD VM
uses: vmactions/openbsd-vm@main
with:
prepare: |
pkg_add cmake llvm-19.1.7p3 ninja
run: |
echo "CMake"
cmake -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DLLVM_ENABLE_LIBXML2=OFF \
-DC3_LLVM_VERSION=${LLVM_RELEASE_VERSION_OPENBSD}
cmake --build build
echo "Compile and run some examples"
cd resources
../build/c3c compile examples/base64.c3
../build/c3c compile examples/binarydigits.c3
../build/c3c compile examples/brainfk.c3
../build/c3c compile examples/factorial_macro.c3
../build/c3c compile examples/fasta.c3
../build/c3c compile examples/gameoflife.c3
../build/c3c compile examples/hash.c3
../build/c3c compile-only examples/levenshtein.c3
../build/c3c compile examples/load_world.c3
../build/c3c compile-only examples/map.c3
../build/c3c compile examples/mandelbrot.c3
../build/c3c compile examples/plus_minus.c3
../build/c3c compile examples/nbodies.c3
../build/c3c compile examples/spectralnorm.c3
../build/c3c compile examples/swap.c3
../build/c3c compile examples/contextfree/boolerr.c3
../build/c3c compile examples/contextfree/dynscope.c3
../build/c3c compile examples/contextfree/guess_number.c3
../build/c3c compile examples/contextfree/multi.c3
../build/c3c compile examples/contextfree/cleanup.c3
../build/c3c compile-run examples/hello_world_many.c3
../build/c3c compile-run examples/time.c3
../build/c3c compile-run examples/fannkuch-redux.c3
../build/c3c compile-run examples/contextfree/boolerr.c3
../build/c3c compile-run examples/load_world.c3
../build/c3c compile-run examples/process.c3
../build/c3c compile-run examples/ls.c3
../build/c3c compile-run examples/args.c3 -- foo -bar "baz baz"
cd ..
echo "Compile and run dynlib-test"
cd resources/examples/dynlib-test
../../../build/c3c -vv dynamic-lib add.c3
mv add.so libadd.so
cc test.c -L. -ladd -Wl,-rpath=.
./a.out
../../../build/c3c compile-run test.c3 -L . -l add -z -Wl,-rpath=.
cd ../../../
echo "Compile and run staticlib-test"
cd resources/examples/staticlib-test
../../../build/c3c -vv static-lib add.c3
mv add.a libadd.a
cc test.c -L. -ladd
./a.out
../../../build/c3c compile-run test.c3 -L . -l add
cd ../../../
echo "Compile run unit tests"
cd test
../build/c3c --max-mem 128 compile-test unit -D SLOW_TESTS
cd ..
echo "Build testproject"
cd resources/testproject
../../build/c3c run -vvv --trust=full
cd ../../
echo "Test WASM"
cd resources/testfragments
../../build/c3c compile --target wasm32 -g0 --no-entry -Os wasm4.c3
cd ../../
echo "Build testproject direct linker"
cd resources/testproject
../../build/c3c run -vvv --linker=builtin --trust=full
cd ../../
echo "Init a library & a project"
./build/c3c init-lib mylib
ls mylib.c3l
./build/c3c init myproject
ls myproject
echo "run compiler tests"
cd test
../build/c3c --max-mem 128 compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/
cd ..
- name: bundle_output
run: |
mkdir c3
cp -r lib c3
cp msvc_build_libraries.py c3
cp build/c3c c3
cp README.md c3
cp releasenotes.md c3
tar czf c3-openbsd-${{matrix.build_type}}.tar.gz c3
- name: upload artifacts
uses: actions/upload-artifact@v4
with:
name: c3-openbsd-${{matrix.build_type}}
path: c3-openbsd-${{matrix.build_type}}.tar.gz
release: release:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu22] needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu22]
@@ -887,8 +769,6 @@ jobs:
- run: zip -r c3-windows-debug.zip c3-windows-Debug - 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-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-linux-Debug/c3-linux-Debug.tar.gz c3-linux-Debug/c3-linux-debug.tar.gz
- run: mv c3-openbsd-Release/c3-openbsd-Release.tar.gz c3-openbsd-Release/c3-openbsd.tar.gz
- run: mv c3-openbsd-Debug/c3-openbsd-Debug.tar.gz c3-openbsd-Debug/c3-openbsd-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-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-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-Release/c3-macos-Release.zip c3-macos-Release/c3-macos.zip
@@ -910,8 +790,6 @@ jobs:
c3-windows-debug.zip c3-windows-debug.zip
c3-linux-Release/c3-linux.tar.gz c3-linux-Release/c3-linux.tar.gz
c3-linux-Debug/c3-linux-debug.tar.gz c3-linux-Debug/c3-linux-debug.tar.gz
c3-openbsd-Release/c3-openbsd.tar.gz
c3-openbsd-Debug/c3-openbsd-debug.tar.gz
c3-ubuntu-22-Release/c3-ubuntu-22.tar.gz c3-ubuntu-22-Release/c3-ubuntu-22.tar.gz
c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz
c3-macos-Release/c3-macos.zip c3-macos-Release/c3-macos.zip

View File

@@ -1,13 +1,5 @@
cmake_minimum_required(VERSION 3.20) 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 # Grab the version
file(READ "src/version.h" ver) file(READ "src/version.h" ver)
if (NOT ${ver} MATCHES "COMPILER_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"") if (NOT ${ver} MATCHES "COMPILER_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"")
@@ -15,20 +7,8 @@ if (NOT ${ver} MATCHES "COMPILER_VERSION \"([0-9]+.[0-9]+.[0-9]+)\"")
endif() endif()
# Set the project and version # Set the project and version
project(c3c VERSION ${CMAKE_MATCH_1} LANGUAGES C CXX) project(c3c VERSION ${CMAKE_MATCH_1})
message("Configuring C3C ${CMAKE_PROJECT_VERSION} for ${CMAKE_SYSTEM_NAME}") message("C3C version: ${CMAKE_PROJECT_VERSION}")
# 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 # Avoid warning for FetchContent
if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
@@ -36,7 +16,7 @@ if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0")
endif() endif()
if (NOT DEFINED CMAKE_INSTALL_LIBDIR) if (NOT DEFINED CMAKE_INSTALL_LIBDIR)
if (WIN32) if (MSVC)
set(CMAKE_INSTALL_LIBDIR "c:\\c3c\\lib") set(CMAKE_INSTALL_LIBDIR "c:\\c3c\\lib")
set(CMAKE_INSTALL_BINDIR "c:\\c3c") set(CMAKE_INSTALL_BINDIR "c:\\c3c")
else () else ()
@@ -56,41 +36,36 @@ set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
set(CMAKE_C_STANDARD 11) set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
# Use /MT or /MTd
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
if(MSVC) if(MSVC)
message(STATUS "MSVC version ${MSVC_VERSION}") message(STATUS "MSVC version ${MSVC_VERSION}")
add_compile_options(/utf-8) 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")
else() else()
add_compile_options(-gdwarf-3 -fno-exceptions) if (true)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O0 -fno-exceptions")
# add_compile_options(-fsanitize=address,undefined) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gdwarf-3 -O0 -fno-exceptions")
# add_link_options(-fsanitize=address,undefined) 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()
endif() endif()
# Options option(C3_LINK_DYNAMIC "link dynamically with LLVM/LLD libs")
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_OPTIONS set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
C3_LINK_DYNAMIC option(C3_USE_MIMALLOC "Use built-in mimalloc" OFF)
C3_WITH_LLVM option(C3_USE_TB "Use TB" OFF)
C3_LLVM_VERSION set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
C3_USE_MIMALLOC option(C3_WITH_LLVM "Build with LLVM" ON)
C3_MIMALLOC_TAG option(C3_LLD_DIR "Use custom LLD directory" "")
C3_USE_TB option(LLVM_CRT_LIBRARY_DIR "Use custom llvm's compiler-rt directory" "")
C3_LLD_DIR
C3_ENABLE_CLANGD_LSP
LLVM_CRT_LIBRARY_DIR
)
set(C3_USE_MIMALLOC OFF) set(C3_USE_MIMALLOC OFF)
if(C3_USE_MIMALLOC) if(C3_USE_MIMALLOC)
@@ -108,6 +83,13 @@ endif()
if (NOT WIN32) if (NOT WIN32)
find_package(CURL) find_package(CURL)
endif() 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) find_package(Git QUIET)
if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git") if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
@@ -125,6 +107,7 @@ if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
endif() endif()
# Clangd LSP support # Clangd LSP support
option(C3_ENABLE_CLANGD_LSP "Enable/Disable output of compile commands during generation." OFF)
if(C3_ENABLE_CLANGD_LSP) if(C3_ENABLE_CLANGD_LSP)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
execute_process( execute_process(
@@ -137,7 +120,7 @@ endif(C3_ENABLE_CLANGD_LSP)
if(C3_WITH_LLVM) if(C3_WITH_LLVM)
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC") if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if (C3_LLVM_VERSION STREQUAL "auto") if (C3_LLVM_VERSION STREQUAL "auto")
set(C3_LLVM_VERSION ${C3_LLVM_DEFAULT_VERSION}) set(C3_LLVM_VERSION "19")
endif() endif()
FetchContent_Declare( FetchContent_Declare(
LLVM_Windows LLVM_Windows
@@ -156,7 +139,6 @@ if(C3_WITH_LLVM)
FetchContent_MakeAvailable(LLVM_Windows) FetchContent_MakeAvailable(LLVM_Windows)
set(llvm_dir ${llvm_windows_SOURCE_DIR}) set(llvm_dir ${llvm_windows_SOURCE_DIR})
endif() endif()
message("Loaded Windows LLVM libraries into ${llvm_dir}")
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH}) set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
find_package(LLVM REQUIRED CONFIG) find_package(LLVM REQUIRED CONFIG)
@@ -167,7 +149,7 @@ if(C3_WITH_LLVM)
# #
# Because of CMAKE_FIND_PACKAGE_SORT_ORDER CMAKE_FIND_PACKAGE_SORT_DIRECTION, # Because of CMAKE_FIND_PACKAGE_SORT_ORDER CMAKE_FIND_PACKAGE_SORT_DIRECTION,
# the newest version will always be found first. # the newest version will always be found first.
c3_print_variables(CMAKE_PREFIX_PATH) message(STATUS "CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}")
if (DEFINED LLVM_DIR) if (DEFINED LLVM_DIR)
message(STATUS "Looking for LLVM CMake files in user-specified directory ${LLVM_DIR}") message(STATUS "Looking for LLVM CMake files in user-specified directory ${LLVM_DIR}")
else() else()
@@ -194,15 +176,12 @@ if(C3_WITH_LLVM)
list(APPEND LLVM_LIBRARY_DIRS /usr/lib) list(APPEND LLVM_LIBRARY_DIRS /usr/lib)
endif() endif()
list(REMOVE_DUPLICATES LLVM_LIBRARY_DIRS)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
message(STATUS "LLVM libraries located in: ${LLVM_LIBRARY_DIRS}") message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
if (${LLVM_PACKAGE_VERSION} VERSION_LESS C3_LLVM_MIN_VERSION OR if (NOT LLVM_PACKAGE_VERSION VERSION_GREATER_EQUAL 15.0)
${LLVM_PACKAGE_VERSION} VERSION_GREATER C3_LLVM_MAX_VERSION) message(FATAL_ERROR "LLVM version 15.0 or later is required.")
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() endif()
if(LLVM_ENABLE_RTTI) if(LLVM_ENABLE_RTTI)
@@ -252,43 +231,44 @@ if(C3_WITH_LLVM)
llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS}) llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
if(NOT ${C3_LLD_DIR} EQUAL "" AND EXISTS ${C3_LLD_DIR}) if(NOT ${C3_LLD_DIR} EQUAL "" AND EXISTS ${C3_LLD_DIR})
list(APPEND LLVM_LIBRARY_DIRS ${C3_LLD_DIR}) message("C3_LLD_DIR: " ${C3_LLD_DIR})
set(LLVM_LIBRARY_DIRS
"${LLVM_LIBRARY_DIRS}"
"${C3_LLD_DIR}"
)
list(REMOVE_DUPLICATES LLVM_LIBRARY_DIRS) list(REMOVE_DUPLICATES LLVM_LIBRARY_DIRS)
endif() endif()
message(STATUS "Looking for static lld libraries in ${LLVM_LIBRARY_DIRS}")
# These don't seem to be reliable on windows. # These don't seem to be reliable on windows.
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED) message(STATUS "using find_library")
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_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.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 REQUIRED) find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") 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 REQUIRED) 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)
else() else()
set(LLD_MACHO "") find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
endif()
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()
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}) set(llvm_libs ${LLVM})
# These don't seem to be reliable on windows. # These don't seem to be reliable on windows.
find_library(LLD_COFF NAMES liblldCOFF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED) message(STATUS "using find_library")
find_library(LLD_COMMON NAMES liblldCommon.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED) find_library(LLD_COFF NAMES liblldCOFF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
find_library(LLD_ELF NAMES liblldELF.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED) find_library(LLD_COMMON NAMES liblldCommon.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD") 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 REQUIRED) find_library(LLD_MACHO NAMES liblldMachO.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
else() find_library(LLD_MINGW NAMES liblldMinGW.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
set(LLD_MACHO "") find_library(LLD_WASM NAMES liblldWasm.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
endif()
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()
endif()
# find_library(LLD_LOONG NAMES libLLVMLoongArchCodeGen.lib libLLVMLoongArchAsmParser.lib libLLVMLoongArchCodeGen.a libLLVMLoongArchAsmParser.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) 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)
set(lld_libs set(lld_libs
${LLD_COFF} ${LLD_COFF}
${LLD_WASM} ${LLD_WASM}
@@ -313,8 +293,8 @@ if(C3_WITH_LLVM)
) )
endif() endif()
message(STATUS "Linking to llvm libs ${llvm_libs}") message(STATUS "linking to llvm libs ${lld_libs}")
message(STATUS "Linking to lld libs ${lld_libs}") message(STATUS "Found lld libs ${lld_libs}")
endif() endif()
add_library(miniz STATIC dependencies/miniz/miniz.c) add_library(miniz STATIC dependencies/miniz/miniz.c)
@@ -432,11 +412,6 @@ if(C3_WITH_LLVM)
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1) target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1)
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp) add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
if (MSVC)
target_compile_options(c3c PRIVATE
"$<$<CONFIG:Debug>:/EHa>"
"$<$<CONFIG:Release>:/EHsc>")
endif()
else() else()
target_sources(c3c PRIVATE src/utils/hostinfo.c) target_sources(c3c PRIVATE src/utils/hostinfo.c)
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=0) target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=0)
@@ -520,7 +495,7 @@ endif ()
if (CURL_FOUND) if (CURL_FOUND)
target_link_libraries(c3c ${CURL_LIBRARIES}) target_link_libraries(c3c ${CURL_LIBRARIES})
target_include_directories(c3c PRIVATE ${CURL_INCLUDE_DIRS}) target_include_directories(c3c PRIVATE ${CURL_INCLUDES})
target_compile_definitions(c3c PUBLIC CURL_FOUND=1) target_compile_definitions(c3c PUBLIC CURL_FOUND=1)
else() else()
target_compile_definitions(c3c PUBLIC CURL_FOUND=0) target_compile_definitions(c3c PUBLIC CURL_FOUND=0)
@@ -528,27 +503,34 @@ endif()
if(MSVC) if(MSVC)
target_compile_options(c3c PRIVATE message("Adding MSVC options")
/wd4068 target_compile_options(c3c PRIVATE /wd4068 /wd4090 /WX /Wv:18)
/wd4090
/WX
/Wv:18
)
if(C3_WITH_LLVM) if(C3_WITH_LLVM)
target_compile_options(c3c_wrappers PUBLIC target_compile_options(c3c_wrappers PUBLIC /wd4624 /wd4267 /wd4244 /WX /Wv:18)
/wd4624
/wd4267
/wd4244
/WX
/Wv:18
)
if(NOT LLVM_ENABLE_RTTI) if(NOT LLVM_ENABLE_RTTI)
target_compile_options(c3c_wrappers PUBLIC /GR-) target_compile_options(c3c_wrappers PUBLIC /GR-)
endif() endif()
target_link_options(c3c_wrappers PUBLIC /ignore:4099) target_link_options(c3c_wrappers PUBLIC /ignore:4099)
endif() 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) if(C3_WITH_LLVM)
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows) set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
set(sanitizer_runtime_libraries set(sanitizer_runtime_libraries
@@ -558,20 +540,13 @@ if(MSVC)
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib) ${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
endif() endif()
else() else()
message(STATUS "using gcc/clang warning switches")
target_link_options(c3c PRIVATE -pthread)
if (C3_WITH_LLVM AND NOT LLVM_ENABLE_RTTI) if (C3_WITH_LLVM AND NOT LLVM_ENABLE_RTTI)
target_compile_options(c3c_wrappers PRIVATE -fno-rtti) target_compile_options(c3c_wrappers PRIVATE -fno-rtti)
endif() endif()
target_compile_options(c3c PRIVATE target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
-pthread -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
-Wall
-Werror
-Wno-unknown-pragmas
-Wno-unused-result
-Wno-unused-function
-Wno-unused-variable
-Wno-unused-parameter
)
target_link_options(c3c PRIVATE -pthread)
endif() endif()
install(TARGETS c3c DESTINATION bin) install(TARGETS c3c DESTINATION bin)
@@ -582,12 +557,6 @@ if (NOT WIN32)
install(FILES c3c.1 DESTINATION "share/man/man1") install(FILES c3c.1 DESTINATION "share/man/man1")
endif() 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) if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
add_custom_command(TARGET c3c POST_BUILD add_custom_command(TARGET c3c POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E rm -rf -- $<TARGET_FILE_DIR:c3c>/c3c_rt COMMAND "${CMAKE_COMMAND}" -E rm -rf -- $<TARGET_FILE_DIR:c3c>/c3c_rt
@@ -607,35 +576,3 @@ if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
endif() endif()
feature_summary(WHAT ALL) 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}\"")

View File

@@ -1,57 +0,0 @@
{
"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"
}
]
}

View File

@@ -12,7 +12,6 @@ Precompiled binaries for the following operating systems are available:
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz), [install instructions](#installing-on-debian-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). - 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). - MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip), [install instructions](#installing-on-macos-with-precompiled-binaries).
- OpenBSD x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd.tar.gz), [install instructions](#installing-on-openbsd-with-precompiled-binaries).
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org). The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
@@ -142,7 +141,7 @@ fn void main()
### Current status ### Current status
The current stable version of the compiler is **version 0.7.4**. The current stable version of the compiler is **version 0.7.2**.
The upcoming 0.7.x releases will focus on expanding the standard library, The upcoming 0.7.x releases will focus on expanding the standard library,
fixing bugs and improving compile time analysis. fixing bugs and improving compile time analysis.
@@ -177,14 +176,12 @@ The compiler is currently verified to compile on Linux, Windows and MacOS.
| NetBSD x86 | Untested | Untested | No | Yes | Untested | Yes* | | NetBSD x86 | Untested | Untested | No | Yes | Untested | Yes* |
| NetBSD x64 | Untested | Untested | No | Yes | Untested | Yes* | | NetBSD x64 | Untested | Untested | No | Yes | Untested | Yes* |
| OpenBSD x86 | Untested | Untested | No | Yes | Untested | Yes* | | OpenBSD x86 | Untested | Untested | No | Yes | Untested | Yes* |
| OpenBSD x64 | Yes* | Yes | Yes* | Yes | Untested | Yes* | | OpenBSD x64 | Untested | Untested | No | Yes | Untested | Yes* |
| MCU x86 | No | Untested | No | No | No | Yes* | | MCU x86 | No | Untested | No | No | No | Yes* |
| Wasm32 | No | Yes | No | No | No | No | | Wasm32 | No | Yes | No | No | No | No |
| Wasm64 | No | Untested | No | No | No | No | | Wasm64 | No | Untested | No | No | No | No |
*\* Inline asm is still a work in progress*<br> *\* Inline asm is still a work in progress*
*\* OpenBSD 7.7 is the only tested version*<br>
*\* OpenBSD has limited stacktrace, needs to be tested further*
More platforms will be supported in the future. More platforms will be supported in the future.
@@ -230,14 +227,6 @@ This installs the latest prerelease build, as opposed to the latest released ver
(*Note that there is a known issue with debug symbol generation on MacOS 13, see [issue #1086](https://github.com/c3lang/c3c/issues/1086)) (*Note that there is a known issue with debug symbol generation on MacOS 13, see [issue #1086](https://github.com/c3lang/c3c/issues/1086))
#### Installing on OpenBSD with precompiled binaries
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd.tar.gz)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd-debug.tar.gz))
2. Unpack executable and standard lib.
3. Run `./c3c`.
(*Note that this is specifically for OpenBSD 7.7, running it on any other version is prone to ABI breaks)
#### Installing on Arch Linux #### Installing on Arch Linux
Arch includes c3c in the official 'extra' repo. It can be easily installed the usual way: Arch includes c3c in the official 'extra' repo. It can be easily installed the usual way:
@@ -276,15 +265,14 @@ See the `build-with-docker.sh` script for more information on other configurable
#### Installing on OS X using Homebrew #### Installing on OS X using Homebrew
1. Install [Homebrew](https://brew.sh/) 2. Install CMake: `brew install cmake`
2. Install LLVM 17+: `brew install llvm` 3. Install LLVM 17+: `brew install llvm`
3. Install lld: `brew install lld` 4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
4. Install CMake: `brew install cmake` 5. Enter the C3C directory `cd c3c`.
5. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git` 6. Create a build directory `mkdir build`
6. Enter the C3C directory `cd c3c`. 7. Change directory to the build directory `cd build`
7. Set up CMake build for debug: `cmake -B build -S .` 8. Set up CMake build for debug: `cmake ..`
8. Build: `cmake --build build` 9. Build: `cmake --build .`
9. Change directory to the build directory `cd build`
#### Installing on Windows using Scoop #### Installing on Windows using Scoop
@@ -325,35 +313,28 @@ 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++" 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 2. Install CMake
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git` 3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
4. Enter the C3C directory: `cd c3c`. 4. Enter the C3C directory `cd c3c`.
5. Set up the CMake build: `cmake --preset windows-vs-2022-release` 5. Set up the CMake build `cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release`
6. Build: `cmake --build --preset windows-vs-2022-release` 6. Build: `cmake --build build --config Release`
7. You should now have the c3c.exe
You should now have a `c3c` executable in `build\Release`. You should now have a `c3c` executable.
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.* *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 #### Compiling on Ubuntu 24.04 LTS
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed. 1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
2. Install LLVM 18 `sudo apt-get install cmake git clang zlib1g zlib1g-dev libllvm18 llvm llvm-dev llvm-runtime liblld-dev liblld-18 libpolly-18-dev`. If you're using Ubuntu 25.04, also install `libpolly-20-dev`. 2. Install LLVM 18 `sudo apt-get install cmake git clang zlib1g zlib1g-dev libllvm18 llvm llvm-dev llvm-runtime liblld-dev liblld-18 libpolly-18-dev`
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git` 3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
4. Enter the C3C directory `cd c3c`. 4. Enter the C3C directory `cd c3c`.
5. Set up CMake build: `cmake -B build -S .` 5. Create a build directory `mkdir build`
6. Build: `cmake --build build` 6. Change directory to the build directory `cd build`
7. Change directory to the build directory `cd build` 7. Set up CMake build: `cmake ..`
8. Build: `cmake --build .`
You should now have a `c3c` executable. You should now have a `c3c` executable.
@@ -366,12 +347,13 @@ You can try it out by running some sample code: `./c3c compile ../resources/exam
2. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git` 2. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git`
- If you only need the latest commit, you may want to make a shallow clone instead: `git clone https://github.com/c3lang/c3c.git --depth=1` - If you only need the latest commit, you may want to make a shallow clone instead: `git clone https://github.com/c3lang/c3c.git --depth=1`
3. Enter the directory: `cd c3c` 3. Enter the directory: `cd c3c`
4. Create the CMake build cache: `cmake -B build -S .` 4. Create a build directory: `mkdir build`
5. Build: `cmake --build build` 5. Enter the build directory: `cd build`
6. Enter the build directory: `cd build` 6. Create the CMake build cache: `cmake ..`
7. Build: `cmake --build .`
Your c3c executable should have compiled properly. You may want to test it: `./c3c compile ../resources/examples/hash.c3` Your c3c executable should have compiled properly. You may want to test it: `./c3c compile ../resources/examples/hash.c3`
For a system-wide installation, run the following as root: `cmake --install .` For a sytem-wide installation, run the following as root: `cmake --install .`
#### Compiling on Fedora #### Compiling on Fedora
@@ -381,15 +363,15 @@ For a system-wide installation, run the following as root: `cmake --install .`
3. Clone the C3C repository: `git clone https://github.com/c3lang/c3c.git` 3. 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` - 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`
4. Enter the C3C directory: `cd c3c` 4. Enter the C3C directory: `cd c3c`
5. Create the CMake build cache. The Fedora repositories provide `.so` libraries for lld, so you need to set the C3_LINK_DYNAMIC flag: `cmake -B build -S . -DC3_LINK_DYNAMIC=1` 5. Create a build directory and navigate into it: `mkdir build && cd build`
6. Build the project: `cmake --build build` 6. Create the CMake build cache. The Fedora repositories provide `.so` libraries for lld, so you need to set the C3_LINK_DYNAMIC flag: `cmake .. -DC3_LINK_DYNAMIC=1`
7. Enter the build directory: `cd build` 7. Build the project: `cmake --build .`
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` 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 #### Compiling on Arch Linux
1. Install required project dependencies: `sudo pacman -S curl lld llvm-libs clang cmake git libedit llvm libxml2` 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` 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` - 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` 3. Enter the C3C directory: `cd c3c`
@@ -399,7 +381,7 @@ cmake -B build \
-D C3_LINK_DYNAMIC=ON \ -D C3_LINK_DYNAMIC=ON \
-D CMAKE_BUILD_TYPE=Release -D CMAKE_BUILD_TYPE=Release
``` ```
5. Build the project: `cmake --build build`. 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`. 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`.
@@ -411,10 +393,11 @@ After compilation, the `c3c` binary will be located in the `build` directory. Yo
2. Install or compile LLVM and LLD *libraries* (version 17+ or higher) 2. Install or compile LLVM and LLD *libraries* (version 17+ or higher)
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git` 3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
4. Enter the C3C directory `cd c3c`. 4. Enter the C3C directory `cd c3c`.
5. Set up CMake build for debug: `cmake -B build -S .`. At this point you may need to manually 5. Create a build directory `mkdir build`
provide the link path to the LLVM CMake directories, e.g. `cmake -B build -S . -DLLVM_DIR=/usr/local/opt/llvm/lib/cmake/llvm/` 6. Change directory to the build directory `cd build`
6. Build: `cmake --build build` 7. Set up CMake build for debug: `cmake ..`. At this point you may need to manually
7. Change directory to the build directory `cd build` provide the link path to the LLVM CMake directories, e.g. `cmake -DLLVM_DIR=/usr/local/opt/llvm/lib/cmake/llvm/ ..`
8. Build: `cmake --build .`
*A note on compiling for Linux/Unix/MacOS: to be able to fetch vendor libraries *A note on compiling for Linux/Unix/MacOS: to be able to fetch vendor libraries
libcurl is needed. The CMake script should detect it if it is available. Note that libcurl is needed. The CMake script should detect it if it is available. Note that
@@ -442,12 +425,7 @@ Editor plugins can be found at https://github.com/c3lang/editor-plugins.
A huge **THANK YOU** goes out to all contributors and sponsors. A huge **THANK YOU** goes out to all contributors and sponsors.
A special thank you to sponsors [Zack Puhl](https://github.com/NotsoanoNimus) and [konimarti](https://github.com/konimarti) for going the extra mile. A special thank you to sponsors [Caleb-o](https://github.com/Caleb-o) and [devdad](https://github.com/devdad) for going the extra mile.
And honorable mention goes to past sponsors:
[Ygor Pontelo](https://github.com/ygorpontelo), [Simone Raimondi](https://github.com/SRaimondi),
[Jan Válek](https://github.com/jan-valek), [Pierre Curto](https://github.com/pierrec),
[Caleb-o](https://github.com/Caleb-o) and [devdad](https://github.com/devdad)
## Star History ## Star History

View File

@@ -1,218 +0,0 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. 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.
//
// Some benchmark test ideas are sourced from this article on C++ hashmap benchmarking:
// https://martin.ankerl.com/2022/08/27/hashmap-bench-01/
//
module hashmap_benchmarks;
import std::collections::map;
import std::math::random;
const DEFAULT_ITERATIONS = 16384;
Lcg64Random rand;
HashMap { int, int } modifying_numbers_random;
fn void bench_setup() @init
{
set_benchmark_warmup_iterations(3);
set_benchmark_max_iterations(DEFAULT_ITERATIONS);
// TODO: Cannot take the address of a @benchmark function. If we could, we could pass &insert_erase as a fn ptr and use the $qnameof CT eval internally.
set_benchmark_func_iterations($qnameof(insert_erase), 32);
set_benchmark_func_iterations($qnameof(random_access), 1024);
random::seed(&rand, 0x4528_21e6_38d0_1377);
for (usz i = 0; i < 1_000; ++i) modifying_numbers_random.set(rand.next_int(), rand.next_int());
}
// ==============================================================================================
module hashmap_benchmarks @benchmark;
import std::collections::map;
import std::math::random;
import std::encoding::base64;
fn void generic_hash_speeds()
{
(char){}.hash();
(char[<100>]){}.hash();
(char[100]){}.hash();
(ichar){}.hash();
(ichar[<100>]){}.hash();
(ichar[100]){}.hash();
(short){}.hash();
(short[<100>]){}.hash();
(short[100]){}.hash();
(ushort){}.hash();
(ushort[<100>]){}.hash();
(ushort[100]){}.hash();
(int){}.hash();
(int[<100>]){}.hash();
(int[100]){}.hash();
(uint){}.hash();
(uint[<100>]){}.hash();
(uint[100]){}.hash();
(long){}.hash();
(long[<20>]){}.hash();
(long[100]){}.hash();
(ulong){}.hash();
(ulong[<20>]){}.hash();
(ulong[100]){}.hash();
(int128){}.hash();
(int128[<20>]){}.hash();
(int128[100]){}.hash();
(uint128){}.hash();
(uint128[<20>]){}.hash();
(uint128[100]){}.hash();
(bool){}.hash();
(bool[<100>]){}.hash();
(bool[100]){}.hash();
String x = "abc";
char[] y = "abc";
assert(x.hash() == y.hash());
String z1 = "This is a much longer string than the above value because longer values lead to longer hashing times.";
char[] z2 = "This is a much longer string than the above value because longer values lead to longer hashing times.";
assert(z1.hash() == z2.hash());
assert(int.typeid.hash());
}
fn void hash_speeds_of_many_random_values() => @pool()
{
var $arrsz = 10_000;
uint fake_checksum;
char[] chars = allocator::new_array(tmem, char, $arrsz)[:$arrsz];
foreach (&v : chars) *v = (char)random::next(&rand, uint.max);
ushort[] shorts = allocator::new_array(tmem, ushort, $arrsz)[:$arrsz];
foreach (&v : shorts) *v = (ushort)random::next(&rand, uint.max);
uint[] ints = allocator::new_array(tmem, uint, $arrsz)[:$arrsz];
foreach (&v : ints) *v = random::next(&rand, uint.max);
ulong[] longs = allocator::new_array(tmem, ulong, $arrsz)[:$arrsz];
foreach (&v : longs) *v = (ulong)random::next(&rand, uint.max);
uint128[] vwideints = allocator::new_array(tmem, uint128, $arrsz)[:$arrsz];
foreach (&v : vwideints) *v = (uint128)random::next(&rand, uint.max);
char[48][] zstrs = allocator::new_array(tmem, char[48], $arrsz)[:$arrsz];
String[$arrsz] strs;
foreach (x, &v : zstrs)
{
foreach (&c : (*v)[:random::next(&rand, 48)]) *c = (char)random::next(&rand, char.max);
strs[x] = ((ZString)&v[0]).str_view();
}
runtime::@start_benchmark();
foreach (v : chars) fake_checksum += v.hash();
foreach (v : shorts) fake_checksum += v.hash();
foreach (v : ints) fake_checksum += v.hash();
foreach (v : longs) fake_checksum += v.hash();
foreach (v : vwideints) fake_checksum += v.hash();
foreach (v : strs) fake_checksum += v.hash();
runtime::@end_benchmark();
}
fn void modifying_numbers_init_from_map() => @pool()
{
HashMap { int, int } v;
v.tinit_from_map(&modifying_numbers_random);
v.free();
}
fn void insert_erase() => @pool()
{
uint iters = 1_000_000;
HashMap { int, int } v;
v.tinit();
runtime::@start_benchmark();
for (int i = 0; i < iters; ++i) v[i] = i;
for (int i = 0; i < iters; ++i) v.remove(i);
runtime::@end_benchmark();
v.free();
}
fn void random_access() => @pool()
{
HashMap { int, int } v;
v.tinit();
uint bound = 10_000;
usz pseudo_checksum = 0;
for (uint i = 0; i < bound; ++i) v[i] = i;
runtime::@start_benchmark();
for (uint i = 0; i < 1_000_000; ++i) pseudo_checksum += (v[i.hash() % bound] ?? 0);
runtime::@end_benchmark();
v.free();
}
fn void random_access_erase() => @pool()
{
HashMap { int, int } v;
v.tinit();
uint bound = 10_000;
for (uint i = 0; i < bound; ++i) v[i] = i;
runtime::@start_benchmark();
for (uint i = 0; i < bound; ++i)
{
v[i.hash() % bound] = i; // supplant an entry
v.remove(random::next(&rand, bound)); // remove a random entry
}
runtime::@end_benchmark();
v.free();
}
fn void random_access_string_keys() => @pool()
{
HashMap { String, ulong } v;
v.tinit();
usz pseudo_checksum = 0;
String[5_000] saved;
for (usz i = 0; i < saved.len; ++i)
{
ulong hash = i.hash();
String b64key = base64::tencode(@as_char_view(hash));
v[b64key] = hash;
if (i < saved.len) saved[i] = b64key;
}
runtime::@start_benchmark();
for (usz i = 0; i < saved.len; ++i)
{
pseudo_checksum += v[ saved[random::next(&rand, saved.len)] ]!! % 512;
}
runtime::@end_benchmark();
v.free();
}

View File

@@ -1,94 +0,0 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. 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 non_crypto_benchmarks;
const usz COMMON_ITERATIONS = 1 << 18;
const char[] COMMON_1 = { 0xA5 };
const char[] COMMON_4 = { 0xA5, 0xA5, 0xA5, 0xA5, };
const char[] COMMON_8 = { [0..7] = 0xA5 };
const char[] COMMON_16 = { [0..15] = 0xA5 };
const char[] COMMON_32 = { [0..31] = 0xA5 };
const char[] COMMON_64 = { [0..63] = 0xA5 };
const char[] COMMON_128 = { [0..127] = 0xA5 };
const char[] COMMON_1024 = { [0..1023] = 0xA5 };
fn void initialize_bench() @init
{
set_benchmark_warmup_iterations(3);
set_benchmark_max_iterations(COMMON_ITERATIONS + 3);
}
// =======================================================================================
module non_crypto_benchmarks @benchmark;
import std::hash;
fn void fnv64a_1() => fnv64a::hash(COMMON_1);
fn void fnv32a_1() => fnv32a::hash(COMMON_1);
fn void wyhash2_1() => wyhash2::hash(COMMON_1);
fn void metro64_1() => metro64::hash(COMMON_1);
fn void metro128_1() => metro128::hash(COMMON_1);
fn void a5hash_1() => a5hash::hash(COMMON_1);
fn void komi_1() => komi::hash(COMMON_1);
fn void fnv64a_4() => fnv64a::hash(COMMON_4);
fn void fnv32a_4() => fnv32a::hash(COMMON_4);
fn void wyhash2_4() => wyhash2::hash(COMMON_4);
fn void metro64_4() => metro64::hash(COMMON_4);
fn void metro128_4() => metro128::hash(COMMON_4);
fn void a5hash_4() => a5hash::hash(COMMON_4);
fn void komi_4() => komi::hash(COMMON_4);
fn void fnv64a_8() => fnv64a::hash(COMMON_8);
fn void fnv32a_8() => fnv32a::hash(COMMON_8);
fn void wyhash2_8() => wyhash2::hash(COMMON_8);
fn void metro64_8() => metro64::hash(COMMON_8);
fn void metro128_8() => metro128::hash(COMMON_8);
fn void a5hash_8() => a5hash::hash(COMMON_8);
fn void komi_8() => komi::hash(COMMON_8);
fn void fnv64a_16() => fnv64a::hash(COMMON_16);
fn void fnv32a_16() => fnv32a::hash(COMMON_16);
fn void wyhash2_16() => wyhash2::hash(COMMON_16);
fn void metro64_16() => metro64::hash(COMMON_16);
fn void metro128_16() => metro128::hash(COMMON_16);
fn void a5hash_16() => a5hash::hash(COMMON_16);
fn void komi_16() => komi::hash(COMMON_16);
fn void fnv64a_32() => fnv64a::hash(COMMON_32);
fn void fnv32a_32() => fnv32a::hash(COMMON_32);
// NOTE: wyhash2 cannot be used on inputs > 16 bytes.
fn void metro64_32() => metro64::hash(COMMON_32);
fn void metro128_32() => metro128::hash(COMMON_32);
fn void a5hash_32() => a5hash::hash(COMMON_32);
fn void komi_32() => komi::hash(COMMON_32);
fn void fnv64a_64() => fnv64a::hash(COMMON_64);
fn void fnv32a_64() => fnv32a::hash(COMMON_64);
// NOTE: wyhash2 cannot be used on inputs > 16 bytes.
fn void metro64_64() => metro64::hash(COMMON_64);
fn void metro128_64() => metro128::hash(COMMON_64);
fn void a5hash_64() => a5hash::hash(COMMON_64);
fn void komi_64() => komi::hash(COMMON_64);
fn void fnv64a_128() => fnv64a::hash(COMMON_128);
fn void fnv32a_128() => fnv32a::hash(COMMON_128);
// NOTE: wyhash2 cannot be used on inputs > 16 bytes.
fn void metro64_128() => metro64::hash(COMMON_128);
fn void metro128_128() => metro128::hash(COMMON_128);
fn void a5hash_128() => a5hash::hash(COMMON_128);
fn void komi_128() => komi::hash(COMMON_128);
fn void fnv64a_1024() => fnv64a::hash(COMMON_1024);
fn void fnv32a_1024() => fnv32a::hash(COMMON_1024);
// NOTE: wyhash2 cannot be used on inputs > 16 bytes.
fn void metro64_1024() => metro64::hash(COMMON_1024);
fn void metro128_1024() => metro128::hash(COMMON_1024);
fn void a5hash_1024() => a5hash::hash(COMMON_1024);
fn void komi_1024() => komi::hash(COMMON_1024);

View File

@@ -91,7 +91,7 @@ macro Type Atomic.or(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT
return @atomic_exec(atomic::fetch_or, data, value, ordering); return @atomic_exec(atomic::fetch_or, data, value, ordering);
} }
macro Type Atomic.xor(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT) fn Type Atomic.xor(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
{ {
Type* data = &self.data; Type* data = &self.data;
return @atomic_exec(atomic::fetch_xor, data, value, ordering); return @atomic_exec(atomic::fetch_xor, data, value, ordering);
@@ -171,7 +171,6 @@ macro bool is_native_atomic_type($Type)
$case SIGNED_INT: $case SIGNED_INT:
$case UNSIGNED_INT: $case UNSIGNED_INT:
$case POINTER: $case POINTER:
$case FUNC:
$case FLOAT: $case FLOAT:
$case BOOL: $case BOOL:
return true; return true;
@@ -194,7 +193,7 @@ macro bool is_native_atomic_type($Type)
@require $defined(*ptr) : "Expected a pointer" @require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr + y) : "+ must be defined between the values." @require $defined(*ptr + y) : "+ must be defined between the values."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*> *>
macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{ {
@@ -214,7 +213,7 @@ macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
@require $defined(*ptr) : "Expected a pointer" @require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr - y) : "- must be defined between the values." @require $defined(*ptr - y) : "- must be defined between the values."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*> *>
macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{ {
@@ -233,13 +232,13 @@ macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
@require $defined(*ptr) : "Expected a pointer" @require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr * y) : "* must be defined between the values." @require $defined(*ptr * y) : "* must be defined between the values."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*> *>
macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{ {
var $load_ordering = $ordering; var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = SEQ_CONSISTENT; $load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif $endif
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr)); var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
@@ -273,13 +272,13 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
@require $defined(*ptr) : "Expected a pointer" @require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr * y) : "/ must be defined between the values." @require $defined(*ptr * y) : "/ must be defined between the values."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*> *>
macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{ {
var $load_ordering = $ordering; var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = SEQ_CONSISTENT; $load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif $endif
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr)); var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
@@ -314,7 +313,7 @@ macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
@require $defined(*ptr) : "Expected a pointer" @require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr | y) : "| must be defined between the values." @require $defined(*ptr | y) : "| must be defined between the values."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*> *>
macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{ {
@@ -331,7 +330,7 @@ macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile
@require $defined(*ptr) : "Expected a pointer" @require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr ^ y) : "^ must be defined between the values." @require $defined(*ptr ^ y) : "^ must be defined between the values."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*> *>
macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{ {
@@ -348,7 +347,7 @@ macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
@require $defined(*ptr) : "Expected a pointer" @require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr ^ y) : "& must be defined between the values." @require $defined(*ptr ^ y) : "& must be defined between the values."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*> *>
macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{ {
@@ -365,13 +364,13 @@ macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require types::is_int($typeof(*ptr)) : "Only integer pointers may be used." @require types::is_int($typeof(*ptr)) : "Only integer pointers may be used."
@require types::is_int($typeof(y)) : "The value for shift right must be an integer" @require types::is_int($typeof(y)) : "The value for shift right must be an integer"
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*> *>
macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{ {
var $load_ordering = $ordering; var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = SEQ_CONSISTENT; $load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif $endif
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr)); var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
@@ -407,13 +406,13 @@ macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require types::is_int($typeof(*ptr)) : "Only integer pointers may be used." @require types::is_int($typeof(*ptr)) : "Only integer pointers may be used."
@require types::is_int($typeof(y)) : "The value for shift left must be an integer" @require types::is_int($typeof(y)) : "The value for shift left must be an integer"
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*> *>
macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT) macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{ {
var $load_ordering = $ordering; var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = SEQ_CONSISTENT; $load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif $endif
var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr)); var $StorageType = types::lower_to_atomic_compatible_type($typeof(*ptr));
@@ -448,7 +447,7 @@ macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require types::flat_kind($typeof(*ptr)) == BOOL : "Only bool pointers may be used." @require types::flat_kind($typeof(*ptr)) == BOOL : "Only bool pointers may be used."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*> *>
macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT) macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
{ {
@@ -456,7 +455,7 @@ macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
$typeof(*ptr) new_value = true; $typeof(*ptr) new_value = true;
var $load_ordering = $ordering; var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = SEQ_CONSISTENT; $load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif $endif
do do
{ {
@@ -475,7 +474,7 @@ macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
@require $defined(*ptr) : "Expected a pointer" @require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require types::flat_kind($typeof(*ptr)) == BOOL : "Only bool pointers may be used." @require types::flat_kind($typeof(*ptr)) == BOOL : "Only bool pointers may be used."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*> *>
macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT) macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
{ {
@@ -483,7 +482,7 @@ macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
$typeof(*ptr) new_value = false; $typeof(*ptr) new_value = false;
var $load_ordering = $ordering; var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE: $if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = SEQ_CONSISTENT; $load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif $endif
do do
{ {
@@ -503,7 +502,7 @@ macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
@require $defined(*ptr) : "Expected a pointer" @require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr > y) : "Only values that are comparable with > may be used" @require $defined(*ptr > y) : "Only values that are comparable with > may be used"
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*> *>
macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{ {
@@ -522,7 +521,7 @@ macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
@require $defined(*ptr) : "Expected a pointer" @require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used." @require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr > y) : "Only values that are comparable with > may be used" @require $defined(*ptr > y) : "Only values that are comparable with > may be used"
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid." @require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*> *>
macro fetch_min(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0) macro fetch_min(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{ {

View File

@@ -90,7 +90,7 @@ macro HashMap* HashMap.init_with_key_values(&self, Allocator allocator, ..., uin
*> *>
macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{ {
return self.init_with_key_values(tmem, $vasplat, capacity: capacity, load_factor: load_factor); return self.tinit_with_key_values(tmem, capacity, load_factor);
} }
<* <*
@@ -195,7 +195,7 @@ fn Entry*? HashMap.get_entry(&map, Key key)
<* <*
Get the value or update and Get the value or update and
@require @assignable_to(#expr, Value) @require $assignable(#expr, Value)
*> *>
macro Value HashMap.@get_or_set(&map, Key key, Value #expr) macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
{ {

View File

@@ -1,636 +0,0 @@
<*
@require $defined((Value){}.hash()) : `No .hash function found on the value`
*>
module std::collections::set {Value};
import std::math;
import std::io @norecurse;
const uint DEFAULT_INITIAL_CAPACITY = 16;
const uint MAXIMUM_CAPACITY = 1u << 31;
const float DEFAULT_LOAD_FACTOR = 0.75;
const Allocator SET_HEAP_ALLOCATOR = (Allocator)&dummy;
<* Copy the ONHEAP allocator to initialize to a set that is heap allocated *>
const HashSet ONHEAP = { .allocator = SET_HEAP_ALLOCATOR };
struct Entry
{
uint hash;
Value value;
Entry* next;
}
struct HashSet (Printable)
{
Entry*[] table;
Allocator allocator;
usz count; // Number of elements
usz threshold; // Resize limit
float load_factor;
}
fn int HashSet.len(&self) @operator(len) => (int) self.count;
<*
@param [&inout] allocator : "The allocator to use"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn HashSet* HashSet.init(&self, Allocator allocator, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
capacity = math::next_power_of_2(capacity);
self.allocator = allocator;
self.threshold = (usz) (capacity * load_factor);
self.load_factor = load_factor;
self.table = allocator::new_array(allocator, Entry*, capacity);
return self;
}
<*
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn HashSet* HashSet.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init(tmem, capacity, load_factor) @inline;
}
<*
@param [&inout] allocator : "The allocator to use"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro HashSet* HashSet.init_with_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.init(allocator, capacity, load_factor);
$for var $i = 0; $i < $vacount; $i++:
self.add($vaarg[$i]);
$endfor
return self;
}
<*
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro HashSet* HashSet.tinit_with_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init_with_values(tmem, $vasplat, capacity: capacity, load_factor: load_factor);
}
<*
@param [in] values : "The values for the HashSet"
@param [&inout] allocator : "The allocator to use"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn HashSet* HashSet.init_from_values(&self, Allocator allocator, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.init(allocator, capacity, load_factor);
foreach (v : values) self.add(v);
return self;
}
<*
@param [in] values : "The values for the HashSet entries"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn HashSet* HashSet.tinit_from_values(&self, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init_from_values(tmem, values, capacity, load_factor);
}
<*
Has this hash set been initialized yet?
@param [&in] set : "The hash set we are testing"
@return "Returns true if it has been initialized, false otherwise"
*>
fn bool HashSet.is_initialized(&set)
{
return set.allocator && set.allocator.ptr != &dummy;
}
<*
@param [&inout] allocator : "The allocator to use"
@param [&in] other_set : "The set to copy from."
@require !self.is_initialized() : "Set was already initialized"
*>
fn HashSet* HashSet.init_from_set(&self, Allocator allocator, HashSet* other_set)
{
self.init(allocator, other_set.table.len, other_set.load_factor);
self.put_all_for_create(other_set);
return self;
}
<*
@param [&in] other_set : "The set to copy from."
@require !set.is_initialized() : "Set was already initialized"
*>
fn HashSet* HashSet.tinit_from_set(&set, HashSet* other_set)
{
return set.init_from_set(tmem, other_set) @inline;
}
<*
Check if the set is empty
@return "true if it is empty"
@pure
*>
fn bool HashSet.is_empty(&set) @inline
{
return !set.count;
}
<*
Add all elements in the slice to the set.
@param [in] list
@return "The number of new elements added"
@ensure total <= list.len
*>
fn usz HashSet.add_all(&set, Value[] list)
{
usz total;
foreach (v : list)
{
if (set.add(v)) total++;
}
return total;
}
<*
@param [&in] other
@return "The number of new elements added"
@ensure return <= other.count
*>
fn usz HashSet.add_all_from(&set, HashSet* other)
{
usz total;
other.@each(;Value value)
{
if (set.add(value)) total++;
};
return total;
}
<*
@param value : "The value to add"
@return "true if the value didn't exist in the set"
*>
fn bool HashSet.add(&set, Value value)
{
// If the set isn't initialized, use the defaults to initialize it.
switch (set.allocator.ptr)
{
case &dummy:
set.init(mem);
case null:
set.tinit();
default:
break;
}
uint hash = rehash(value.hash());
uint index = index_for(hash, set.table.len);
for (Entry *e = set.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(value, e.value)) return false;
}
set.add_entry(hash, value, index);
return true;
}
<*
Iterate over all the values in the set
*>
macro HashSet.@each(set; @body(value))
{
if (!set.count) return;
foreach (Entry* entry : set.table)
{
while (entry)
{
@body(entry.value);
entry = entry.next;
}
}
}
<*
Check if the set contains the given value.
@param value : "The value to check"
@return "true if it exists in the set"
*>
fn bool HashSet.contains(&set, Value value)
{
if (!set.count) return false;
uint hash = rehash(value.hash());
for (Entry *e = set.table[index_for(hash, set.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(value, e.value)) return true;
}
return false;
}
<*
Remove a single value from the set.
@param value : "The value to remove"
@return? NOT_FOUND : "If the entry is not found"
*>
fn void? HashSet.remove(&set, Value value) @maydiscard
{
if (!set.remove_entry_for_value(value)) return NOT_FOUND?;
}
fn usz HashSet.remove_all(&set, Value[] values)
{
usz total;
foreach (v : values)
{
if (set.remove_entry_for_value(v)) total++;
}
return total;
}
<*
@param [&in] other : "Other set"
*>
fn usz HashSet.remove_all_from(&set, HashSet* other)
{
usz total;
other.@each(;Value val)
{
if (set.remove_entry_for_value(val)) total++;
};
return total;
}
<*
Free all memory allocated by the hash set.
*>
fn void HashSet.free(&set)
{
if (!set.is_initialized()) return;
set.clear();
set.free_internal(set.table.ptr);
*set = {};
}
<*
Clear all elements from the set while keeping the underlying storage
@ensure set.count == 0
*>
fn void HashSet.clear(&set)
{
if (!set.count) return;
foreach (Entry** &entry_ref : set.table)
{
Entry* entry = *entry_ref;
if (!entry) continue;
Entry *next = entry.next;
while (next)
{
Entry *to_delete = next;
next = next.next;
set.free_entry(to_delete);
}
set.free_entry(entry);
*entry_ref = null;
}
set.count = 0;
}
fn void HashSet.reserve(&set, usz capacity)
{
if (capacity > set.threshold)
{
set.resize(math::next_power_of_2(capacity));
}
}
// --- Set Operations ---
<*
Returns the union of two sets (A | B)
@param [&in] other : "The other set to union with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing the union of both sets"
*>
fn HashSet HashSet.set_union(&self, Allocator allocator, HashSet* other)
{
usz new_capacity = math::next_power_of_2(self.count + other.count);
HashSet result;
result.init(allocator, new_capacity, self.load_factor);
result.add_all_from(self);
result.add_all_from(other);
return result;
}
fn HashSet HashSet.tset_union(&self, HashSet* other) => self.set_union(tmem, other);
<*
Returns the intersection of the two sets (A & B)
@param [&in] other : "The other set to intersect with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing the intersection of both sets"
*>
fn HashSet HashSet.intersection(&self, Allocator allocator, HashSet* other)
{
HashSet result;
result.init(allocator, math::min(self.table.len, other.table.len), self.load_factor);
// Iterate through the smaller set for efficiency
HashSet* smaller = self.count <= other.count ? self : other;
HashSet* larger = self.count > other.count ? self : other;
smaller.@each(;Value value)
{
if (larger.contains(value)) result.add(value);
};
return result;
}
fn HashSet HashSet.tintersection(&self, HashSet* other) => self.intersection(tmem, other);
<*
Return this set - other, so (A & ~B)
@param [&in] other : "The other set to compare with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing elements in this set but not in the other"
*>
fn HashSet HashSet.difference(&self, Allocator allocator, HashSet* other)
{
HashSet result;
result.init(allocator, self.table.len, self.load_factor);
self.@each(;Value value)
{
if (!other.contains(value))
{
result.add(value);
}
};
return result;
}
fn HashSet HashSet.tdifference(&self, HashSet* other) => self.difference(tmem, other) @inline;
<*
Return (A ^ B)
@param [&in] other : "The other set to compare with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing elements in this set or the other, but not both"
*>
fn HashSet HashSet.symmetric_difference(&self, Allocator allocator, HashSet* other)
{
HashSet result;
result.init(allocator, self.table.len, self.load_factor);
result.add_all_from(self);
other.@each(;Value value)
{
if (!result.add(value))
{
result.remove(value);
}
};
return result;
}
fn HashSet HashSet.tsymmetric_difference(&self, HashSet* other) => self.symmetric_difference(tmem, other) @inline;
<*
Check if this hash set is a subset of another set.
@param [&in] other : "The other set to check against"
@return "True if all elements of this set are in the other set"
*>
fn bool HashSet.is_subset(&self, HashSet* other)
{
if (self.count == 0) return true;
if (self.count > other.count) return false;
self.@each(;Value value)
{
if (!other.contains(value)) return false;
};
return true;
}
// --- private methods
fn void HashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
{
Entry* entry = allocator::new(set.allocator, Entry, { .hash = hash, .value = value, .next = set.table[bucket_index] });
set.table[bucket_index] = entry;
if (set.count++ >= set.threshold)
{
set.resize(set.table.len * 2);
}
}
fn void HashSet.resize(&self, usz new_capacity) @private
{
Entry*[] old_table = self.table;
usz old_capacity = old_table.len;
if (old_capacity == MAXIMUM_CAPACITY)
{
self.threshold = uint.max;
return;
}
Entry*[] new_table = allocator::new_array(self.allocator, Entry*, new_capacity);
self.transfer(new_table);
self.table = new_table;
self.free_internal(old_table.ptr);
self.threshold = (uint)(new_capacity * self.load_factor);
}
fn usz? HashSet.to_format(&self, Formatter* f) @dynamic
{
usz len;
len += f.print("{ ")!;
self.@each(; Value value)
{
if (len > 2) len += f.print(", ")!;
len += f.printf("%s", value)!;
};
return len + f.print(" }");
}
fn void HashSet.transfer(&self, Entry*[] new_table) @private
{
Entry*[] src = self.table;
uint new_capacity = new_table.len;
foreach (uint j, Entry *e : src)
{
if (!e) continue;
do
{
Entry* next = e.next;
uint i = index_for(e.hash, new_capacity);
e.next = new_table[i];
new_table[i] = e;
e = next;
}
while (e);
}
}
fn void HashSet.put_all_for_create(&set, HashSet* other_set) @private
{
if (!other_set.count) return;
foreach (Entry *e : other_set.table)
{
while (e)
{
set.put_for_create(e.value);
e = e.next;
}
}
}
fn void HashSet.put_for_create(&set, Value value) @private
{
uint hash = rehash(value.hash());
uint i = index_for(hash, set.table.len);
for (Entry *e = set.table[i]; e != null; e = e.next)
{
if (e.hash == hash && equals(value, e.value))
{
// Value already exists, no need to do anything
return;
}
}
set.create_entry(hash, value, i);
}
fn void HashSet.free_internal(&self, void* ptr) @inline @private
{
allocator::free(self.allocator, ptr);
}
fn void HashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
{
Entry* entry = allocator::new(set.allocator, Entry, {
.hash = hash,
.value = value,
.next = set.table[bucket_index]
});
set.table[bucket_index] = entry;
set.count++;
}
<*
Removes the entry for the specified value if present
@return "true if found and removed, false otherwise"
*>
fn bool HashSet.remove_entry_for_value(&set, Value value) @private
{
if (!set.count) return false;
uint hash = rehash(value.hash());
uint i = index_for(hash, set.table.len);
Entry* prev = set.table[i];
Entry* e = prev;
while (e)
{
Entry *next = e.next;
if (e.hash == hash && equals(value, e.value))
{
set.count--;
if (prev == e)
{
set.table[i] = next;
}
else
{
prev.next = next;
}
set.free_entry(e);
return true;
}
prev = e;
e = next;
}
return false;
}
fn void HashSet.free_entry(&set, Entry *entry) @private
{
allocator::free(set.allocator, entry);
}
struct HashSetIterator
{
HashSet* set;
usz bucket_index;
Entry* current;
}
fn HashSetIterator HashSet.iter(&set) => { .set = set, .bucket_index = 0, .current = null };
fn Value? HashSetIterator.next(&self)
{
if (self.current)
{
Value value = self.current.value;
self.current = self.current.next;
return value;
}
while (self.bucket_index < self.set.table.len)
{
self.current = self.set.table[self.bucket_index++];
if (self.current)
{
Value value = self.current.value;
self.current = self.current.next;
return value;
}
}
return NOT_FOUND?;
}
fn usz HashSetIterator.len(&self) @operator(len)
{
return self.set.count;
}
<* @pure *>
fn uint rehash(uint hash) @inline @private
{
hash ^= (hash >> 20) ^ (hash >> 12);
return hash ^ ((hash >> 7) ^ (hash >> 4));
}
macro uint index_for(uint hash, uint capacity) @private => hash & (capacity - 1);
int dummy @local;

View File

@@ -1,327 +0,0 @@
module std::collections::blockingqueue { Value };
import std::thread, std::time;
const INITIAL_CAPACITY = 16;
struct QueueEntry
{
Value value;
QueueEntry* next; // Next in queue order
QueueEntry* prev; // Previous in queue order
}
struct LinkedBlockingQueue
{
QueueEntry* head; // First element in queue
QueueEntry* tail; // Last element in queue
usz count; // Current number of elements
usz capacity; // Maximum capacity (0 for unbounded)
Mutex lock;
ConditionVariable not_empty;
ConditionVariable not_full;
Allocator allocator;
}
<*
@param [&inout] allocator : "The allocator to use"
@param capacity : "Maximum capacity (0 for unbounded)"
@require !self.is_initialized() : "Queue was already initialized"
*>
fn LinkedBlockingQueue* LinkedBlockingQueue.init(&self, Allocator allocator, usz capacity = 0)
{
self.allocator = allocator;
self.capacity = capacity;
self.count = 0;
self.head = null;
self.tail = null;
self.lock.init()!!;
self.not_empty.init()!!;
self.not_full.init()!!;
return self;
}
fn LinkedBlockingQueue* LinkedBlockingQueue.tinit(&self, usz capacity = 0)
{
return self.init(tmem, capacity) @inline;
}
<*
@require self.is_initialized() : "Queue must be initialized"
*>
fn void LinkedBlockingQueue.free(&self)
{
self.lock.@in_lock()
{
// Free all remaining entries
QueueEntry* entry = self.head;
while (entry != null)
{
QueueEntry* next = entry.next;
allocator::free(self.allocator, entry);
entry = next;
}
};
(void)self.lock.destroy();
(void)self.not_empty.destroy();
(void)self.not_full.destroy();
}
fn void LinkedBlockingQueue.link_entry(&self, QueueEntry* entry) @private
{
entry.next = null;
entry.prev = self.tail;
if (self.tail == null)
{
// First element in queue
self.head = entry;
}
else
{
// Append to tail
self.tail.next = entry;
}
self.tail = entry;
self.count++;
}
fn QueueEntry* LinkedBlockingQueue.unlink_head(&self) @private
{
if (self.head == null) return null;
QueueEntry* entry = self.head;
self.head = entry.next;
if (self.head != null)
{
self.head.prev = null;
}
else
{
// Queue is now empty
self.tail = null;
}
self.count--;
return entry;
}
<*
@param value : "Value to add to the queue"
@require self.is_initialized() : "Queue must be initialized"
*>
fn void LinkedBlockingQueue.push(&self, Value value)
{
self.lock.@in_lock()
{
while (self.capacity > 0 && self.count >= self.capacity)
{
self.not_full.wait(&self.lock)!!;
}
QueueEntry* entry = allocator::new(self.allocator, QueueEntry, {
.value = value,
.next = null,
.prev = null
});
self.link_entry(entry);
// Signal that queue is no longer empty
self.not_empty.signal()!!;
};
}
<*
Get a value from the queue, blocking if there is no element in the queue.
@require self.is_initialized() : "Queue must be initialized"
@return "The removed value"
*>
fn Value LinkedBlockingQueue.poll(&self)
{
self.lock.@in_lock()
{
while (self.count == 0)
{
self.not_empty.wait(&self.lock)!!;
}
QueueEntry* entry = self.unlink_head();
Value value = entry.value;
allocator::free(self.allocator, entry);
if (self.capacity > 0)
{
self.not_full.signal()!!;
}
return value;
};
}
<*
Pop an element from the queue, fail is it is empty.
@require self.is_initialized() : "Queue must be initialized"
@return "The removed value"
@return? NO_MORE_ELEMENT : "If the queue is empty"
*>
fn Value? LinkedBlockingQueue.pop(&self)
{
self.lock.@in_lock()
{
if (self.count == 0) return NO_MORE_ELEMENT?;
QueueEntry* entry = self.unlink_head();
Value value = entry.value;
allocator::free(self.allocator, entry);
if (self.capacity > 0)
{
self.not_full.signal()!!;
}
return value;
};
}
<*
Poll with a timeout.
@param timeout : "Timeout in microseconds"
@require self.is_initialized() : "Queue must be initialized"
@return "The removed value or null if timeout occurred"
@return? NO_MORE_ELEMENT : "If we reached the timeout"
*>
fn Value? LinkedBlockingQueue.poll_timeout(&self, Duration timeout)
{
self.lock.@in_lock()
{
// Use while loop to handle spurious wakeups
if (!self.count)
{
Time start = time::now();
Time end = start + timeout;
while (!self.count)
{
if (end <= time::now()) break;
if (catch self.not_empty.wait_until(&self.lock, end)) break;
}
if (!self.count) return NO_MORE_ELEMENT?;
}
QueueEntry* entry = self.unlink_head();
Value value = entry.value;
allocator::free(self.allocator, entry);
// Must signal not_full after removing an item
if (self.capacity > 0)
{
self.not_full.signal()!!;
}
return value;
};
}
<*
@require self.is_initialized() : "Queue must be initialized"
@return "Current size of the queue"
*>
fn usz LinkedBlockingQueue.size(&self)
{
self.lock.@in_lock()
{
return self.count;
};
}
<*
@require self.is_initialized() : "Queue must be initialized"
@return "True if queue is empty"
*>
fn bool LinkedBlockingQueue.is_empty(&self)
{
self.lock.@in_lock()
{
return self.count == 0;
};
}
<*
Try to push, return CAPACITY_EXCEEDED if the queue is full.
@param value : "Value to add to the queue"
@require self.is_initialized() : "Queue must be initialized"
@return? CAPACITY_EXCEEDED : "If the queue is full"
*>
fn void? LinkedBlockingQueue.try_push(&self, Value value)
{
self.lock.@in_lock()
{
if (self.capacity > 0 && self.count >= self.capacity) return CAPACITY_EXCEEDED?;
QueueEntry* entry = allocator::new(self.allocator, QueueEntry, {
.value = value,
.next = null,
.prev = null
});
self.link_entry(entry);
self.not_empty.signal()!!;
};
}
<*
Try to push, return CAPACITY_EXCEEDED if the queue is still full after timeout is reached.
@param value : "Value to add to the queue"
@param timeout : "Timeout in microseconds"
@require self.is_initialized() : "Queue must be initialized"
@return? CAPACITY_EXCEEDED : "If the queue is full"
*>
fn void? LinkedBlockingQueue.push_timeout(&self, Value value, Duration timeout)
{
self.lock.@in_lock()
{
if (self.capacity > 0 && self.count >= self.capacity)
{
Time start = time::now();
Time end = start + timeout;
while (self.capacity > 0 && self.count >= self.capacity)
{
if (end <= time::now()) break;
if (catch self.not_empty.wait_until(&self.lock, end)) break;
}
if (self.capacity > 0 && self.count >= self.capacity) return CAPACITY_EXCEEDED?;
}
QueueEntry* entry = allocator::new(self.allocator, QueueEntry, {
.value = value,
.next = null,
.prev = null
});
self.link_entry(entry);
self.not_empty.signal()!!;
};
}
<*
@require self.is_initialized() : "Queue must be initialized"
@return "The head value or NO_MORE_ELEMENT? if queue is empty"
*>
fn Value? LinkedBlockingQueue.peek(&self)
{
self.lock.@in_lock()
{
return (self.head != null) ? self.head.value : NO_MORE_ELEMENT?;
};
}
<*
@return "True if queue is initialized"
*>
fn bool LinkedBlockingQueue.is_initialized(&self)
{
return self.allocator && self.lock.initialized;
}

View File

@@ -1,645 +0,0 @@
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
<*
@require $defined((Key){}.hash()) : `No .hash function found on the key`
*>
module std::collections::map{Key, Value};
import std::math;
import std::io @norecurse;
const LinkedHashMap LINKEDONHEAP = { .allocator = MAP_HEAP_ALLOCATOR };
struct LinkedEntry
{
uint hash;
Key key;
Value value;
LinkedEntry* next; // For bucket chain
LinkedEntry* before; // Previous in insertion order
LinkedEntry* after; // Next in insertion order
}
struct LinkedHashMap (Printable)
{
LinkedEntry*[] table;
Allocator allocator;
usz count;
usz threshold;
float load_factor;
LinkedEntry* head; // First inserted LinkedEntry
LinkedEntry* tail; // Last inserted LinkedEntry
}
<*
@param [&inout] allocator : "The allocator to use"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashMap* LinkedHashMap.init(&self, Allocator allocator, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
capacity = math::next_power_of_2(capacity);
self.allocator = allocator;
self.load_factor = load_factor;
self.threshold = (usz)(capacity * load_factor);
self.table = allocator::new_array(allocator, LinkedEntry*, capacity);
self.head = null;
self.tail = null;
return self;
}
<*
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashMap* LinkedHashMap.tinit(&self, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init(tmem, capacity, load_factor) @inline;
}
<*
@param [&inout] allocator : "The allocator to use"
@require $vacount % 2 == 0 : "There must be an even number of arguments provided for keys and values"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro LinkedHashMap* LinkedHashMap.init_with_key_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.init(allocator, capacity, load_factor);
$for var $i = 0; $i < $vacount; $i += 2:
self.set($vaarg[$i], $vaarg[$i + 1]);
$endfor
return self;
}
<*
@require $vacount % 2 == 0 : "There must be an even number of arguments provided for keys and values"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro LinkedHashMap* LinkedHashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init_with_key_values(tmem, $vasplat, capacity: capacity, load_factor: load_factor);
}
<*
@param [in] keys : "The keys for the LinkedHashMap entries"
@param [in] values : "The values for the LinkedHashMap entries"
@param [&inout] allocator : "The allocator to use"
@require keys.len == values.len : "Both keys and values arrays must be the same length"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashMap* LinkedHashMap.init_from_keys_and_values(&self, Allocator allocator, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
assert(keys.len == values.len);
self.init(allocator, capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
self.set(keys[i], values[i]);
}
return self;
}
<*
@param [in] keys : "The keys for the LinkedHashMap entries"
@param [in] values : "The values for the LinkedHashMap entries"
@require keys.len == values.len : "Both keys and values arrays must be the same length"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashMap* LinkedHashMap.tinit_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init_from_keys_and_values(tmem, keys, values, capacity, load_factor);
}
<*
Has this hash map been initialized yet?
@param [&in] map : "The hash map we are testing"
@return "Returns true if it has been initialized, false otherwise"
*>
fn bool LinkedHashMap.is_initialized(&map)
{
return map.allocator && map.allocator.ptr != &dummy;
}
<*
@param [&inout] allocator : "The allocator to use"
@param [&in] other_map : "The map to copy from."
@require !self.is_initialized() : "Map was already initialized"
*>
fn LinkedHashMap* LinkedHashMap.init_from_map(&self, Allocator allocator, LinkedHashMap* other_map)
{
self.init(allocator, other_map.table.len, other_map.load_factor);
self.put_all_for_create(other_map);
return self;
}
<*
@param [&in] other_map : "The map to copy from."
@require !map.is_initialized() : "Map was already initialized"
*>
fn LinkedHashMap* LinkedHashMap.tinit_from_map(&map, LinkedHashMap* other_map)
{
return map.init_from_map(tmem, other_map) @inline;
}
fn bool LinkedHashMap.is_empty(&map) @inline
{
return !map.count;
}
fn usz LinkedHashMap.len(&map) @inline => map.count;
fn Value*? LinkedHashMap.get_ref(&map, Key key)
{
if (!map.count) return NOT_FOUND?;
uint hash = rehash(key.hash());
for (LinkedEntry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return &e.value;
}
return NOT_FOUND?;
}
fn LinkedEntry*? LinkedHashMap.get_entry(&map, Key key)
{
if (!map.count) return NOT_FOUND?;
uint hash = rehash(key.hash());
for (LinkedEntry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e;
}
return NOT_FOUND?;
}
<*
Get the value or update and
@require @assignable_to(#expr, Value)
*>
macro Value LinkedHashMap.@get_or_set(&map, Key key, Value #expr)
{
if (!map.count)
{
Value val = #expr;
map.set(key, val);
return val;
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (LinkedEntry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e.value;
}
Value val = #expr;
map.add_entry(hash, key, val, index);
return val;
}
fn Value? LinkedHashMap.get(&map, Key key) @operator([]) => *map.get_ref(key) @inline;
fn bool LinkedHashMap.has_key(&map, Key key) => @ok(map.get_ref(key));
fn bool LinkedHashMap.set(&map, Key key, Value value) @operator([]=)
{
// If the map isn't initialized, use the defaults to initialize it.
switch (map.allocator.ptr)
{
case &dummy:
map.init(mem);
case null:
map.tinit();
default:
break;
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (LinkedEntry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return true;
}
}
map.add_entry(hash, key, value, index);
return false;
}
fn void? LinkedHashMap.remove(&map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return NOT_FOUND?;
}
fn void LinkedHashMap.clear(&map)
{
if (!map.count) return;
LinkedEntry* entry = map.head;
while (entry)
{
LinkedEntry* next = entry.after;
map.free_entry(entry);
entry = next;
}
foreach (LinkedEntry** &bucket : map.table)
{
*bucket = null;
}
map.count = 0;
map.head = null;
map.tail = null;
}
fn void LinkedHashMap.free(&map)
{
if (!map.is_initialized()) return;
map.clear();
map.free_internal(map.table.ptr);
map.table = {};
}
fn Key[] LinkedHashMap.tkeys(&self)
{
return self.keys(tmem) @inline;
}
fn Key[] LinkedHashMap.keys(&self, Allocator allocator)
{
if (!self.count) return {};
Key[] list = allocator::alloc_array(allocator, Key, self.count);
usz index = 0;
LinkedEntry* entry = self.head;
while (entry)
{
$if COPY_KEYS:
list[index++] = entry.key.copy(allocator);
$else
list[index++] = entry.key;
$endif
entry = entry.after;
}
return list;
}
macro LinkedHashMap.@each(map; @body(key, value))
{
map.@each_entry(; LinkedEntry* entry)
{
@body(entry.key, entry.value);
};
}
macro LinkedHashMap.@each_entry(map; @body(entry))
{
LinkedEntry* entry = map.head;
while (entry)
{
@body(entry);
entry = entry.after;
}
}
fn Value[] LinkedHashMap.tvalues(&map) => map.values(tmem) @inline;
fn Value[] LinkedHashMap.values(&self, Allocator allocator)
{
if (!self.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, self.count);
usz index = 0;
LinkedEntry* entry = self.head;
while (entry)
{
list[index++] = entry.value;
entry = entry.after;
}
return list;
}
fn bool LinkedHashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
{
if (!map.count) return false;
LinkedEntry* entry = map.head;
while (entry)
{
if (equals(v, entry.value)) return true;
entry = entry.after;
}
return false;
}
fn LinkedHashMapIterator LinkedHashMap.iter(&self) => { .map = self, .current = self.head, .started = false };
fn LinkedHashMapValueIterator LinkedHashMap.value_iter(&self) => { .map = self, .current = self.head, .started = false };
fn LinkedHashMapKeyIterator LinkedHashMap.key_iter(&self) => { .map = self, .current = self.head, .started = false };
fn bool LinkedHashMapIterator.next(&self)
{
if (!self.started)
{
self.current = self.map.head;
self.started = true;
}
else if (self.current)
{
self.current = self.current.after;
}
return self.current != null;
}
fn LinkedEntry*? LinkedHashMapIterator.get(&self)
{
return self.current ? self.current : NOT_FOUND?;
}
fn Value*? LinkedHashMapValueIterator.get(&self)
{
return self.current ? &self.current.value : NOT_FOUND?;
}
fn Key*? LinkedHashMapKeyIterator.get(&self)
{
return self.current ? &self.current.key : NOT_FOUND?;
}
fn bool LinkedHashMapIterator.has_next(&self)
{
if (!self.started) return self.map.head != null;
return self.current && self.current.after != null;
}
// --- private methods
fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
{
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
LinkedEntry* entry = allocator::new(map.allocator, LinkedEntry, {
.hash = hash,
.key = key,
.value = value,
.next = map.table[bucket_index],
.before = map.tail,
.after = null
});
// Update bucket chain
map.table[bucket_index] = entry;
// Update linked list
if (map.tail)
{
map.tail.after = entry;
entry.before = map.tail;
}
else
{
map.head = entry;
}
map.tail = entry;
if (map.count++ >= map.threshold)
{
map.resize(map.table.len * 2);
}
}
fn void LinkedHashMap.resize(&map, uint new_capacity) @private
{
LinkedEntry*[] old_table = map.table;
uint old_capacity = old_table.len;
if (old_capacity == MAXIMUM_CAPACITY)
{
map.threshold = uint.max;
return;
}
LinkedEntry*[] new_table = allocator::new_array(map.allocator, LinkedEntry*, new_capacity);
map.table = new_table;
map.threshold = (uint)(new_capacity * map.load_factor);
// Rehash all entries - linked list order remains unchanged
foreach (uint i, LinkedEntry *e : old_table)
{
if (!e) continue;
// Split the bucket chain into two chains based on new bit
LinkedEntry* lo_head = null;
LinkedEntry* lo_tail = null;
LinkedEntry* hi_head = null;
LinkedEntry* hi_tail = null;
do
{
LinkedEntry* next = e.next;
if ((e.hash & old_capacity) == 0)
{
if (!lo_tail)
{
lo_head = e;
}
else
{
lo_tail.next = e;
}
lo_tail = e;
}
else
{
if (!hi_tail)
{
hi_head = e;
}
else
{
hi_tail.next = e;
}
hi_tail = e;
}
e.next = null;
e = next;
}
while (e);
if (lo_tail)
{
lo_tail.next = null;
new_table[i] = lo_head;
}
if (hi_tail)
{
hi_tail.next = null;
new_table[i + old_capacity] = hi_head;
}
}
map.free_internal(old_table.ptr);
}
fn usz? LinkedHashMap.to_format(&self, Formatter* f) @dynamic
{
usz len;
len += f.print("{ ")!;
self.@each_entry(; LinkedEntry* entry)
{
if (len > 2) len += f.print(", ")!;
len += f.printf("%s: %s", entry.key, entry.value)!;
};
return len + f.print(" }");
}
fn void LinkedHashMap.transfer(&map, LinkedEntry*[] new_table) @private
{
LinkedEntry*[] src = map.table;
uint new_capacity = new_table.len;
foreach (uint j, LinkedEntry *e : src)
{
if (!e) continue;
do
{
LinkedEntry* next = e.next;
uint i = index_for(e.hash, new_capacity);
e.next = new_table[i];
new_table[i] = e;
e = next;
}
while (e);
}
}
fn void LinkedHashMap.put_all_for_create(&map, LinkedHashMap* other_map) @private
{
if (!other_map.count) return;
other_map.@each(; Key key, Value value) {
map.set(key, value);
};
}
fn void LinkedHashMap.put_for_create(&map, Key key, Value value) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
for (LinkedEntry *e = map.table[i]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return;
}
}
map.create_entry(hash, key, value, i);
}
fn void LinkedHashMap.free_internal(&map, void* ptr) @inline @private
{
allocator::free(map.allocator, ptr);
}
fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
{
if (!map.count) return false;
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
LinkedEntry* prev = null;
LinkedEntry* e = map.table[i];
while (e)
{
if (e.hash == hash && equals(key, e.key))
{
if (prev)
{
prev.next = e.next;
}
else
{
map.table[i] = e.next;
}
if (e.before)
{
e.before.after = e.after;
}
else
{
map.head = e.after;
}
if (e.after)
{
e.after.before = e.before;
}
else
{
map.tail = e.before;
}
map.count--;
map.free_entry(e);
return true;
}
prev = e;
e = e.next;
}
return false;
}
fn void LinkedHashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
{
LinkedEntry *e = map.table[bucket_index];
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
LinkedEntry* entry = allocator::new(map.allocator, LinkedEntry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
map.count++;
}
fn void LinkedHashMap.free_entry(&self, LinkedEntry *entry) @local
{
$if COPY_KEYS:
allocator::free(self.allocator, entry.key);
$endif
self.free_internal(entry);
}
struct LinkedHashMapIterator
{
LinkedHashMap* map;
LinkedEntry* current;
bool started;
}
typedef LinkedHashMapValueIterator = inline LinkedHashMapIterator;
typedef LinkedHashMapKeyIterator = inline LinkedHashMapIterator;
fn usz LinkedHashMapValueIterator.len(self) @operator(len) => self.map.count;
fn usz LinkedHashMapKeyIterator.len(self) @operator(len) => self.map.count;
fn usz LinkedHashMapIterator.len(self) @operator(len) => self.map.count;
int dummy @local;

View File

@@ -1,723 +0,0 @@
<*
@require $defined((Value){}.hash()) : `No .hash function found on the value`
*>
module std::collections::set {Value};
import std::math;
import std::io @norecurse;
const LinkedHashSet LINKEDONHEAP = { .allocator = SET_HEAP_ALLOCATOR };
struct LinkedEntry
{
uint hash;
Value value;
LinkedEntry* next; // For bucket chain
LinkedEntry* before; // Previous in insertion order
LinkedEntry* after; // Next in insertion order
}
struct LinkedHashSet (Printable)
{
LinkedEntry*[] table;
Allocator allocator;
usz count; // Number of elements
usz threshold; // Resize limit
float load_factor;
LinkedEntry* head; // First inserted LinkedEntry
LinkedEntry* tail; // Last inserted LinkedEntry
}
fn int LinkedHashSet.len(&self) @operator(len) => (int) self.count;
<*
@param [&inout] allocator : "The allocator to use"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashSet* LinkedHashSet.init(&self, Allocator allocator, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
capacity = math::next_power_of_2(capacity);
self.allocator = allocator;
self.threshold = (usz)(capacity * load_factor);
self.load_factor = load_factor;
self.table = allocator::new_array(allocator, LinkedEntry*, capacity);
self.head = null;
self.tail = null;
return self;
}
<*
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashSet* LinkedHashSet.tinit(&self, usz capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init(tmem, capacity, load_factor) @inline;
}
<*
@param [&inout] allocator : "The allocator to use"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro LinkedHashSet* LinkedHashSet.init_with_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.init(allocator, capacity, load_factor);
$for var $i = 0; $i < $vacount; $i++:
self.add($vaarg[$i]);
$endfor
return self;
}
<*
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro LinkedHashSet* LinkedHashSet.tinit_with_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init_with_values(tmem, $vasplat, capacity: capacity, load_factor: load_factor);
}
<*
@param [in] values : "The values for the LinkedHashSet"
@param [&inout] allocator : "The allocator to use"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashSet* LinkedHashSet.init_from_values(&self, Allocator allocator, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.init(allocator, capacity, load_factor);
foreach (v : values) self.add(v);
return self;
}
<*
@param [in] values : "The values for the LinkedHashSet entries"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Set was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn LinkedHashSet* LinkedHashSet.tinit_from_values(&self, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init_from_values(tmem, values, capacity, load_factor);
}
<*
Has this linked hash set been initialized yet?
@param [&in] set : "The linked hash set we are testing"
@return "Returns true if it has been initialized, false otherwise"
*>
fn bool LinkedHashSet.is_initialized(&set)
{
return set.allocator && set.allocator.ptr != &dummy;
}
<*
@param [&inout] allocator : "The allocator to use"
@param [&in] other_set : "The set to copy from."
@require !self.is_initialized() : "Set was already initialized"
*>
fn LinkedHashSet* LinkedHashSet.init_from_set(&self, Allocator allocator, LinkedHashSet* other_set)
{
self.init(allocator, other_set.table.len, other_set.load_factor);
LinkedEntry* entry = other_set.head;
while (entry) // Save insertion order
{
self.put_for_create(entry.value);
entry = entry.after;
}
return self;
}
<*
@param [&in] other_set : "The set to copy from."
@require !set.is_initialized() : "Set was already initialized"
*>
fn LinkedHashSet* LinkedHashSet.tinit_from_set(&set, LinkedHashSet* other_set)
{
return set.init_from_set(tmem, other_set) @inline;
}
<*
Check if the set is empty
@return "true if it is empty"
@pure
*>
fn bool LinkedHashSet.is_empty(&set) @inline
{
return !set.count;
}
<*
Add all elements in the slice to the set.
@param [in] list
@return "The number of new elements added"
@ensure total <= list.len
*>
fn usz LinkedHashSet.add_all(&set, Value[] list)
{
usz total;
foreach (v : list)
{
if (set.add(v)) total++;
}
return total;
}
<*
@param [&in] other
@return "The number of new elements added"
@ensure return <= other.count
*>
fn usz LinkedHashSet.add_all_from(&set, LinkedHashSet* other)
{
usz total;
other.@each(;Value value)
{
if (set.add(value)) total++;
};
return total;
}
<*
@param value : "The value to add"
@return "true if the value didn't exist in the set"
*>
fn bool LinkedHashSet.add(&set, Value value)
{
// If the set isn't initialized, use the defaults to initialize it.
switch (set.allocator.ptr)
{
case &dummy:
set.init(mem);
case null:
set.tinit();
default:
break;
}
uint hash = rehash(value.hash());
uint index = index_for(hash, set.table.len);
for (LinkedEntry *e = set.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(value, e.value)) return false;
}
set.add_entry(hash, value, index);
return true;
}
<*
Iterate over all the values in the set
*>
macro LinkedHashSet.@each(set; @body(value))
{
if (!set.count) return;
LinkedEntry* entry = set.head;
while (entry)
{
@body(entry.value);
entry = entry.after;
}
}
<*
Check if the set contains the given value.
@param value : "The value to check"
@return "true if it exists in the set"
*>
fn bool LinkedHashSet.contains(&set, Value value)
{
if (!set.count) return false;
uint hash = rehash(value.hash());
for (LinkedEntry *e = set.table[index_for(hash, set.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(value, e.value)) return true;
}
return false;
}
<*
Remove a single value from the set.
@param value : "The value to remove"
@return? NOT_FOUND : "If the entry is not found"
*>
fn void? LinkedHashSet.remove(&set, Value value) @maydiscard
{
if (!set.remove_entry_for_value(value)) return NOT_FOUND?;
}
fn usz LinkedHashSet.remove_all(&set, Value[] values)
{
usz total;
foreach (v : values)
{
if (set.remove_entry_for_value(v)) total++;
}
return total;
}
<*
@param [&in] other : "Other set"
*>
fn usz LinkedHashSet.remove_all_from(&set, LinkedHashSet* other)
{
usz total;
other.@each(;Value val)
{
if (set.remove_entry_for_value(val)) total++;
};
return total;
}
<*
Free all memory allocated by the hash set.
*>
fn void LinkedHashSet.free(&set)
{
if (!set.is_initialized()) return;
set.clear();
set.free_internal(set.table.ptr);
set.table = {};
}
<*
Clear all elements from the set while keeping the underlying storage
@ensure set.count == 0
*>
fn void LinkedHashSet.clear(&set)
{
if (!set.count) return;
LinkedEntry* entry = set.head;
while (entry)
{
LinkedEntry* next = entry.after;
set.free_entry(entry);
entry = next;
}
foreach (LinkedEntry** &bucket : set.table)
{
*bucket = null;
}
set.count = 0;
set.head = null;
set.tail = null;
}
fn void LinkedHashSet.reserve(&set, usz capacity)
{
if (capacity > set.threshold)
{
set.resize(math::next_power_of_2(capacity));
}
}
// --- Set Operations ---
<*
Returns the union of two sets (A | B)
@param [&in] other : "The other set to union with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing the union of both sets"
*>
fn LinkedHashSet LinkedHashSet.set_union(&self, Allocator allocator, LinkedHashSet* other)
{
usz new_capacity = math::next_power_of_2(self.count + other.count);
LinkedHashSet result;
result.init(allocator, new_capacity, self.load_factor);
result.add_all_from(self);
result.add_all_from(other);
return result;
}
fn LinkedHashSet LinkedHashSet.tset_union(&self, LinkedHashSet* other) => self.set_union(tmem, other) @inline;
<*
Returns the intersection of the two sets (A & B)
@param [&in] other : "The other set to intersect with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing the intersection of both sets"
*>
fn LinkedHashSet LinkedHashSet.intersection(&self, Allocator allocator, LinkedHashSet* other)
{
LinkedHashSet result;
result.init(allocator, math::min(self.table.len, other.table.len), self.load_factor);
// Iterate through the smaller set for efficiency
LinkedHashSet* smaller = self.count <= other.count ? self : other;
LinkedHashSet* larger = self.count > other.count ? self : other;
smaller.@each(;Value value)
{
if (larger.contains(value)) result.add(value);
};
return result;
}
fn LinkedHashSet LinkedHashSet.tintersection(&self, LinkedHashSet* other) => self.intersection(tmem, other) @inline;
<*
Return this set - other, so (A & ~B)
@param [&in] other : "The other set to compare with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing elements in this set but not in the other"
*>
fn LinkedHashSet LinkedHashSet.difference(&self, Allocator allocator, LinkedHashSet* other)
{
LinkedHashSet result;
result.init(allocator, self.table.len, self.load_factor);
self.@each(;Value value)
{
if (!other.contains(value))
{
result.add(value);
}
};
return result;
}
fn LinkedHashSet LinkedHashSet.tdifference(&self, LinkedHashSet* other) => self.difference(tmem, other) @inline;
<*
Return (A ^ B)
@param [&in] other : "The other set to compare with"
@param [&inout] allocator : "Allocator for the new set"
@return "A new set containing elements in this set or the other, but not both"
*>
fn LinkedHashSet LinkedHashSet.symmetric_difference(&self, Allocator allocator, LinkedHashSet* other)
{
LinkedHashSet result;
result.init(allocator, self.table.len, self.load_factor);
result.add_all_from(self);
other.@each(;Value value)
{
if (!result.add(value))
{
result.remove(value);
}
};
return result;
}
fn LinkedHashSet LinkedHashSet.tsymmetric_difference(&self, LinkedHashSet* other) => self.symmetric_difference(tmem, other) @inline;
<*
Check if this hash set is a subset of another set.
@param [&in] other : "The other set to check against"
@return "True if all elements of this set are in the other set"
*>
fn bool LinkedHashSet.is_subset(&self, LinkedHashSet* other)
{
if (self.count == 0) return true;
if (self.count > other.count) return false;
self.@each(; Value value) {
if (!other.contains(value)) return false;
};
return true;
}
// --- private methods
fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
{
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
.hash = hash,
.value = value,
.next = set.table[bucket_index],
.before = set.tail,
.after = null
});
// Update bucket chain
set.table[bucket_index] = entry;
// Update linked list
if (set.tail)
{
set.tail.after = entry;
entry.before = set.tail;
}
else
{
set.head = entry;
}
set.tail = entry;
if (set.count++ >= set.threshold)
{
set.resize(set.table.len * 2);
}
}
fn void LinkedHashSet.resize(&set, usz new_capacity) @private
{
LinkedEntry*[] old_table = set.table;
usz old_capacity = old_table.len;
if (old_capacity == MAXIMUM_CAPACITY)
{
set.threshold = uint.max;
return;
}
LinkedEntry*[] new_table = allocator::new_array(set.allocator, LinkedEntry*, new_capacity);
set.table = new_table;
set.threshold = (uint)(new_capacity * set.load_factor);
// Rehash all entries - linked list order remains unchanged
foreach (uint i, LinkedEntry *e : old_table)
{
if (!e) continue;
// Split the bucket chain into two chains based on new bit
LinkedEntry* lo_head = null;
LinkedEntry* lo_tail = null;
LinkedEntry* hi_head = null;
LinkedEntry* hi_tail = null;
do
{
LinkedEntry* next = e.next;
if ((e.hash & old_capacity) == 0)
{
if (!lo_tail)
{
lo_head = e;
}
else
{
lo_tail.next = e;
}
lo_tail = e;
}
else
{
if (!hi_tail)
{
hi_head = e;
}
else
{
hi_tail.next = e;
}
hi_tail = e;
}
e.next = null;
e = next;
}
while (e);
if (lo_tail)
{
lo_tail.next = null;
new_table[i] = lo_head;
}
if (hi_tail)
{
hi_tail.next = null;
new_table[i + old_capacity] = hi_head;
}
}
set.free_internal(old_table.ptr);
}
fn usz? LinkedHashSet.to_format(&self, Formatter* f) @dynamic
{
usz len;
len += f.print("{ ")!;
self.@each(; Value value)
{
if (len > 2) len += f.print(", ")!;
len += f.printf("%s", value)!;
};
return len + f.print(" }");
}
fn void LinkedHashSet.transfer(&set, LinkedEntry*[] new_table) @private
{
LinkedEntry*[] src = set.table;
uint new_capacity = new_table.len;
foreach (uint j, LinkedEntry *e : src)
{
if (!e) continue;
do
{
LinkedEntry* next = e.next;
uint i = index_for(e.hash, new_capacity);
e.next = new_table[i];
new_table[i] = e;
e = next;
}
while (e);
}
}
fn void LinkedHashSet.put_for_create(&set, Value value) @private
{
uint hash = rehash(value.hash());
uint i = index_for(hash, set.table.len);
for (LinkedEntry *e = set.table[i]; e != null; e = e.next)
{
if (e.hash == hash && equals(value, e.value))
{
// Value already exists, no need to do anything
return;
}
}
set.create_entry(hash, value, i);
}
fn void LinkedHashSet.free_internal(&set, void* ptr) @inline @private
{
allocator::free(set.allocator, ptr);
}
fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
{
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
.hash = hash,
.value = value,
.next = set.table[bucket_index],
.before = set.tail,
.after = null
});
set.table[bucket_index] = entry;
// Update linked list
if (set.tail)
{
set.tail.after = entry;
entry.before = set.tail;
}
else
{
set.head = entry;
}
set.tail = entry;
set.count++;
}
fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
{
if (!set.count) return false;
uint hash = rehash(value.hash());
uint i = index_for(hash, set.table.len);
LinkedEntry* prev = null;
LinkedEntry* e = set.table[i];
while (e)
{
if (e.hash == hash && equals(value, e.value))
{
if (prev)
{
prev.next = e.next;
}
else
{
set.table[i] = e.next;
}
if (e.before)
{
e.before.after = e.after;
}
else
{
set.head = e.after;
}
if (e.after)
{
e.after.before = e.before;
}
else
{
set.tail = e.before;
}
set.count--;
set.free_entry(e);
return true;
}
prev = e;
e = e.next;
}
return false;
}
fn void LinkedHashSet.free_entry(&set, LinkedEntry *entry) @private
{
allocator::free(set.allocator, entry);
}
struct LinkedHashSetIterator
{
LinkedHashSet* set;
LinkedEntry* current;
bool started;
}
fn LinkedHashSetIterator LinkedHashSet.iter(&set) => { .set = set, .current = set.head, .started = false };
fn bool LinkedHashSetIterator.next(&self)
{
if (!self.started)
{
self.current = self.set.head;
self.started = true;
}
else if (self.current)
{
self.current = self.current.after;
}
return self.current != null;
}
fn Value*? LinkedHashSetIterator.get(&self)
{
return self.current ? &self.current.value : NOT_FOUND?;
}
fn bool LinkedHashSetIterator.has_next(&self)
{
if (!self.started) return self.set.head != null;
return self.current && self.current.after != null;
}
fn usz LinkedHashSetIterator.len(&self) @operator(len)
{
return self.set.count;
}
int dummy @local;

View File

@@ -137,10 +137,9 @@ fn Type? List.pop_first(&self)
*> *>
fn void List.remove_at(&self, usz index) fn void List.remove_at(&self, usz index)
{ {
usz new_size = self.size - 1; self.set_size(self.size - 1);
defer self.set_size(new_size); if (!self.size || index == self.size) return;
if (!new_size || index == new_size) return; self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
self.entries[index .. new_size - 1] = self.entries[index + 1 .. new_size];
} }
fn void List.add_all(&self, List* other_list) fn void List.add_all(&self, List* other_list)

View File

@@ -178,8 +178,11 @@ fn void Object.init_array_if_needed(&self) @private
fn void Object.set_object(&self, String key, Object* new_object) @private fn void Object.set_object(&self, String key, Object* new_object) @private
{ {
self.init_map_if_needed(); self.init_map_if_needed();
Object*? val = self.map.get_entry(key).value; ObjectInternalMapEntry*? entry = self.map.get_entry(key);
defer (void)val.free(); defer
{
(void)entry.value.free();
}
self.map.set(key, new_object); self.map.set(key, new_object);
} }
@@ -203,7 +206,7 @@ macro Object* Object.object_from_value(&self, value) @private
return value; return value;
$case $Type.typeid == void*.typeid: $case $Type.typeid == void*.typeid:
return &NULL_OBJECT; return &NULL_OBJECT;
$case @assignable_to(value, String): $case $assignable(value, String):
return new_string(value, self.allocator); return new_string(value, self.allocator);
$default: $default:
$error "Unsupported object type."; $error "Unsupported object type.";

View File

@@ -1,75 +1,16 @@
module std::collections::pair{Type1, Type2}; module std::collections::tuple{Type1, Type2};
import std::io;
struct Pair (Printable) struct Tuple
{ {
Type1 first; Type1 first;
Type2 second; Type2 second;
} }
fn usz? Pair.to_format(&self, Formatter* f) @dynamic
{
return f.printf("{ %s, %s }", self.first, self.second);
}
<*
@param [&out] a
@param [&out] b
@require @assignable_to(self.first, $typeof(*a)) : "You cannot assign the first value to a"
@require @assignable_to(self.second, $typeof(*b)) : "You cannot assign the second value to b"
*>
macro void Pair.unpack(&self, a, b)
{
*a = self.first;
*b = self.second;
}
fn bool Pair.equal(self, Pair other) @operator(==) @if (types::has_equals(Type1) &&& types::has_equals(Type2))
{
return self.first == other.first && self.second == other.second;
}
module std::collections::triple{Type1, Type2, Type3}; module std::collections::triple{Type1, Type2, Type3};
import std::io;
struct Triple (Printable) struct Triple
{ {
Type1 first; Type1 first;
Type2 second; Type2 second;
Type3 third; Type3 third;
} }
fn usz? Triple.to_format(&self, Formatter* f) @dynamic
{
return f.printf("{ %s, %s, %s }", self.first, self.second, self.third);
}
<*
@param [&out] a
@param [&out] b
@param [&out] c
@require @assignable_to(self.first, $typeof(*a)) : "You cannot assign the first value to a"
@require @assignable_to(self.second, $typeof(*b)) : "You cannot assign the second value to b"
@require @assignable_to(self.third, $typeof(*c)) : "You cannot assign the second value to c"
*>
macro void Triple.unpack(&self, a, b, c)
{
*a = self.first;
*b = self.second;
*c = self.third;
}
fn bool Triple.equal(self, Triple other) @operator(==) @if (types::has_equals(Type1) &&& types::has_equals(Type2) &&& types::has_equals(Type3))
{
return self.first == other.first && self.second == other.second && self.third == other.third;
}
module std::collections::tuple{Type1, Type2};
struct Tuple @deprecated("Use 'Pair' instead")
{
Type1 first;
Type2 second;
}

View File

@@ -138,7 +138,7 @@ fn void*? ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
} }
// Otherwise just allocate new memory. // Otherwise just allocate new memory.
void* mem = self.acquire(size, NO_ZERO, alignment)!; void* mem = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(mem, old_pointer, math::min(size, old_size), mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT); mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem; return mem;
} }

View File

@@ -137,7 +137,7 @@ fn void*? BackedArenaAllocator.resize(&self, void* pointer, usz size, usz alignm
} }
AllocChunk* data = self.acquire(size, NO_ZERO, alignment)!; AllocChunk* data = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(data, pointer, math::min(size, chunk.size), mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT); mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return data; return data;
} }

View File

@@ -117,7 +117,7 @@ fn void*? DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz a
return old_pointer; return old_pointer;
} }
void* new_mem = self.acquire(size, NO_ZERO, alignment)!; void* new_mem = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(new_mem, old_pointer, math::min(old_size, size), mem::DEFAULT_MEM_ALIGNMENT); mem::copy(new_mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT);
return new_mem; return new_mem;
} }

View File

@@ -8,9 +8,12 @@ import libc;
<* <*
The LibcAllocator is a wrapper around malloc to conform to the Allocator interface. The LibcAllocator is a wrapper around malloc to conform to the Allocator interface.
*> *>
typedef LibcAllocator (Allocator) = uptr; typedef LibcAllocator (Allocator, Printable) = uptr;
const LibcAllocator LIBC_ALLOCATOR = {}; 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");
module std::core::mem::allocator @if(env::POSIX); module std::core::mem::allocator @if(env::POSIX);
import std::os; import std::os;
import libc; import libc;

View File

@@ -1,5 +1,5 @@
module std::core::mem::allocator; module std::core::mem::allocator;
import std::math;
<* <*
The OnStackAllocator is similar to the ArenaAllocator: it allocates from a chunk of memory The OnStackAllocator is similar to the ArenaAllocator: it allocates from a chunk of memory
given to it. given to it.
@@ -124,7 +124,7 @@ fn void*? OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignm
OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof; OnStackAllocatorHeader* header = old_pointer - OnStackAllocatorHeader.sizeof;
usz old_size = header.size; usz old_size = header.size;
void* mem = self.acquire(size, NO_ZERO, alignment)!; void* mem = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(mem, old_pointer, math::min(old_size, size), mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT); mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem; return mem;
} }

View File

@@ -1,5 +1,6 @@
module std::core::mem::allocator @if(!(env::POSIX || env::WIN32) || !$feature(VMEM_TEMP)); module std::core::mem::allocator;
import std::io, std::math; import std::io, std::math;
import std::core::sanitizer::asan;
// This implements the temp allocator. // This implements the temp allocator.
// The temp allocator is a specialized allocator only intended for use where // The temp allocator is a specialized allocator only intended for use where
@@ -326,81 +327,3 @@ fn void*? TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
return &page.data[0]; return &page.data[0];
} }
module std::core::mem::allocator @if((env::POSIX || env::WIN32) && $feature(VMEM_TEMP));
import std::math;
tlocal VmemOptions temp_allocator_default_options = {
.shrink_on_reset = env::MEMORY_ENV != NORMAL,
.protect_unused_pages = env::COMPILER_OPT_LEVEL <= O1 || env::COMPILER_SAFE_MODE,
.scratch_released_data = env::COMPILER_SAFE_MODE
};
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)
{
Vmem mem;
TempAllocator* t = allocator::new(allocator, TempAllocator);
defer catch allocator::free(allocator, t);
t.vmem.init(preferred_size: isz.sizeof > 4 ? 4 * mem::GB : 512 * mem::MB,
reserve_page_size: isz.sizeof > 4 ? 256 * mem::KB : 0,
options: temp_allocator_default_options)!;
t.allocator = allocator;
return t;
}
struct TempAllocator (Allocator)
{
Vmem vmem;
TempAllocator* derived;
Allocator allocator;
}
<*
@require size > 0
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
*>
fn void*? TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
return self.vmem.acquire(size, init_type, alignment) @inline;
}
fn TempAllocator*? TempAllocator.derive_allocator(&self, usz reserve = 0)
{
if (self.derived) return self.derived;
return self.derived = new_temp_allocator(self.allocator, 0)!;
}
<*
Reset the entire temp allocator, destroying all children
*>
fn void TempAllocator.reset(&self)
{
TempAllocator* child = self.derived;
if (!child) return;
child.reset();
child.vmem.reset(0);
}
fn void TempAllocator.free(&self)
{
self.destroy();
}
fn void TempAllocator.destroy(&self) @local
{
TempAllocator* child = self.derived;
if (!child) return;
child.destroy();
self.vmem.free() @inline;
allocator::free(self.allocator, self) @inline;
}
fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
{
return self.vmem.resize(pointer, size, alignment) @inline;
}
fn void TempAllocator.release(&self, void* old_pointer, bool b) @dynamic
{
self.vmem.release(old_pointer, b) @inline;
}

View File

@@ -1,252 +0,0 @@
module std::core::mem::allocator @if(env::POSIX || env::WIN32);
import std::math, std::os::posix, libc, std::bits;
import std::core::mem;
import std::core::env;
// Virtual Memory allocator
faultdef VMEM_RESERVE_FAILED;
struct Vmem (Allocator)
{
VirtualMemory memory;
usz allocated;
usz pagesize;
usz page_pot;
usz last_page;
usz high_water;
VmemOptions options;
}
bitstruct VmemOptions : int
{
bool shrink_on_reset; // Release memory on reset
bool protect_unused_pages; // Protect unused pages on reset
bool scratch_released_data; // Overwrite released data with 0xAA
}
<*
Implements the Allocator interface method.
@require !reserve_page_size || math::is_power_of_2(reserve_page_size)
@require reserve_page_size <= preferred_size : "The min reserve_page_size size must be less or equal to the preferred size"
@require preferred_size >= 1 * mem::KB : "The preferred size must exceed 1 KB"
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY, VMEM_RESERVE_FAILED
*>
fn void? Vmem.init(&self, usz preferred_size, usz reserve_page_size = 0, VmemOptions options = { true, true, env::COMPILER_SAFE_MODE }, usz min_size = 0)
{
static usz page_size = 0;
if (!page_size) page_size = mem::os_pagesize();
if (page_size < reserve_page_size) page_size = reserve_page_size;
preferred_size = mem::aligned_offset(preferred_size, page_size);
if (!min_size) min_size = max(preferred_size / 1024, 1);
VirtualMemory? memory = mem::OUT_OF_MEMORY?;
while (preferred_size >= min_size)
{
memory = vm::virtual_alloc(preferred_size, PROTECTED);
// It worked?
if (try memory) break;
switch (@catch(memory))
{
case mem::OUT_OF_MEMORY:
case vm::RANGE_OVERFLOW:
// Try a smaller size.
preferred_size /= 2;
continue;
default:
break;
}
}
if (catch memory) return VMEM_RESERVE_FAILED?;
if (page_size > preferred_size) page_size = preferred_size;
$if env::ADDRESS_SANITIZER:
asan::poison_memory_region(memory.ptr, memory.size);
$endif
*self = { .memory = memory,
.high_water = 0,
.pagesize = page_size,
.page_pot = page_size.ctz(),
.options = options,
};
}
<*
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
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*? Vmem.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
usz total_len = self.memory.size;
if (size > total_len) return mem::INVALID_ALLOC_SIZE?;
void* start_mem = self.memory.ptr;
void* unaligned_pointer_to_offset = start_mem + self.allocated + VmemHeader.sizeof;
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz after = (usz)(mem - start_mem) + size;
if (after > total_len) return mem::OUT_OF_MEMORY?;
if (init_type == ZERO && self.high_water <= self.allocated)
{
init_type = NO_ZERO;
}
protect(self, after)!;
VmemHeader* header = mem - VmemHeader.sizeof;
header.size = size;
if (init_type == ZERO) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
fn bool Vmem.owns_pointer(&self, void* ptr) @inline
{
return (uptr)ptr >= (uptr)self.memory.ptr && (uptr)ptr < (uptr)self.memory.ptr + self.memory.size;
}
<*
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
@require size > 0
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*? Vmem.resize(&self, void *old_pointer, usz size, usz alignment) @dynamic
{
if (size > self.memory.size) return mem::INVALID_ALLOC_SIZE?;
alignment = alignment_for_allocation(alignment);
assert(self.owns_pointer(old_pointer), "Pointer originates from a different allocator: %p, not in %p - %p", old_pointer, self.memory.ptr, self.memory.ptr + self.allocated);
VmemHeader* header = old_pointer - VmemHeader.sizeof;
usz old_size = header.size;
if (old_size == size) return old_pointer;
// Do last allocation and alignment match?
if (self.memory.ptr + self.allocated == old_pointer + old_size && mem::ptr_is_aligned(old_pointer, alignment))
{
if (old_size > size)
{
unprotect(self, self.allocated + size - old_size);
}
else
{
usz allocated = self.allocated + size - old_size;
if (allocated > self.memory.size) return mem::OUT_OF_MEMORY?;
protect(self, allocated)!;
}
header.size = size;
return old_pointer;
}
if (old_size > size)
{
$if env::ADDRESS_SANITIZER:
asan::poison_memory_region(old_pointer + size, old_size - size);
$endif
header.size = size;
return old_pointer;
}
// Otherwise just allocate new memory.
void* mem = self.acquire(size, NO_ZERO, alignment)!;
assert(size > old_size);
mem::copy(mem, old_pointer, old_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return mem;
}
<*
Implements the Allocator interface method.
@require ptr != null
*>
fn void Vmem.release(&self, void* ptr, bool) @dynamic
{
assert(self.owns_pointer(ptr), "Pointer originates from a different allocator %p.", ptr);
VmemHeader* header = ptr - VmemHeader.sizeof;
// Reclaim memory if it's the last element.
if (ptr + header.size == self.memory.ptr + self.allocated)
{
unprotect(self, self.allocated - header.size - VmemHeader.sizeof);
}
}
fn usz Vmem.mark(&self)
{
return self.allocated;
}
<*
@require mark <= self.allocated : "Invalid mark"
*>
fn void Vmem.reset(&self, usz mark)
{
if (mark == self.allocated) return;
unprotect(self, mark);
}
fn void Vmem.free(&self)
{
if (!self.memory.ptr) return;
$switch:
$case env::ADDRESS_SANITIZER:
asan::poison_memory_region(self.memory.ptr, self.memory.size);
$case env::COMPILER_SAFE_MODE:
((char*)self.memory.ptr)[0:self.allocated] = 0xAA;
$endswitch
(void)self.memory.destroy();
*self = {};
}
// Internal data
struct VmemHeader @local
{
usz size;
char[*] data;
}
macro void? protect(Vmem* mem, usz after) @local
{
usz shift = mem.page_pot;
usz page_after = (after + mem.pagesize - 1) >> shift;
usz last_page = mem.last_page;
bool over_high_water = mem.high_water < after;
if (page_after > last_page)
{
usz page_start = last_page << shift;
usz page_len = (page_after - last_page) << shift;
mem.memory.commit(page_start, page_len)!;
if (mem.options.protect_unused_pages || over_high_water)
{
mem.memory.protect(page_start, page_len, READWRITE)!;
}
mem.last_page = page_after;
}
$if env::ADDRESS_SANITIZER:
asan::unpoison_memory_region(mem.memory.ptr + mem.allocated, after - mem.allocated);
$endif
mem.allocated = after;
if (over_high_water) mem.high_water = after;
}
macro void unprotect(Vmem* mem, usz after) @local
{
usz shift = mem.page_pot;
usz last_page = mem.last_page;
usz page_after = mem.last_page = (after + mem.pagesize - 1) >> shift;
$if env::ADDRESS_SANITIZER:
asan::poison_memory_region(mem.memory.ptr + after, mem.allocated - after);
$else
if (mem.options.scratch_released_data)
{
mem::set(mem.memory.ptr + after, 0xAA, mem.allocated - after);
}
$endif
if ((mem.options.shrink_on_reset || mem.options.protect_unused_pages) && page_after < last_page)
{
usz start = page_after << shift;
usz len = (last_page - page_after) << shift;
if (mem.options.shrink_on_reset) (void)mem.memory.decommit(start, len, false);
if (mem.options.protect_unused_pages) (void)mem.memory.protect(start, len, PROTECTED);
}
mem.allocated = after;
}

View File

@@ -1,133 +0,0 @@
module std::core::string::ansi;
enum Ansi : const inline String
{
RESET = "\e[0m",
BOLD = "\e[1m",
DIM = "\e[2m",
ITALIC = "\e[3m",
UNDERLINE = "\e[4m",
BLINK = "\e[5m",
BLINK_FAST = "\e[6m",
INVERT = "\e[7m",
HIDDEN = "\e[8m",
STRIKETHROUGH = "\e[9m",
DOUBLE_UNDER = "\e[21m",
NO_DIM = "\e[22m",
NO_ITALIC = "\e[23m",
NO_UNDERLINE = "\e[24m",
NO_BLINK = "\e[25m",
NO_INVERT = "\e[27m",
NO_HIDDEN = "\e[28m",
NO_STRIKETHROUGH = "\e[29m",
BLACK = "\e[30m",
RED = "\e[31m",
GREEN = "\e[32m",
YELLOW = "\e[33m",
BLUE = "\e[34m",
MAGENTA = "\e[35m",
CYAN = "\e[36m",
WHITE = "\e[37m",
DEFAULT = "\e[39m",
BRIGHT_BLACK = "\e[90m",
BRIGHT_RED = "\e[91m",
BRIGHT_GREEN = "\e[92m",
BRIGHT_YELLOW = "\e[93m",
BRIGHT_BLUE = "\e[94m",
BRIGHT_MAGENTA = "\e[95m",
BRIGHT_CYAN = "\e[96m",
BRIGHT_WHITE = "\e[97m",
BG_BLACK = "\e[40m",
BG_RED = "\e[41m",
BG_GREEN = "\e[42m",
BG_YELLOW = "\e[43m",
BG_BLUE = "\e[44m",
BG_MAGENTA = "\e[45m",
BG_CYAN = "\e[46m",
BG_WHITE = "\e[47m",
BG_DEFAULT = "\e[49m",
BG_BRIGHT_BLACK = "\e[100m",
BG_BRIGHT_RED = "\e[101m",
BG_BRIGHT_GREEN = "\e[102m",
BG_BRIGHT_YELLOW = "\e[103m",
BG_BRIGHT_BLUE = "\e[104m",
BG_BRIGHT_MAGENTA = "\e[105m",
BG_BRIGHT_CYAN = "\e[106m",
BG_BRIGHT_WHITE = "\e[107m",
}
<*
8-bit color code
@return `the formatting char for the given background color`
*>
macro String color_8bit(char $index, bool $bg = false) @const
{
int $mode = $bg ? 4 : 3;
return @sprintf("\e[%s8;5;%sm", $mode, $index);
}
<*
24-bit color code
@return `the string for the given foreground color`
*>
macro String color_rgb(char $r, char $g, char $b, bool $bg = false) @const
{
int $mode = $bg ? 4 : 3;
return @sprintf("\e[%s8;2;%s;%s;%sm", $mode, $r, $g, $b);
}
<*
24-bit color code rgb
@require $rgb <= 0xFF_FF_FF : `Expected a 24 bit RGB value`
@return `the string char for the given foreground color`
*>
macro String color(uint $rgb, bool $bg = false) @const
{
int $mode = $bg ? 4 : 3;
return @sprintf("\e[%s8;2;%s;%s;%sm", $mode, $rgb >> 16, ($rgb & 0xFF00) >> 8, $rgb & 0xFF);
}
<*
24-bit color code rgb
@require rgb <= 0xFF_FF_FF : `Expected a 24 bit RGB value`
@return `the string char for the given foreground color`
*>
fn String make_color(Allocator mem, uint rgb, bool bg = false)
{
return make_color_rgb(mem, (char)(rgb >> 16), (char)((rgb & 0xFF00) >> 8), (char)rgb, bg);
}
<*
24-bit color code rgb
@require rgb <= 0xFF_FF_FF : `Expected a 24 bit RGB value`
@return `the string char for the given foreground color`
*>
fn String make_tcolor(uint rgb, bool bg = false)
{
return make_color_rgb(tmem, (char)(rgb >> 16), (char)((rgb & 0xFF00) >> 8), (char)rgb, bg);
}
<*
24-bit color code rgb
@return `the string char for the given foreground color`
*>
fn String make_color_rgb(Allocator mem, char r, char g, char b, bool bg = false)
{
return string::format(mem, "\e[%s8;2;%s;%s;%sm", bg ? 4 : 3, r, g, b);
}
<*
24-bit color code rgb
@return `the string char for the given foreground color`
*>
fn String make_tcolor_rgb(char r, char g, char b, bool bg = false)
{
return string::format(tmem, "\e[%s8;2;%s;%s;%sm", bg ? 4 : 3, r, g, b);
}

View File

@@ -39,28 +39,20 @@ macro @is_valid_macro_slot(#arg) @const @builtin => !@typeis(#arg, EmptySlot);
macro @rnd() @const @builtin => $$rnd(); macro @rnd() @const @builtin => $$rnd();
/* /*
Use `NO_MORE_ELEMENT` when reading the end of an iterator, or accessing a result out of bounds. Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
*/ */
faultdef NO_MORE_ELEMENT @builtin; faultdef NO_MORE_ELEMENT @builtin;
/* /*
Use `NOT_FOUND` when trying to return a value from some collection but the element is missing. Use `SearchResult` when trying to return a value from some collection but the element is missing.
*/ */
faultdef NOT_FOUND @builtin; faultdef NOT_FOUND @builtin;
/* /*
Use `TYPE_MISMATCH` when an attempt at conversion fails. Use `CastResult` when an attempt at conversion fails.
*/ */
faultdef TYPE_MISMATCH @builtin; faultdef TYPE_MISMATCH @builtin;
/*
Use `CAPACITY_EXCEEDED` when trying to add to a bounded list or similar.
*/
faultdef CAPACITY_EXCEEDED @builtin;
/*
Use `NOT_IMPLEMENTED` when something is conditionally available.
*/
faultdef NOT_IMPLEMENTED @builtin;
alias VoidFn = fn void(); alias VoidFn = fn void();
@@ -89,10 +81,6 @@ macro void @swap(#a, #b) @builtin
#b = temp; #b = temp;
} }
macro usz bitsizeof($Type) @builtin @const => $Type.sizeof * 8u;
macro usz @bitsizeof(#expr) @builtin @const => $sizeof(#expr) * 8u;
<* <*
Convert an `any` type to a type, returning an failure if there is a type mismatch. Convert an `any` type to a type, returning an failure if there is a type mismatch.
@@ -108,41 +96,20 @@ macro anycast(any v, $Type) @builtin
return ($Type*)v.ptr; return ($Type*)v.ptr;
} }
macro bool @assignable_to(#foo, $Type) @const @builtin => $defined(*&&($Type){} = #foo); fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
macro @addr(#val) @builtin
{
$if $defined(&#val):
return &#val;
$else
return &&#val;
$endif
}
macro typeid @typeid(#value) @const @builtin
{
return $typeof(#value).typeid;
}
macro TypeKind @typekind(#value) @const @builtin
{
return $typeof(#value).kindof;
}
macro bool @typeis(#value, $Type) @const @builtin
{
return $typeof(#value).typeid == $Type.typeid;
}
fn bool print_backtrace(String message, int backtraces_to_ignore) @if (env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
{ {
Allocator t = allocator::current_temp;
TempAllocator* new_t = allocator::new_temp_allocator(smem, 0x1000)!!;
allocator::current_temp = new_t;
defer
{
allocator::current_temp = t;
new_t.free();
}
void*[256] buffer; void*[256] buffer;
void*[] backtraces = backtrace::capture_current(&buffer); void*[] backtraces = backtrace::capture_current(&buffer);
backtraces_to_ignore++; backtraces_to_ignore++;
@stack_mem(2048; Allocator mem) BacktraceList? backtrace = backtrace::symbolize_backtrace(tmem, backtraces);
{
BacktraceList? backtrace = backtrace::symbolize_backtrace(mem, backtraces);
if (catch backtrace) return false; if (catch backtrace) return false;
if (backtrace.len() <= backtraces_to_ignore) return false; if (backtrace.len() <= backtraces_to_ignore) return false;
io::eprint("\nERROR: '"); io::eprint("\nERROR: '");
@@ -164,14 +131,13 @@ fn bool print_backtrace(String message, int backtraces_to_ignore) @if (env::NATI
} }
io::eprintfn(" in %s (source unavailable) [%s]%s", trace.function, trace.object_file, inline_suffix); io::eprintfn(" in %s (source unavailable) [%s]%s", trace.function, trace.object_file, inline_suffix);
} }
};
return true; return true;
} }
fn void default_panic(String message, String file, String function, uint line) @if(env::NATIVE_STACKTRACE) fn void default_panic(String message, String file, String function, uint line) @if(env::NATIVE_STACKTRACE)
{ {
$if $defined(io::stderr) && env::PANIC_MSG: $if $defined(io::stderr):
if (!print_backtrace(message, 2)) if (!print_backtrace(message, 2))
{ {
io::eprintfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line); io::eprintfn("\nERROR: '%s', in %s (%s:%d)", message, function, file, line);
@@ -181,7 +147,7 @@ fn void default_panic(String message, String file, String function, uint line) @
} }
macro void abort(String string = "Unrecoverable error reached", ...) @format(0) @builtin @noreturn macro void abort(String string = "Unrecoverable error reached", ...) @builtin @noreturn
{ {
panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat); panicf(string, $$FILE, $$FUNC, $$LINE, $vasplat);
$$trap(); $$trap();
@@ -189,9 +155,8 @@ macro void abort(String string = "Unrecoverable error reached", ...) @format(0)
bool in_panic @local = false; bool in_panic @local = false;
fn void default_panic(String message, String file, String function, uint line) @if (!env::NATIVE_STACKTRACE) fn void default_panic(String message, String file, String function, uint line) @if(!env::NATIVE_STACKTRACE)
{ {
$if $defined(io::stderr) && env::PANIC_MSG:
if (in_panic) if (in_panic)
{ {
io::eprintn("Panic inside of panic."); io::eprintn("Panic inside of panic.");
@@ -204,7 +169,6 @@ fn void default_panic(String message, String file, String function, uint line) @
io::eprintfn("', in %s (%s:%d)", function, file, line); io::eprintfn("', in %s (%s:%d)", function, file, line);
$endif $endif
in_panic = false; in_panic = false;
$endif
$$trap(); $$trap();
} }
@@ -214,7 +178,6 @@ PanicFn panic = &default_panic;
fn void panicf(String fmt, String file, String function, uint line, args...) fn void panicf(String fmt, String file, String function, uint line, args...)
{ {
$if $defined(io::stderr) && env::PANIC_MSG:
if (in_panic) if (in_panic)
{ {
io::eprint("Panic inside of panic: "); io::eprint("Panic inside of panic: ");
@@ -230,7 +193,6 @@ fn void panicf(String fmt, String file, String function, uint line, args...)
in_panic = false; in_panic = false;
panic(s.str_view(), file, function, line); panic(s.str_view(), file, function, line);
}; };
$endif
} }
<* <*
@@ -319,7 +281,7 @@ macro enum_by_name($Type, String enum_name) @builtin
@param $Type : `The type of the enum` @param $Type : `The type of the enum`
@require $Type.kindof == ENUM : `Only enums may be used` @require $Type.kindof == ENUM : `Only enums may be used`
@require $defined($Type.#value) : `Expected '#value' to match an enum associated value` @require $defined($Type.#value) : `Expected '#value' to match an enum associated value`
@require @assignable_to(value, $typeof(($Type){}.#value)) : `Expected the value to match the type of the associated value` @require $assignable(value, $typeof(($Type){}.#value)) : `Expected the value to match the type of the associated value`
@ensure @typeis(return, $Type) @ensure @typeis(return, $Type)
@return? NOT_FOUND @return? NOT_FOUND
*> *>
@@ -372,7 +334,7 @@ macro bool @unlikely(bool #value, $probability = 1.0) @builtin
<* <*
@require values::@is_int(#value) || values::@is_bool(#value) @require values::@is_int(#value) || values::@is_bool(#value)
@require @assignable_to(expected, $typeof(#value)) @require $assignable(expected, $typeof(#value))
@require $probability >= 0 && $probability <= 1.0 @require $probability >= 0 && $probability <= 1.0
*> *>
macro @expect(#value, expected, $probability = 1.0) @builtin macro @expect(#value, expected, $probability = 1.0) @builtin
@@ -422,34 +384,6 @@ macro swizzle2(v, v2, ...) @builtin
{ {
return $$swizzle2(v, v2, $vasplat); return $$swizzle2(v, v2, $vasplat);
} }
<*
Returns the count of leading zero bits from an integer at compile-time.
@require types::is_int($typeof($value)) : "Input value must be an integer"
@require $sizeof($value) * 8 <= 128 : "Input value must be 128 bits wide or lower"
*>
macro @clz($value) @builtin @const
{
$if $value == 0:
return $sizeof($value) * 8; // it's all leading zeroes
$endif
usz $n = 0;
uint128 $x = (uint128)$value;
$if $x <= 0x0000_0000_0000_0000_FFFF_FFFF_FFFF_FFFF: $n += 64; $x <<= 64; $endif
$if $x <= 0x0000_0000_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF: $n += 32; $x <<= 32; $endif
$if $x <= 0x0000_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF: $n += 16; $x <<= 16; $endif
$if $x <= 0x00FF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF: $n += 8; $x <<= 8; $endif
$if $x <= 0x0FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF: $n += 4; $x <<= 4; $endif
$if $x <= 0x3FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF: $n += 2; $x <<= 2; $endif
$if $x <= 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF: $n += 1; $endif
return $n % ($sizeof($value) * 8); // mod by the bitsize of the input value to go back from uint128 -> it's-type
}
<* <*
Return the excuse in the Optional if it is Empty, otherwise Return the excuse in the Optional if it is Empty, otherwise
return a null fault. return a null fault.
@@ -474,67 +408,6 @@ macro bool @ok(#expr) @builtin
return true; return true;
} }
<*
Check if an Optional expression evaluates to a fault. If so, return it;
else, assign the result to an expression.
@require $defined(#v = #v) : "#v must be a variable"
@require $defined(#expr!) : "Expected an optional expression"
@require @assignable_to(#expr!!, $typeof(#v)) : `Type of #expr must be an optional of #v's type`
*>
macro void? @try(#v, #expr) @builtin
{
var res = #expr;
if (catch err = res) return err?;
#v = res;
}
<*
Check if an Optional expression evaluates to a fault. If so, return true if it is the
expected fault, the optional if it is unexpected, or false if there was no fault and
the assign happened.
This can be used in like this:
while (true)
{
char[] data;
// Read until end of file
if (@try_catch(data, load_line(), io::EOF)) break;
.. use data ..
}
In this example we read until we reach an EOF, which is expected. However, if we encounter some other
fault, we rethrow is. Without this macro, the code is instead written like:
while (true)
{
char[]? data;
data = load_line();
if (catch err = data)
{
if (err = io::EOF) break;
return err?
}
.. use data ..
}
@require $defined(#v = #v) : "#v must be a variable"
@require $defined(#expr!) : "Expected an optional expression"
@require @assignable_to(#expr!!, $typeof(#v)) : `Type of #expr must be an optional of #v's type`
@return "True if it was the expected fault, false if the variable was assigned, otherwise returns an optional."
*>
macro bool? @try_catch(#v, #expr, fault expected_fault) @builtin
{
var res = #expr;
if (catch err = res)
{
return err == expected_fault ? true : err?;
}
#v = res;
return false;
}
<* <*
@require $defined(&#value, (char*)&#value) : "This must be a value that can be viewed as a char array" @require $defined(&#value, (char*)&#value) : "This must be a value that can be viewed as a char array"
*> *>
@@ -604,8 +477,8 @@ macro uint ichar[<*>].hash(self) => hash_vec(self);
macro uint bool[<*>].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 typeid.hash(typeid t) => @generic_hash(((ulong)(uptr)t));
macro uint String.hash(String c) => (uint)a5hash::hash(c); macro uint String.hash(String c) => (uint)fnv32a::hash(c);
macro uint char[].hash(char[] c) => (uint)a5hash::hash(c); macro uint char[].hash(char[] c) => (uint)fnv32a::hash(c);
macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr)); macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr));
<* <*
@@ -613,13 +486,7 @@ macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr));
*> *>
macro uint hash_array(array_ptr) @local macro uint hash_array(array_ptr) @local
{ {
var $len = $sizeof(*array_ptr); return (uint)fnv32a::hash(((char*)array_ptr)[:$sizeof(*array_ptr)]);
$if $len > 16:
return (uint)komi::hash(((char*)array_ptr)[:$len]);
$else
return (uint)wyhash2::hash(((char*)array_ptr)[:$len]);
$endif
} }
<* <*
@@ -627,13 +494,7 @@ macro uint hash_array(array_ptr) @local
*> *>
macro uint hash_vec(vec) @local macro uint hash_vec(vec) @local
{ {
var $len = $sizeof(vec.len * $typeof(vec).inner.sizeof); return (uint)fnv32a::hash(((char*)&&vec)[:$sizeof(vec.len * $typeof(vec).inner.sizeof)]);
$if $len > 16:
return (uint)komi::hash(((char*)&&vec)[:$len]);
$else
return (uint)wyhash2::hash(((char*)&&vec)[:$len]);
$endif
} }
const MAX_FRAMEADDRESS = 128; const MAX_FRAMEADDRESS = 128;

View File

@@ -137,13 +137,12 @@ const bool BACKTRACE = $$BACKTRACE;
const usz LLVM_VERSION = $$LLVM_VERSION; const usz LLVM_VERSION = $$LLVM_VERSION;
const bool BENCHMARKING = $$BENCHMARKING; const bool BENCHMARKING = $$BENCHMARKING;
const bool TESTING = $$TESTING; const bool TESTING = $$TESTING;
const bool PANIC_MSG = $$PANIC_MSG;
const MemoryEnvironment MEMORY_ENV = MemoryEnvironment.from_ordinal($$MEMORY_ENVIRONMENT); const MemoryEnvironment MEMORY_ENV = MemoryEnvironment.from_ordinal($$MEMORY_ENVIRONMENT);
const bool TRACK_MEMORY = DEBUG_SYMBOLS && (COMPILER_SAFE_MODE || TESTING); const bool TRACK_MEMORY = DEBUG_SYMBOLS && (COMPILER_SAFE_MODE || TESTING);
const bool X86_64 = ARCH_TYPE == X86_64; const bool X86_64 = ARCH_TYPE == X86_64;
const bool X86 = ARCH_TYPE == X86; const bool X86 = ARCH_TYPE == X86;
const bool AARCH64 = ARCH_TYPE == AARCH64; const bool AARCH64 = ARCH_TYPE == AARCH64;
const bool NATIVE_STACKTRACE = LINUX || DARWIN || OPENBSD || WIN32; const bool NATIVE_STACKTRACE = LINUX || DARWIN || WIN32;
const bool LINUX = LIBC && OS_TYPE == LINUX; const bool LINUX = LIBC && OS_TYPE == LINUX;
const bool DARWIN = LIBC && os_is_darwin(); const bool DARWIN = LIBC && os_is_darwin();
const bool WIN32 = LIBC && OS_TYPE == WIN32; const bool WIN32 = LIBC && OS_TYPE == WIN32;
@@ -160,7 +159,7 @@ const bool MEMORY_SANITIZER = $$MEMORY_SANITIZER;
const bool THREAD_SANITIZER = $$THREAD_SANITIZER; const bool THREAD_SANITIZER = $$THREAD_SANITIZER;
const bool ANY_SANITIZER = ADDRESS_SANITIZER || MEMORY_SANITIZER || THREAD_SANITIZER; const bool ANY_SANITIZER = ADDRESS_SANITIZER || MEMORY_SANITIZER || THREAD_SANITIZER;
const int LANGUAGE_DEV_VERSION = $$LANGUAGE_DEV_VERSION; const int LANGUAGE_DEV_VERSION = $$LANGUAGE_DEV_VERSION;
const bool HAS_NATIVE_ERRNO = env::LINUX || env::ANDROID || env::OPENBSD || env::DARWIN || env::WIN32; const bool HAS_NATIVE_ERRNO = env::LINUX || env::ANDROID || env::DARWIN || env::WIN32;
macro bool os_is_darwin() @const macro bool os_is_darwin() @const
{ {
@@ -199,7 +198,6 @@ macro bool os_is_posix() @const
return false; return false;
$endswitch $endswitch
} }
const String[] AUTHORS = $$AUTHORS;
const String[] AUTHOR_EMAILS = $$AUTHOR_EMAILS;
const BUILTIN_EXPECT_IS_DISABLED = $feature(DISABLE_BUILTIN_EXPECT); const BUILTIN_EXPECT_IS_DISABLED = $feature(DISABLE_BUILTIN_EXPECT);
const BUILTIN_PREFETCH_IS_DISABLED = $feature(DISABLE_BUILTIN_PREFETCH); const BUILTIN_PREFETCH_IS_DISABLED = $feature(DISABLE_BUILTIN_PREFETCH);

View File

@@ -1,230 +0,0 @@
module std::core::log;
import std::io, std::thread, std::time, std::math::random;
const FULL_LOG = env::COMPILER_SAFE_MODE || $feature(FULL_LOG);
typedef LogCategory = inline char;
typedef LogTag = char[12];
const LogCategory CATEGORY_APPLICATION = 0;
const LogCategory CATEGORY_SYSTEM = 1;
const LogCategory CATEGORY_KERNEL = 2;
const LogCategory CATEGORY_AUDIO = 3;
const LogCategory CATEGORY_VIDEO = 4;
const LogCategory CATEGORY_RENDER = 5;
const LogCategory CATEGORY_INPUT = 6;
const LogCategory CATEGORY_NETWORK = 7;
const LogCategory CATEGORY_SOCKET = 8;
const LogCategory CATEGORY_SECURITY = 9;
const LogCategory CATEGORY_TEST = 10;
const LogCategory CATEGORY_ERROR = 11;
const LogCategory CATEGORY_ASSERT = 12;
const LogCategory CATEGORY_CRASH = 13;
const LogCategory CATEGORY_STATS = 14;
const LogCategory CATEGORY_CUSTOM_START = 100;
tlocal LogCategory default_category = CATEGORY_APPLICATION;
tlocal LogTag current_tag;
enum LogPriority : int
{
VERBOSE,
DEBUG,
INFO,
WARN,
ERROR,
CRITICAL,
}
interface Logger
{
fn void log(LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args);
}
macro void verbose(String fmt, ..., LogCategory category = default_category) => call_log(VERBOSE, category, fmt, $vasplat);
macro void debug(String fmt, ..., LogCategory category = default_category) => call_log(DEBUG, category, fmt, $vasplat);
macro void info(String fmt, ..., LogCategory category = default_category) => call_log(INFO, category, fmt, $vasplat);
macro void warn(String fmt, ..., LogCategory category = default_category) => call_log(WARN, category, fmt, $vasplat);
macro void error(String fmt, ..., LogCategory category = default_category) => call_log(ERROR, category, fmt, $vasplat);
macro void critical(String fmt, ..., LogCategory category = default_category) => call_log(CRITICAL, category, fmt, $vasplat);
macro void @category_scope(LogCategory new_category; @body)
{
LogCategory old = default_category;
default_category = new_category;
defer default_category = old;
@body();
}
<*
@require tag_prefix.len <= 3 : "The prefix may not exceed 3 bytes"
*>
macro void @tag_scope(String tag_prefix = ""; @body)
{
LogTag old = current_tag;
push_tag(tag_prefix);
defer current_tag = old;
@body();
}
<*
@require tag_prefix.len <= 3 : "The prefix may not exceed 3 bytes"
*>
macro void push_tag(String tag_prefix = "")
{
current_tag = create_tag(tag_prefix);
}
<*
@require tag_prefix.len <= 3 : "The prefix may not exceed 3 bytes"
*>
fn LogTag create_tag(String tag_prefix)
{
LogTag tag @noinit;
int start = 0;
foreach (int i, c : tag_prefix)
{
if (c == 0) break;
tag[start++] = c;
}
if (start > 0) tag[start++] = '_';
for (int i = start; i < tag.len; i++)
{
tag[i] = (char)rand_in_range('a', 'z');
}
return tag;
}
fn void set_priority_for_category(LogCategory category, LogPriority new_priority)
{
@atomic_store(config_priorities[category], new_priority, UNORDERED);
}
fn LogPriority get_priority_for_category(LogCategory category)
{
return @atomic_load(config_priorities[category], UNORDERED);
}
fn void set_priority_all(LogPriority new_priority)
{
for (int i = 0; i < config_priorities.len; i++)
{
@atomic_store(config_priorities[i], new_priority, UNORDERED);
}
}
fn void set_logger(Logger logger)
{
init();
if (!logger_mutex.is_initialized())
{
current_logger = logger;
current_logfn = &logger.log;
return;
}
logger_mutex.@in_lock()
{
current_logger = logger;
current_logfn = &logger.log;
};
}
macro void init()
{
log_init.call(fn () => (void)logger_mutex.init());
}
fn void call_log(LogPriority prio, LogCategory category, String fmt, args...)
{
LogPriority priority = mem::@atomic_load(config_priorities[category], UNORDERED);
if (priority < prio) return;
init();
bool locked = logger_mutex.is_initialized() && @ok(logger_mutex.lock());
Logger logger = current_logger;
LogFn logfn = current_logfn;
defer if (locked) (void)logger_mutex.unlock();
$if FULL_LOG:
logfn(logger.ptr, prio, category, current_tag, $$FILE, $$FUNC, $$LINE, fmt, args);
$else
logfn(logger.ptr, prio, category, current_tag, "", "", 0, fmt, args);
$endif
}
fn String? get_category_name(LogCategory category)
{
String val = category_names[category];
return val ?: NOT_FOUND?;
}
fn void set_category_name(LogCategory category, String name)
{
category_names[category] = name;
}
struct NullLogger (Logger)
{
void* dummy;
}
fn void NullLogger.log(&self, LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args) @dynamic
{}
struct MultiLogger (Logger)
{
Logger[] loggers;
}
fn void MultiLogger.log(&self, LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args) @dynamic
{
foreach (logger : self.loggers)
{
logger.log(priority, category, tag, file, function, line, fmt, args);
}
}
module std::core::log @private;
import std::io, std::thread, std::time;
struct StderrLogger (Logger) @if(env::LIBC)
{
void* dummy;
}
fn void StderrLogger.log(&self, LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args) @dynamic @if(env::LIBC)
{
@stack_mem(256 + 64; Allocator mem)
{
DString str;
str.init(mem, 256);
str.appendf(fmt, ...args);
TzDateTime time = datetime::now().to_local();
io::eprintfn("[%02d:%02d:%02d:%04d] [%s] %s", time.hour, time.min, time.sec, (time.usec / 1000), priority, str);
};
}
alias LogFn = fn void(void*, LogPriority priority, LogCategory category, LogTag tag, String file, String function, int line, String fmt, any[] args);
LogFn current_logfn = @select(env::LIBC, (LogFn)&StderrLogger.log, (LogFn)&NullLogger.log);
OnceFlag log_init;
Mutex logger_mutex;
Logger current_logger = @select(env::LIBC, &stderr_logger, &null_logger);
StderrLogger stderr_logger @if (env::LIBC);
NullLogger null_logger;
LogPriority[256] config_priorities = { [0..255] = ERROR, [CATEGORY_APPLICATION] = INFO, [CATEGORY_TEST] = VERBOSE, [CATEGORY_ASSERT] = WARN};
String[256] category_names = {
[CATEGORY_APPLICATION] = "APP",
[CATEGORY_SYSTEM] = "SYSTEM",
[CATEGORY_KERNEL] = "KERNEL",
[CATEGORY_AUDIO] = "AUDIO",
[CATEGORY_VIDEO] = "VIDEO",
[CATEGORY_RENDER] = "RENDER",
[CATEGORY_INPUT] = "INPUT",
[CATEGORY_NETWORK] = "NETWORD",
[CATEGORY_SOCKET] = "SOCKET",
[CATEGORY_SECURITY] = "SECURITY",
[CATEGORY_TEST] = "TEST",
[CATEGORY_ERROR] = "ERROR",
[CATEGORY_ASSERT] = "ASSERT",
[CATEGORY_CRASH] = "CRASH",
[CATEGORY_STATS] = "STATS"
};

View File

@@ -3,15 +3,10 @@
// a copy of which can be found in the LICENSE_STDLIB file. // a copy of which can be found in the LICENSE_STDLIB file.
module std::core::mem; module std::core::mem;
import std::core::mem::allocator @public; import std::core::mem::allocator @public;
import std::os::posix, std::os::win32;
import std::math; import std::math;
const MAX_MEMORY_ALIGNMENT = 0x1000_0000; const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2; const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
const ulong KB = 1024;
const ulong MB = KB * 1024;
const ulong GB = MB * 1024;
const ulong TB = GB * 1024;
faultdef OUT_OF_MEMORY, INVALID_ALLOC_SIZE; faultdef OUT_OF_MEMORY, INVALID_ALLOC_SIZE;
@@ -20,31 +15,13 @@ macro bool @constant_is_power_of_2($x) @const @private
return $x != 0 && ($x & ($x - 1)) == 0; return $x != 0 && ($x & ($x - 1)) == 0;
} }
fn usz os_pagesize()
{
$switch:
$case env::POSIX:
static usz pagesize;
if (pagesize) return pagesize;
return pagesize = posix::getpagesize();
$case env::WIN32:
static usz pagesize;
if (pagesize) return pagesize;
Win32_SYSTEM_INFO info;
win32::getSystemInfo(&info);
return pagesize = info.dwPageSize;
$default:
return 4096;
$endswitch
}
<* <*
Load a vector from memory according to a mask assuming default alignment. Load a vector from memory according to a mask assuming default alignment.
@param ptr : "The pointer address to load from." @param ptr : "The pointer address to load from."
@param mask : "The mask for the load" @param mask : "The mask for the load"
@param passthru : "The value to use for non masked values" @param passthru : "The value to use for non masked values"
@require @assignable_to(&&passthru, $typeof(ptr)) : "Pointer and passthru must match" @require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@require passthru.len == mask.len : "Mask and passthru must have the same length" @require passthru.len == mask.len : "Mask and passthru must have the same length"
@@ -63,7 +40,7 @@ macro masked_load(ptr, bool[<*>] mask, passthru)
@param passthru : "The value to use for non masked values" @param passthru : "The value to use for non masked values"
@param $alignment : "The alignment to assume for the pointer" @param $alignment : "The alignment to assume for the pointer"
@require @assignable_to(&&passthru, $typeof(ptr)) : "Pointer and passthru must match" @require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@require passthru.len == mask.len : "Mask and passthru must have the same length" @require passthru.len == mask.len : "Mask and passthru must have the same length"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
@@ -84,7 +61,7 @@ macro @masked_load_aligned(ptr, bool[<*>] mask, passthru, usz $alignment)
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@require @assignable_to(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match" @require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
@require passthru.len == mask.len : "Mask and passthru must have the same length" @require passthru.len == mask.len : "Mask and passthru must have the same length"
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
@@ -106,7 +83,7 @@ macro gather(ptrvec, bool[<*>] mask, passthru)
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector" @require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@require @assignable_to(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match" @require $assignable(&&passthru[0], $typeof(ptrvec[0])) : "Pointer and passthru must match"
@require passthru.len == mask.len : "Mask and passthru must have the same length" @require passthru.len == mask.len : "Mask and passthru must have the same length"
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
@@ -126,7 +103,7 @@ macro @gather_aligned(ptrvec, bool[<*>] mask, passthru, usz $alignment)
@param value : "The value to store masked" @param value : "The value to store masked"
@param mask : "The mask for the store" @param mask : "The mask for the store"
@require @assignable_to(&&value, $typeof(ptr)) : "Pointer and value must match" @require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
@require @typekind(value) == VECTOR : "Expected value to be a vector" @require @typekind(value) == VECTOR : "Expected value to be a vector"
@require value.len == mask.len : "Mask and value must have the same length" @require value.len == mask.len : "Mask and value must have the same length"
*> *>
@@ -141,7 +118,7 @@ macro masked_store(ptr, value, bool[<*>] mask)
@param mask : "The mask for the store" @param mask : "The mask for the store"
@param $alignment : "The alignment of the pointer" @param $alignment : "The alignment of the pointer"
@require @assignable_to(&&value, $typeof(ptr)) : "Pointer and value must match" @require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
@require @typekind(value) == VECTOR : "Expected value to be a vector" @require @typekind(value) == VECTOR : "Expected value to be a vector"
@require value.len == mask.len : "Mask and value must have the same length" @require value.len == mask.len : "Mask and value must have the same length"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
@@ -158,7 +135,7 @@ macro @masked_store_aligned(ptr, value, bool[<*>] mask, usz $alignment)
@param mask : "The mask for the store" @param mask : "The mask for the store"
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(value) == VECTOR : "Expected value to be a vector" @require @typekind(value) == VECTOR : "Expected value to be a vector"
@require @assignable_to(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match" @require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
@require value.len == mask.len : "Mask and value must have the same length" @require value.len == mask.len : "Mask and value must have the same length"
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
@@ -176,7 +153,7 @@ macro scatter(ptrvec, value, bool[<*>] mask)
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector" @require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(value) == VECTOR : "Expected value to be a vector" @require @typekind(value) == VECTOR : "Expected value to be a vector"
@require @assignable_to(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match" @require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
@require value.len == mask.len : "Mask and value must have the same length" @require value.len == mask.len : "Mask and value must have the same length"
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length" @require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two" @require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
@@ -305,7 +282,7 @@ macro compare_exchange_volatile(ptr, compare, value, AtomicOrdering $success = S
*> *>
fn usz aligned_offset(usz offset, usz alignment) fn usz aligned_offset(usz offset, usz alignment)
{ {
return (offset + alignment - 1) & ~(alignment - 1); return alignment * ((offset + alignment - 1) / alignment);
} }
macro void* aligned_pointer(void* ptr, usz alignment) macro void* aligned_pointer(void* ptr, usz alignment)
@@ -321,11 +298,6 @@ fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
return (uptr)ptr & ((uptr)alignment - 1) == 0; return (uptr)ptr & ((uptr)alignment - 1) == 0;
} }
fn bool ptr_is_page_aligned(void* ptr) @inline
{
return (uptr)ptr & ((uptr)os_pagesize() - 1) == 0;
}
macro void zero_volatile(char[] data) macro void zero_volatile(char[] data)
{ {
$$memset(data.ptr, (char)0, data.len, true, (usz)1); $$memset(data.ptr, (char)0, data.len, true, (usz)1);
@@ -728,7 +700,7 @@ fn void* tmalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard
<* <*
@require $vacount < 2 : "Too many arguments." @require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type" @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead" @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
*> *>
macro new($Type, ...) @nodiscard macro new($Type, ...) @nodiscard
@@ -744,7 +716,7 @@ macro new($Type, ...) @nodiscard
<* <*
@require $vacount < 2 : "Too many arguments." @require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type" @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead" @require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
*> *>
macro new_with_padding($Type, usz padding, ...) @nodiscard macro new_with_padding($Type, usz padding, ...) @nodiscard
@@ -762,7 +734,7 @@ macro new_with_padding($Type, usz padding, ...) @nodiscard
Allocate using an aligned allocation. This is necessary for types with a default memory alignment Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
@require $vacount < 2 : "Too many arguments." @require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type" @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*> *>
macro new_aligned($Type, ...) @nodiscard macro new_aligned($Type, ...) @nodiscard
{ {
@@ -802,7 +774,7 @@ macro alloc_aligned($Type) @nodiscard
<* <*
@require $vacount < 2 : "Too many arguments." @require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type" @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*> *>
macro tnew($Type, ...) @nodiscard macro tnew($Type, ...) @nodiscard
{ {
@@ -817,7 +789,7 @@ macro tnew($Type, ...) @nodiscard
<* <*
@require $vacount < 2 : "Too many arguments." @require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type" @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*> *>
macro temp_with_padding($Type, usz padding, ...) @nodiscard macro temp_with_padding($Type, usz padding, ...) @nodiscard
{ {
@@ -931,18 +903,6 @@ fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMEN
return tmem.resize(ptr, size, alignment)!!; return tmem.resize(ptr, size, alignment)!!;
} }
<*
Takes the address of a possibly unaligned variable or member,
and offers safe access to that member, by constructing an UnalignedRef.
@require $defined(&#arg) : "It must be possible to take the address of the argument."
@return "An 'UnalignedRef' with the proper type and alignment, with a pointer to argument"
*>
macro @unaligned_addr(#arg) @builtin
{
return (UnalignedRef{$typeof(#arg), $alignof(#arg)})&#arg;
}
module std::core::mem @if(env::NO_LIBC); module std::core::mem @if(env::NO_LIBC);
fn CInt __memcmp(void* s1, void* s2, usz n) @weak @export("memcmp") fn CInt __memcmp(void* s1, void* s2, usz n) @weak @export("memcmp")
@@ -980,39 +940,3 @@ fn void* __memcpy(void* dst, void* src, usz n) @weak @export("memcpy")
} }
return dst; return dst;
} }
module std::core::mem::volatile { Type };
typedef Volatile @structlike = Type;
macro Type Volatile.get(&self)
{
return @volatile_load(*(Type*)self);
}
macro Type Volatile.set(&self, Type val)
{
return @volatile_store(*(Type*)self, val);
}
<*
@require mem::@constant_is_power_of_2(ALIGNMENT) : "The alignment must be a power of 2"
*>
module std::core::mem::alignment { Type, ALIGNMENT };
import std::core::mem @public;
<*
An UnalignedRef offers correctly aligned access to addresses that may be unaligned or overaligned.
*>
typedef UnalignedRef = Type*;
macro Type UnalignedRef.get(self)
{
return @unaligned_load(*(Type*)self, ALIGNMENT);
}
macro Type UnalignedRef.set(&self, Type val)
{
return @unaligned_store(*(Type*)self, val, ALIGNMENT);
}

View File

@@ -1,7 +1,7 @@
module std::core::mem::allocator; module std::core::mem::allocator;
import std::math; import std::math;
// C3 has several different allocators available: // C3 has multiple different allocators available:
// //
// Name Arena Uses buffer OOM Fallback? Mark? Reset? // Name Arena Uses buffer OOM Fallback? Mark? Reset?
// ArenaAllocator Yes Yes No Yes Yes // ArenaAllocator Yes Yes No Yes Yes
@@ -12,7 +12,6 @@ import std::math;
// OnStackAllocator Yes Yes Yes No No *Note: Used by @stack_mem // OnStackAllocator Yes Yes Yes No No *Note: Used by @stack_mem
// TempAllocator Yes No Yes No* No* *Note: Mark/reset using @pool // TempAllocator Yes No Yes No* No* *Note: Mark/reset using @pool
// TrackingAllocator No No N/A No No *Note: Wraps other heap allocator // TrackingAllocator No No N/A No No *Note: Wraps other heap allocator
// Vmem Yes No No Yes Yes *Note: Can be set to huge sizes
const DEFAULT_SIZE_PREFIX = usz.sizeof; const DEFAULT_SIZE_PREFIX = usz.sizeof;
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof; const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
@@ -65,7 +64,7 @@ alias MemoryAllocFn = fn char[]?(usz);
fn usz alignment_for_allocation(usz alignment) @inline fn usz alignment_for_allocation(usz alignment) @inline @private
{ {
return alignment < mem::DEFAULT_MEM_ALIGNMENT ? mem::DEFAULT_MEM_ALIGNMENT : alignment; return alignment < mem::DEFAULT_MEM_ALIGNMENT ? mem::DEFAULT_MEM_ALIGNMENT : alignment;
} }
@@ -167,7 +166,7 @@ macro void free_aligned(Allocator allocator, void* ptr)
<* <*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead" @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
@require $vacount < 2 : "Too many arguments." @require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type" @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*> *>
macro new(Allocator allocator, $Type, ...) @nodiscard macro new(Allocator allocator, $Type, ...) @nodiscard
{ {
@@ -183,7 +182,7 @@ macro new(Allocator allocator, $Type, ...) @nodiscard
<* <*
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead" @require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_aligned' instead"
@require $vacount < 2 : "Too many arguments." @require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type" @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*> *>
macro new_try(Allocator allocator, $Type, ...) @nodiscard macro new_try(Allocator allocator, $Type, ...) @nodiscard
{ {
@@ -200,7 +199,7 @@ macro new_try(Allocator allocator, $Type, ...) @nodiscard
Allocate using an aligned allocation. This is necessary for types with a default memory alignment Allocate using an aligned allocation. This is necessary for types with a default memory alignment
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned. exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
@require $vacount < 2 : "Too many arguments." @require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| @assignable_to($vaexpr[0], $Type) : "The second argument must be an initializer for the type" @require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*> *>
macro new_aligned(Allocator allocator, $Type, ...) @nodiscard macro new_aligned(Allocator allocator, $Type, ...) @nodiscard
{ {
@@ -464,7 +463,7 @@ macro usz temp_allocator_default_reserve_size() @local
$endswitch $endswitch
} }
macro Allocator heap() @deprecated("Use 'mem' instead.") => thread_allocator; macro Allocator heap() => thread_allocator;
<* <*
@require !top_temp : "This should never be called when temp already exists" @require !top_temp : "This should never be called when temp already exists"
@@ -487,7 +486,7 @@ fn Allocator create_temp_allocator(Allocator allocator, usz size, usz reserve, u
return current_temp = top_temp = allocator::new_temp_allocator(allocator, size, reserve, min_size, realloc_size)!!; return current_temp = top_temp = allocator::new_temp_allocator(allocator, size, reserve, min_size, realloc_size)!!;
} }
macro Allocator temp() @deprecated("Use 'tmem' instead") macro Allocator temp()
{ {
return current_temp; return current_temp;
} }

View File

@@ -1,234 +0,0 @@
module std::core::mem::mempool;
import std::core::mem, std::core::mem::allocator, std::math;
const INITIAL_CAPACITY = 0;
struct FixedBlockPoolNode
{
void* buffer;
FixedBlockPoolNode *next;
usz capacity;
}
struct FixedBlockPoolEntry
{
void *previous;
}
<*
Fixed blocks pool pre-allocating blocks backed by an Allocator which are then reserved for the user,
blocks deallocated by the user are later re-used by future blocks allocations
`grow_capacity` can be changed in order to affect how many blocks will be allocated by next pool allocation,
it has to be greater than 0
`allocated` number of allocated blocks
`used` number of used blocks by the user
*>
struct FixedBlockPool
{
Allocator allocator;
FixedBlockPoolNode head;
FixedBlockPoolNode *tail;
void *next_free;
void *freelist;
usz block_size;
usz grow_capacity;
usz allocated;
usz page_size;
usz alignment;
usz used;
bool initialized;
}
<*
Initialize an block pool
@param [in] allocator : "The allocator to use"
@param block_size : "The block size to use"
@param capacity : "The amount of blocks to be pre-allocated"
@param alignment : "The alignment of the buffer"
@require !alignment || math::is_power_of_2(alignment)
@require !self.initialized : "The block pool must not be initialized"
@require block_size > 0 : "Block size must be non zero"
@require calculate_actual_capacity(capacity, block_size) * block_size >= block_size
: "Total memory would overflow"
*>
macro FixedBlockPool* FixedBlockPool.init(&self, Allocator allocator, usz block_size, usz capacity = INITIAL_CAPACITY, usz alignment = 0)
{
self.allocator = allocator;
self.tail = &self.head;
self.head.next = null;
self.block_size = math::max(block_size, FixedBlockPoolEntry.sizeof);
capacity = calculate_actual_capacity(capacity, self.block_size);
self.alignment = allocator::alignment_for_allocation(alignment);
self.page_size = capacity * self.block_size;
assert(self.page_size >= self.block_size, "Total memory would overflow %d %d", block_size, capacity);
self.head.buffer = self.allocate_page();
self.head.capacity = capacity;
self.next_free = self.head.buffer;
self.freelist = null;
self.grow_capacity = capacity;
self.initialized = true;
self.allocated = capacity;
self.used = 0;
return self;
}
<*
Initialize an block pool
@param [in] allocator : "The allocator to use"
@param $Type : "The type used for setting the block size"
@param capacity : "The amount of blocks to be pre-allocated"
@require !self.initialized : "The block pool must not be initialized"
*>
macro FixedBlockPool* FixedBlockPool.init_for_type(&self, Allocator allocator, $Type, usz capacity = INITIAL_CAPACITY)
{
return self.init(allocator, $Type.sizeof, capacity, $Type.alignof);
}
<*
Initialize an block pool using Temporary allocator
@param $Type : "The type used for setting the block size"
@param capacity : "The amount of blocks to be pre-allocated"
@require !self.initialized : "The block pool must not be initialized"
*>
macro FixedBlockPool* FixedBlockPool.tinit_for_type(&self, $Type, usz capacity = INITIAL_CAPACITY) => self.init_for_type(tmem, $Type, capacity);
<*
Initialize an block pool using Temporary allocator
@param block_size : "The block size to use"
@param capacity : "The amount of blocks to be pre-allocated"
@require !self.initialized : "The block pool must not be initialized"
*>
macro FixedBlockPool* FixedBlockPool.tinit(&self, usz block_size, usz capacity = INITIAL_CAPACITY) => self.init(tmem, block_size, capacity);
<*
Free up the entire block pool
@require self.initialized : "The block pool must be initialized"
*>
fn void FixedBlockPool.free(&self)
{
self.free_page(self.head.buffer);
FixedBlockPoolNode* iter = self.head.next;
while (iter)
{
self.free_page(iter.buffer);
FixedBlockPoolNode* current = iter;
iter = iter.next;
allocator::free(self.allocator, current);
}
self.initialized = false;
self.allocated = 0;
self.used = 0;
}
<*
Allocate an block on the block pool, re-uses previously deallocated blocks
@require self.initialized : "The block pool must be initialized"
*>
fn void* FixedBlockPool.alloc(&self)
{
defer self.used++;
if (self.freelist)
{
FixedBlockPoolEntry* entry = self.freelist;
self.freelist = entry.previous;
mem::clear(entry, self.block_size);
return entry;
}
void* end = self.tail.buffer + (self.tail.capacity * self.block_size);
if (self.next_free >= end) self.new_node();
void* ptr = self.next_free;
self.next_free += self.block_size;
return ptr;
}
<*
Deallocate a block from the block pool
@require self.initialized : "The block pool must be initialized"
@require self.check_ptr(ptr) : "The pointer should be part of the pool"
*>
fn void FixedBlockPool.dealloc(&self, void* ptr)
{
$if env::COMPILER_SAFE_MODE && !env::ADDRESS_SANITIZER:
if (self.block_size > FixedBlockPoolEntry.sizeof)
{
mem::set(ptr + FixedBlockPoolEntry.sizeof, 0xAA, self.block_size);
}
$else
// POINT FOR IMPROVEMENT, something like:
// asan::poison_memory_region(&ptr, self.block_size);
$endif
FixedBlockPoolEntry* entry = ptr;
entry.previous = self.freelist;
self.freelist = entry;
self.used--;
}
<*
@require self.initialized : "The block pool must be initialized"
*>
fn bool FixedBlockPool.check_ptr(&self, void *ptr) @local
{
FixedBlockPoolNode* iter = &self.head;
while (iter)
{
void* end = iter.buffer + (iter.capacity * self.block_size);
if (ptr >= iter.buffer && ptr < end) return true;
iter = iter.next;
}
return false;
}
<*
@require self.grow_capacity > 0 : "How many blocks will it store"
*>
fn void FixedBlockPool.new_node(&self) @local
{
FixedBlockPoolNode* node = allocator::new(self.allocator, FixedBlockPoolNode);
node.buffer = self.allocate_page();
node.capacity = self.grow_capacity;
self.tail.next = node;
self.tail = node;
self.next_free = node.buffer;
self.allocated += node.capacity;
}
macro void* FixedBlockPool.allocate_page(&self) @private
{
return self.alignment > mem::DEFAULT_MEM_ALIGNMENT
? allocator::calloc_aligned(self.allocator, self.page_size, self.alignment)!!
: allocator::calloc(self.allocator, self.page_size);
}
macro void FixedBlockPool.free_page(&self, void* page) @private
{
if (self.alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
allocator::free_aligned(self.allocator, page);
}
else
{
allocator::free(self.allocator, page);
}
}
macro usz calculate_actual_capacity(usz capacity, usz block_size) @private
{
// Assume some overhead
if (capacity) return capacity;
capacity = (mem::os_pagesize() - 128) / block_size;
return capacity ?: 1;
}

View File

@@ -1,353 +0,0 @@
<*
The VM module holds code for working with virtual memory on supported platforms (currently Win32 and Posix)
*>
module std::core::mem::vm;
import std::io, std::os::win32, std::os::posix, libc;
<*
VirtualMemory is an abstraction for working with an allocated virtual memory area. It will invoke vm:: functions
but will perform more checks and track its size (required to unmap the memory on Posix)
*>
struct VirtualMemory
{
void* ptr;
usz size;
VirtualMemoryAccess default_access;
}
faultdef RANGE_OVERFLOW, UNKNOWN_ERROR, ACCESS_DENIED, UNMAPPED_ACCESS, UNALIGNED_ADDRESS, RELEASE_FAILED, UPDATE_FAILED, INVALID_ARGS;
enum VirtualMemoryAccess
{
PROTECTED,
READ,
WRITE,
READWRITE,
EXEC,
EXECREAD,
EXECWRITE,
ANY
}
fn usz aligned_alloc_size(usz size)
{
$if env::WIN32:
return size > 0 ? mem::aligned_offset(size, win32::allocation_granularity()) : win32::allocation_granularity();
$else
return size > 0 ? mem::aligned_offset(size, mem::os_pagesize()) : mem::os_pagesize();
$endif
}
<*
Allocate virtual memory, size is rounded up to platform granularity (Win32) / page size (Posix).
@param size : "The size of the memory to allocate, will be rounded up"
@param access : "The initial access permissions."
@return? mem::OUT_OF_MEMORY, RANGE_OVERFLOW, UNKNOWN_ERROR, ACCESS_DENIED, INVALID_ARGS
@return "Pointer to the allocated memory, page aligned"
*>
fn void*? alloc(usz size, VirtualMemoryAccess access)
{
$switch:
$case env::POSIX:
void* ptr = posix::mmap(null, aligned_alloc_size(size), access.to_posix(), posix::MAP_PRIVATE | posix::MAP_ANONYMOUS, -1, 0);
if (ptr != posix::MAP_FAILED) return ptr;
switch (libc::errno())
{
case errno::ENOMEM: return mem::OUT_OF_MEMORY?;
case errno::EOVERFLOW: return RANGE_OVERFLOW?;
case errno::EPERM: return ACCESS_DENIED?;
case errno::EINVAL: return INVALID_ARGS?;
default: return UNKNOWN_ERROR?;
}
$case env::WIN32:
void* ptr = win32::virtualAlloc(null, aligned_alloc_size(size), MEM_RESERVE, access.to_win32());
if (ptr) return ptr;
switch (win32::getLastError())
{
case win32::ERROR_NOT_ENOUGH_MEMORY:
case win32::ERROR_COMMITMENT_LIMIT: return mem::OUT_OF_MEMORY?;
default: return UNKNOWN_ERROR?;
}
$default:
unsupported("Virtual alloc only available on Win32 and Posix");
$endswitch
}
<*
Release memory allocated with "alloc".
@param [&inout] ptr : "Pointer to page to release, should be allocated using vm::alloc"
@param size : "The size of the allocated pointer"
@require mem::ptr_is_page_aligned(ptr) : "The pointer should be page aligned"
*>
fn void? release(void* ptr, usz size)
{
$switch:
$case env::POSIX:
if (posix::munmap(ptr, aligned_alloc_size(size)))
{
switch (libc::errno())
{
case errno::EINVAL: return INVALID_ARGS?; // Not a valid mapping or size
case errno::ENOMEM: return UNMAPPED_ACCESS?; // Address not mapped
default: return RELEASE_FAILED?;
}
}
$case env::WIN32:
if (win32::virtualFree(ptr, 0, MEM_RELEASE)) return;
switch (win32::getLastError())
{
case win32::ERROR_INVALID_ADDRESS: return INVALID_ARGS?;
case win32::ERROR_NOT_ENOUGH_MEMORY: return mem::OUT_OF_MEMORY?;
default: return RELEASE_FAILED?;
}
$default:
unsupported("Virtual free only available on Win32 and Posix");
$endswitch
}
<*
Change the access protection of a region in memory. The region must be page aligned.
@param [&inout] ptr : "Pointer to page to update, must be page aligned"
@param len : "To what len to update, must be page aligned"
@param access : "The new access"
@require mem::ptr_is_page_aligned(ptr) : "The pointer should be page aligned"
@require mem::ptr_is_page_aligned(ptr + len) : "The length must be page aligned"
@return? ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UPDATE_FAILED, UNMAPPED_ACCESS, INVALID_ARGS
*>
fn void? protect(void* ptr, usz len, VirtualMemoryAccess access)
{
$switch:
$case env::POSIX:
if (!posix::mprotect(ptr, len, access.to_posix())) return;
switch (libc::errno())
{
case errno::EACCES: return ACCESS_DENIED?;
case errno::EINVAL: return UNALIGNED_ADDRESS?;
case errno::EOVERFLOW: return RANGE_OVERFLOW?;
case errno::ENOMEM: return UNMAPPED_ACCESS?;
default: return UPDATE_FAILED?;
}
$case env::WIN32:
Win32_Protect old;
if (win32::virtualProtect(ptr, len, access.to_win32(), &old)) return;
switch (win32::getLastError())
{
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS?;
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED?;
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS?;
default: return UPDATE_FAILED?;
}
$default:
unsupported("'virtual_protect' is only available on Win32 and Posix.");
$endswitch
}
<*
Makes a region of memory available that was previously retrieved using 'alloc'. This is necessary on Win32,
but optional on Posix.
@param [&inout] ptr : "Pointer to page to update, must be page aligned"
@param len : "To what len to commit, must be page aligned"
@require mem::ptr_is_page_aligned(ptr) : "The pointer should be page aligned"
@require mem::ptr_is_page_aligned(ptr + len) : "The length must be page aligned"
@return? UNKNOWN_ERROR, mem::OUT_OF_MEMORY, ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UPDATE_FAILED, UNMAPPED_ACCESS, INVALID_ARGS
*>
fn void? commit(void* ptr, usz len, VirtualMemoryAccess access = READWRITE)
{
$switch:
$case env::POSIX:
return protect(ptr, len, READWRITE) @inline;
$case env::WIN32:
void* result = win32::virtualAlloc(ptr, len, MEM_COMMIT, access.to_win32());
if (result) return;
switch (win32::getLastError())
{
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS?;
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED?;
case win32::ERROR_COMMITMENT_LIMIT:
case win32::ERROR_NOT_ENOUGH_MEMORY: return mem::OUT_OF_MEMORY?;
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS?;
default: return UNKNOWN_ERROR?;
}
$default:
unsupported("'virtual_commit' is only available on Win32 and Posix.");
$endswitch
}
<*
Notifies that the memory in the region can be released back to the OS. On Win32 this decommits the region,
whereas on Posix it tells the system that it may be reused using madvise. The "block" parameter is only
respected on Posix, and protects the region from read/write/exec. On Win32 this always happens.
@param [&inout] ptr : "Pointer to page to update, must be page aligned"
@param len : "To what len to commit, must be page aligned"
@param block : "Set the released memory to protected"
@require mem::ptr_is_page_aligned(ptr) : "The pointer should be page aligned"
@require mem::ptr_is_page_aligned(ptr + len) : "The length must be page aligned"
@return? ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UPDATE_FAILED, UNMAPPED_ACCESS, INVALID_ARGS
*>
fn void? decommit(void* ptr, usz len, bool block = true)
{
$switch:
$case env::POSIX:
if (posix::madvise(ptr, len, posix::MADV_DONTNEED))
{
switch (libc::errno())
{
case errno::EINVAL: return UNALIGNED_ADDRESS?;
case errno::ENOMEM: return UNMAPPED_ACCESS?;
default: return UPDATE_FAILED?;
}
}
if (block) (void)protect(ptr, len, PROTECTED) @inline;
$case env::WIN32:
if (!win32::virtualFree(ptr, len, MEM_DECOMMIT))
{
switch (win32::getLastError())
{
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS?;
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS?;
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED?;
default: return UPDATE_FAILED?;
}
}
$default:
unsupported("'virtual_decommit' is only available on Win32 and Posix.");
$endswitch
}
<*
Map a portion of an already-opened file into memory.
@param fd : "File descriptor"
@param size : "Number of bytes to map, will be rounded up to page size"
@param offset : "Byte offset in file, must be page size aligned"
@param access : "The initial access permissions"
@param shared : "if True then MAP_SHARED else MAP_PRIVATE"
@return? mem::OUT_OF_MEMORY, RANGE_OVERFLOW, UNKNOWN_ERROR, ACCESS_DENIED, INVALID_ARGS, io::NO_PERMISSION, io::FILE_NOT_VALID, io::WOULD_BLOCK, io::FILE_NOT_FOUND
@return "Pointer to the mapped region"
*>
fn void*? mmap_file(Fd fd, usz size, usz offset = 0, VirtualMemoryAccess access = READ, bool shared = false) @if (env::POSIX)
{
CInt flags = shared ? posix::MAP_SHARED : posix::MAP_PRIVATE;
void* ptr = posix::mmap(null, aligned_alloc_size(size), access.to_posix(), flags, fd, offset);
if (ptr != posix::MAP_FAILED) return ptr;
switch (libc::errno())
{
case errno::ENOMEM: return mem::OUT_OF_MEMORY?;
case errno::EOVERFLOW: return RANGE_OVERFLOW?;
case errno::EPERM: return ACCESS_DENIED?;
case errno::EINVAL: return INVALID_ARGS?;
case errno::EACCES: return io::NO_PERMISSION?;
case errno::EBADF: return io::FILE_NOT_VALID?;
case errno::EAGAIN: return io::WOULD_BLOCK?;
case errno::ENXIO: return io::FILE_NOT_FOUND?;
default: return UNKNOWN_ERROR?;
}
}
<*
Create a VirtualMemory using
@param size : "The size of the memory to allocate."
@require size > 0 : "The size must be non-zero"
@return? mem::OUT_OF_MEMORY, RANGE_OVERFLOW, UNKNOWN_ERROR, ACCESS_DENIED, INVALID_ARGS
*>
fn VirtualMemory? virtual_alloc(usz size, VirtualMemoryAccess access = PROTECTED)
{
size = aligned_alloc_size(size);
void* ptr = alloc(size, access)!;
return { ptr, size, access };
}
<*
Commits memory, using vm::commit
@param offset : "Starting from what offset to commit"
@param len : "To what len to commit"
@require mem::ptr_is_page_aligned(self.ptr + offset) : "The offset should be page aligned"
@require mem::ptr_is_page_aligned(self.ptr + offset + len) : "The length must be page aligned"
@require offset < self.size : "Offset out of range"
@require offset + len <= self.size : "Length out of range"
@return? UPDATE_FAILED, ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UNKNOWN_ERROR
*>
macro void? VirtualMemory.commit(self, usz offset, usz len)
{
return commit(self.ptr + offset, len, self.default_access);
}
<*
Changes protection of a part of memory using vm::protect
@param offset : "Starting from what offset to update"
@param len : "To what len to update"
@require mem::ptr_is_page_aligned(self.ptr + offset) : "The offset should be page aligned"
@require mem::ptr_is_page_aligned(self.ptr + offset + len) : "The length must be page aligned"
@require offset < self.size : "Offset out of range"
@require offset + len < self.size : "Length out of range"
@return? UPDATE_FAILED, ACCESS_DENIED, UNALIGNED_ADDRESS, RANGE_OVERFLOW, UNKNOWN_ERROR
*>
macro void? VirtualMemory.protect(self, usz offset, usz len, VirtualMemoryAccess access)
{
return protect(self.ptr + offset, len, access);
}
<*
Decommits a part of memory using vm::decommit
@param offset : "Starting from what offset to decommit"
@param len : "To what len to decommit"
@param block : "Should the memory be blocked from access after decommit"
@require mem::ptr_is_page_aligned(self.ptr + offset) : "The offset should be page aligned"
@require mem::ptr_is_page_aligned(self.ptr + offset + len) : "The length must be page aligned"
@require offset < self.size : "Offset out of range"
@require offset + len < self.size : "Length out of range"
@return? UPDATE_FAILED
*>
fn void? VirtualMemory.decommit(self, usz offset, usz len, bool block = true)
{
return decommit(self.ptr + offset, len, block);
}
<*
Releases the memory region
@require self.ptr != null : "Virtual memory must be initialized to call destroy"
*>
fn void? VirtualMemory.destroy(&self)
{
return release(self.ptr, self.size);
}
fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX) @private
{
switch (self)
{
case PROTECTED: return posix::PROT_NONE;
case READ: return posix::PROT_READ;
case WRITE: return posix::PROT_WRITE;
case EXEC: return posix::PROT_EXEC;
case READWRITE: return posix::PROT_READ | posix::PROT_WRITE;
case EXECREAD: return posix::PROT_READ | posix::PROT_EXEC;
case EXECWRITE: return posix::PROT_WRITE | posix::PROT_EXEC;
case ANY: return posix::PROT_WRITE | posix::PROT_READ | posix::PROT_EXEC;
}
}
fn Win32_Protect VirtualMemoryAccess.to_win32(self) @if(env::WIN32) @private
{
switch (self)
{
case PROTECTED: return PAGE_NOACCESS;
case READ: return PAGE_READONLY;
case WRITE: return PAGE_READWRITE;
case EXEC: return PAGE_EXECUTE;
case READWRITE: return PAGE_READWRITE;
case EXECWRITE: return PAGE_EXECUTE_READWRITE;
case EXECREAD: return PAGE_EXECUTE_READ;
case ANY: return PAGE_EXECUTE_READWRITE;
}
}

View File

@@ -1,132 +0,0 @@
<*
Ref provides a general *external* ref counted wrapper for a pointer. For convenience, a ref count of 0
means the reference is still valid.
When the rc drops to -1, it will first run the dealloc function on the underlying pointer (if it exists),
then free the pointer and the atomic variable assuming that they are allocated using the Allocator in the Ref.
@require !$defined(Type.dealloc) ||| $defined(Type.dealloc(&&(Type){})) : "'dealloc' must only take a pointer to the underlying type"
@require !$defined(Type.dealloc) ||| @typeis((Type){}.dealloc(), void) : "'dealloc' must return 'void'"
*>
module std::core::mem::ref { Type };
import std::thread, std::atomic;
const OVERALIGNED @private = Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
alias DeallocFn = fn void(void*);
fn Ref wrap(Type* ptr, Allocator allocator = mem)
{
return { .refcount = allocator::new(allocator, Atomic{int}), .ptr = ptr, .allocator = allocator };
}
<*
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| @assignable_to($vaexpr[0], Type) : "The first argument must be an initializer for the type"
*>
macro Ref new(..., Allocator allocator = mem)
{
$switch:
$case OVERALIGNED && !$vacount:
Type* ptr = allocator::calloc_aligned(allocator, Type.sizeof, Type.alignof)!!;
$case OVERALIGNED:
Type* ptr = allocator::malloc_aligned(allocator, Type.sizeof, Type.alignof)!!;
*ptr = $vaexpr[0];
$case !$vacount:
Type* ptr = allocator::calloc(allocator, Type.sizeof);
$default:
Type* ptr = allocator::malloc(allocator, Type.sizeof);
*ptr = $vaexpr[0];
$endswitch
return { .refcount = allocator::new(allocator, Atomic{int}),
.ptr = ptr,
.allocator = allocator };
}
struct Ref
{
Atomic{int}* refcount;
Type* ptr;
Allocator allocator;
}
fn Ref* Ref.retain(&self)
{
assert(self.refcount != null, "Reference already released");
assert(self.refcount.load(RELAXED) >= 0, "Retaining zombie");
self.refcount.add(1, RELAXED);
return self;
}
fn void Ref.release(&self)
{
assert(self.refcount != null, "Reference already released");
assert(self.refcount.load(RELAXED) >= 0, "Overrelease of refcount");
if (self.refcount.sub(1, RELAXED) == 0)
{
thread::fence(ACQUIRE);
$if $defined(Type.dealloc):
self.ptr.dealloc();
$endif
$if OVERALIGNED:
allocator::free_aligned(self.allocator, self.ptr);
$else
allocator::free(self.allocator, self.ptr);
$endif
allocator::free(self.allocator, self.refcount);
*self = {};
}
}
module std::core::mem::rc;
import std::thread, std::atomic;
<*
A RefCounted struct should be an inline base of a struct.
If a `dealloc` is defined, then it will be called rather than `free`
For convenience, a ref count of 0 is still valid, and the struct is
only freed when when ref count drops to -1.
The macros rc::retain and rc::release must be used on the full pointer,
not on the RefCounted substruct.
So `Foo* f = ...; RefCounted* rc = f; rc::release(rc);` will not do the right thing.
*>
struct RefCounted
{
Atomic{int} refcount;
}
<*
@require @assignable_to(refcounted, RefCounted*) : "Expected a ref counted value"
*>
macro retain(refcounted)
{
if (refcounted)
{
assert(refcounted.refcount.load(RELAXED) >= 0, "Retaining zombie");
refcounted.refcount.add(1, RELAXED);
}
return refcounted;
}
<*
@require @assignable_to(refcounted, RefCounted*) : "Expected a ref counted value"
@require !$defined(refcounted.dealloc()) ||| @typeis(refcounted.dealloc(), void)
: "Expected refcounted type to have a valid dealloc"
*>
macro void release(refcounted)
{
if (!refcounted) return;
assert(refcounted.refcount.load(RELAXED) >= 0, "Overrelease of refcount");
if (refcounted.refcount.sub(1, RELAXED) == 0)
{
thread::fence(ACQUIRE);
$if $defined(refcounted.dealloc):
refcounted.dealloc();
$else
free(refcounted);
$endif
}
}

View File

@@ -1,9 +1,7 @@
module std::core::runtime; module std::core::runtime;
import libc, std::time, std::io, std::sort, std::math, std::collections::map; import libc, std::time, std::io, std::sort;
alias BenchmarkFn = fn void (); alias BenchmarkFn = fn void();
HashMap { String, uint } bench_fn_iters @local;
struct BenchmarkUnit struct BenchmarkUnit
{ {
@@ -19,7 +17,6 @@ fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator)
foreach (i, benchmark : fns) foreach (i, benchmark : fns)
{ {
benchmarks[i] = { names[i], fns[i] }; benchmarks[i] = { names[i], fns[i] };
if (!bench_fn_iters.has_key(names[i])) bench_fn_iters[names[i]] = benchmark_max_iterations;
} }
return benchmarks; return benchmarks;
} }
@@ -39,30 +36,6 @@ fn void set_benchmark_max_iterations(uint value) @builtin
{ {
assert(value > 0); assert(value > 0);
benchmark_max_iterations = value; benchmark_max_iterations = value;
foreach (k : bench_fn_iters.key_iter()) bench_fn_iters[k] = value;
}
fn void set_benchmark_func_iterations(String func, uint value) @builtin
{
assert(value > 0);
bench_fn_iters[func] = value;
}
Clock benchmark_clock @local;
NanoDuration benchmark_nano_seconds @local;
DString benchmark_log @local;
bool benchmark_warming @local;
uint this_iteration @local;
macro @start_benchmark() => benchmark_clock = std::time::clock::now();
macro @end_benchmark() => benchmark_nano_seconds = benchmark_clock.mark();
macro @log_benchmark(msg, args...) => @pool()
{
if (benchmark_warming) return;
benchmark_log.appendf("%s [%d]: ", $$FUNC, this_iteration);
benchmark_log.appendfn(msg, ...args);
} }
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
@@ -88,58 +61,33 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
long sys_clock_started; long sys_clock_started;
long sys_clock_finished; long sys_clock_finished;
long sys_clocks; long sys_clocks;
Clock clock;
foreach (unit : benchmarks) foreach(unit : benchmarks)
{ {
defer name.clear(); defer name.clear();
name.appendf("Benchmarking %s ", unit.name); name.appendf("Benchmarking %s ", unit.name);
name.append_repeat('.', max_name - unit.name.len + 2); name.append_repeat('.', max_name - unit.name.len + 2);
io::printf("%s ", name.str_view()); io::printf("%s ", name.str_view());
benchmark_warming = true;
for (uint i = 0; i < benchmark_warmup_iterations; i++) for (uint i = 0; i < benchmark_warmup_iterations; i++)
{ {
unit.func() @inline; unit.func() @inline;
} }
benchmark_warming = false;
NanoDuration running_timer; clock = std::time::clock::now();
sys_clock_started = $$sysclock(); sys_clock_started = $$sysclock();
benchmark_nano_seconds = {};
uint current_benchmark_iterations = bench_fn_iters[unit.name] ?? benchmark_max_iterations; for (uint i = 0; i < benchmark_max_iterations; i++)
char[] perc_str = { [0..19] = ' ', [20] = 0 };
int perc = 0;
for (this_iteration = 0; this_iteration < current_benchmark_iterations; ++this_iteration)
{ {
perc_str[0..(uint)math::floor((this_iteration / (float)current_benchmark_iterations) * 20)] = '#';
perc = (uint)math::ceil(100 * (this_iteration / (float)current_benchmark_iterations));
io::printf("\r%s [%s] %d / %d (%d%%)", name.str_view(), (ZString)perc_str, this_iteration, current_benchmark_iterations, perc);
io::stdout().flush()!!;
@start_benchmark(); // can be overridden by calls inside the unit's func
unit.func() @inline; unit.func() @inline;
if (!benchmark_nano_seconds) @end_benchmark();
running_timer += benchmark_nano_seconds;
} }
sys_clock_finished = $$sysclock(); sys_clock_finished = $$sysclock();
NanoDuration nano_seconds = clock.mark();
sys_clocks = sys_clock_finished - sys_clock_started; sys_clocks = sys_clock_finished - sys_clock_started;
float clock_cycles = (float)sys_clocks / current_benchmark_iterations; io::printfn("[COMPLETE] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
float measurement = (float)running_timer / current_benchmark_iterations;
String[] units = { "nanoseconds", "microseconds", "milliseconds", "seconds" };
float adjusted_measurement = measurement;
while (adjusted_measurement > 1_000) adjusted_measurement /= 1_000;
io::printf("\r%s ", name.str_view());
io::printfn("[COMPLETE] %.2f %s, %.2f CPU clocks, %d iterations",
adjusted_measurement, units[math::min(3, (int)math::floor(math::log(measurement, 1_000)))], clock_cycles, current_benchmark_iterations);
} }
io::printfn("\n%d benchmark%s run.\n", benchmarks.len, benchmarks.len > 1 ? "s" : ""); io::printfn("\n%d benchmark%s run.\n", benchmarks.len, benchmarks.len > 1 ? "s" : "");
@@ -148,12 +96,5 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
fn bool default_benchmark_runner(String[] args) => @pool() fn bool default_benchmark_runner(String[] args) => @pool()
{ {
benchmark_log.init(mem);
defer
{
if (benchmark_log.len()) io::printfn("\n---------- BENCHMARK LOG ----------\n%s\n", benchmark_log.str_view());
benchmark_log.free();
}
return run_benchmarks(benchmark_collection_create(tmem)); return run_benchmarks(benchmark_collection_create(tmem));
} }

View File

@@ -171,11 +171,6 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
usz max_name; usz max_name;
bool sort_tests = true; bool sort_tests = true;
bool check_leaks = true; bool check_leaks = true;
if (!tests.len)
{
io::printn("There are no test units to run.");
return true; // no tests == technically a pass
}
foreach (&unit : tests) foreach (&unit : tests)
{ {
if (max_name < unit.name.len) max_name = unit.name.len; if (max_name < unit.name.len) max_name = unit.name.len;
@@ -310,7 +305,7 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
} }
mem.free(); mem.free();
} }
io::printfn("\n%d test%s run.\n", test_count-tests_skipped, test_count != 1 ? "s" : ""); io::printfn("\n%d test%s run.\n", test_count-tests_skipped, test_count > 1 ? "s" : "");
int n_failed = test_count - tests_passed - tests_skipped; int n_failed = test_count - tests_passed - tests_skipped;
io::printf("Test Result: %s%s%s: ", io::printf("Test Result: %s%s%s: ",

View File

@@ -1,7 +1,5 @@
module std::core::string; module std::core::string;
import std::io; import std::io;
import std::core::mem::allocator;
typedef String @if(!$defined(String)) = inline char[]; typedef String @if(!$defined(String)) = inline char[];
<* <*
@@ -109,19 +107,6 @@ fn String format(Allocator allocator, String fmt, args...) @format(1) => @pool()
return str.copy_str(allocator); return str.copy_str(allocator);
} }
<*
Return a new String created using the formatting function.
@param [inout] buffer : `The buffer to use`
@param [in] fmt : `The formatting string`
*>
fn String bformat(char[] buffer, String fmt, args...) @format(1)
{
DString str = dstring::new_with_capacity(allocator::wrap(buffer), fmt.len + args.len * 8);
str.appendf(fmt, ...args);
return str.str_view();
}
<* <*
Return a temporary String created using the formatting function. Return a temporary String created using the formatting function.
@@ -597,7 +582,6 @@ fn bool ZString.eq(self, ZString other) @operator(==)
char* a = self; char* a = self;
char* b = other; char* b = other;
if (a == b) return true; if (a == b) return true;
if (!a || !b) return false;
for (;; a++, b++) for (;; a++, b++)
{ {
char c = *a; char c = *a;
@@ -624,17 +608,12 @@ fn usz ZString.char_len(str)
fn usz ZString.len(self) fn usz ZString.len(self)
{ {
usz len; usz len = 0;
for (char* ptr = (char*)self; *ptr; ptr++) len++; char* ptr = (char*)self;
while (char c = ptr++[0]) len++;
return len; return len;
} }
fn usz WString.len(self)
{
usz len;
for (Char16* ptr = (Char16*)self; *ptr; ptr++) len++;
return len;
}
fn ZString String.zstr_copy(self, Allocator allocator) fn ZString String.zstr_copy(self, Allocator allocator)
{ {
@@ -777,92 +756,6 @@ fn String String.to_upper_copy(self, Allocator allocator)
return copy; return copy;
} }
fn String String.capitalize_copy(self, Allocator allocator)
{
String s = self.copy(allocator);
if (s.len > 0 && s[0].is_lower())
{
s[0] &= (char)~0x20;
}
return s;
}
<*
Convert a string from `snake_case` to PascalCase.
@param [in] self
@return `"FooBar" from "foo_bar" the resulting pointer may safely be cast to ZString.`
*>
fn String String.snake_to_pascal_copy(self, Allocator allocator)
{
Splitter splitter = self.tokenize("_");
char[] new_string = allocator::alloc_array(allocator, char, self.len + 1);
usz index = 0;
while (try s = splitter.next())
{
assert(s.len > 0);
char c = s[0];
if (c.is_lower()) c = c.to_upper();
new_string[index++] = c;
s = s[1..];
new_string[index:s.len] = s[..];
index += s.len;
}
new_string[index] = 0;
return (String)new_string[:index];
}
<*
Movifies the current string from `snake_case` to PascalCase.
@param [inout] self
*>
fn void String.convert_snake_to_pascal(&self)
{
Splitter splitter = self.tokenize("_");
String new_string = *self;
usz index = 0;
while (try s = splitter.next())
{
assert(s.len > 0);
char c = s[0];
if (c.is_lower()) c = c.to_upper();
new_string[index++] = c;
s = s[1..];
new_string[index:s.len] = s[..];
index += s.len;
}
*self = new_string[:index];
}
<*
Convert a string from `PascalCase` to `snake_case`.
@param [in] self
@return `"foo_bar" from "FooBar" the resulting pointer may safely be cast to ZString.`
*>
fn String String.pascal_to_snake_copy(self, Allocator allocator) => @pool()
{
DString d;
d.init(tmem, (usz)(self.len * 1.5));
usz index = 0;
foreach (i, c : self)
{
if (c.is_upper())
{
if (i > 0 && ((self[i - 1].is_lower() || self[i - 1].is_digit()) || (i < self.len - 1 && self[i + 1].is_lower())))
{
d.append_char('_');
}
d.append_char(c.to_lower());
continue;
}
d.append_char(c);
}
return d.copy_str(allocator);
}
fn StringIterator String.iterator(self) fn StringIterator String.iterator(self)
{ {
return { self, 0 }; return { self, 0 };

View File

@@ -1,233 +0,0 @@
// 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;
}
}

View File

@@ -462,7 +462,7 @@ macro String.to_real(chars, $Type) @private
$error "Unexpected type"; $error "Unexpected type";
$endswitch $endswitch
chars = chars.trim(); while (chars.len && chars[0] == ' ') chars = chars[1..];
if (!chars.len) return MALFORMED_FLOAT?; if (!chars.len) return MALFORMED_FLOAT?;
if (chars.len != 1) if (chars.len != 1)
@@ -476,9 +476,6 @@ macro String.to_real(chars, $Type) @private
chars = chars[1..]; chars = chars[1..];
} }
} }
chars = chars.trim();
if (!chars.len) return MALFORMED_FLOAT?;
if (chars == "infinity" || chars == "INFINITY") return sign * $Type.inf; if (chars == "infinity" || chars == "INFINITY") return sign * $Type.inf;
if (chars == "NAN" || chars == "nan") return $Type.nan; if (chars == "NAN" || chars == "nan") return $Type.nan;

View File

@@ -339,8 +339,6 @@ macro bool is_same_vector_type($Type1, $Type2) @const
$endif $endif
} }
macro bool has_equals($Type) @const => $defined(($Type){} == ($Type){});
macro bool is_equatable_type($Type) @const macro bool is_equatable_type($Type) @const
{ {
$if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals): $if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals):

View File

@@ -2,6 +2,9 @@ module std::core::values;
import std::core::types; import std::core::types;
macro typeid @typeid(#value) @const @builtin => $typeof(#value).typeid;
macro TypeKind @typekind(#value) @const @builtin => $typeof(#value).kindof;
macro bool @typeis(#value, $Type) @const @builtin => $typeof(#value).typeid == $Type.typeid;
<* <*
Return true if two values have the same type before any conversions. Return true if two values have the same type before any conversions.
*> *>
@@ -15,13 +18,8 @@ macro bool @is_promotable_to_floatlike(#value) @const => types::is_promotable_to
macro bool @is_promotable_to_float(#value) @const => types::is_promotable_to_float($typeof(#value)); macro bool @is_promotable_to_float(#value) @const => types::is_promotable_to_float($typeof(#value));
macro bool @is_vector(#value) @const => types::is_vector($typeof(#value)); macro bool @is_vector(#value) @const => types::is_vector($typeof(#value));
macro bool @is_same_vector_type(#value1, #value2) @const => types::is_same_vector_type($typeof(#value1), $typeof(#value2)); macro bool @is_same_vector_type(#value1, #value2) @const => types::is_same_vector_type($typeof(#value1), $typeof(#value2));
macro bool @assign_to(#value1, #value2) @const => @assignable_to(#value1, $typeof(#value2)); macro bool @assign_to(#value1, #value2) @const => $assignable(#value1, $typeof(#value2));
macro bool @is_lvalue(#value) => $defined(#value = #value); macro bool @is_lvalue(#value) => $defined(#value = #value);
macro bool @is_const(#foo) @const @builtin
{
var $v;
return $defined($v = #foo);
}
macro promote_int(x) macro promote_int(x)
{ {

View File

@@ -1,736 +0,0 @@
/*
Ed25519 Digital Signature Algorithm
*/
module std::crypto::ed25519;
import std::hash::sha512;
alias Ed25519PrivateKey = char[32];
alias Ed25519PublicKey = char[Ed25519PrivateKey.len];
alias Ed25519Signature = char[2 * Ed25519PublicKey.len];
<*
Generate a public key from a private key.
@param [in] private_key : "32 bytes of cryptographically secure random data"
@require private_key.len == Ed25519PrivateKey.len
*>
fn Ed25519PublicKey public_keygen(char[] private_key)
{
return pack(&&unproject(&&(BASE * expand_private_key(private_key)[:FBaseInt.len])));
}
<*
Sign a message.
@param [in] message
@param [in] private_key
@param [in] public_key
@require private_key.len == Ed25519PrivateKey.len
@require public_key.len == Ed25519PublicKey.len
*>
fn Ed25519Signature sign(char[] message, char[] private_key, char[] public_key)
{
Ed25519Signature r @noinit;
char[*] exp = expand_private_key(private_key);
Sha512 sha @noinit;
sha.init();
sha.update(exp[FBaseInt.len..]);
sha.update(message);
FBaseInt k = from_bytes(&&sha.final());
r[:F25519Int.len] = pack(&&unproject(&&(BASE * k[..])))[..];
sha.init();
sha.update(r[:F25519Int.len]);
sha.update(public_key);
sha.update(message);
FBaseInt z = from_bytes(&&sha.final());
FBaseInt e = from_bytes(exp[:FBaseInt.len]);
r[F25519Int.len..] = (z * e + k)[..];
return r;
}
<*
Verify the signature of a message.
@param [in] message
@param [in] signature
@param [in] public_key
@require signature.len == Ed25519Signature.len
@require public_key.len == Ed25519PublicKey.len
*>
fn bool verify(char[] message, char[] signature, char[] public_key)
{
char ok = 1;
F25519Int lhs = pack(&&unproject(&&(BASE * signature[F25519Int.len..])));
Unpacking unp_p = unpack_on_curve((F25519Int*)public_key);
Projection p = project(&unp_p.point);
ok &= unp_p.on_curve;
Sha512 sha @noinit;
sha.init();
sha.update(signature[:F25519Int.len]);
sha.update(public_key);
sha.update(message);
FBaseInt z = from_bytes(&&sha.final());
p = p * z[..];
Unpacking unp_q = unpack_on_curve((F25519Int*)signature[:F25519Int.len]);
Projection q = project(&unp_q.point);
ok &= unp_q.on_curve;
p = p + q;
F25519Int rhs = pack(&&unproject(&p));
return (bool)(ok & eq(&lhs, &rhs));
}
// Base point for Ed25519. Generate a subgroup of order 2^252+0x14def9dea2f79cd65812631a5cf5d3ed
const Projection BASE @private =
{
x"1ad5258f602d56c9 b2a7259560c72c69 5cdcd6fd31e2a4c0 fe536ecdd3366921",
x"5866666666666666 6666666666666666 6666666666666666 6666666666666666",
x"a3ddb7a5b38ade6d f5525177809ff020 7de3ab648e4eea66 65768bd70f5f8767",
ONE
};
<*
Compute the pruned SHA-512 hash of a private key.
@param [in] private_key
@require private_key.len == Ed25519PrivateKey.len
*>
fn char[sha512::HASH_SIZE] expand_private_key(char[] private_key) @local
{
char[*] r = sha512::hash(private_key);
r[0] &= 0b11111000;
r[FBaseInt.len - 1] &= 0b01111111;
r[FBaseInt.len - 1] |= 0b01000000;
return r;
}
/*
Operations on the twisted Edwards curve -x^2+y^2=1-121665/121666*x^2*y^2 over the prime field F_(2^255-19) (edwards25519)
The set of F_(2^255-19)-rational curve points is a group of order 2^3*(2^252+0x14def9dea2f79cd65812631a5cf5d3ed)
*/
module std::crypto::ed25519 @private;
// Affine coordinates.
struct Point
{
F25519Int x;
F25519Int y;
}
// Projective coordinates.
struct Projection
{
F25519Int x;
F25519Int y;
F25519Int t;
F25519Int z;
}
// Neutral.
const Projection NEUTRAL =
{
ZERO,
ONE,
ZERO,
ONE
};
<*
Convert affine to projective coordinates.
@param [&in] p
*>
fn Projection project(Point* p) => { p.x, p.y, p.x * p.y, ONE };
<*
Convert projective to affine coordinates.
@param [&in] p
*>
fn Point unproject(Projection* p)
{
Point r @noinit;
F25519Int inv = p.z.inv();
r.x = p.x * inv;
r.y = p.y * inv;
r.x.normalize();
r.y.normalize();
return r;
}
// d parameter for edwards25519 : -121665/121666
const F25519Int D = x"a3785913ca4deb75 abd841414d0a7000 98e879777940c78c 73fe6f2bee6c0352";
// 2*d
const F25519Int DD = x"59f1b226949bd6eb 56b183829a14e000 30d1f3eef2808e19 e7fcdf56dcd90624";
<*
Compress a point.
@param [&in] p
*>
fn F25519Int pack(Point* p)
{
Point r = *p;
r.x.normalize();
r.y.normalize();
r.y[^1] |= (r.x[0] & 1) << 7;
return r.y;
}
struct Unpacking
{
Point point;
char on_curve; // Non-zero if true.
}
<*
Uncompress a point. Check if it is on the curve.
@param [&in] encoding
*>
fn Unpacking unpack_on_curve(F25519Int* encoding)
{
Point p @noinit;
char parity = (*encoding)[^1] >> 7;
p.y = *encoding;
p.y[^1] &= 0b01111111;
F25519Int y2 = p.y * p.y;
F25519Int x2 = (D * y2 + ONE).inv() * (y2 - ONE);
F25519Int x = x2.sqrt();
p.x = f25519_select(&x, &&-x, (x[0] ^ parity) & 1);
F25519Int _x2 = p.x * p.x;
x2.normalize();
_x2.normalize();
return {p, eq(&x2, &_x2)};
}
macro Projection Projection.@add(&s, Projection #p) @operator(+) => s.add(@addr(#p));
<*
Addition.
@param [&in] s
*>
fn Projection Projection.add(&s, Projection* p) @operator(+)
{
Projection r @noinit;
F25519Int a = (s.y - s.x) * (p.y - p.x);
F25519Int b = (s.y + s.x) * (p.y + p.x);
F25519Int c = s.t * DD * p.t;
F25519Int d = (s.z * p.z).mul_s(2);
F25519Int e = b - a;
F25519Int f = d - c;
F25519Int g = d + c;
F25519Int h = b + a;
r.x = e * f;
r.y = g * h;
r.t = e * h;
r.z = f * g;
return r;
}
<*
Double a point.
@param [&in] s
*>
fn Projection Projection.twice(&s)
{
Projection r @noinit;
F25519Int a = s.x * s.x;
F25519Int b = s.y * s.y;
F25519Int c = (s.z * s.z).mul_s(2);
F25519Int d = s.x + s.y;
F25519Int e = d * d - a - b;
F25519Int g = b - a;
F25519Int f = g - c;
F25519Int h = -b - a;
r.x = e * f;
r.y = g * h;
r.t = e * h;
r.z = f * g;
return r;
}
<*
Variable base scalar multiplication.
@param [&in] s
@param [in] n
*>
fn Projection Projection.mul(&s, char[] n) @operator(*)
{
Projection r = NEUTRAL;
for (isz i = n.len << 3 - 1; i >= 0; i--)
{
r = r.twice();
Projection t = r + s;
char bit = n[i >> 3] >> (i & 7) & 1;
r.x = f25519_select(&r.x, &t.x, bit);
r.y = f25519_select(&r.y, &t.y, bit);
r.z = f25519_select(&r.z, &t.z, bit);
r.t = f25519_select(&r.t, &t.t, bit);
}
return r;
}
/*
Modular arithmetic over the prime field F_(2^255-19)
*/
module std::crypto::ed25519 @private;
typedef F25519Int = inline char[32];
const F25519Int ZERO = {};
const F25519Int ONE = {[0] = 1};
<*
Reduce an element with carry to at most 2^255+18 (32 bytes)
@param [&inout] s
*>
fn void F25519Int.reduce_carry(&s, uint carry)
{
// Reduce using 2^255 = 19 mod p
(*s)[^1] &= 0b01111111;
carry *= 19;
foreach (i, &v : s)
{
carry += *v;
*v = (char)carry;
carry >>= 8;
}
}
<*
Reduce an element to at most 2^255-19
@param [&inout] s
*>
fn void F25519Int.normalize(&s)
{
s.reduce_carry((*s)[^1] >> 7);
// Substract p
F25519Int sub @noinit;
ushort c = 19;
foreach (i, v : (*s)[:^1])
{
c += v;
sub[i] = (char)c;
c >>= 8;
}
c += (*s)[^1] - 0b10000000;
sub[^1] = (char)c;
*s = f25519_select(&sub, s, (char)(c >> 15));
}
<*
Constant-time equality comparison. Return is non-zero if true.
@param [&in] a
@param [&in] b
*>
fn char eq(F25519Int* a, F25519Int* b)
{
char e;
foreach (i, v : a) e |= v ^ (*b)[i];
e |= (e >> 4);
e |= (e >> 2);
e |= (e >> 1);
return e ^ 1;
}
<*
Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1.
@param [&in] zero : "selected if condition is 0"
@param [&in] one : "selected if condition is 1"
*>
fn F25519Int f25519_select(F25519Int* zero, F25519Int* one, char condition)
{
F25519Int r @noinit;
foreach (i, z : zero) r[i] = z ^ (-condition & ((*one)[i] ^ z));
return r;
}
macro F25519Int F25519Int.@add(&s, F25519Int #n) @operator(+) => s.add(@addr(#n));
<*
Addition.
@param [&in] s
@param [&in] n
*>
fn F25519Int F25519Int.add(&s, F25519Int* n) @operator(+)
{
F25519Int r @noinit;
ushort c;
foreach (i, v : s)
{
c >>= 8;
c += v + (*n)[i];
r[i] = (char)c;
}
r.reduce_carry(c >> 7);
return r;
}
macro F25519Int F25519Int.@sub(&s, F25519Int #n) @operator(-) => s.sub(@addr(#n));
<*
Substraction.
@param [&in] s
@param [&in] n
*>
fn F25519Int F25519Int.sub(&s, F25519Int* n) @operator(-)
{
// Compute s+2*p-n instead of s-n to avoid underflow.
F25519Int r @noinit;
uint c = (char)~(2 * 19 - 1);
foreach (i, v : (*s)[:^1])
{
c += 0b11111111_00000000 + v - (*n)[i];
r[i] = (char)c;
c >>= 8;
}
c += (*s)[^1] - (*n)[^1];
r[^1] = (char)c;
r.reduce_carry(c >> 7);
return r;
}
<*
Negation.
@param [&in] s
*>
fn F25519Int F25519Int.neg(&s) @operator(-)
{
// Compute 2*p-s instead of -s to avoid underflow.
F25519Int r @noinit;
uint c = (char)~(2 * 19 - 1);
foreach (i, v : (*s)[:^1])
{
c += 0b11111111_00000000 - v;
r[i] = (char)c;
c >>= 8;
}
c -= (*s)[^1];
r[^1] = (char)c;
r.reduce_carry(c >> 7);
return r;
}
macro F25519Int F25519Int.@mul(&s, F25519Int #n) @operator(*) => s.mul(@addr(#n));
<*
Multiplication.
@param [&in] s
@param [&in] n
*>
fn F25519Int F25519Int.mul(&s, F25519Int* n) @operator(*)
{
F25519Int r @noinit;
uint c;
for (usz i = 0; i < F25519Int.len; i++)
{
c >>= 8;
for (usz j; j <= i; j++) c += (*s)[j] * (*n)[i - j];
// Reduce using 2^256 = 2*19 mod p
for (usz j = i + 1; j < F25519Int.len; j++) c += (*s)[j] * (*n)[^j - i] * 2 * 19;
r[i] = (char)c;
}
r.reduce_carry(c >> 7);
return r;
}
<*
Multiplication by a small element.
@param [&in] s
*>
fn F25519Int F25519Int.mul_s(&s, uint n)
{
F25519Int r @noinit;
uint c;
foreach (i, v : s)
{
c >>= 8;
c += v * n;
r[i] = (char)c;
}
r.reduce_carry(c >> 7);
return r;
}
<*
Inverse an element.
@param [&in] s
*>
fn F25519Int F25519Int.inv(&s)
{
//Compute s^(p-2)
F25519Int r = *s;
for (usz i; i < 255 - 1 - 5; i++) r = r * r * s;
r *= r;
r = r * r * s;
r *= r;
r = r * r * s;
r = r * r * s;
return r;
}
<*
Raise an element to the power of 2^252-3
@param [&in] s
*>
fn F25519Int F25519Int.pow_2523(&s) @local
{
F25519Int r = *s;
for (usz i; i < 252 - 1 - 2; i++) r = r * r * s;
r *= r;
r = r * r * s;
return r;
}
<*
Compute the square root of an element.
@param [&in] s
*>
fn F25519Int F25519Int.sqrt(&s)
{
F25519Int twice = s.mul_s(2);
F25519Int pow = twice.pow_2523();
return (twice * pow * pow - ONE) * s * pow;
}
/*
Modular arithmetic over the prime field F_(2^252+0x14def9dea2f79cd65812631a5cf5d3ed)
*/
module std::crypto::ed25519 @private;
import std::math;
typedef FBaseInt = inline char[32];
// Order of the field : 2^252+0x14def9dea2f79cd65812631a5cf5d3ed
const FBaseInt ORDER = x"edd3f55c1a631258 d69cf7a2def9de14 0000000000000000 0000000000000010";
<*
Interpret bytes as a normalized element.
@param [in] bytes
*>
fn FBaseInt from_bytes(char[] bytes)
{
FBaseInt r;
usz bitc = min(252 - 1, bytes.len << 3);
usz bytec = bitc >> 3;
usz mod = bitc & 7;
usz rem = bytes.len << 3 - bitc;
r[:bytec] = bytes[^bytec..];
if (mod)
{
r <<= mod;
r[0] |= bytes[^bytec + 1] >> (8 - mod);
}
for (isz i = rem - 1; i >= 0; i--)
{
r <<= 1;
r[0] |= bytes[i >> 3] >> (i & 7) & 1;
r = r.sub_l(&ORDER);
}
return r;
}
<*
Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1.
@param [&in] zero : "selected if condition is 0"
@param [&in] one : "selected if condition is 1"
*>
fn FBaseInt fbase_select(FBaseInt* zero, FBaseInt* one, char condition)
{
FBaseInt r @noinit;
foreach (i, z : zero) r[i] = z ^ (-condition & ((*one)[i] ^ z));
return r;
}
macro FBaseInt FBaseInt.@add(&s, FBaseInt #n) @operator(+) => s.add(@addr(#n));
<*
Addition.
@param [&in] s
@param [&in] n
*>
fn FBaseInt FBaseInt.add(&s, FBaseInt* n) @operator(+)
{
FBaseInt r @noinit;
ushort c;
foreach (i, v : s)
{
c += v + (*n)[i];
r[i] = (char)c;
c >>= 8;
}
return r.sub_l(&ORDER);
}
<*
Substraction if RHS is less than LHS else identity.
@param [&in] s
@param [&in] n
*>
fn FBaseInt FBaseInt.sub_l(&s, FBaseInt* n)
{
FBaseInt sub @noinit;
ushort c;
foreach (i, v : s)
{
c = v - (*n)[i] - c;
sub[i] = (char)c;
c = (c >> 8) & 1;
}
return fbase_select(&sub, s, (char)c);
}
<*
Left shift.
@param [&in] s
*>
fn FBaseInt FBaseInt.shl(&s, usz n) @operator(<<)
{
FBaseInt r @noinit;
ushort c;
foreach (i, v : s)
{
c |= v << n;
r[i] = (char)c;
c >>= 8;
}
return r;
}
macro FBaseInt FBaseInt.@mul(&s, FBaseInt #n) @operator(*) => s.mul(@addr(#n));
<*
Multiplication.
@param [&in] s
@param [&in] n
*>
fn FBaseInt FBaseInt.mul(&s, FBaseInt* n) @operator(*)
{
FBaseInt r;
for (isz i = 252; i >= 0; i--)
{
r = (r << 1).sub_l(&ORDER);
r = fbase_select(&r, &&(r + s), (*n)[i >> 3] >> (i & 7) & 1);
}
return r;
}

View File

@@ -5,9 +5,7 @@ module std::encoding::json;
import std::io; import std::io;
import std::collections::object; import std::collections::object;
faultdef UNEXPECTED_CHARACTER, INVALID_ESCAPE_SEQUENCE, INVALID_NUMBER, MAX_DEPTH_REACHED; faultdef UNEXPECTED_CHARACTER, INVALID_ESCAPE_SEQUENCE, DUPLICATE_MEMBERS, INVALID_NUMBER;
int max_depth = 128;
fn Object*? parse_string(Allocator allocator, String s) fn Object*? parse_string(Allocator allocator, String s)
{ {
@@ -26,15 +24,7 @@ fn Object*? parse(Allocator allocator, InStream s)
JsonContext context = { .last_string = dstring::new_with_capacity(smem, 64), .stream = s, .allocator = allocator }; JsonContext context = { .last_string = dstring::new_with_capacity(smem, 64), .stream = s, .allocator = allocator };
@pool() @pool()
{ {
Object* o = parse_any(&context)!; return parse_any(&context);
defer catch o.free();
while (char c = read_next(&context)!, c != 0)
{
if (c.is_space()) continue;
return UNEXPECTED_CHARACTER?;
}
if (!@catch(context.stream.read_byte())) return UNEXPECTED_CHARACTER?;
return o;
}; };
}; };
} }
@@ -72,14 +62,11 @@ struct JsonContext @local
DString last_string; DString last_string;
double last_number; double last_number;
char current; char current;
int depth; bitstruct : char {
bitstruct : char
{
bool skip_comments; bool skip_comments;
bool reached_end; bool reached_end;
bool pushed_back; bool pushed_back;
} }
} }
@@ -118,16 +105,10 @@ fn JsonTokenType? lex_number(JsonContext *context, char c) @local
t.append(c); t.append(c);
c = read_next(context)!; c = read_next(context)!;
} }
bool leading_zero = c == '0';
while (c.is_digit()) while (c.is_digit())
{ {
t.append(c); t.append(c);
c = read_next(context)!; c = read_next(context)!;
if (leading_zero)
{
if (c.is_digit()) return INVALID_NUMBER?;
leading_zero = false;
}
} }
if (c == '.') if (c == '.')
{ {
@@ -167,8 +148,6 @@ fn Object*? parse_map(JsonContext* context) @local
Object* map = object::new_obj(context.allocator); Object* map = object::new_obj(context.allocator);
defer catch map.free(); defer catch map.free();
JsonTokenType token = advance(context)!; JsonTokenType token = advance(context)!;
defer context.depth--;
if (++context.depth >= max_depth) return json::MAX_DEPTH_REACHED?;
@stack_mem(256; Allocator mem) @stack_mem(256; Allocator mem)
{ {
@@ -177,6 +156,7 @@ fn Object*? parse_map(JsonContext* context) @local
{ {
if (token != JsonTokenType.STRING) return UNEXPECTED_CHARACTER?; if (token != JsonTokenType.STRING) return UNEXPECTED_CHARACTER?;
DString string = context.last_string; DString string = context.last_string;
if (map.has_key(string.str_view())) return DUPLICATE_MEMBERS?;
// Copy the key to our temp holder, since our // Copy the key to our temp holder, since our
// last_string may be used in parse_any // last_string may be used in parse_any
temp_key.clear(); temp_key.clear();
@@ -200,8 +180,6 @@ fn Object*? parse_array(JsonContext* context) @local
{ {
Object* list = object::new_obj(context.allocator); Object* list = object::new_obj(context.allocator);
defer catch list.free(); defer catch list.free();
defer context.depth--;
if (++context.depth >= max_depth) return json::MAX_DEPTH_REACHED?;
JsonTokenType token = advance(context)!; JsonTokenType token = advance(context)!;
while (token != JsonTokenType.RBRACKET) while (token != JsonTokenType.RBRACKET)
{ {
@@ -270,7 +248,7 @@ fn JsonTokenType? advance(JsonContext* context) @local
case '\v': case '\v':
continue; continue;
case '/': case '/':
if (!context.skip_comments) break WS; if (!context.skip_comments) break;
c = read_next(context)!; c = read_next(context)!;
if (c != '*') if (c != '*')
{ {

View File

@@ -1,96 +0,0 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. 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.
//
// An implementation of Aleksey Vaneev's a5hash, version 5.16, in C3:
// https://github.com/avaneev/komihash
//
// The license for komihash from the above repository at the time of writing is as follows:
//
// >> MIT License
// >>
// >> Copyright (c) 2025 Aleksey Vaneev
// >>
// >> 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.
//
//
module std::hash::a5hash;
macro @a5mul(#u, #v, #lo, #hi) @local
{
uint128 imd = (uint128)#u * (uint128)#v;
#lo = (ulong)imd;
#hi = (ulong)(imd >> 64);
}
fn ulong hash(char[] data, ulong seed = 0)
{
ulong seed1 = 0x243F_6A88_85A3_08D3 ^ data.len;
ulong seed2 = 0x4528_21E6_38D0_1377 ^ data.len;
ulong val10 = 0xAAAA_AAAA_AAAA_AAAA;
ulong val01 = 0x5555_5555_5555_5555;
ulong a, b;
@a5mul(seed2 ^ (seed & val10), seed1 ^ (seed & val01), seed1, seed2);
val10 ^= seed2;
if (@likely(data.len > 3))
{
if (data.len > 16)
{
val01 ^= seed1;
for (; data.len > 16; data = data[16..])
{
@a5mul(
@unaligned_load(((ulong*)data.ptr)[0], 1) ^ seed1,
@unaligned_load(((ulong*)data.ptr)[1], 1) ^ seed2,
seed1, seed2
);
seed1 += val01;
seed2 += val10;
}
a = @unaligned_load(*(ulong*)(data.ptr + (uptr)data.len - 16), 1);
b = @unaligned_load(*(ulong*)(data.ptr + (uptr)data.len - 8), 1);
}
else
{
a = ((ulong)@unaligned_load(*(uint*)&data[0], 1) << 32)
| @unaligned_load(*(uint*)&data[^4], 1);
b = ((ulong)@unaligned_load(*(uint*)&data[(data.len >> 3) * 4], 1) << 32)
| @unaligned_load(*(uint*)(data.ptr + data.len - 4 - (data.len >> 3) * 4), 1);
}
}
else
{
a = data.len ? (data[0] | (data.len > 1 ? ((ulong)data[1] << 8) : 0) | (data.len > 2 ? ((ulong)data[2] << 16) : 0)) : 0;
b = 0;
}
@a5mul(a ^ seed1, b ^ seed2, seed1, seed2);
@a5mul(val01 ^ seed1, seed2, a, b);
return a ^ b;
}

View File

@@ -1,156 +0,0 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. 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.
//
// An implementation of Aleksey Vaneev's komihash, version 5.27, in C3:
// https://github.com/avaneev/komihash
//
// The license for komihash from the above repository at the time of writing is as follows:
//
// >> MIT License
// >>
// >> Copyright (c) 2021-2025 Aleksey Vaneev
// >>
// >> 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.
//
//
module std::hash::komi;
macro @komimul(#u, #v, #lo, #hi) @local
{
uint128 imd = (uint128)#u * (uint128)#v;
#lo = (ulong)imd;
#hi += (ulong)(imd >> 64);
}
fn ulong hash(char[] data, ulong seed = 0)
{
ulong seed1 = 0x243F_6A88_85A3_08D3 ^ (seed & 0x5555_5555_5555_5555);
ulong seed5 = 0x4528_21E6_38D0_1377 ^ (seed & 0xAAAA_AAAA_AAAA_AAAA);
ulong r1h, r2h;
// HASHROUND
@komimul(seed1, seed5, seed1, seed5);
seed1 ^= seed5;
if (@likely(data.len < 16))
{
r1h = seed1;
r2h = seed5;
if (@likely(data.len >= 8))
{
r1h ^= @unaligned_load(*(ulong*)data.ptr, 1);
r2h ^= (data.len < 12)
? ((data[data.len - 3] | ((ulong)data[data.len - 2] << 8) | ((ulong)data[data.len - 1] << 16) | ((ulong)1 << 24)) >> ((data.len * 8) ^ 88))
: (((@unaligned_load(*(uint*)&data[^4], 1) | ((ulong)1 << 32)) >> (128 - data.len * 8)) << 32 | @unaligned_load(*(uint*)&data[8], 1));
}
else if (data.len != 0)
{
r1h ^= (data.len < 4)
? (((ulong)1 << (data.len * 8)) ^ data[0] ^ (data.len > 1 ? (ulong)data[1] << 8 : 0) ^ (data.len > 2 ? (ulong)data[2] << 16 : 0))
: (((@unaligned_load(*(uint*)&data[^4], 1) | ((ulong)1 << 32)) >> (64 - data.len * 8)) << 32 | @unaligned_load(*(uint*)&data[0], 1));
}
}
else if (data.len < 32)
{
// HASH16
@komimul(
@unaligned_load(*(ulong*)&data[0], 1) ^ seed1,
@unaligned_load(*(ulong*)&data[8], 1) ^ seed5,
seed1, seed5
);
seed1 ^= seed5;
if (data.len < 24)
{
r1h = (((@unaligned_load(*(ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> (((int)(data.len * 8) ^ 184))) ^ seed1;
r2h = seed5;
}
else
{
r1h = @unaligned_load(*(ulong*)&data[16], 1) ^ seed1;
r2h = (((@unaligned_load(*(ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> (((int)(data.len * 8) ^ 248))) ^ seed5;
}
}
else
{
if (data.len >= 64)
{
ulong[8] seeds = {
seed1, 0x1319_8A2E_0370_7344 ^ seed1, 0xA409_3822_299F_31D0 ^ seed1, 0x082E_FA98_EC4E_6C89 ^ seed1,
seed5, 0xBE54_66CF_34E9_0C6C ^ seed5, 0xC0AC_29B7_C97C_50DD ^ seed5, 0x3F84_D5B5_B547_0917 ^ seed5,
};
// HASHLOOP64
for (; data.len >= 64; data = data[64:^64])
{
$for var $x = 0; $x < 4; ++$x :
@komimul(
@unaligned_load(*(ulong*)&data[0 + ($x * 8)], 1) ^ seeds[$x],
@unaligned_load(*(ulong*)&data[32 + ($x * 8)], 1) ^ seeds[4 + $x],
seeds[$x], seeds[4 + $x]
);
$endfor
seeds[3] ^= seeds[6];
seeds[0] ^= seeds[7];
seeds[2] ^= seeds[5];
seeds[1] ^= seeds[4];
}
seed1 = seeds[0] ^ seeds[1] ^ seeds[2] ^ seeds[3];
seed5 = seeds[4] ^ seeds[5] ^ seeds[6] ^ seeds[7];
}
for (; data.len >= 16; data = data[16:^16])
{
@komimul(
@unaligned_load(*(ulong*)&data[0], 1) ^ seed1,
@unaligned_load(*(ulong*)&data[8], 1) ^ seed5,
seed1, seed5
);
seed1 ^= seed5;
}
if (data.len < 8)
{
// NOTE: This is translated from the original code. It grabs the last ulong off the buffer even though the
// data slice is less than 8 bytes. This is possible because this branch only occurs in a loop where
// the original data slice length is >= 32.
r1h = (((@unaligned_load(*(ulong*)(data.ptr + data.len - 8), 1) >> 8) | ((ulong)1 << 56)) >> ((data.len * 8) ^ 0x38)) ^ seed1;
r2h = seed5;
}
else
{
r1h = @unaligned_load(*(ulong*)data.ptr, 1) ^ seed1;
r2h = (((@unaligned_load(*(ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> ((data.len * 8) ^ 0x78)) ^ seed5;
}
}
// HASHFIN
@komimul(r1h, r2h, seed1, seed5);
seed1 ^= seed5;
@komimul(seed1, seed5, seed1, seed5);
seed1 ^= seed5;
return seed1;
}

View File

@@ -1,149 +0,0 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. 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.
//
// MetroHash64 and MetroHash128 are different enough to warrant their own
// modules, and there would be no reason to create a generic module just
// for the two. If you inspect the differences, the only shared portion
// of the entire process is the `update` method.
//
module std::hash::metro128;
const ulong[4] K @local = {
0xc83a91e1,
0x8648dbdb,
0x7bdec03b,
0x2f5870a5,
};
struct MetroHash128
{
union
{
ulong[4] state;
uint128 result;
}
union
{
ulong[4] stomach_64;
char[32] stomach;
}
ulong bytes;
}
fn uint128 hash(char[] data, ulong seed = 0)
{
MetroHash128 m;
m.init(seed);
m.update(data);
return m.final();
}
fn void MetroHash128.init(&self, ulong seed = 0)
{
self.state = {
(seed - K[0]) * K[3],
(seed + K[1]) * K[2],
(seed + K[0]) * K[2],
(seed - K[1]) * K[3],
};
}
fn void MetroHash128.update(&self, char[] data)
{
if (self.bytes % 32) // partial buffer
{
ulong to_fill = min(data.len, (32 - (self.bytes % 32)));
self.stomach[(self.bytes % 32):to_fill] = data[:to_fill];
data = data[to_fill..];
self.bytes += to_fill;
if (self.bytes % 32) return; // still awaiting more input, or final
self.state[0] += self.stomach_64[0] * K[0]; self.state[0] = self.state[0].rotr(29) + self.state[2];
self.state[1] += self.stomach_64[1] * K[1]; self.state[1] = self.state[1].rotr(29) + self.state[3];
self.state[2] += self.stomach_64[2] * K[2]; self.state[2] = self.state[2].rotr(29) + self.state[0];
self.state[3] += self.stomach_64[3] * K[3]; self.state[3] = self.state[3].rotr(29) + self.state[1];
}
self.bytes += data.len;
for (; data.len >= 32; data = data[32:^32])
{
self.state[0] += @unaligned_load(((ulong*)data.ptr)[0], 1) * K[0]; self.state[0] = self.state[0].rotr(29) + self.state[2];
self.state[1] += @unaligned_load(((ulong*)data.ptr)[1], 1) * K[1]; self.state[1] = self.state[1].rotr(29) + self.state[3];
self.state[2] += @unaligned_load(((ulong*)data.ptr)[2], 1) * K[2]; self.state[2] = self.state[2].rotr(29) + self.state[0];
self.state[3] += @unaligned_load(((ulong*)data.ptr)[3], 1) * K[3]; self.state[3] = self.state[3].rotr(29) + self.state[1];
}
// Gobble up the leftover bytes. Nom nom.
if (data.len > 0) self.stomach[:data.len] = data[..];
}
fn uint128 MetroHash128.final(&self)
{
if (self.bytes >= 32)
{
self.state[2] ^= (((self.state[0] + self.state[3]) * K[0]) + self.state[1]).rotr(21) * K[1];
self.state[3] ^= (((self.state[1] + self.state[2]) * K[1]) + self.state[0]).rotr(21) * K[0];
self.state[0] ^= (((self.state[0] + self.state[2]) * K[0]) + self.state[3]).rotr(21) * K[1];
self.state[1] ^= (((self.state[1] + self.state[3]) * K[1]) + self.state[2]).rotr(21) * K[0];
}
char[] final_data = self.stomach[:(self.bytes % 32)];
if (final_data.len >= 16)
{
self.state[0] += ((ulong*)final_data.ptr)[0] * K[2]; self.state[0] = self.state[0].rotr(33) * K[3];
self.state[1] += ((ulong*)final_data.ptr)[1] * K[2]; self.state[1] = self.state[1].rotr(33) * K[3];
self.state[0] ^= ((self.state[0] * K[2]) + self.state[1]).rotr(45) * K[1];
self.state[1] ^= ((self.state[1] * K[3]) + self.state[0]).rotr(45) * K[0];
final_data = final_data[16:^16];
}
if (final_data.len >= 8)
{
self.state[0] += @unaligned_load(((ulong*)final_data.ptr)[0], 1) * K[2]; self.state[0] = self.state[0].rotr(33) * K[3];
self.state[0] ^= ((self.state[0] * K[2]) + self.state[1]).rotr(27) * K[1];
final_data = final_data[8:^8];
}
if (final_data.len >= 4)
{
self.state[1] += @unaligned_load(((uint*)final_data.ptr)[0], 1) * K[2]; self.state[1] = self.state[1].rotr(33) * K[3];
self.state[1] ^= ((self.state[1] * K[3]) + self.state[0]).rotr(46) * K[0];
final_data = final_data[4:^4];
}
if (final_data.len >= 2)
{
self.state[0] += @unaligned_load(((ushort*)final_data.ptr)[0], 1) * K[2]; self.state[0] = self.state[0].rotr(33) * K[3];
self.state[0] ^= ((self.state[0] * K[2]) + self.state[1]).rotr(22) * K[1];
final_data = final_data[2:^2];
}
if (final_data.len >= 1)
{
self.state[1] += ((char*)final_data.ptr)[0] * K[2]; self.state[1] = self.state[1].rotr(33) * K[3];
self.state[1] ^= ((self.state[1] * K[3]) + self.state[0]).rotr(58) * K[0];
}
self.state[0] += ((self.state[0] * K[0]) + self.state[1]).rotr(13);
self.state[1] += ((self.state[1] * K[1]) + self.state[0]).rotr(37);
self.state[0] += ((self.state[0] * K[2]) + self.state[1]).rotr(13);
self.state[1] += ((self.state[1] * K[3]) + self.state[0]).rotr(37);
return self.result;
}

View File

@@ -1,152 +0,0 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. 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.
//
// MetroHash64 and MetroHash128 are different enough to warrant their own
// modules, and there would be no reason to create a generic module just
// for the two. If you inspect the differences, the only shared portion
// of the entire process is the `update` method.
//
module std::hash::metro64;
const ulong[4] K @local = {
0xd6d018f5,
0xa2aa033b,
0x62992fc1,
0x30bc5b29,
};
struct MetroHash64
{
union
{
ulong[4] state;
ulong result;
}
union
{
ulong[4] stomach_64;
char[32] stomach;
}
ulong bytes;
ulong vseed;
}
fn ulong hash(char[] data, ulong seed = 0)
{
MetroHash64 m;
m.init(seed);
m.update(data);
return m.final();
}
fn void MetroHash64.init(&self, ulong seed = 0)
{
self.vseed = (seed + K[2]) * K[0];
self.state[0] = self.vseed;
self.state[1] = self.vseed;
self.state[2] = self.vseed;
self.state[3] = self.vseed;
}
fn void MetroHash64.update(&self, char[] data)
{
if (self.bytes % 32) // partial buffer
{
ulong to_fill = min(data.len, (32 - (self.bytes % 32)));
self.stomach[(self.bytes % 32):to_fill] = data[:to_fill];
data = data[to_fill..];
self.bytes += to_fill;
if (self.bytes % 32) return; // still awaiting more input, or final
self.state[0] += self.stomach_64[0] * K[0]; self.state[0] = self.state[0].rotr(29) + self.state[2];
self.state[1] += self.stomach_64[1] * K[1]; self.state[1] = self.state[1].rotr(29) + self.state[3];
self.state[2] += self.stomach_64[2] * K[2]; self.state[2] = self.state[2].rotr(29) + self.state[0];
self.state[3] += self.stomach_64[3] * K[3]; self.state[3] = self.state[3].rotr(29) + self.state[1];
}
self.bytes += data.len;
for (; data.len >= 32; data = data[32:^32])
{
self.state[0] += @unaligned_load(((ulong*)data.ptr)[0], 1) * K[0]; self.state[0] = self.state[0].rotr(29) + self.state[2];
self.state[1] += @unaligned_load(((ulong*)data.ptr)[1], 1) * K[1]; self.state[1] = self.state[1].rotr(29) + self.state[3];
self.state[2] += @unaligned_load(((ulong*)data.ptr)[2], 1) * K[2]; self.state[2] = self.state[2].rotr(29) + self.state[0];
self.state[3] += @unaligned_load(((ulong*)data.ptr)[3], 1) * K[3]; self.state[3] = self.state[3].rotr(29) + self.state[1];
}
// Gobble up the leftover bytes. Nom nom.
if (data.len > 0) self.stomach[:data.len] = data[..];
}
fn ulong MetroHash64.final(&self)
{
if (self.bytes >= 32)
{
self.state[2] ^= (((self.state[0] + self.state[3]) * K[0]) + self.state[1]).rotr(37) * K[1];
self.state[3] ^= (((self.state[1] + self.state[2]) * K[1]) + self.state[0]).rotr(37) * K[0];
self.state[0] ^= (((self.state[0] + self.state[2]) * K[0]) + self.state[3]).rotr(37) * K[1];
self.state[1] ^= (((self.state[1] + self.state[3]) * K[1]) + self.state[2]).rotr(37) * K[0];
self.state[0] = self.vseed + (self.state[0] ^ self.state[1]);
}
char[] final_data = self.stomach[:(self.bytes % 32)];
if (final_data.len >= 16)
{
self.state[1] = self.state[0] + @unaligned_load(((ulong*)final_data.ptr)[0], 1) * K[2]; self.state[1] = self.state[1].rotr(29) * K[3];
self.state[2] = self.state[0] + @unaligned_load(((ulong*)final_data.ptr)[1], 1) * K[2]; self.state[2] = self.state[2].rotr(29) * K[3];
self.state[1] ^= (self.state[1] * K[0]).rotr(21) + self.state[2];
self.state[2] ^= (self.state[2] * K[3]).rotr(21) + self.state[1];
self.state[0] += self.state[2];
final_data = final_data[16:^16];
}
if (final_data.len >= 8)
{
self.state[0] += @unaligned_load(((ulong*)final_data.ptr)[0], 1) * K[3];
self.state[0] ^= self.state[0].rotr(55) * K[1];
final_data = final_data[8:^8];
}
if (final_data.len >= 4)
{
self.state[0] += @unaligned_load(((uint*)final_data.ptr)[0], 1) * K[3];
self.state[0] ^= self.state[0].rotr(26) * K[1];
final_data = final_data[4:^4];
}
if (final_data.len >= 2)
{
self.state[0] += @unaligned_load(((ushort*)final_data.ptr)[0], 1) * K[3];
self.state[0] ^= self.state[0].rotr(48) * K[1];
final_data = final_data[2:^2];
}
if (final_data.len >= 1)
{
self.state[0] += ((char*)final_data.ptr)[0] * K[3];
self.state[0] ^= self.state[0].rotr(37) * K[1];
}
self.state[0] ^= self.state[0].rotr(28);
self.state[0] *= K[0];
self.state[0] ^= self.state[0].rotr(29);
return self.result;
}

View File

@@ -1,164 +0,0 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. 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.
//
// SipHash is a secure pseudorandom function (PRF) which digests a 128-bit key
// and a variable-length message to produce a 64- or 128-bit hash value.
//
// SipHash can be employed in numerous useful ways and structures, e.g.:
// - Hash Tables
// - Message Authentication Codes
// - Denial of Service (hash flooding) resistance
// - Bloom filters
// - Keyed runtime identifier derivation
//
// Read more: https://en.wikipedia.org/wiki/SipHash
//
//
// COMMON HASH VARIANTS.
// These two forms of SipHash (24 and 48) are the most widely
// used by many implementations.
// These provide typical 64-bit hash results.
// -- Best for performance-critical applications.
module std::hash::siphash24;
import std::hash::siphash;
alias SipHash24 = SipHash { ulong, 2, 4 };
alias hash = siphash::hash { ulong, 2, 4 };
// -- Best for conservative security applications.
module std::hash::siphash48;
import std::hash::siphash;
alias SipHash48 = SipHash { ulong, 4, 8 };
alias hash = siphash::hash { ulong, 4, 8 };
// Exact same as above, but for 128-bit outputs. Algorithm internally changes slightly.
module std::hash::siphash24_128;
import std::hash::siphash;
alias SipHash24_128 = SipHash { uint128, 2, 4 };
alias hash = siphash::hash { uint128, 2, 4 };
module std::hash::siphash48_128;
import std::hash::siphash;
alias SipHash48_128 = SipHash { uint128, 4, 8 };
alias hash = siphash::hash { uint128, 4, 8 };
<*
@require OutType.typeid == uint128.typeid || OutType.typeid == ulong.typeid : "Module OutType must be either uint128 or ulong."
*>
module std::hash::siphash { OutType, BLOCK_ROUNDS, FINALIZE_ROUNDS };
struct SipHash
{
ulong[4] v;
long m;
int m_idx;
usz len;
}
fn OutType hash(char[] data, uint128 key)
{
SipHash s;
s.init(key);
s.update(data);
return s.final();
}
fn void SipHash.init(&self, uint128 key)
{
ulong[2] key_64 = bitcast(key, ulong[2]);
self.v = {
0x736f_6d65_7073_6575 ^ key_64[0],
0x646f_7261_6e64_6f6d ^ key_64[1],
0x6c79_6765_6e65_7261 ^ key_64[0],
0x7465_6462_7974_6573 ^ key_64[1],
};
$if OutType.typeid == uint128.typeid :
self.v[1] ^= 0xEE;
$endif
}
<*
@param [in] data : "Bytes to hash"
*>
fn void SipHash.update(&self, char[] data)
{
self.len += data.len;
foreach (byte : data)
{
self.m |= (long)byte << (self.m_idx++ << 3);
if (self.m_idx < 8) continue;
// This runs every time the m_idx is 8, then it resets to 0.
self.v[3] ^= self.m;
$for var $i = 0; $i < BLOCK_ROUNDS; ++$i : // unrolled loop
self.round();
$endfor
self.v[0] ^= self.m;
self.m_idx = 0;
self.m = 0;
}
}
fn OutType SipHash.final(&self)
{
char[8] last = { [7] = (char)self.len };
self.update(last[(self.m_idx < 7 ? self.m_idx : 7)..]);
$if OutType.typeid == uint128.typeid :
self.v[2] ^= 0xEE;
$else
self.v[2] ^= 0xFF;
$endif
$for var $i = 0; $i < FINALIZE_ROUNDS; ++$i : // unrolled loop
self.round();
$endfor
$if OutType.typeid == ulong.typeid :
return self.v[0] ^ self.v[1] ^ self.v[2] ^ self.v[3];
$else
ulong lo = self.v[0] ^ self.v[1] ^ self.v[2] ^ self.v[3];
self.v[1] ^= 0xDD;
$for var $i = 0; $i < FINALIZE_ROUNDS; ++$i : // unrolled loop
self.round();
$endfor
return lo | ((uint128)(self.v[0] ^ self.v[1] ^ self.v[2] ^ self.v[3]) << 64);
$endif
}
fn void SipHash.round(&self) @local
{
self.v[0] += self.v[1];
self.v[1] = self.v[1].rotl(13);
self.v[1] ^= self.v[0];
self.v[0] = self.v[0].rotl(32);
self.v[2] += self.v[3];
self.v[3] = self.v[3].rotl(16);
self.v[3] ^= self.v[2];
self.v[0] += self.v[3];
self.v[3] = self.v[3].rotl(21);
self.v[3] ^= self.v[0];
self.v[2] += self.v[1];
self.v[1] = self.v[1].rotl(17);
self.v[1] ^= self.v[2];
self.v[2] = self.v[2].rotl(32);
}

View File

@@ -1,185 +0,0 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. 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.
//
// Dedicated from repo: https://github.com/NotsoanoNimus/whirlpool.c3l
module std::hash::whirlpool;
import std::hash::hmac;
const BLOCK_SIZE = 64;
const HASH_SIZE = 64;
const BLOCK_128 = 64 / int128.sizeof;
struct Whirlpool
{
ulong[8] hash;
union
{
char[BLOCK_SIZE] block;
int128[BLOCK_128] block_128;
}
// Using these two integers with a funnel shift permits the original WHIRLPOOL 2^256 length limit.
uint128 counter_high;
uint128 counter_low;
}
alias HmacWhirlpool = Hmac { Whirlpool, HASH_SIZE, BLOCK_SIZE };
alias hmac = hmac::hash { Whirlpool, HASH_SIZE, BLOCK_SIZE };
alias pbkdf2 = hmac::pbkdf2 { Whirlpool, HASH_SIZE, BLOCK_SIZE };
fn char[HASH_SIZE] hash(char[] data)
{
Whirlpool w;
w.update(data);
return w.final();
}
// Added to satisfy HMAC
macro void Whirlpool.init(&self) => *self = { };
<*
@require data.len <= isz.max : "Update with smaller slices"
*>
fn void Whirlpool.update(&self, char[] data)
{
char remainder = (char)self.counter_low & 0x3F; // if not at an even BLOCK_SIZE, how many bytes are over it
uint to_pad = BLOCK_SIZE - remainder; // how many bytes must be filled to get the BLOCK_SIZE
uint128 prev_ctr = self.counter_low;
self.counter_low += data.len;
// Handle counter rollover by just adding 1 onto 'high'. The overflow amount in 'low' remains valid.
// Since 'data' is limited to INT64_MAX, there are no edge cases where 'high' must be incremented by more than 1.
// This is so much data (> 2^128 bytes) that this will likely NEVER happen in practice...
if (@unlikely(self.counter_low < prev_ctr)) ++self.counter_high;
// Pad partial blocks of input data.
if (remainder > 0)
{
usz span = to_pad < data.len ? to_pad : data.len;
self.block[remainder:span] = data[:span];
// When there wasn't enough data ingested to fill a block, just return to allow more incoming data to fill in.
// If the algorithm is finalized during a 'partial' block, it has its own padding to fill the remaining gap.
if (data.len < to_pad) return;
self.process_block(&self.block);
data = data[to_pad..];
}
// Digest blocks wholesale.
while (data.len >= BLOCK_SIZE)
{
self.process_block(data);
data = (data.len > BLOCK_SIZE) ? data[BLOCK_SIZE..] : {};
}
// Any leftovers should be swept into the 'block' buffer in the context for the next update or for 'final'.
if (data.len)
{
usz leftover_span = (BLOCK_SIZE < data.len ? BLOCK_SIZE : data.len);
self.block[:leftover_span] = data[:leftover_span];
}
}
fn char[HASH_SIZE] Whirlpool.final(&self)
{
char remainder = (char)self.counter_low & 0x3F; // if not at an even BLOCK_SIZE, how many bytes are over it
// Terminating byte gets added to the block.
self.block[remainder++] = 0x80;
// When fewer than 256 bits are available to store the counter into, pad the block with zeroes and process it.
if (remainder > 32)
{
if (remainder < 64) self.block[remainder..63] = 0x00;
self.process_block(&self.block);
remainder = 0;
}
// Zero out the rest of the chunk (or a new chunk if the above 'if' was true), up to byte 32.
if (remainder < 32) self.block[remainder..31] = 0x00;
// Insert the big-endian values of the counter as a suffix of the block.
// This 'counter' value is actually the number of BITS processed, hence the << 3 (or x8).
self.block_128[2] = $$bswap(self.counter_high << 3 | (self.counter_low >> 125 & 0x07));
self.block_128[3] = $$bswap(self.counter_low << 3);
// Process the final block.
self.process_block(&self.block);
// Each ulong in the resultant hash should be bit-swapped before the final return.
char[HASH_SIZE] hash @align(ulong.alignof);
ulong* hash_ref = (ulong*)&hash;
$for var $i = 0; $i < 8; ++$i :
hash_ref[$i] = $$bswap(self.hash[$i]);
$endfor
// All done!
return hash;
}
macro ulong @w_op(#src, $shift) @private
=> S_BOX[(0 * 256) + (int)(#src[($shift + 0) & 7] >> 56) ]
^ S_BOX[(1 * 256) + (int)(#src[($shift + 7) & 7] >> 48) & 0xFF]
^ S_BOX[(2 * 256) + (int)(#src[($shift + 6) & 7] >> 40) & 0xFF]
^ S_BOX[(3 * 256) + (int)(#src[($shift + 5) & 7] >> 32) & 0xFF]
^ S_BOX[(4 * 256) + (int)(#src[($shift + 4) & 7] >> 24) & 0xFF]
^ S_BOX[(5 * 256) + (int)(#src[($shift + 3) & 7] >> 16) & 0xFF]
^ S_BOX[(6 * 256) + (int)(#src[($shift + 2) & 7] >> 8) & 0xFF]
^ S_BOX[(7 * 256) + (int)(#src[($shift + 1) & 7] >> 0) & 0xFF];
const ulong[10] RC @private = {
0x1823c6e887b8014f,
0x36a6d2f5796f9152,
0x60bc9b8ea30c7b35,
0x1de0d7c22e4bfe57,
0x157737e59ff04ada,
0x58c9290ab1a06b85,
0xbd5d10f4cb3e0567,
0xe427418ba77d95d8,
0xfbee7c66dd17479e,
0xca2dbf07ad5a8333
};
const ROUNDS = 10;
fn void Whirlpool.process_block(&self, char* block) @local
{
ulong[2 * 8] k; // key
ulong[2 * 8] state; // state
// NOTE: These loops are unrolled with C3's Chad-tier compile-time evaluation.
$for var $round = 0; $round < 8; $round++:
k[$round] = self.hash[$round];
state[$round] = $$bswap(@unaligned_load(((ulong*)block)[$round], 1)) ^ self.hash[$round];
self.hash[$round] = state[$round];
$endfor
$for var $round = 0; $round < ROUNDS; ++$round :
var $m = $round % 2;
k[(($m ^ 1) * 8) + 0] = @w_op((&k[$m * 8]), 0) ^ RC[$round];
$for var $i = 1; $i < 8; $i++ :
k[(($m ^ 1) * 8) + $i] = @w_op((&k[$m * 8]), $i);
$endfor
$for var $i = 0; $i < 8; $i++ :
state[(($m ^ 1) * 8) + $i] = @w_op(&(state[$m * 8]), $i) ^ k[(($m ^ 1) * 8) + $i];
$endfor
$endfor
$for var $x = 0; $x < 8; $x++:
self.hash[$x] ^= state[$x];
$endfor
}

View File

@@ -1,539 +0,0 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. 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.
//
// Dedicated from repo: https://github.com/NotsoanoNimus/whirlpool.c3l
module std::hash::whirlpool @private;
// =========================================================================
// WHIRLPOOL s-box data.
const ulong[8 * 256] S_BOX = {
// C0
0x18186018c07830d8, 0x23238c2305af4626, 0xc6c63fc67ef991b8, 0xe8e887e8136fcdfb,
0x878726874ca113cb, 0xb8b8dab8a9626d11, 0x0101040108050209, 0x4f4f214f426e9e0d,
0x3636d836adee6c9b, 0xa6a6a2a6590451ff, 0xd2d26fd2debdb90c, 0xf5f5f3f5fb06f70e,
0x7979f979ef80f296, 0x6f6fa16f5fcede30, 0x91917e91fcef3f6d, 0x52525552aa07a4f8,
0x60609d6027fdc047, 0xbcbccabc89766535, 0x9b9b569baccd2b37, 0x8e8e028e048c018a,
0xa3a3b6a371155bd2, 0x0c0c300c603c186c, 0x7b7bf17bff8af684, 0x3535d435b5e16a80,
0x1d1d741de8693af5, 0xe0e0a7e05347ddb3, 0xd7d77bd7f6acb321, 0xc2c22fc25eed999c,
0x2e2eb82e6d965c43, 0x4b4b314b627a9629, 0xfefedffea321e15d, 0x575741578216aed5,
0x15155415a8412abd, 0x7777c1779fb6eee8, 0x3737dc37a5eb6e92, 0xe5e5b3e57b56d79e,
0x9f9f469f8cd92313, 0xf0f0e7f0d317fd23, 0x4a4a354a6a7f9420, 0xdada4fda9e95a944,
0x58587d58fa25b0a2, 0xc9c903c906ca8fcf, 0x2929a429558d527c, 0x0a0a280a5022145a,
0xb1b1feb1e14f7f50, 0xa0a0baa0691a5dc9, 0x6b6bb16b7fdad614, 0x85852e855cab17d9,
0xbdbdcebd8173673c, 0x5d5d695dd234ba8f, 0x1010401080502090, 0xf4f4f7f4f303f507,
0xcbcb0bcb16c08bdd, 0x3e3ef83eedc67cd3, 0x0505140528110a2d, 0x676781671fe6ce78,
0xe4e4b7e47353d597, 0x27279c2725bb4e02, 0x4141194132588273, 0x8b8b168b2c9d0ba7,
0xa7a7a6a7510153f6, 0x7d7de97dcf94fab2, 0x95956e95dcfb3749, 0xd8d847d88e9fad56,
0xfbfbcbfb8b30eb70, 0xeeee9fee2371c1cd, 0x7c7ced7cc791f8bb, 0x6666856617e3cc71,
0xdddd53dda68ea77b, 0x17175c17b84b2eaf, 0x4747014702468e45, 0x9e9e429e84dc211a,
0xcaca0fca1ec589d4, 0x2d2db42d75995a58, 0xbfbfc6bf9179632e, 0x07071c07381b0e3f,
0xadad8ead012347ac, 0x5a5a755aea2fb4b0, 0x838336836cb51bef, 0x3333cc3385ff66b6,
0x636391633ff2c65c, 0x02020802100a0412, 0xaaaa92aa39384993, 0x7171d971afa8e2de,
0xc8c807c80ecf8dc6, 0x19196419c87d32d1, 0x494939497270923b, 0xd9d943d9869aaf5f,
0xf2f2eff2c31df931, 0xe3e3abe34b48dba8, 0x5b5b715be22ab6b9, 0x88881a8834920dbc,
0x9a9a529aa4c8293e, 0x262698262dbe4c0b, 0x3232c8328dfa64bf, 0xb0b0fab0e94a7d59,
0xe9e983e91b6acff2, 0x0f0f3c0f78331e77, 0xd5d573d5e6a6b733, 0x80803a8074ba1df4,
0xbebec2be997c6127, 0xcdcd13cd26de87eb, 0x3434d034bde46889, 0x48483d487a759032,
0xffffdbffab24e354, 0x7a7af57af78ff48d, 0x90907a90f4ea3d64, 0x5f5f615fc23ebe9d,
0x202080201da0403d, 0x6868bd6867d5d00f, 0x1a1a681ad07234ca, 0xaeae82ae192c41b7,
0xb4b4eab4c95e757d, 0x54544d549a19a8ce, 0x93937693ece53b7f, 0x222288220daa442f,
0x64648d6407e9c863, 0xf1f1e3f1db12ff2a, 0x7373d173bfa2e6cc, 0x12124812905a2482,
0x40401d403a5d807a, 0x0808200840281048, 0xc3c32bc356e89b95, 0xecec97ec337bc5df,
0xdbdb4bdb9690ab4d, 0xa1a1bea1611f5fc0, 0x8d8d0e8d1c830791, 0x3d3df43df5c97ac8,
0x97976697ccf1335b, 0x0000000000000000, 0xcfcf1bcf36d483f9, 0x2b2bac2b4587566e,
0x7676c57697b3ece1, 0x8282328264b019e6, 0xd6d67fd6fea9b128, 0x1b1b6c1bd87736c3,
0xb5b5eeb5c15b7774, 0xafaf86af112943be, 0x6a6ab56a77dfd41d, 0x50505d50ba0da0ea,
0x45450945124c8a57, 0xf3f3ebf3cb18fb38, 0x3030c0309df060ad, 0xefef9bef2b74c3c4,
0x3f3ffc3fe5c37eda, 0x55554955921caac7, 0xa2a2b2a2791059db, 0xeaea8fea0365c9e9,
0x656589650fecca6a, 0xbabad2bab9686903, 0x2f2fbc2f65935e4a, 0xc0c027c04ee79d8e,
0xdede5fdebe81a160, 0x1c1c701ce06c38fc, 0xfdfdd3fdbb2ee746, 0x4d4d294d52649a1f,
0x92927292e4e03976, 0x7575c9758fbceafa, 0x06061806301e0c36, 0x8a8a128a249809ae,
0xb2b2f2b2f940794b, 0xe6e6bfe66359d185, 0x0e0e380e70361c7e, 0x1f1f7c1ff8633ee7,
0x6262956237f7c455, 0xd4d477d4eea3b53a, 0xa8a89aa829324d81, 0x96966296c4f43152,
0xf9f9c3f99b3aef62, 0xc5c533c566f697a3, 0x2525942535b14a10, 0x59597959f220b2ab,
0x84842a8454ae15d0, 0x7272d572b7a7e4c5, 0x3939e439d5dd72ec, 0x4c4c2d4c5a619816,
0x5e5e655eca3bbc94, 0x7878fd78e785f09f, 0x3838e038ddd870e5, 0x8c8c0a8c14860598,
0xd1d163d1c6b2bf17, 0xa5a5aea5410b57e4, 0xe2e2afe2434dd9a1, 0x616199612ff8c24e,
0xb3b3f6b3f1457b42, 0x2121842115a54234, 0x9c9c4a9c94d62508, 0x1e1e781ef0663cee,
0x4343114322528661, 0xc7c73bc776fc93b1, 0xfcfcd7fcb32be54f, 0x0404100420140824,
0x51515951b208a2e3, 0x99995e99bcc72f25, 0x6d6da96d4fc4da22, 0x0d0d340d68391a65,
0xfafacffa8335e979, 0xdfdf5bdfb684a369, 0x7e7ee57ed79bfca9, 0x242490243db44819,
0x3b3bec3bc5d776fe, 0xabab96ab313d4b9a, 0xcece1fce3ed181f0, 0x1111441188552299,
0x8f8f068f0c890383, 0x4e4e254e4a6b9c04, 0xb7b7e6b7d1517366, 0xebeb8beb0b60cbe0,
0x3c3cf03cfdcc78c1, 0x81813e817cbf1ffd, 0x94946a94d4fe3540, 0xf7f7fbf7eb0cf31c,
0xb9b9deb9a1676f18, 0x13134c13985f268b, 0x2c2cb02c7d9c5851, 0xd3d36bd3d6b8bb05,
0xe7e7bbe76b5cd38c, 0x6e6ea56e57cbdc39, 0xc4c437c46ef395aa, 0x03030c03180f061b,
0x565645568a13acdc, 0x44440d441a49885e, 0x7f7fe17fdf9efea0, 0xa9a99ea921374f88,
0x2a2aa82a4d825467, 0xbbbbd6bbb16d6b0a, 0xc1c123c146e29f87, 0x53535153a202a6f1,
0xdcdc57dcae8ba572, 0x0b0b2c0b58271653, 0x9d9d4e9d9cd32701, 0x6c6cad6c47c1d82b,
0x3131c43195f562a4, 0x7474cd7487b9e8f3, 0xf6f6fff6e309f115, 0x464605460a438c4c,
0xacac8aac092645a5, 0x89891e893c970fb5, 0x14145014a04428b4, 0xe1e1a3e15b42dfba,
0x16165816b04e2ca6, 0x3a3ae83acdd274f7, 0x6969b9696fd0d206, 0x09092409482d1241,
0x7070dd70a7ade0d7, 0xb6b6e2b6d954716f, 0xd0d067d0ceb7bd1e, 0xeded93ed3b7ec7d6,
0xcccc17cc2edb85e2, 0x424215422a578468, 0x98985a98b4c22d2c, 0xa4a4aaa4490e55ed,
0x2828a0285d885075, 0x5c5c6d5cda31b886, 0xf8f8c7f8933fed6b, 0x8686228644a411c2,
// C1
0xd818186018c07830, 0x2623238c2305af46, 0xb8c6c63fc67ef991, 0xfbe8e887e8136fcd,
0xcb878726874ca113, 0x11b8b8dab8a9626d, 0x0901010401080502, 0x0d4f4f214f426e9e,
0x9b3636d836adee6c, 0xffa6a6a2a6590451, 0x0cd2d26fd2debdb9, 0x0ef5f5f3f5fb06f7,
0x967979f979ef80f2, 0x306f6fa16f5fcede, 0x6d91917e91fcef3f, 0xf852525552aa07a4,
0x4760609d6027fdc0, 0x35bcbccabc897665, 0x379b9b569baccd2b, 0x8a8e8e028e048c01,
0xd2a3a3b6a371155b, 0x6c0c0c300c603c18, 0x847b7bf17bff8af6, 0x803535d435b5e16a,
0xf51d1d741de8693a, 0xb3e0e0a7e05347dd, 0x21d7d77bd7f6acb3, 0x9cc2c22fc25eed99,
0x432e2eb82e6d965c, 0x294b4b314b627a96, 0x5dfefedffea321e1, 0xd5575741578216ae,
0xbd15155415a8412a, 0xe87777c1779fb6ee, 0x923737dc37a5eb6e, 0x9ee5e5b3e57b56d7,
0x139f9f469f8cd923, 0x23f0f0e7f0d317fd, 0x204a4a354a6a7f94, 0x44dada4fda9e95a9,
0xa258587d58fa25b0, 0xcfc9c903c906ca8f, 0x7c2929a429558d52, 0x5a0a0a280a502214,
0x50b1b1feb1e14f7f, 0xc9a0a0baa0691a5d, 0x146b6bb16b7fdad6, 0xd985852e855cab17,
0x3cbdbdcebd817367, 0x8f5d5d695dd234ba, 0x9010104010805020, 0x07f4f4f7f4f303f5,
0xddcbcb0bcb16c08b, 0xd33e3ef83eedc67c, 0x2d0505140528110a, 0x78676781671fe6ce,
0x97e4e4b7e47353d5, 0x0227279c2725bb4e, 0x7341411941325882, 0xa78b8b168b2c9d0b,
0xf6a7a7a6a7510153, 0xb27d7de97dcf94fa, 0x4995956e95dcfb37, 0x56d8d847d88e9fad,
0x70fbfbcbfb8b30eb, 0xcdeeee9fee2371c1, 0xbb7c7ced7cc791f8, 0x716666856617e3cc,
0x7bdddd53dda68ea7, 0xaf17175c17b84b2e, 0x454747014702468e, 0x1a9e9e429e84dc21,
0xd4caca0fca1ec589, 0x582d2db42d75995a, 0x2ebfbfc6bf917963, 0x3f07071c07381b0e,
0xacadad8ead012347, 0xb05a5a755aea2fb4, 0xef838336836cb51b, 0xb63333cc3385ff66,
0x5c636391633ff2c6, 0x1202020802100a04, 0x93aaaa92aa393849, 0xde7171d971afa8e2,
0xc6c8c807c80ecf8d, 0xd119196419c87d32, 0x3b49493949727092, 0x5fd9d943d9869aaf,
0x31f2f2eff2c31df9, 0xa8e3e3abe34b48db, 0xb95b5b715be22ab6, 0xbc88881a8834920d,
0x3e9a9a529aa4c829, 0x0b262698262dbe4c, 0xbf3232c8328dfa64, 0x59b0b0fab0e94a7d,
0xf2e9e983e91b6acf, 0x770f0f3c0f78331e, 0x33d5d573d5e6a6b7, 0xf480803a8074ba1d,
0x27bebec2be997c61, 0xebcdcd13cd26de87, 0x893434d034bde468, 0x3248483d487a7590,
0x54ffffdbffab24e3, 0x8d7a7af57af78ff4, 0x6490907a90f4ea3d, 0x9d5f5f615fc23ebe,
0x3d202080201da040, 0x0f6868bd6867d5d0, 0xca1a1a681ad07234, 0xb7aeae82ae192c41,
0x7db4b4eab4c95e75, 0xce54544d549a19a8, 0x7f93937693ece53b, 0x2f222288220daa44,
0x6364648d6407e9c8, 0x2af1f1e3f1db12ff, 0xcc7373d173bfa2e6, 0x8212124812905a24,
0x7a40401d403a5d80, 0x4808082008402810, 0x95c3c32bc356e89b, 0xdfecec97ec337bc5,
0x4ddbdb4bdb9690ab, 0xc0a1a1bea1611f5f, 0x918d8d0e8d1c8307, 0xc83d3df43df5c97a,
0x5b97976697ccf133, 0x0000000000000000, 0xf9cfcf1bcf36d483, 0x6e2b2bac2b458756,
0xe17676c57697b3ec, 0xe68282328264b019, 0x28d6d67fd6fea9b1, 0xc31b1b6c1bd87736,
0x74b5b5eeb5c15b77, 0xbeafaf86af112943, 0x1d6a6ab56a77dfd4, 0xea50505d50ba0da0,
0x5745450945124c8a, 0x38f3f3ebf3cb18fb, 0xad3030c0309df060, 0xc4efef9bef2b74c3,
0xda3f3ffc3fe5c37e, 0xc755554955921caa, 0xdba2a2b2a2791059, 0xe9eaea8fea0365c9,
0x6a656589650fecca, 0x03babad2bab96869, 0x4a2f2fbc2f65935e, 0x8ec0c027c04ee79d,
0x60dede5fdebe81a1, 0xfc1c1c701ce06c38, 0x46fdfdd3fdbb2ee7, 0x1f4d4d294d52649a,
0x7692927292e4e039, 0xfa7575c9758fbcea, 0x3606061806301e0c, 0xae8a8a128a249809,
0x4bb2b2f2b2f94079, 0x85e6e6bfe66359d1, 0x7e0e0e380e70361c, 0xe71f1f7c1ff8633e,
0x556262956237f7c4, 0x3ad4d477d4eea3b5, 0x81a8a89aa829324d, 0x5296966296c4f431,
0x62f9f9c3f99b3aef, 0xa3c5c533c566f697, 0x102525942535b14a, 0xab59597959f220b2,
0xd084842a8454ae15, 0xc57272d572b7a7e4, 0xec3939e439d5dd72, 0x164c4c2d4c5a6198,
0x945e5e655eca3bbc, 0x9f7878fd78e785f0, 0xe53838e038ddd870, 0x988c8c0a8c148605,
0x17d1d163d1c6b2bf, 0xe4a5a5aea5410b57, 0xa1e2e2afe2434dd9, 0x4e616199612ff8c2,
0x42b3b3f6b3f1457b, 0x342121842115a542, 0x089c9c4a9c94d625, 0xee1e1e781ef0663c,
0x6143431143225286, 0xb1c7c73bc776fc93, 0x4ffcfcd7fcb32be5, 0x2404041004201408,
0xe351515951b208a2, 0x2599995e99bcc72f, 0x226d6da96d4fc4da, 0x650d0d340d68391a,
0x79fafacffa8335e9, 0x69dfdf5bdfb684a3, 0xa97e7ee57ed79bfc, 0x19242490243db448,
0xfe3b3bec3bc5d776, 0x9aabab96ab313d4b, 0xf0cece1fce3ed181, 0x9911114411885522,
0x838f8f068f0c8903, 0x044e4e254e4a6b9c, 0x66b7b7e6b7d15173, 0xe0ebeb8beb0b60cb,
0xc13c3cf03cfdcc78, 0xfd81813e817cbf1f, 0x4094946a94d4fe35, 0x1cf7f7fbf7eb0cf3,
0x18b9b9deb9a1676f, 0x8b13134c13985f26, 0x512c2cb02c7d9c58, 0x05d3d36bd3d6b8bb,
0x8ce7e7bbe76b5cd3, 0x396e6ea56e57cbdc, 0xaac4c437c46ef395, 0x1b03030c03180f06,
0xdc565645568a13ac, 0x5e44440d441a4988, 0xa07f7fe17fdf9efe, 0x88a9a99ea921374f,
0x672a2aa82a4d8254, 0x0abbbbd6bbb16d6b, 0x87c1c123c146e29f, 0xf153535153a202a6,
0x72dcdc57dcae8ba5, 0x530b0b2c0b582716, 0x019d9d4e9d9cd327, 0x2b6c6cad6c47c1d8,
0xa43131c43195f562, 0xf37474cd7487b9e8, 0x15f6f6fff6e309f1, 0x4c464605460a438c,
0xa5acac8aac092645, 0xb589891e893c970f, 0xb414145014a04428, 0xbae1e1a3e15b42df,
0xa616165816b04e2c, 0xf73a3ae83acdd274, 0x066969b9696fd0d2, 0x4109092409482d12,
0xd77070dd70a7ade0, 0x6fb6b6e2b6d95471, 0x1ed0d067d0ceb7bd, 0xd6eded93ed3b7ec7,
0xe2cccc17cc2edb85, 0x68424215422a5784, 0x2c98985a98b4c22d, 0xeda4a4aaa4490e55,
0x752828a0285d8850, 0x865c5c6d5cda31b8, 0x6bf8f8c7f8933fed, 0xc28686228644a411,
// C2
0x30d818186018c078, 0x462623238c2305af, 0x91b8c6c63fc67ef9, 0xcdfbe8e887e8136f,
0x13cb878726874ca1, 0x6d11b8b8dab8a962, 0x0209010104010805, 0x9e0d4f4f214f426e,
0x6c9b3636d836adee, 0x51ffa6a6a2a65904, 0xb90cd2d26fd2debd, 0xf70ef5f5f3f5fb06,
0xf2967979f979ef80, 0xde306f6fa16f5fce, 0x3f6d91917e91fcef, 0xa4f852525552aa07,
0xc04760609d6027fd, 0x6535bcbccabc8976, 0x2b379b9b569baccd, 0x018a8e8e028e048c,
0x5bd2a3a3b6a37115, 0x186c0c0c300c603c, 0xf6847b7bf17bff8a, 0x6a803535d435b5e1,
0x3af51d1d741de869, 0xddb3e0e0a7e05347, 0xb321d7d77bd7f6ac, 0x999cc2c22fc25eed,
0x5c432e2eb82e6d96, 0x96294b4b314b627a, 0xe15dfefedffea321, 0xaed5575741578216,
0x2abd15155415a841, 0xeee87777c1779fb6, 0x6e923737dc37a5eb, 0xd79ee5e5b3e57b56,
0x23139f9f469f8cd9, 0xfd23f0f0e7f0d317, 0x94204a4a354a6a7f, 0xa944dada4fda9e95,
0xb0a258587d58fa25, 0x8fcfc9c903c906ca, 0x527c2929a429558d, 0x145a0a0a280a5022,
0x7f50b1b1feb1e14f, 0x5dc9a0a0baa0691a, 0xd6146b6bb16b7fda, 0x17d985852e855cab,
0x673cbdbdcebd8173, 0xba8f5d5d695dd234, 0x2090101040108050, 0xf507f4f4f7f4f303,
0x8bddcbcb0bcb16c0, 0x7cd33e3ef83eedc6, 0x0a2d050514052811, 0xce78676781671fe6,
0xd597e4e4b7e47353, 0x4e0227279c2725bb, 0x8273414119413258, 0x0ba78b8b168b2c9d,
0x53f6a7a7a6a75101, 0xfab27d7de97dcf94, 0x374995956e95dcfb, 0xad56d8d847d88e9f,
0xeb70fbfbcbfb8b30, 0xc1cdeeee9fee2371, 0xf8bb7c7ced7cc791, 0xcc716666856617e3,
0xa77bdddd53dda68e, 0x2eaf17175c17b84b, 0x8e45474701470246, 0x211a9e9e429e84dc,
0x89d4caca0fca1ec5, 0x5a582d2db42d7599, 0x632ebfbfc6bf9179, 0x0e3f07071c07381b,
0x47acadad8ead0123, 0xb4b05a5a755aea2f, 0x1bef838336836cb5, 0x66b63333cc3385ff,
0xc65c636391633ff2, 0x041202020802100a, 0x4993aaaa92aa3938, 0xe2de7171d971afa8,
0x8dc6c8c807c80ecf, 0x32d119196419c87d, 0x923b494939497270, 0xaf5fd9d943d9869a,
0xf931f2f2eff2c31d, 0xdba8e3e3abe34b48, 0xb6b95b5b715be22a, 0x0dbc88881a883492,
0x293e9a9a529aa4c8, 0x4c0b262698262dbe, 0x64bf3232c8328dfa, 0x7d59b0b0fab0e94a,
0xcff2e9e983e91b6a, 0x1e770f0f3c0f7833, 0xb733d5d573d5e6a6, 0x1df480803a8074ba,
0x6127bebec2be997c, 0x87ebcdcd13cd26de, 0x68893434d034bde4, 0x903248483d487a75,
0xe354ffffdbffab24, 0xf48d7a7af57af78f, 0x3d6490907a90f4ea, 0xbe9d5f5f615fc23e,
0x403d202080201da0, 0xd00f6868bd6867d5, 0x34ca1a1a681ad072, 0x41b7aeae82ae192c,
0x757db4b4eab4c95e, 0xa8ce54544d549a19, 0x3b7f93937693ece5, 0x442f222288220daa,
0xc86364648d6407e9, 0xff2af1f1e3f1db12, 0xe6cc7373d173bfa2, 0x248212124812905a,
0x807a40401d403a5d, 0x1048080820084028, 0x9b95c3c32bc356e8, 0xc5dfecec97ec337b,
0xab4ddbdb4bdb9690, 0x5fc0a1a1bea1611f, 0x07918d8d0e8d1c83, 0x7ac83d3df43df5c9,
0x335b97976697ccf1, 0x0000000000000000, 0x83f9cfcf1bcf36d4, 0x566e2b2bac2b4587,
0xece17676c57697b3, 0x19e68282328264b0, 0xb128d6d67fd6fea9, 0x36c31b1b6c1bd877,
0x7774b5b5eeb5c15b, 0x43beafaf86af1129, 0xd41d6a6ab56a77df, 0xa0ea50505d50ba0d,
0x8a5745450945124c, 0xfb38f3f3ebf3cb18, 0x60ad3030c0309df0, 0xc3c4efef9bef2b74,
0x7eda3f3ffc3fe5c3, 0xaac755554955921c, 0x59dba2a2b2a27910, 0xc9e9eaea8fea0365,
0xca6a656589650fec, 0x6903babad2bab968, 0x5e4a2f2fbc2f6593, 0x9d8ec0c027c04ee7,
0xa160dede5fdebe81, 0x38fc1c1c701ce06c, 0xe746fdfdd3fdbb2e, 0x9a1f4d4d294d5264,
0x397692927292e4e0, 0xeafa7575c9758fbc, 0x0c3606061806301e, 0x09ae8a8a128a2498,
0x794bb2b2f2b2f940, 0xd185e6e6bfe66359, 0x1c7e0e0e380e7036, 0x3ee71f1f7c1ff863,
0xc4556262956237f7, 0xb53ad4d477d4eea3, 0x4d81a8a89aa82932, 0x315296966296c4f4,
0xef62f9f9c3f99b3a, 0x97a3c5c533c566f6, 0x4a102525942535b1, 0xb2ab59597959f220,
0x15d084842a8454ae, 0xe4c57272d572b7a7, 0x72ec3939e439d5dd, 0x98164c4c2d4c5a61,
0xbc945e5e655eca3b, 0xf09f7878fd78e785, 0x70e53838e038ddd8, 0x05988c8c0a8c1486,
0xbf17d1d163d1c6b2, 0x57e4a5a5aea5410b, 0xd9a1e2e2afe2434d, 0xc24e616199612ff8,
0x7b42b3b3f6b3f145, 0x42342121842115a5, 0x25089c9c4a9c94d6, 0x3cee1e1e781ef066,
0x8661434311432252, 0x93b1c7c73bc776fc, 0xe54ffcfcd7fcb32b, 0x0824040410042014,
0xa2e351515951b208, 0x2f2599995e99bcc7, 0xda226d6da96d4fc4, 0x1a650d0d340d6839,
0xe979fafacffa8335, 0xa369dfdf5bdfb684, 0xfca97e7ee57ed79b, 0x4819242490243db4,
0x76fe3b3bec3bc5d7, 0x4b9aabab96ab313d, 0x81f0cece1fce3ed1, 0x2299111144118855,
0x03838f8f068f0c89, 0x9c044e4e254e4a6b, 0x7366b7b7e6b7d151, 0xcbe0ebeb8beb0b60,
0x78c13c3cf03cfdcc, 0x1ffd81813e817cbf, 0x354094946a94d4fe, 0xf31cf7f7fbf7eb0c,
0x6f18b9b9deb9a167, 0x268b13134c13985f, 0x58512c2cb02c7d9c, 0xbb05d3d36bd3d6b8,
0xd38ce7e7bbe76b5c, 0xdc396e6ea56e57cb, 0x95aac4c437c46ef3, 0x061b03030c03180f,
0xacdc565645568a13, 0x885e44440d441a49, 0xfea07f7fe17fdf9e, 0x4f88a9a99ea92137,
0x54672a2aa82a4d82, 0x6b0abbbbd6bbb16d, 0x9f87c1c123c146e2, 0xa6f153535153a202,
0xa572dcdc57dcae8b, 0x16530b0b2c0b5827, 0x27019d9d4e9d9cd3, 0xd82b6c6cad6c47c1,
0x62a43131c43195f5, 0xe8f37474cd7487b9, 0xf115f6f6fff6e309, 0x8c4c464605460a43,
0x45a5acac8aac0926, 0x0fb589891e893c97, 0x28b414145014a044, 0xdfbae1e1a3e15b42,
0x2ca616165816b04e, 0x74f73a3ae83acdd2, 0xd2066969b9696fd0, 0x124109092409482d,
0xe0d77070dd70a7ad, 0x716fb6b6e2b6d954, 0xbd1ed0d067d0ceb7, 0xc7d6eded93ed3b7e,
0x85e2cccc17cc2edb, 0x8468424215422a57, 0x2d2c98985a98b4c2, 0x55eda4a4aaa4490e,
0x50752828a0285d88, 0xb8865c5c6d5cda31, 0xed6bf8f8c7f8933f, 0x11c28686228644a4,
// C3
0x7830d818186018c0, 0xaf462623238c2305, 0xf991b8c6c63fc67e, 0x6fcdfbe8e887e813,
0xa113cb878726874c, 0x626d11b8b8dab8a9, 0x0502090101040108, 0x6e9e0d4f4f214f42,
0xee6c9b3636d836ad, 0x0451ffa6a6a2a659, 0xbdb90cd2d26fd2de, 0x06f70ef5f5f3f5fb,
0x80f2967979f979ef, 0xcede306f6fa16f5f, 0xef3f6d91917e91fc, 0x07a4f852525552aa,
0xfdc04760609d6027, 0x766535bcbccabc89, 0xcd2b379b9b569bac, 0x8c018a8e8e028e04,
0x155bd2a3a3b6a371, 0x3c186c0c0c300c60, 0x8af6847b7bf17bff, 0xe16a803535d435b5,
0x693af51d1d741de8, 0x47ddb3e0e0a7e053, 0xacb321d7d77bd7f6, 0xed999cc2c22fc25e,
0x965c432e2eb82e6d, 0x7a96294b4b314b62, 0x21e15dfefedffea3, 0x16aed55757415782,
0x412abd15155415a8, 0xb6eee87777c1779f, 0xeb6e923737dc37a5, 0x56d79ee5e5b3e57b,
0xd923139f9f469f8c, 0x17fd23f0f0e7f0d3, 0x7f94204a4a354a6a, 0x95a944dada4fda9e,
0x25b0a258587d58fa, 0xca8fcfc9c903c906, 0x8d527c2929a42955, 0x22145a0a0a280a50,
0x4f7f50b1b1feb1e1, 0x1a5dc9a0a0baa069, 0xdad6146b6bb16b7f, 0xab17d985852e855c,
0x73673cbdbdcebd81, 0x34ba8f5d5d695dd2, 0x5020901010401080, 0x03f507f4f4f7f4f3,
0xc08bddcbcb0bcb16, 0xc67cd33e3ef83eed, 0x110a2d0505140528, 0xe6ce78676781671f,
0x53d597e4e4b7e473, 0xbb4e0227279c2725, 0x5882734141194132, 0x9d0ba78b8b168b2c,
0x0153f6a7a7a6a751, 0x94fab27d7de97dcf, 0xfb374995956e95dc, 0x9fad56d8d847d88e,
0x30eb70fbfbcbfb8b, 0x71c1cdeeee9fee23, 0x91f8bb7c7ced7cc7, 0xe3cc716666856617,
0x8ea77bdddd53dda6, 0x4b2eaf17175c17b8, 0x468e454747014702, 0xdc211a9e9e429e84,
0xc589d4caca0fca1e, 0x995a582d2db42d75, 0x79632ebfbfc6bf91, 0x1b0e3f07071c0738,
0x2347acadad8ead01, 0x2fb4b05a5a755aea, 0xb51bef838336836c, 0xff66b63333cc3385,
0xf2c65c636391633f, 0x0a04120202080210, 0x384993aaaa92aa39, 0xa8e2de7171d971af,
0xcf8dc6c8c807c80e, 0x7d32d119196419c8, 0x70923b4949394972, 0x9aaf5fd9d943d986,
0x1df931f2f2eff2c3, 0x48dba8e3e3abe34b, 0x2ab6b95b5b715be2, 0x920dbc88881a8834,
0xc8293e9a9a529aa4, 0xbe4c0b262698262d, 0xfa64bf3232c8328d, 0x4a7d59b0b0fab0e9,
0x6acff2e9e983e91b, 0x331e770f0f3c0f78, 0xa6b733d5d573d5e6, 0xba1df480803a8074,
0x7c6127bebec2be99, 0xde87ebcdcd13cd26, 0xe468893434d034bd, 0x75903248483d487a,
0x24e354ffffdbffab, 0x8ff48d7a7af57af7, 0xea3d6490907a90f4, 0x3ebe9d5f5f615fc2,
0xa0403d202080201d, 0xd5d00f6868bd6867, 0x7234ca1a1a681ad0, 0x2c41b7aeae82ae19,
0x5e757db4b4eab4c9, 0x19a8ce54544d549a, 0xe53b7f93937693ec, 0xaa442f222288220d,
0xe9c86364648d6407, 0x12ff2af1f1e3f1db, 0xa2e6cc7373d173bf, 0x5a24821212481290,
0x5d807a40401d403a, 0x2810480808200840, 0xe89b95c3c32bc356, 0x7bc5dfecec97ec33,
0x90ab4ddbdb4bdb96, 0x1f5fc0a1a1bea161, 0x8307918d8d0e8d1c, 0xc97ac83d3df43df5,
0xf1335b97976697cc, 0x0000000000000000, 0xd483f9cfcf1bcf36, 0x87566e2b2bac2b45,
0xb3ece17676c57697, 0xb019e68282328264, 0xa9b128d6d67fd6fe, 0x7736c31b1b6c1bd8,
0x5b7774b5b5eeb5c1, 0x2943beafaf86af11, 0xdfd41d6a6ab56a77, 0x0da0ea50505d50ba,
0x4c8a574545094512, 0x18fb38f3f3ebf3cb, 0xf060ad3030c0309d, 0x74c3c4efef9bef2b,
0xc37eda3f3ffc3fe5, 0x1caac75555495592, 0x1059dba2a2b2a279, 0x65c9e9eaea8fea03,
0xecca6a656589650f, 0x686903babad2bab9, 0x935e4a2f2fbc2f65, 0xe79d8ec0c027c04e,
0x81a160dede5fdebe, 0x6c38fc1c1c701ce0, 0x2ee746fdfdd3fdbb, 0x649a1f4d4d294d52,
0xe0397692927292e4, 0xbceafa7575c9758f, 0x1e0c360606180630, 0x9809ae8a8a128a24,
0x40794bb2b2f2b2f9, 0x59d185e6e6bfe663, 0x361c7e0e0e380e70, 0x633ee71f1f7c1ff8,
0xf7c4556262956237, 0xa3b53ad4d477d4ee, 0x324d81a8a89aa829, 0xf4315296966296c4,
0x3aef62f9f9c3f99b, 0xf697a3c5c533c566, 0xb14a102525942535, 0x20b2ab59597959f2,
0xae15d084842a8454, 0xa7e4c57272d572b7, 0xdd72ec3939e439d5, 0x6198164c4c2d4c5a,
0x3bbc945e5e655eca, 0x85f09f7878fd78e7, 0xd870e53838e038dd, 0x8605988c8c0a8c14,
0xb2bf17d1d163d1c6, 0x0b57e4a5a5aea541, 0x4dd9a1e2e2afe243, 0xf8c24e616199612f,
0x457b42b3b3f6b3f1, 0xa542342121842115, 0xd625089c9c4a9c94, 0x663cee1e1e781ef0,
0x5286614343114322, 0xfc93b1c7c73bc776, 0x2be54ffcfcd7fcb3, 0x1408240404100420,
0x08a2e351515951b2, 0xc72f2599995e99bc, 0xc4da226d6da96d4f, 0x391a650d0d340d68,
0x35e979fafacffa83, 0x84a369dfdf5bdfb6, 0x9bfca97e7ee57ed7, 0xb44819242490243d,
0xd776fe3b3bec3bc5, 0x3d4b9aabab96ab31, 0xd181f0cece1fce3e, 0x5522991111441188,
0x8903838f8f068f0c, 0x6b9c044e4e254e4a, 0x517366b7b7e6b7d1, 0x60cbe0ebeb8beb0b,
0xcc78c13c3cf03cfd, 0xbf1ffd81813e817c, 0xfe354094946a94d4, 0x0cf31cf7f7fbf7eb,
0x676f18b9b9deb9a1, 0x5f268b13134c1398, 0x9c58512c2cb02c7d, 0xb8bb05d3d36bd3d6,
0x5cd38ce7e7bbe76b, 0xcbdc396e6ea56e57, 0xf395aac4c437c46e, 0x0f061b03030c0318,
0x13acdc565645568a, 0x49885e44440d441a, 0x9efea07f7fe17fdf, 0x374f88a9a99ea921,
0x8254672a2aa82a4d, 0x6d6b0abbbbd6bbb1, 0xe29f87c1c123c146, 0x02a6f153535153a2,
0x8ba572dcdc57dcae, 0x2716530b0b2c0b58, 0xd327019d9d4e9d9c, 0xc1d82b6c6cad6c47,
0xf562a43131c43195, 0xb9e8f37474cd7487, 0x09f115f6f6fff6e3, 0x438c4c464605460a,
0x2645a5acac8aac09, 0x970fb589891e893c, 0x4428b414145014a0, 0x42dfbae1e1a3e15b,
0x4e2ca616165816b0, 0xd274f73a3ae83acd, 0xd0d2066969b9696f, 0x2d12410909240948,
0xade0d77070dd70a7, 0x54716fb6b6e2b6d9, 0xb7bd1ed0d067d0ce, 0x7ec7d6eded93ed3b,
0xdb85e2cccc17cc2e, 0x578468424215422a, 0xc22d2c98985a98b4, 0x0e55eda4a4aaa449,
0x8850752828a0285d, 0x31b8865c5c6d5cda, 0x3fed6bf8f8c7f893, 0xa411c28686228644,
// C4
0xc07830d818186018, 0x05af462623238c23, 0x7ef991b8c6c63fc6, 0x136fcdfbe8e887e8,
0x4ca113cb87872687, 0xa9626d11b8b8dab8, 0x0805020901010401, 0x426e9e0d4f4f214f,
0xadee6c9b3636d836, 0x590451ffa6a6a2a6, 0xdebdb90cd2d26fd2, 0xfb06f70ef5f5f3f5,
0xef80f2967979f979, 0x5fcede306f6fa16f, 0xfcef3f6d91917e91, 0xaa07a4f852525552,
0x27fdc04760609d60, 0x89766535bcbccabc, 0xaccd2b379b9b569b, 0x048c018a8e8e028e,
0x71155bd2a3a3b6a3, 0x603c186c0c0c300c, 0xff8af6847b7bf17b, 0xb5e16a803535d435,
0xe8693af51d1d741d, 0x5347ddb3e0e0a7e0, 0xf6acb321d7d77bd7, 0x5eed999cc2c22fc2,
0x6d965c432e2eb82e, 0x627a96294b4b314b, 0xa321e15dfefedffe, 0x8216aed557574157,
0xa8412abd15155415, 0x9fb6eee87777c177, 0xa5eb6e923737dc37, 0x7b56d79ee5e5b3e5,
0x8cd923139f9f469f, 0xd317fd23f0f0e7f0, 0x6a7f94204a4a354a, 0x9e95a944dada4fda,
0xfa25b0a258587d58, 0x06ca8fcfc9c903c9, 0x558d527c2929a429, 0x5022145a0a0a280a,
0xe14f7f50b1b1feb1, 0x691a5dc9a0a0baa0, 0x7fdad6146b6bb16b, 0x5cab17d985852e85,
0x8173673cbdbdcebd, 0xd234ba8f5d5d695d, 0x8050209010104010, 0xf303f507f4f4f7f4,
0x16c08bddcbcb0bcb, 0xedc67cd33e3ef83e, 0x28110a2d05051405, 0x1fe6ce7867678167,
0x7353d597e4e4b7e4, 0x25bb4e0227279c27, 0x3258827341411941, 0x2c9d0ba78b8b168b,
0x510153f6a7a7a6a7, 0xcf94fab27d7de97d, 0xdcfb374995956e95, 0x8e9fad56d8d847d8,
0x8b30eb70fbfbcbfb, 0x2371c1cdeeee9fee, 0xc791f8bb7c7ced7c, 0x17e3cc7166668566,
0xa68ea77bdddd53dd, 0xb84b2eaf17175c17, 0x02468e4547470147, 0x84dc211a9e9e429e,
0x1ec589d4caca0fca, 0x75995a582d2db42d, 0x9179632ebfbfc6bf, 0x381b0e3f07071c07,
0x012347acadad8ead, 0xea2fb4b05a5a755a, 0x6cb51bef83833683, 0x85ff66b63333cc33,
0x3ff2c65c63639163, 0x100a041202020802, 0x39384993aaaa92aa, 0xafa8e2de7171d971,
0x0ecf8dc6c8c807c8, 0xc87d32d119196419, 0x7270923b49493949, 0x869aaf5fd9d943d9,
0xc31df931f2f2eff2, 0x4b48dba8e3e3abe3, 0xe22ab6b95b5b715b, 0x34920dbc88881a88,
0xa4c8293e9a9a529a, 0x2dbe4c0b26269826, 0x8dfa64bf3232c832, 0xe94a7d59b0b0fab0,
0x1b6acff2e9e983e9, 0x78331e770f0f3c0f, 0xe6a6b733d5d573d5, 0x74ba1df480803a80,
0x997c6127bebec2be, 0x26de87ebcdcd13cd, 0xbde468893434d034, 0x7a75903248483d48,
0xab24e354ffffdbff, 0xf78ff48d7a7af57a, 0xf4ea3d6490907a90, 0xc23ebe9d5f5f615f,
0x1da0403d20208020, 0x67d5d00f6868bd68, 0xd07234ca1a1a681a, 0x192c41b7aeae82ae,
0xc95e757db4b4eab4, 0x9a19a8ce54544d54, 0xece53b7f93937693, 0x0daa442f22228822,
0x07e9c86364648d64, 0xdb12ff2af1f1e3f1, 0xbfa2e6cc7373d173, 0x905a248212124812,
0x3a5d807a40401d40, 0x4028104808082008, 0x56e89b95c3c32bc3, 0x337bc5dfecec97ec,
0x9690ab4ddbdb4bdb, 0x611f5fc0a1a1bea1, 0x1c8307918d8d0e8d, 0xf5c97ac83d3df43d,
0xccf1335b97976697, 0x0000000000000000, 0x36d483f9cfcf1bcf, 0x4587566e2b2bac2b,
0x97b3ece17676c576, 0x64b019e682823282, 0xfea9b128d6d67fd6, 0xd87736c31b1b6c1b,
0xc15b7774b5b5eeb5, 0x112943beafaf86af, 0x77dfd41d6a6ab56a, 0xba0da0ea50505d50,
0x124c8a5745450945, 0xcb18fb38f3f3ebf3, 0x9df060ad3030c030, 0x2b74c3c4efef9bef,
0xe5c37eda3f3ffc3f, 0x921caac755554955, 0x791059dba2a2b2a2, 0x0365c9e9eaea8fea,
0x0fecca6a65658965, 0xb9686903babad2ba, 0x65935e4a2f2fbc2f, 0x4ee79d8ec0c027c0,
0xbe81a160dede5fde, 0xe06c38fc1c1c701c, 0xbb2ee746fdfdd3fd, 0x52649a1f4d4d294d,
0xe4e0397692927292, 0x8fbceafa7575c975, 0x301e0c3606061806, 0x249809ae8a8a128a,
0xf940794bb2b2f2b2, 0x6359d185e6e6bfe6, 0x70361c7e0e0e380e, 0xf8633ee71f1f7c1f,
0x37f7c45562629562, 0xeea3b53ad4d477d4, 0x29324d81a8a89aa8, 0xc4f4315296966296,
0x9b3aef62f9f9c3f9, 0x66f697a3c5c533c5, 0x35b14a1025259425, 0xf220b2ab59597959,
0x54ae15d084842a84, 0xb7a7e4c57272d572, 0xd5dd72ec3939e439, 0x5a6198164c4c2d4c,
0xca3bbc945e5e655e, 0xe785f09f7878fd78, 0xddd870e53838e038, 0x148605988c8c0a8c,
0xc6b2bf17d1d163d1, 0x410b57e4a5a5aea5, 0x434dd9a1e2e2afe2, 0x2ff8c24e61619961,
0xf1457b42b3b3f6b3, 0x15a5423421218421, 0x94d625089c9c4a9c, 0xf0663cee1e1e781e,
0x2252866143431143, 0x76fc93b1c7c73bc7, 0xb32be54ffcfcd7fc, 0x2014082404041004,
0xb208a2e351515951, 0xbcc72f2599995e99, 0x4fc4da226d6da96d, 0x68391a650d0d340d,
0x8335e979fafacffa, 0xb684a369dfdf5bdf, 0xd79bfca97e7ee57e, 0x3db4481924249024,
0xc5d776fe3b3bec3b, 0x313d4b9aabab96ab, 0x3ed181f0cece1fce, 0x8855229911114411,
0x0c8903838f8f068f, 0x4a6b9c044e4e254e, 0xd1517366b7b7e6b7, 0x0b60cbe0ebeb8beb,
0xfdcc78c13c3cf03c, 0x7cbf1ffd81813e81, 0xd4fe354094946a94, 0xeb0cf31cf7f7fbf7,
0xa1676f18b9b9deb9, 0x985f268b13134c13, 0x7d9c58512c2cb02c, 0xd6b8bb05d3d36bd3,
0x6b5cd38ce7e7bbe7, 0x57cbdc396e6ea56e, 0x6ef395aac4c437c4, 0x180f061b03030c03,
0x8a13acdc56564556, 0x1a49885e44440d44, 0xdf9efea07f7fe17f, 0x21374f88a9a99ea9,
0x4d8254672a2aa82a, 0xb16d6b0abbbbd6bb, 0x46e29f87c1c123c1, 0xa202a6f153535153,
0xae8ba572dcdc57dc, 0x582716530b0b2c0b, 0x9cd327019d9d4e9d, 0x47c1d82b6c6cad6c,
0x95f562a43131c431, 0x87b9e8f37474cd74, 0xe309f115f6f6fff6, 0x0a438c4c46460546,
0x092645a5acac8aac, 0x3c970fb589891e89, 0xa04428b414145014, 0x5b42dfbae1e1a3e1,
0xb04e2ca616165816, 0xcdd274f73a3ae83a, 0x6fd0d2066969b969, 0x482d124109092409,
0xa7ade0d77070dd70, 0xd954716fb6b6e2b6, 0xceb7bd1ed0d067d0, 0x3b7ec7d6eded93ed,
0x2edb85e2cccc17cc, 0x2a57846842421542, 0xb4c22d2c98985a98, 0x490e55eda4a4aaa4,
0x5d8850752828a028, 0xda31b8865c5c6d5c, 0x933fed6bf8f8c7f8, 0x44a411c286862286,
// C5
0x18c07830d8181860, 0x2305af462623238c, 0xc67ef991b8c6c63f, 0xe8136fcdfbe8e887,
0x874ca113cb878726, 0xb8a9626d11b8b8da, 0x0108050209010104, 0x4f426e9e0d4f4f21,
0x36adee6c9b3636d8, 0xa6590451ffa6a6a2, 0xd2debdb90cd2d26f, 0xf5fb06f70ef5f5f3,
0x79ef80f2967979f9, 0x6f5fcede306f6fa1, 0x91fcef3f6d91917e, 0x52aa07a4f8525255,
0x6027fdc04760609d, 0xbc89766535bcbcca, 0x9baccd2b379b9b56, 0x8e048c018a8e8e02,
0xa371155bd2a3a3b6, 0x0c603c186c0c0c30, 0x7bff8af6847b7bf1, 0x35b5e16a803535d4,
0x1de8693af51d1d74, 0xe05347ddb3e0e0a7, 0xd7f6acb321d7d77b, 0xc25eed999cc2c22f,
0x2e6d965c432e2eb8, 0x4b627a96294b4b31, 0xfea321e15dfefedf, 0x578216aed5575741,
0x15a8412abd151554, 0x779fb6eee87777c1, 0x37a5eb6e923737dc, 0xe57b56d79ee5e5b3,
0x9f8cd923139f9f46, 0xf0d317fd23f0f0e7, 0x4a6a7f94204a4a35, 0xda9e95a944dada4f,
0x58fa25b0a258587d, 0xc906ca8fcfc9c903, 0x29558d527c2929a4, 0x0a5022145a0a0a28,
0xb1e14f7f50b1b1fe, 0xa0691a5dc9a0a0ba, 0x6b7fdad6146b6bb1, 0x855cab17d985852e,
0xbd8173673cbdbdce, 0x5dd234ba8f5d5d69, 0x1080502090101040, 0xf4f303f507f4f4f7,
0xcb16c08bddcbcb0b, 0x3eedc67cd33e3ef8, 0x0528110a2d050514, 0x671fe6ce78676781,
0xe47353d597e4e4b7, 0x2725bb4e0227279c, 0x4132588273414119, 0x8b2c9d0ba78b8b16,
0xa7510153f6a7a7a6, 0x7dcf94fab27d7de9, 0x95dcfb374995956e, 0xd88e9fad56d8d847,
0xfb8b30eb70fbfbcb, 0xee2371c1cdeeee9f, 0x7cc791f8bb7c7ced, 0x6617e3cc71666685,
0xdda68ea77bdddd53, 0x17b84b2eaf17175c, 0x4702468e45474701, 0x9e84dc211a9e9e42,
0xca1ec589d4caca0f, 0x2d75995a582d2db4, 0xbf9179632ebfbfc6, 0x07381b0e3f07071c,
0xad012347acadad8e, 0x5aea2fb4b05a5a75, 0x836cb51bef838336, 0x3385ff66b63333cc,
0x633ff2c65c636391, 0x02100a0412020208, 0xaa39384993aaaa92, 0x71afa8e2de7171d9,
0xc80ecf8dc6c8c807, 0x19c87d32d1191964, 0x497270923b494939, 0xd9869aaf5fd9d943,
0xf2c31df931f2f2ef, 0xe34b48dba8e3e3ab, 0x5be22ab6b95b5b71, 0x8834920dbc88881a,
0x9aa4c8293e9a9a52, 0x262dbe4c0b262698, 0x328dfa64bf3232c8, 0xb0e94a7d59b0b0fa,
0xe91b6acff2e9e983, 0x0f78331e770f0f3c, 0xd5e6a6b733d5d573, 0x8074ba1df480803a,
0xbe997c6127bebec2, 0xcd26de87ebcdcd13, 0x34bde468893434d0, 0x487a75903248483d,
0xffab24e354ffffdb, 0x7af78ff48d7a7af5, 0x90f4ea3d6490907a, 0x5fc23ebe9d5f5f61,
0x201da0403d202080, 0x6867d5d00f6868bd, 0x1ad07234ca1a1a68, 0xae192c41b7aeae82,
0xb4c95e757db4b4ea, 0x549a19a8ce54544d, 0x93ece53b7f939376, 0x220daa442f222288,
0x6407e9c86364648d, 0xf1db12ff2af1f1e3, 0x73bfa2e6cc7373d1, 0x12905a2482121248,
0x403a5d807a40401d, 0x0840281048080820, 0xc356e89b95c3c32b, 0xec337bc5dfecec97,
0xdb9690ab4ddbdb4b, 0xa1611f5fc0a1a1be, 0x8d1c8307918d8d0e, 0x3df5c97ac83d3df4,
0x97ccf1335b979766, 0x0000000000000000, 0xcf36d483f9cfcf1b, 0x2b4587566e2b2bac,
0x7697b3ece17676c5, 0x8264b019e6828232, 0xd6fea9b128d6d67f, 0x1bd87736c31b1b6c,
0xb5c15b7774b5b5ee, 0xaf112943beafaf86, 0x6a77dfd41d6a6ab5, 0x50ba0da0ea50505d,
0x45124c8a57454509, 0xf3cb18fb38f3f3eb, 0x309df060ad3030c0, 0xef2b74c3c4efef9b,
0x3fe5c37eda3f3ffc, 0x55921caac7555549, 0xa2791059dba2a2b2, 0xea0365c9e9eaea8f,
0x650fecca6a656589, 0xbab9686903babad2, 0x2f65935e4a2f2fbc, 0xc04ee79d8ec0c027,
0xdebe81a160dede5f, 0x1ce06c38fc1c1c70, 0xfdbb2ee746fdfdd3, 0x4d52649a1f4d4d29,
0x92e4e03976929272, 0x758fbceafa7575c9, 0x06301e0c36060618, 0x8a249809ae8a8a12,
0xb2f940794bb2b2f2, 0xe66359d185e6e6bf, 0x0e70361c7e0e0e38, 0x1ff8633ee71f1f7c,
0x6237f7c455626295, 0xd4eea3b53ad4d477, 0xa829324d81a8a89a, 0x96c4f43152969662,
0xf99b3aef62f9f9c3, 0xc566f697a3c5c533, 0x2535b14a10252594, 0x59f220b2ab595979,
0x8454ae15d084842a, 0x72b7a7e4c57272d5, 0x39d5dd72ec3939e4, 0x4c5a6198164c4c2d,
0x5eca3bbc945e5e65, 0x78e785f09f7878fd, 0x38ddd870e53838e0, 0x8c148605988c8c0a,
0xd1c6b2bf17d1d163, 0xa5410b57e4a5a5ae, 0xe2434dd9a1e2e2af, 0x612ff8c24e616199,
0xb3f1457b42b3b3f6, 0x2115a54234212184, 0x9c94d625089c9c4a, 0x1ef0663cee1e1e78,
0x4322528661434311, 0xc776fc93b1c7c73b, 0xfcb32be54ffcfcd7, 0x0420140824040410,
0x51b208a2e3515159, 0x99bcc72f2599995e, 0x6d4fc4da226d6da9, 0x0d68391a650d0d34,
0xfa8335e979fafacf, 0xdfb684a369dfdf5b, 0x7ed79bfca97e7ee5, 0x243db44819242490,
0x3bc5d776fe3b3bec, 0xab313d4b9aabab96, 0xce3ed181f0cece1f, 0x1188552299111144,
0x8f0c8903838f8f06, 0x4e4a6b9c044e4e25, 0xb7d1517366b7b7e6, 0xeb0b60cbe0ebeb8b,
0x3cfdcc78c13c3cf0, 0x817cbf1ffd81813e, 0x94d4fe354094946a, 0xf7eb0cf31cf7f7fb,
0xb9a1676f18b9b9de, 0x13985f268b13134c, 0x2c7d9c58512c2cb0, 0xd3d6b8bb05d3d36b,
0xe76b5cd38ce7e7bb, 0x6e57cbdc396e6ea5, 0xc46ef395aac4c437, 0x03180f061b03030c,
0x568a13acdc565645, 0x441a49885e44440d, 0x7fdf9efea07f7fe1, 0xa921374f88a9a99e,
0x2a4d8254672a2aa8, 0xbbb16d6b0abbbbd6, 0xc146e29f87c1c123, 0x53a202a6f1535351,
0xdcae8ba572dcdc57, 0x0b582716530b0b2c, 0x9d9cd327019d9d4e, 0x6c47c1d82b6c6cad,
0x3195f562a43131c4, 0x7487b9e8f37474cd, 0xf6e309f115f6f6ff, 0x460a438c4c464605,
0xac092645a5acac8a, 0x893c970fb589891e, 0x14a04428b4141450, 0xe15b42dfbae1e1a3,
0x16b04e2ca6161658, 0x3acdd274f73a3ae8, 0x696fd0d2066969b9, 0x09482d1241090924,
0x70a7ade0d77070dd, 0xb6d954716fb6b6e2, 0xd0ceb7bd1ed0d067, 0xed3b7ec7d6eded93,
0xcc2edb85e2cccc17, 0x422a578468424215, 0x98b4c22d2c98985a, 0xa4490e55eda4a4aa,
0x285d8850752828a0, 0x5cda31b8865c5c6d, 0xf8933fed6bf8f8c7, 0x8644a411c2868622,
// C6
0x6018c07830d81818, 0x8c2305af46262323, 0x3fc67ef991b8c6c6, 0x87e8136fcdfbe8e8,
0x26874ca113cb8787, 0xdab8a9626d11b8b8, 0x0401080502090101, 0x214f426e9e0d4f4f,
0xd836adee6c9b3636, 0xa2a6590451ffa6a6, 0x6fd2debdb90cd2d2, 0xf3f5fb06f70ef5f5,
0xf979ef80f2967979, 0xa16f5fcede306f6f, 0x7e91fcef3f6d9191, 0x5552aa07a4f85252,
0x9d6027fdc0476060, 0xcabc89766535bcbc, 0x569baccd2b379b9b, 0x028e048c018a8e8e,
0xb6a371155bd2a3a3, 0x300c603c186c0c0c, 0xf17bff8af6847b7b, 0xd435b5e16a803535,
0x741de8693af51d1d, 0xa7e05347ddb3e0e0, 0x7bd7f6acb321d7d7, 0x2fc25eed999cc2c2,
0xb82e6d965c432e2e, 0x314b627a96294b4b, 0xdffea321e15dfefe, 0x41578216aed55757,
0x5415a8412abd1515, 0xc1779fb6eee87777, 0xdc37a5eb6e923737, 0xb3e57b56d79ee5e5,
0x469f8cd923139f9f, 0xe7f0d317fd23f0f0, 0x354a6a7f94204a4a, 0x4fda9e95a944dada,
0x7d58fa25b0a25858, 0x03c906ca8fcfc9c9, 0xa429558d527c2929, 0x280a5022145a0a0a,
0xfeb1e14f7f50b1b1, 0xbaa0691a5dc9a0a0, 0xb16b7fdad6146b6b, 0x2e855cab17d98585,
0xcebd8173673cbdbd, 0x695dd234ba8f5d5d, 0x4010805020901010, 0xf7f4f303f507f4f4,
0x0bcb16c08bddcbcb, 0xf83eedc67cd33e3e, 0x140528110a2d0505, 0x81671fe6ce786767,
0xb7e47353d597e4e4, 0x9c2725bb4e022727, 0x1941325882734141, 0x168b2c9d0ba78b8b,
0xa6a7510153f6a7a7, 0xe97dcf94fab27d7d, 0x6e95dcfb37499595, 0x47d88e9fad56d8d8,
0xcbfb8b30eb70fbfb, 0x9fee2371c1cdeeee, 0xed7cc791f8bb7c7c, 0x856617e3cc716666,
0x53dda68ea77bdddd, 0x5c17b84b2eaf1717, 0x014702468e454747, 0x429e84dc211a9e9e,
0x0fca1ec589d4caca, 0xb42d75995a582d2d, 0xc6bf9179632ebfbf, 0x1c07381b0e3f0707,
0x8ead012347acadad, 0x755aea2fb4b05a5a, 0x36836cb51bef8383, 0xcc3385ff66b63333,
0x91633ff2c65c6363, 0x0802100a04120202, 0x92aa39384993aaaa, 0xd971afa8e2de7171,
0x07c80ecf8dc6c8c8, 0x6419c87d32d11919, 0x39497270923b4949, 0x43d9869aaf5fd9d9,
0xeff2c31df931f2f2, 0xabe34b48dba8e3e3, 0x715be22ab6b95b5b, 0x1a8834920dbc8888,
0x529aa4c8293e9a9a, 0x98262dbe4c0b2626, 0xc8328dfa64bf3232, 0xfab0e94a7d59b0b0,
0x83e91b6acff2e9e9, 0x3c0f78331e770f0f, 0x73d5e6a6b733d5d5, 0x3a8074ba1df48080,
0xc2be997c6127bebe, 0x13cd26de87ebcdcd, 0xd034bde468893434, 0x3d487a7590324848,
0xdbffab24e354ffff, 0xf57af78ff48d7a7a, 0x7a90f4ea3d649090, 0x615fc23ebe9d5f5f,
0x80201da0403d2020, 0xbd6867d5d00f6868, 0x681ad07234ca1a1a, 0x82ae192c41b7aeae,
0xeab4c95e757db4b4, 0x4d549a19a8ce5454, 0x7693ece53b7f9393, 0x88220daa442f2222,
0x8d6407e9c8636464, 0xe3f1db12ff2af1f1, 0xd173bfa2e6cc7373, 0x4812905a24821212,
0x1d403a5d807a4040, 0x2008402810480808, 0x2bc356e89b95c3c3, 0x97ec337bc5dfecec,
0x4bdb9690ab4ddbdb, 0xbea1611f5fc0a1a1, 0x0e8d1c8307918d8d, 0xf43df5c97ac83d3d,
0x6697ccf1335b9797, 0x0000000000000000, 0x1bcf36d483f9cfcf, 0xac2b4587566e2b2b,
0xc57697b3ece17676, 0x328264b019e68282, 0x7fd6fea9b128d6d6, 0x6c1bd87736c31b1b,
0xeeb5c15b7774b5b5, 0x86af112943beafaf, 0xb56a77dfd41d6a6a, 0x5d50ba0da0ea5050,
0x0945124c8a574545, 0xebf3cb18fb38f3f3, 0xc0309df060ad3030, 0x9bef2b74c3c4efef,
0xfc3fe5c37eda3f3f, 0x4955921caac75555, 0xb2a2791059dba2a2, 0x8fea0365c9e9eaea,
0x89650fecca6a6565, 0xd2bab9686903baba, 0xbc2f65935e4a2f2f, 0x27c04ee79d8ec0c0,
0x5fdebe81a160dede, 0x701ce06c38fc1c1c, 0xd3fdbb2ee746fdfd, 0x294d52649a1f4d4d,
0x7292e4e039769292, 0xc9758fbceafa7575, 0x1806301e0c360606, 0x128a249809ae8a8a,
0xf2b2f940794bb2b2, 0xbfe66359d185e6e6, 0x380e70361c7e0e0e, 0x7c1ff8633ee71f1f,
0x956237f7c4556262, 0x77d4eea3b53ad4d4, 0x9aa829324d81a8a8, 0x6296c4f431529696,
0xc3f99b3aef62f9f9, 0x33c566f697a3c5c5, 0x942535b14a102525, 0x7959f220b2ab5959,
0x2a8454ae15d08484, 0xd572b7a7e4c57272, 0xe439d5dd72ec3939, 0x2d4c5a6198164c4c,
0x655eca3bbc945e5e, 0xfd78e785f09f7878, 0xe038ddd870e53838, 0x0a8c148605988c8c,
0x63d1c6b2bf17d1d1, 0xaea5410b57e4a5a5, 0xafe2434dd9a1e2e2, 0x99612ff8c24e6161,
0xf6b3f1457b42b3b3, 0x842115a542342121, 0x4a9c94d625089c9c, 0x781ef0663cee1e1e,
0x1143225286614343, 0x3bc776fc93b1c7c7, 0xd7fcb32be54ffcfc, 0x1004201408240404,
0x5951b208a2e35151, 0x5e99bcc72f259999, 0xa96d4fc4da226d6d, 0x340d68391a650d0d,
0xcffa8335e979fafa, 0x5bdfb684a369dfdf, 0xe57ed79bfca97e7e, 0x90243db448192424,
0xec3bc5d776fe3b3b, 0x96ab313d4b9aabab, 0x1fce3ed181f0cece, 0x4411885522991111,
0x068f0c8903838f8f, 0x254e4a6b9c044e4e, 0xe6b7d1517366b7b7, 0x8beb0b60cbe0ebeb,
0xf03cfdcc78c13c3c, 0x3e817cbf1ffd8181, 0x6a94d4fe35409494, 0xfbf7eb0cf31cf7f7,
0xdeb9a1676f18b9b9, 0x4c13985f268b1313, 0xb02c7d9c58512c2c, 0x6bd3d6b8bb05d3d3,
0xbbe76b5cd38ce7e7, 0xa56e57cbdc396e6e, 0x37c46ef395aac4c4, 0x0c03180f061b0303,
0x45568a13acdc5656, 0x0d441a49885e4444, 0xe17fdf9efea07f7f, 0x9ea921374f88a9a9,
0xa82a4d8254672a2a, 0xd6bbb16d6b0abbbb, 0x23c146e29f87c1c1, 0x5153a202a6f15353,
0x57dcae8ba572dcdc, 0x2c0b582716530b0b, 0x4e9d9cd327019d9d, 0xad6c47c1d82b6c6c,
0xc43195f562a43131, 0xcd7487b9e8f37474, 0xfff6e309f115f6f6, 0x05460a438c4c4646,
0x8aac092645a5acac, 0x1e893c970fb58989, 0x5014a04428b41414, 0xa3e15b42dfbae1e1,
0x5816b04e2ca61616, 0xe83acdd274f73a3a, 0xb9696fd0d2066969, 0x2409482d12410909,
0xdd70a7ade0d77070, 0xe2b6d954716fb6b6, 0x67d0ceb7bd1ed0d0, 0x93ed3b7ec7d6eded,
0x17cc2edb85e2cccc, 0x15422a5784684242, 0x5a98b4c22d2c9898, 0xaaa4490e55eda4a4,
0xa0285d8850752828, 0x6d5cda31b8865c5c, 0xc7f8933fed6bf8f8, 0x228644a411c28686,
// C7
0x186018c07830d818, 0x238c2305af462623, 0xc63fc67ef991b8c6, 0xe887e8136fcdfbe8,
0x8726874ca113cb87, 0xb8dab8a9626d11b8, 0x0104010805020901, 0x4f214f426e9e0d4f,
0x36d836adee6c9b36, 0xa6a2a6590451ffa6, 0xd26fd2debdb90cd2, 0xf5f3f5fb06f70ef5,
0x79f979ef80f29679, 0x6fa16f5fcede306f, 0x917e91fcef3f6d91, 0x525552aa07a4f852,
0x609d6027fdc04760, 0xbccabc89766535bc, 0x9b569baccd2b379b, 0x8e028e048c018a8e,
0xa3b6a371155bd2a3, 0x0c300c603c186c0c, 0x7bf17bff8af6847b, 0x35d435b5e16a8035,
0x1d741de8693af51d, 0xe0a7e05347ddb3e0, 0xd77bd7f6acb321d7, 0xc22fc25eed999cc2,
0x2eb82e6d965c432e, 0x4b314b627a96294b, 0xfedffea321e15dfe, 0x5741578216aed557,
0x155415a8412abd15, 0x77c1779fb6eee877, 0x37dc37a5eb6e9237, 0xe5b3e57b56d79ee5,
0x9f469f8cd923139f, 0xf0e7f0d317fd23f0, 0x4a354a6a7f94204a, 0xda4fda9e95a944da,
0x587d58fa25b0a258, 0xc903c906ca8fcfc9, 0x29a429558d527c29, 0x0a280a5022145a0a,
0xb1feb1e14f7f50b1, 0xa0baa0691a5dc9a0, 0x6bb16b7fdad6146b, 0x852e855cab17d985,
0xbdcebd8173673cbd, 0x5d695dd234ba8f5d, 0x1040108050209010, 0xf4f7f4f303f507f4,
0xcb0bcb16c08bddcb, 0x3ef83eedc67cd33e, 0x05140528110a2d05, 0x6781671fe6ce7867,
0xe4b7e47353d597e4, 0x279c2725bb4e0227, 0x4119413258827341, 0x8b168b2c9d0ba78b,
0xa7a6a7510153f6a7, 0x7de97dcf94fab27d, 0x956e95dcfb374995, 0xd847d88e9fad56d8,
0xfbcbfb8b30eb70fb, 0xee9fee2371c1cdee, 0x7ced7cc791f8bb7c, 0x66856617e3cc7166,
0xdd53dda68ea77bdd, 0x175c17b84b2eaf17, 0x47014702468e4547, 0x9e429e84dc211a9e,
0xca0fca1ec589d4ca, 0x2db42d75995a582d, 0xbfc6bf9179632ebf, 0x071c07381b0e3f07,
0xad8ead012347acad, 0x5a755aea2fb4b05a, 0x8336836cb51bef83, 0x33cc3385ff66b633,
0x6391633ff2c65c63, 0x020802100a041202, 0xaa92aa39384993aa, 0x71d971afa8e2de71,
0xc807c80ecf8dc6c8, 0x196419c87d32d119, 0x4939497270923b49, 0xd943d9869aaf5fd9,
0xf2eff2c31df931f2, 0xe3abe34b48dba8e3, 0x5b715be22ab6b95b, 0x881a8834920dbc88,
0x9a529aa4c8293e9a, 0x2698262dbe4c0b26, 0x32c8328dfa64bf32, 0xb0fab0e94a7d59b0,
0xe983e91b6acff2e9, 0x0f3c0f78331e770f, 0xd573d5e6a6b733d5, 0x803a8074ba1df480,
0xbec2be997c6127be, 0xcd13cd26de87ebcd, 0x34d034bde4688934, 0x483d487a75903248,
0xffdbffab24e354ff, 0x7af57af78ff48d7a, 0x907a90f4ea3d6490, 0x5f615fc23ebe9d5f,
0x2080201da0403d20, 0x68bd6867d5d00f68, 0x1a681ad07234ca1a, 0xae82ae192c41b7ae,
0xb4eab4c95e757db4, 0x544d549a19a8ce54, 0x937693ece53b7f93, 0x2288220daa442f22,
0x648d6407e9c86364, 0xf1e3f1db12ff2af1, 0x73d173bfa2e6cc73, 0x124812905a248212,
0x401d403a5d807a40, 0x0820084028104808, 0xc32bc356e89b95c3, 0xec97ec337bc5dfec,
0xdb4bdb9690ab4ddb, 0xa1bea1611f5fc0a1, 0x8d0e8d1c8307918d, 0x3df43df5c97ac83d,
0x976697ccf1335b97, 0x0000000000000000, 0xcf1bcf36d483f9cf, 0x2bac2b4587566e2b,
0x76c57697b3ece176, 0x82328264b019e682, 0xd67fd6fea9b128d6, 0x1b6c1bd87736c31b,
0xb5eeb5c15b7774b5, 0xaf86af112943beaf, 0x6ab56a77dfd41d6a, 0x505d50ba0da0ea50,
0x450945124c8a5745, 0xf3ebf3cb18fb38f3, 0x30c0309df060ad30, 0xef9bef2b74c3c4ef,
0x3ffc3fe5c37eda3f, 0x554955921caac755, 0xa2b2a2791059dba2, 0xea8fea0365c9e9ea,
0x6589650fecca6a65, 0xbad2bab9686903ba, 0x2fbc2f65935e4a2f, 0xc027c04ee79d8ec0,
0xde5fdebe81a160de, 0x1c701ce06c38fc1c, 0xfdd3fdbb2ee746fd, 0x4d294d52649a1f4d,
0x927292e4e0397692, 0x75c9758fbceafa75, 0x061806301e0c3606, 0x8a128a249809ae8a,
0xb2f2b2f940794bb2, 0xe6bfe66359d185e6, 0x0e380e70361c7e0e, 0x1f7c1ff8633ee71f,
0x62956237f7c45562, 0xd477d4eea3b53ad4, 0xa89aa829324d81a8, 0x966296c4f4315296,
0xf9c3f99b3aef62f9, 0xc533c566f697a3c5, 0x25942535b14a1025, 0x597959f220b2ab59,
0x842a8454ae15d084, 0x72d572b7a7e4c572, 0x39e439d5dd72ec39, 0x4c2d4c5a6198164c,
0x5e655eca3bbc945e, 0x78fd78e785f09f78, 0x38e038ddd870e538, 0x8c0a8c148605988c,
0xd163d1c6b2bf17d1, 0xa5aea5410b57e4a5, 0xe2afe2434dd9a1e2, 0x6199612ff8c24e61,
0xb3f6b3f1457b42b3, 0x21842115a5423421, 0x9c4a9c94d625089c, 0x1e781ef0663cee1e,
0x4311432252866143, 0xc73bc776fc93b1c7, 0xfcd7fcb32be54ffc, 0x0410042014082404,
0x515951b208a2e351, 0x995e99bcc72f2599, 0x6da96d4fc4da226d, 0x0d340d68391a650d,
0xfacffa8335e979fa, 0xdf5bdfb684a369df, 0x7ee57ed79bfca97e, 0x2490243db4481924,
0x3bec3bc5d776fe3b, 0xab96ab313d4b9aab, 0xce1fce3ed181f0ce, 0x1144118855229911,
0x8f068f0c8903838f, 0x4e254e4a6b9c044e, 0xb7e6b7d1517366b7, 0xeb8beb0b60cbe0eb,
0x3cf03cfdcc78c13c, 0x813e817cbf1ffd81, 0x946a94d4fe354094, 0xf7fbf7eb0cf31cf7,
0xb9deb9a1676f18b9, 0x134c13985f268b13, 0x2cb02c7d9c58512c, 0xd36bd3d6b8bb05d3,
0xe7bbe76b5cd38ce7, 0x6ea56e57cbdc396e, 0xc437c46ef395aac4, 0x030c03180f061b03,
0x5645568a13acdc56, 0x440d441a49885e44, 0x7fe17fdf9efea07f, 0xa99ea921374f88a9,
0x2aa82a4d8254672a, 0xbbd6bbb16d6b0abb, 0xc123c146e29f87c1, 0x535153a202a6f153,
0xdc57dcae8ba572dc, 0x0b2c0b582716530b, 0x9d4e9d9cd327019d, 0x6cad6c47c1d82b6c,
0x31c43195f562a431, 0x74cd7487b9e8f374, 0xf6fff6e309f115f6, 0x4605460a438c4c46,
0xac8aac092645a5ac, 0x891e893c970fb589, 0x145014a04428b414, 0xe1a3e15b42dfbae1,
0x165816b04e2ca616, 0x3ae83acdd274f73a, 0x69b9696fd0d20669, 0x092409482d124109,
0x70dd70a7ade0d770, 0xb6e2b6d954716fb6, 0xd067d0ceb7bd1ed0, 0xed93ed3b7ec7d6ed,
0xcc17cc2edb85e2cc, 0x4215422a57846842, 0x985a98b4c22d2c98, 0xa4aaa4490e55eda4,
0x28a0285d88507528, 0x5c6d5cda31b8865c, 0xf8c7f8933fed6bf8, 0x86228644a411c286,
};

View File

@@ -1,56 +0,0 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. 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.
//
// An implementation of Wang Yi's wyhash(2) algorithm in C3:
// https://github.com/wangyi-fudan/wyhash
//
module std::hash::wyhash2;
fn ulong wyr3(char* in, usz len) @inline
=> ((ulong)in[0] << 16) | ((ulong)in[len >> 1] << 8) | (ulong)in[len - 1];
// See: https://docs.google.com/spreadsheets/d/1HmqDj-suH4wBFNg7etwE8WVBlfCufvD5-gAnIENs94k/edit?gid=1915335726#gid=1915335726
// Credit to article:
// https://medium.com/@tprodanov/benchmarking-non-cryptographic-hash-functions-in-rust-2e6091077d11
//
// wyhash2 has a >90% chance of collisions when its input data is above 16 bytes in length.
// However, it is the fastest performing and most evenly randomized hash for very low-length inputs,
// making it an ideal candidate for hashing primitive data types quickly and making things like hash
// tables even faster. Therefore, a 16-byte input limit is imposed on all calls to the hash function.
//
<*
@require input.len <= 16 : `wyhash2 is not useable for inputs over 16 bytes in length.`
*>
fn ulong hash(char[] input, ulong seed = 0)
{
seed ^= 0xa076_1d64_78bd_642f;
ulong a, b;
if (@likely(input.len <= 8)) // more likely to encounter 8-byte or lower type here
{
if (@likely(input.len >= 4))
{
a = (ulong)@unaligned_load(*(uint*)input.ptr, 1); // first 4 bytes widened to a u64
b = (ulong)@unaligned_load(*(uint*)&input[^4], 1); // a walking 4-byte window based on input.len
}
else if (input.len > 0)
{
a = wyr3(input, input.len);
}
}
else
{
a = @unaligned_load(*(ulong*)input.ptr, 1); // first 8 bytes
b = @unaligned_load(*(ulong*)&input[^8], 1); // a walking 8-byte window based on input.len
}
uint128 r = ((uint128)a ^ 0xe703_7ed1_a0b4_28db) * ((uint128)b ^ seed);
ulong pre_res = (ulong)r ^ (ulong)(r >> 64);
r = ((uint128)0xe703_7ed1_a0b4_28db ^ input.len) * (uint128)pre_res;
return (ulong)r ^ (ulong)(r >> 64);
}

View File

@@ -1,92 +0,0 @@
module std::io::file::mmap @if(env::LIBC &&& env::POSIX);
import std::core::mem::vm, std::io::file;
struct FileMmap
{
File file;
VirtualMemory vm;
usz offset;
usz len;
}
<*
Provides a slice of bytes to the expected mapped range discarding the extra bytes due to misaligment of offset and/or size.
@return "Slice of the mapped range where the first byte matches the file's byte at the offset specified to File::file_mmap()"
*>
fn char[] FileMmap.bytes(&self)
{
return self.vm.ptr[self.offset:self.len];
}
<*
Destroys the underlyng VirtualMemory object ie. calls munmap()"
*>
fn void? FileMmap.destroy(&self) @maydiscard
{
fault err1 = @catch(self.file.close());
fault err2 = @catch(self.vm.destroy());
if (err1) return err1?;
if (err2) return err2?;
}
module std::io::file @if(env::LIBC &&& env::POSIX);
<*
Maps a region of an already-opened file into memory
@param file : "Already opened file created on the caller scope"
@param offset : "Byte offset in file, will be rounded down to page size"
@param len : "Size in bytes to map starting from offset, will be rounded up to page size"
@return? mem::OUT_OF_MEMORY, vm::ACCESS_DENIED, vm::RANGE_OVERFLOW, vm::INVALID_ARGS, vm::UNKNOWN_ERROR, io::NO_PERMISSION, io::FILE_NOT_VALID, io::WOULD_BLOCK, io::FILE_NOT_FOUND
@return "Memory mapped region. Must be released with FileMmap.destroy(). Provided File will not be closed"
*>
fn mmap::FileMmap? mmap_file(File file, usz offset = 0, usz len = 0, vm::VirtualMemoryAccess access = READ, bool shared = false)
{
if (len == 0)
{
usz cur = file.seek(0, CURSOR)!;
defer file.seek(cur, SET)!!;
usz file_size = file.seek(0, END)!;
len = file_size - offset;
}
// get the page size
usz page_size = vm::aligned_alloc_size(0);
// align the offset specified by the user (might be not aligned)
usz page_offset = offset & (page_size - 1);
usz map_offset = offset - page_offset;
// adjust map length (both the region start and the region end might be not aligned)
usz map_len = len + page_offset; // when region start not aligned
map_len = vm::aligned_alloc_size(map_len); // when region end not aligned
void* ptr = vm::mmap_file(file.fd(), map_len, map_offset, access, shared)!;
// FileMmap does not own the supplied file
return {{}, {ptr, map_len, access}, page_offset, len};
}
<*
Maps a region of the given file into memory
@param filename : "File path"
@param mode : "File opening mode"
@param offset : "Byte offset in file, will be rounded down to page size"
@param len : "Size in bytes to map starting from offset, will be rounded up to page size"
@return? mem::OUT_OF_MEMORY, vm::ACCESS_DENIED, vm::RANGE_OVERFLOW, vm::INVALID_ARGS, vm::UNKNOWN_ERROR, io::NO_PERMISSION, io::FILE_NOT_VALID, io::WOULD_BLOCK, io::FILE_NOT_FOUND
@return "Memory mapped region. Must be released with FileMmap.destroy()"
*>
fn mmap::FileMmap? mmap_open(String filename, String mode, usz offset = 0, usz len = 0, vm::VirtualMemoryAccess access = READ, bool shared = false)
{
File file = open(filename, mode)!;
defer catch (void)file.close();
FileMmap mm = mmap_file(file, offset, len, access, shared)!;
// FileMmap owns the file and it will close it on destroy()
mm.file = file;
return mm;
}

View File

@@ -69,6 +69,7 @@ struct Formatter
PrintFlags flags; PrintFlags flags;
uint width; uint width;
uint prec; uint prec;
usz idx;
fault first_fault; fault first_fault;
} }
} }
@@ -146,14 +147,10 @@ fn usz? Formatter.out_str(&self, any arg) @private
case VOID: case VOID:
return self.out_substr("void"); return self.out_substr("void");
case FAULT: case FAULT:
fault f = *(fault*)arg.ptr; return self.out_substr((*(fault*)arg.ptr).nameof);
return self.out_substr(f ? f.nameof : "(empty-fault)");
case INTERFACE: case INTERFACE:
any a = *(any*)arg;
return a ? self.out_str(a) : self.out_substr("(empty-interface)");
case ANY: case ANY:
any a = *(any*)arg; return self.out_str(*(any*)arg);
return a ? self.out_str(a) : self.out_substr("(empty-any)");
case OPTIONAL: case OPTIONAL:
unreachable(); unreachable();
case SIGNED_INT: case SIGNED_INT:
@@ -498,11 +495,6 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
out = ((char*)current.ptr)[:current.type.sizeof]; out = ((char*)current.ptr)[:current.type.sizeof];
break; break;
} }
if (current.type.kindof == POINTER)
{
out = ((*(char**)current.ptr))[:current.type.inner.sizeof];
break;
}
total_len += self.out_substr("<INVALID>")!; total_len += self.out_substr("<INVALID>")!;
continue; continue;
} }
@@ -574,5 +566,5 @@ fn usz? Formatter.print(&self, String str)
self.out_fn = &out_null_fn; self.out_fn = &out_null_fn;
} }
foreach (c : str) self.out(c)!; foreach (c : str) self.out(c)!;
return str.len; return self.idx;
} }

View File

@@ -660,6 +660,7 @@ fn usz? Formatter.out_char(&self, any arg) @private
fn usz? Formatter.out_reverse(&self, char[] buf) @private fn usz? Formatter.out_reverse(&self, char[] buf) @private
{ {
usz n; usz n;
usz buffer_start_idx = self.idx;
usz len = buf.len; usz len = buf.len;
// pad spaces up to given width // pad spaces up to given width
if (!self.flags.zeropad && !self.flags.left) if (!self.flags.zeropad && !self.flags.left)

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021-2025 Christoffer Lerno. All rights reserved. // Copyright (c) 2021-2022 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license // Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file. // a copy of which can be found in the LICENSE_STDLIB file.
module std::io; module std::io;
@@ -16,7 +16,6 @@ faultdef
BUSY, BUSY,
CANNOT_READ_DIR, CANNOT_READ_DIR,
DIR_NOT_EMPTY, DIR_NOT_EMPTY,
PARENT_DIR_MISSING,
EOF, EOF,
FILE_CANNOT_DELETE, FILE_CANNOT_DELETE,
FILE_IS_DIR, FILE_IS_DIR,
@@ -50,23 +49,41 @@ faultdef
"\r" will be filtered from the String. "\r" will be filtered from the String.
@param stream : `The stream to read from.` @param stream : `The stream to read from.`
@require !($defined(&stream) &&& @is_instream(&stream)) : "The value for 'stream' should have been passed as a pointer and not as a value, please add '&'." @require @is_instream(stream) : `The stream must implement InStream.`
@require @is_instream(stream) : `Make sure that the stream is actually an InStream.`
@param [inout] allocator : `the allocator to use.` @param [inout] allocator : `the allocator to use.`
@return `The string containing the data read.` @return `The string containing the data read.`
*> *>
macro String? readline(Allocator allocator, stream = io::stdin()) macro String? readline(Allocator allocator, stream = io::stdin())
{ {
if (allocator == tmem) bool $is_stream = @typeis(stream, InStream);
{ $if $is_stream:
DString str = dstring::temp_with_capacity(256); $typeof(&stream.read_byte) func = &stream.read_byte;
readline_to_stream(&str, stream)!; char val = func((void*)stream)!;
return str.str_view(); $else
} char val = stream.read_byte()!;
$endif
if (val == '\n') return "";
@pool() @pool()
{ {
DString str = dstring::temp_with_capacity(256); DString str = dstring::temp_with_capacity(256);
readline_to_stream(&str, stream)!; if (val != '\r') str.append(val);
while (1)
{
$if $is_stream:
char? c = func((void*)stream);
$else
char? c = stream.read_byte();
$endif
if (catch err = c)
{
if (err == io::EOF) break;
return err?;
}
if (c == '\r') continue;
if (c == '\n') break;
str.append_char(c);
}
return str.copy_str(allocator); return str.copy_str(allocator);
}; };
} }
@@ -76,7 +93,6 @@ macro String? readline(Allocator allocator, stream = io::stdin())
on the temporary allocator and does not need to be freed. on the temporary allocator and does not need to be freed.
@param stream : `The stream to read from.` @param stream : `The stream to read from.`
@require !($defined(&stream) &&& @is_instream(&stream)) : "The value for 'stream' should have been passed as a pointer and not as a value, please add '&'."
@require @is_instream(stream) : `The stream must implement InStream.` @require @is_instream(stream) : `The stream must implement InStream.`
@return `The temporary string containing the data read.` @return `The temporary string containing the data read.`
*> *>
@@ -85,65 +101,6 @@ macro String? treadline(stream = io::stdin())
return readline(tmem, stream) @inline; return readline(tmem, stream) @inline;
} }
<*
Reads a string, see `readline`, the data is passed to an outstream
@param out_stream : `The stream to write to`
@param in_stream : `The stream to read from.`
@require !($defined(&in_stream) &&& @is_instream(&in_stream)) : "The value for 'in_stream' should have been passed as a pointer and not as a value, please add '&'."
@require !($defined(&out_stream) &&& @is_outstream(&out_stream)) : "The value for 'out_stream' should have been passed as a pointer and not as a value, please add '&'."
@require @is_instream(in_stream) : `The in_stream must implement InStream.`
@require @is_outstream(out_stream) : `The out_stream must implement OutStream.`
@return `The number of bytes written`
*>
macro usz? readline_to_stream(out_stream, in_stream = io::stdin())
{
bool $is_stream = @typeis(in_stream, InStream);
$if $is_stream:
var func = &in_stream.read_byte;
char val = func((void*)in_stream)!;
$else
char val = in_stream.read_byte()!;
$endif
bool $is_out_stream = @typeis(out_stream, OutStream);
$if $is_out_stream:
var out_func = &out_stream.write_byte;
$endif
if (val == '\n') return 0;
usz len;
if (val != '\r')
{
$if $is_out_stream:
out_func((void*)out_stream.ptr, val)!;
$else
out_stream.write_byte(val)!;
$endif
len++;
}
while (1)
{
$if $is_stream:
char? c = func((void*)in_stream);
$else
char? c = in_stream.read_byte();
$endif
if (catch err = c)
{
if (err == io::EOF) break;
return err?;
}
if (c == '\r') continue;
if (c == '\n') break;
$if $is_out_stream:
out_func((void*)out_stream.ptr, c)!;
$else
out_stream.write_byte(c)!;
$endif
len++;
}
return len;
}
<* <*
Print a value to a stream. Print a value to a stream.
@@ -160,7 +117,7 @@ macro usz? fprint(out, x)
$case ZString: return out.write(x.str_view()); $case ZString: return out.write(x.str_view());
$case DString: return out.write(x.str_view()); $case DString: return out.write(x.str_view());
$default: $default:
$if @assignable_to(x, String): $if $assignable(x, String):
return out.write((String)x); return out.write((String)x);
$else $else
$if is_struct_with_default_print($Type): $if is_struct_with_default_print($Type):
@@ -254,7 +211,7 @@ macro void eprint(x)
@param x : "The value to print" @param x : "The value to print"
*> *>
macro void eprintn(x = "") macro void eprintn(x)
{ {
(void)fprintn(io::stderr(), x); (void)fprintn(io::stderr(), x);
} }

View File

@@ -3,11 +3,9 @@ import std::io::path, libc, std::os;
macro void? native_chdir(Path path) macro void? native_chdir(Path path)
{ {
@pool()
{
$switch: $switch:
$case env::POSIX: $case env::POSIX:
if (posix::chdir(path.str_view().zstr_tcopy())) if (posix::chdir(path.as_zstr()))
{ {
switch (libc::errno()) switch (libc::errno())
{ {
@@ -20,11 +18,13 @@ macro void? native_chdir(Path path)
} }
} }
$case env::WIN32: $case env::WIN32:
@pool()
{
// TODO improve with better error handling. // TODO improve with better error handling.
if (win32::setCurrentDirectoryW(path.str_view().to_temp_utf16()!!)) return; if (win32::setCurrentDirectoryW(path.str_view().to_temp_utf16()!!)) return;
};
return io::GENERAL_ERROR?; return io::GENERAL_ERROR?;
$default: $default:
return io::UNSUPPORTED_OPERATION?; return io::UNSUPPORTED_OPERATION?;
$endswitch $endswitch
};
} }

View File

@@ -5,11 +5,7 @@ fn PathList? native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
{ {
PathList list; PathList list;
list.init(allocator); list.init(allocator);
DIRPtr directory @noinit; DIRPtr directory = posix::opendir(dir.str_view() ? dir.as_zstr() : (ZString)".");
@pool()
{
directory = posix::opendir(dir.str_view() ? dir.str_view().zstr_tcopy() : (ZString)".");
};
defer if (directory) posix::closedir(directory); defer if (directory) posix::closedir(directory);
if (!directory) return (path::is_dir(dir) ? io::CANNOT_READ_DIR : io::FILE_NOT_DIR)?; if (!directory) return (path::is_dir(dir) ? io::CANNOT_READ_DIR : io::FILE_NOT_DIR)?;
Posix_dirent* entry; Posix_dirent* entry;

View File

@@ -7,11 +7,9 @@ import std::os::posix;
macro bool? native_mkdir(Path path, MkdirPermissions permissions) macro bool? native_mkdir(Path path, MkdirPermissions permissions)
{ {
@pool()
{
$switch: $switch:
$case env::POSIX: $case env::POSIX:
if (!posix::mkdir(path.str_view().zstr_tcopy(), permissions == NORMAL ? 0o777 : 0o700)) return true; if (!posix::mkdir(path.as_zstr(), permissions == NORMAL ? 0o777 : 0o700)) return true;
switch (libc::errno()) switch (libc::errno())
{ {
case errno::EACCES: case errno::EACCES:
@@ -25,11 +23,11 @@ macro bool? native_mkdir(Path path, MkdirPermissions permissions)
case errno::EEXIST: return false; case errno::EEXIST: return false;
case errno::ELOOP: return io::SYMLINK_FAILED?; case errno::ELOOP: return io::SYMLINK_FAILED?;
case errno::ENOTDIR: return io::FILE_NOT_FOUND?; case errno::ENOTDIR: return io::FILE_NOT_FOUND?;
case errno::ENOENT: return io::PARENT_DIR_MISSING?; default: return io::GENERAL_ERROR?;
default:
return io::GENERAL_ERROR?;
} }
$case env::WIN32: $case env::WIN32:
@pool()
{
// TODO security attributes // TODO security attributes
if (win32::createDirectoryW(path.str_view().to_temp_utf16()!!, null)) return true; if (win32::createDirectoryW(path.str_view().to_temp_utf16()!!, null)) return true;
switch (win32::getLastError()) switch (win32::getLastError())
@@ -45,8 +43,8 @@ macro bool? native_mkdir(Path path, MkdirPermissions permissions)
default: default:
return io::GENERAL_ERROR?; return io::GENERAL_ERROR?;
} }
};
$default: $default:
return io::UNSUPPORTED_OPERATION?; return io::UNSUPPORTED_OPERATION?;
$endswitch $endswitch
};
} }

View File

@@ -6,11 +6,9 @@ import std::os::posix;
macro bool? native_rmdir(Path path) macro bool? native_rmdir(Path path)
{ {
@pool()
{
$switch: $switch:
$case env::POSIX: $case env::POSIX:
if (!posix::rmdir(path.str_view().zstr_tcopy())) return true; if (!posix::rmdir(path.as_zstr())) return true;
switch (libc::errno()) switch (libc::errno())
{ {
case errno::EBUSY: return io::BUSY?; case errno::EBUSY: return io::BUSY?;
@@ -26,6 +24,8 @@ macro bool? native_rmdir(Path path)
default: return io::GENERAL_ERROR?; default: return io::GENERAL_ERROR?;
} }
$case env::WIN32: $case env::WIN32:
@pool()
{
if (win32::removeDirectoryW(path.str_view().to_temp_utf16()!!)) return true; if (win32::removeDirectoryW(path.str_view().to_temp_utf16()!!)) return true;
switch (win32::getLastError()) switch (win32::getLastError())
{ {
@@ -41,8 +41,8 @@ macro bool? native_rmdir(Path path)
default: default:
return io::GENERAL_ERROR?; return io::GENERAL_ERROR?;
} }
};
$default: $default:
return io::UNSUPPORTED_OPERATION?; return io::UNSUPPORTED_OPERATION?;
$endswitch $endswitch
};
} }

View File

@@ -6,9 +6,7 @@ import std::io, std::os, libc;
*> *>
fn void? native_rmtree(Path dir) fn void? native_rmtree(Path dir)
{ {
@pool() DIRPtr directory = posix::opendir(dir.as_zstr());
{
DIRPtr directory = posix::opendir(dir.str_view().zstr_tcopy());
defer if (directory) posix::closedir(directory); defer if (directory) posix::closedir(directory);
if (!directory) return path::is_dir(dir) ? io::CANNOT_READ_DIR? : io::FILE_NOT_DIR?; if (!directory) return path::is_dir(dir) ? io::CANNOT_READ_DIR? : io::FILE_NOT_DIR?;
Posix_dirent* entry; Posix_dirent* entry;
@@ -24,7 +22,7 @@ fn void? native_rmtree(Path dir)
native_rmtree(new_path)!; native_rmtree(new_path)!;
continue; continue;
} }
if (libc::remove(new_path.str_view().zstr_tcopy())) if (libc::remove(new_path.as_zstr()))
{ {
// TODO improve // TODO improve
return io::GENERAL_ERROR?; return io::GENERAL_ERROR?;
@@ -32,7 +30,6 @@ fn void? native_rmtree(Path dir)
}; };
} }
os::native_rmdir(dir)!; os::native_rmdir(dir)!;
};
} }
module std::io::os @if(env::WIN32); module std::io::os @if(env::WIN32);

View File

@@ -93,7 +93,7 @@ enum MkdirPermissions
@param recursive : `If directories in between should be created if they're missing, defaults to false` @param recursive : `If directories in between should be created if they're missing, defaults to false`
@param permissions : `The permissions to set on the directory` @param permissions : `The permissions to set on the directory`
*> *>
macro bool? mkdir(path, bool recursive = false, MkdirPermissions permissions = NORMAL) macro bool? mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
{ {
$if @typeis(path, String): $if @typeis(path, String):
@pool() { return _mkdir(temp(path), recursive, permissions); }; @pool() { return _mkdir(temp(path), recursive, permissions); };
@@ -148,20 +148,27 @@ fn Path? new(Allocator allocator, String path, PathEnv path_env = DEFAULT_ENV)
@return? INVALID_PATH : `if the path was invalid` @return? INVALID_PATH : `if the path was invalid`
*> *>
fn Path? temp(String path, PathEnv path_env = DEFAULT_ENV) => new(tmem, path, path_env); fn Path? temp(String path, PathEnv path_env = DEFAULT_ENV)
{
return new(tmem, path, path_env);
}
fn Path? from_wstring(Allocator allocator, WString path) => @pool() fn Path? from_win32_wstring(Allocator allocator, WString path) => @pool()
{ {
return path::new(allocator, string::tfrom_wstring(path)!); return path::new(allocator, string::tfrom_wstring(path)!);
} }
fn Path? from_win32_wstring(Allocator allocator, WString path) @deprecated("Use 'from_wstring' instead") => from_wstring(allocator, path); fn Path? for_windows(Allocator allocator, String path)
{
return new(allocator, path, WIN32);
}
fn Path? for_windows(Allocator allocator, String path) => new(allocator, path, WIN32); fn Path? for_posix(Allocator allocator, String path)
{
return new(allocator, path, POSIX);
}
fn Path? for_posix(Allocator allocator, String path) => new(allocator, path, POSIX); fn bool Path.equals(self, Path p2)
fn bool Path.equals(self, Path p2) @operator(==)
{ {
return self.env == p2.env && self.path_string == p2.path_string; return self.env == p2.env && self.path_string == p2.path_string;
} }
@@ -333,9 +340,15 @@ fn String Path.volume_name(self)
return self.path_string[:len]; return self.path_string[:len];
} }
fn Path? String.to_path(self, Allocator allocator) => new(allocator, self); fn Path? String.to_path(self, Allocator allocator)
{
return new(allocator, self);
}
fn Path? String.to_tpath(self) => new(tmem, self); fn Path? String.to_tpath(self)
{
return new(tmem, self);
}
fn usz? volume_name_len(String path, PathEnv path_env) @local fn usz? volume_name_len(String path, PathEnv path_env) @local
{ {
@@ -514,7 +527,7 @@ fn String? normalize(String path_str, PathEnv path_env = DEFAULT_ENV)
return path_str[:len]; return path_str[:len];
} }
fn ZString Path.as_zstr(self) @deprecated => (ZString)self.path_string.ptr; fn ZString Path.as_zstr(self) => (ZString)self.path_string.ptr;
fn String Path.root_directory(self) fn String Path.root_directory(self)
{ {
@@ -594,17 +607,29 @@ fn bool? traverse(Path path, TraverseCallback callback, any data)
return false; return false;
} }
fn String Path.str_view(self) @inline => self.path_string; fn String Path.str_view(self) @inline
{
return self.path_string;
}
fn bool Path.has_suffix(self, String str) => self.str_view().ends_with(str); fn bool Path.has_suffix(self, String str)
{
return self.str_view().ends_with(str);
}
<* <*
@require self.allocator != null : "This Path should never be freed" @require self.allocator != null : "This Path should never be freed"
*> *>
fn void Path.free(self) => allocator::free(self.allocator, self.path_string.ptr); fn void Path.free(self)
{
allocator::free(self.allocator, self.path_string.ptr);
}
fn usz? Path.to_format(&self, Formatter* formatter) @dynamic => formatter.print(self.str_view()); fn usz? Path.to_format(&self, Formatter* formatter) @dynamic
{
return formatter.print(self.str_view());
}
const bool[256] RESERVED_PATH_CHAR_POSIX = { const bool[256] RESERVED_PATH_CHAR_POSIX = {
@@ -624,7 +649,10 @@ const bool[256] RESERVED_PATH_CHAR_WIN32 = {
['*'] = true, ['*'] = true,
}; };
macro bool is_reserved_win32_path_char(char c) => RESERVED_PATH_CHAR_WIN32[c]; macro bool is_reserved_win32_path_char(char c)
{
return RESERVED_PATH_CHAR_WIN32[c];
}
macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_ENV) macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_ENV)
{ {

View File

@@ -39,12 +39,12 @@ fn usz? available(InStream s)
macro bool @is_instream(#expr) macro bool @is_instream(#expr)
{ {
return @assignable_to(#expr, InStream); return $assignable(#expr, InStream);
} }
macro bool @is_outstream(#expr) macro bool @is_outstream(#expr)
{ {
return @assignable_to(#expr, OutStream); return $assignable(#expr, OutStream);
} }
<* <*

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021-2025 Christoffer Lerno. All rights reserved. // Copyright (c) 2021 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license // Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file. // a copy of which can be found in the LICENSE_STDLIB file.
module libc; module libc;
@@ -8,9 +8,6 @@ const int EXIT_FAILURE = 1;
const int EXIT_SUCCESS = 0; const int EXIT_SUCCESS = 0;
const int RAND_MAX = 0x7fffffff; const int RAND_MAX = 0x7fffffff;
alias WChar @if(env::WIN32) = Char16;
alias WChar @if(!env::WIN32) = Char32;
struct DivResult struct DivResult
{ {
CInt quot; CInt quot;
@@ -151,7 +148,6 @@ extern fn void qsort(void* base, usz items, usz size, CompareFunction compare);
extern fn CInt raise(CInt signal); extern fn CInt raise(CInt signal);
extern fn CInt rand(); extern fn CInt rand();
extern fn isz read(Fd fd, void* buf, usz nbyte) @if(!env::WIN32); extern fn isz read(Fd fd, void* buf, usz nbyte) @if(!env::WIN32);
extern fn isz readlink(ZString pathname, char* buf, int bufsize) @if(!env::WIN32);
extern fn void* realloc(void* ptr, usz size); extern fn void* realloc(void* ptr, usz size);
extern fn CInt remove(ZString filename); extern fn CInt remove(ZString filename);
extern fn CInt rename(ZString old_name, ZString new_name); extern fn CInt rename(ZString old_name, ZString new_name);

View File

@@ -4,7 +4,7 @@ import std::time;
<* <*
Return a "timespec" from a duration. Return a "timespec" from a duration.
@require self >= time::NANO_DURATION_ZERO @require self >= 0
*> *>
fn TimeSpec NanoDuration.to_timespec(self) @inline fn TimeSpec NanoDuration.to_timespec(self) @inline
{ {
@@ -16,7 +16,7 @@ fn TimeSpec NanoDuration.to_timespec(self) @inline
<* <*
Convert a duration to a timespec. Convert a duration to a timespec.
@require self >= time::DURATION_ZERO @require self >= 0
*> *>
fn TimeSpec Duration.to_timespec(self) @inline fn TimeSpec Duration.to_timespec(self) @inline
{ {
@@ -24,11 +24,3 @@ fn TimeSpec Duration.to_timespec(self) @inline
Time_t sec = (Time_t)(self / time::SEC); Time_t sec = (Time_t)(self / time::SEC);
return { .s = sec, .ns = ns }; return { .s = sec, .ns = ns };
} }
<*
Convert a timestamp to a timespec.
*>
fn TimeSpec Time.to_timespec(self) @inline
{
return ((Duration)self).to_timespec();
}

View File

@@ -11,11 +11,6 @@ extern fn int* __errno() @if(env::ANDROID);
macro int errno() @if(env::ANDROID) => *__errno(); macro int errno() @if(env::ANDROID) => *__errno();
macro void errno_set(int err) @if(env::ANDROID) => *(__errno()) = err; macro void errno_set(int err) @if(env::ANDROID) => *(__errno()) = err;
// OpenBSD
extern fn int* __errno() @if(env::OPENBSD);
macro int errno() @if(env::OPENBSD) => *__errno();
macro void errno_set(int err) @if(env::OPENBSD) => *(__errno()) = err;
// Darwin // Darwin
extern fn int* __error() @if(env::DARWIN); extern fn int* __error() @if(env::DARWIN);
macro int errno() @if(env::DARWIN) => *__error(); macro int errno() @if(env::DARWIN) => *__error();

View File

@@ -1,60 +0,0 @@
module libc @if(env::OPENBSD);
// Checked for x86_64
alias Blksize_t = int;
alias Nlink_t = $typefrom(env::X86_64 ? uint.typeid : CUInt.typeid);
alias Dev_t = int;
alias Ino_t = ulong;
alias Mode_t = uint;
alias Blkcnt_t = long;
alias Fflags_t = uint;
struct Stat @if(env::X86_64)
{
Mode_t st_mode;
Dev_t st_dev;
Ino_t st_ino;
Nlink_t st_nlink;
Uid_t st_uid;
Gid_t st_gid;
Dev_t st_rdev;
TimeSpec st_atime;
TimeSpec st_mtime;
TimeSpec st_ctime;
Off_t st_size;
Blkcnt_t st_blocks;
Blksize_t st_blksize;
Fflags_t st_flags;
uint st_gen;
ulong[10] st_spare;
}
// TODO: Investigate if this needs to be fixed
struct Stat @if(!env::X86_64)
{
Dev_t st_dev;
Ino_t st_ino;
Mode_t st_mode;
Nlink_t st_nlink;
Uid_t st_uid;
Gid_t st_gid;
Dev_t st_rdev;
CInt __pad1;
Off_t st_size;
Blksize_t st_blksize;
CInt __pad2;
Blkcnt_t st_blocks;
Time_t st_atime;
long st_atime_nsec;
Time_t st_mtime;
long st_mtime_nsec;
Time_t st_ctime;
long st_ctime_nsec;
CInt[2] __unused;
}
extern fn CInt stat(ZString path, Stat* stat);
extern fn CInt get_nprocs();
extern fn CInt get_nprocs_conf();

View File

@@ -43,7 +43,7 @@ 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.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.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(self, Real r) @operator(/) => { .v = self.v / r };
macro Complex Complex.div_real_inverse(Complex c, Real r) @operator_r(/) => ((Complex) { .r = r }).div(c); 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(/) macro Complex Complex.div(self, Complex b) @operator(/)
{ {
Real div = b.v.dot(b.v); Real div = b.v.dot(b.v);

View File

@@ -6,8 +6,6 @@ import std::math::complex;
import std::math::matrix; import std::math::matrix;
import std::math::quaternion; import std::math::quaternion;
attrdef @MathLibc(name) = @extern(name), @link(env::POSIX, "m");
const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466; const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466;
const LOG2E = 1.44269504088896340735992468100189214; // log2(e) const LOG2E = 1.44269504088896340735992468100189214; // log2(e)
const LOG10E = 0.434294481903251827651128918916605082; // log10(e) const LOG10E = 0.434294481903251827651128918916605082; // log10(e)
@@ -130,7 +128,7 @@ macro atan2(x, y)
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value" @require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
@require @typekind(sinp) == POINTER : "Expected sinp to be a pointer" @require @typekind(sinp) == POINTER : "Expected sinp to be a pointer"
@require values::@is_same_type(sinp, cosp) : "Expected sinp and cosp to have the same type" @require values::@is_same_type(sinp, cosp) : "Expected sinp and cosp to have the same type"
@require @assignable_to(x, $typeof(*sinp)) : "Expected x and sinp/cosp to have the same type" @require $assignable(x, $typeof(*sinp)) : "Expected x and sinp/cosp to have the same type"
*> *>
macro sincos_ref(x, sinp, cosp) macro sincos_ref(x, sinp, cosp)
{ {
@@ -344,23 +342,6 @@ macro log(x, base)
*> *>
macro log2(x) => $$log2(values::promote_int(x)); macro log2(x) => $$log2(values::promote_int(x));
<*
@require values::@is_int($x) : `The input value must be an integer.`
@require $x >= 0 : `The input value must be a positive integer.`
@return `A floored base-2 log of an input integer value.`
*>
macro @intlog2($x)
{
$if $x <= 1:
return 0;
$endif
$typeof($x) $z = 0;
$for var $y = $x; $y > 0; $y >>= 1, ++$z: $endfor
return $z - 1;
}
<* <*
@require values::@is_promotable_to_floatlike(x) : `The input must be a number or a float vector` @require values::@is_promotable_to_floatlike(x) : `The input must be a number or a float vector`
*> *>
@@ -413,7 +394,7 @@ macro nearbyint(x) => $$nearbyint(x);
<* <*
@require values::@is_promotable_to_floatlike(x) : `The input must be a number or a float vector` @require values::@is_promotable_to_floatlike(x) : `The input must be a number or a float vector`
@require @assignable_to(exp, $typeof(values::promote_int(x))) || values::@is_int(exp) : `The input must be an integer, castable to the type of x` @require $assignable(exp, $typeof(values::promote_int(x))) || values::@is_int(exp) : `The input must be an integer, castable to the type of x`
*> *>
macro pow(x, exp) macro pow(x, exp)
{ {
@@ -1031,33 +1012,33 @@ macro void float.set_word(float* f, uint u) => *f = bitcast(u, float);
macro double scalbn(double x, int n) => _scalbn(x, n); macro double scalbn(double x, int n) => _scalbn(x, n);
extern fn double _atan(double x) @MathLibc("atan"); extern fn double _atan(double x) @extern("atan");
extern fn float _atanf(float x) @MathLibc("atanf"); extern fn float _atanf(float x) @extern("atanf");
extern fn double _atan2(double, double) @MathLibc("atan2"); extern fn double _atan2(double, double) @extern("atan2");
extern fn float _atan2f(float, float) @MathLibc("atan2f"); extern fn float _atan2f(float, float) @extern("atan2f");
extern fn void _sincos(double, double*, double*) @MathLibc("__sincos") @if(env::DARWIN); extern fn void _sincos(double, double*, double*) @extern("__sincos") @link("m") @if(env::DARWIN);
extern fn void _sincosf(float, float*, float*) @MathLibc("__sincosf") @if(env::DARWIN); extern fn void _sincosf(float, float*, float*) @extern("__sincosf") @link("m") @if(env::DARWIN);
extern fn void _sincos(double, double*, double*) @MathLibc("sincos") @if(!env::DARWIN && !env::WIN32); extern fn void _sincos(double, double*, double*) @extern("sincos") @link("m") @if(!env::DARWIN && !env::WIN32);
extern fn void _sincosf(float, float*, float*) @MathLibc("sincosf") @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 _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); } 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) @MathLibc("tan"); extern fn double _tan(double x) @extern("tan");
extern fn float _tanf(float x) @MathLibc("tanf"); extern fn float _tanf(float x) @extern("tanf");
extern fn double _scalbn(double x, int n) @MathLibc("scalbn"); extern fn double _scalbn(double x, int n) @extern("scalbn");
extern fn double _acos(double x) @MathLibc("acos"); extern fn double _acos(double x) @extern("acos");
extern fn double _asin(double x) @MathLibc("asin"); extern fn double _asin(double x) @extern("asin");
extern fn double _acosh(double x) @MathLibc("acosh"); extern fn double _acosh(double x) @extern("acosh");
extern fn double _asinh(double x) @MathLibc("asinh"); extern fn double _asinh(double x) @extern("asinh");
extern fn double _atanh(double x) @MathLibc("atanh"); extern fn double _atanh(double x) @extern("atanh");
extern fn float _acosf(float x) @MathLibc("acosf"); extern fn float _acosf(float x) @extern("acosf");
extern fn float _asinf(float x) @MathLibc("asinf"); extern fn float _asinf(float x) @extern("asinf");
extern fn float _acoshf(float x) @MathLibc("acoshf"); extern fn float _acoshf(float x) @extern("acoshf");
extern fn float _asinhf(float x) @MathLibc("asinhf"); extern fn float _asinhf(float x) @extern("asinhf");
extern fn float _atanhf(float x) @MathLibc("atanhf"); extern fn float _atanhf(float x) @extern("atanhf");
fn double _frexp(double x, int* e) fn double _frexp(double x, int* e)

View File

@@ -117,7 +117,7 @@ macro bool next_bool(random)
*> *>
macro float next_float(random) macro float next_float(random)
{ {
uint val = random.next_int() & (1U << 24 - 1); uint val = random.next_int() & (1 << 24 - 1);
return val * 0x1.0p-24f; return val * 0x1.0p-24f;
} }
@@ -133,7 +133,7 @@ macro double next_double(random)
} }
// True if the value is a Random. // True if the value is a Random.
macro bool is_random(random) => @assignable_to(random, Random); macro bool is_random(random) => $assignable(random, Random);
macro uint128 @long_to_int128(#function) => (uint128)#function << 64 + #function; macro uint128 @long_to_int128(#function) => (uint128)#function << 64 + #function;
macro ulong @int_to_long(#function) => (ulong)#function << 32 + #function; macro ulong @int_to_long(#function) => (ulong)#function << 32 + #function;

View File

@@ -1,5 +1,5 @@
module std::math::random; module std::math::random;
import std::hash::a5hash, std::time; import std::hash::fnv32a, std::time;
const ODD_PHI64 @local = 0x9e3779b97f4a7c15; const ODD_PHI64 @local = 0x9e3779b97f4a7c15;
const MUL_MCG64 @local = 0xf1357aea2e62a9c5; const MUL_MCG64 @local = 0xf1357aea2e62a9c5;
@@ -69,11 +69,12 @@ fn void seeder(char[] input, char[] out_buffer)
macro uint hash(value) @local macro uint hash(value) @local
{ {
return (uint)a5hash::hash(&&bitcast(value, char[$sizeof(value)])); return fnv32a::hash(&&bitcast(value, char[$sizeof(value)]));
} }
fn char[8 * 4] entropy() @if(!env::WASM_NOLIBC) fn char[8 * 4] entropy() @if(!env::WASM_NOLIBC)
{ {
void* addr = malloc(1); void* addr = malloc(1);
free(addr); free(addr);
static uint random_int; static uint random_int;

View File

@@ -1,5 +1,5 @@
module std::net::os; module std::net::os;
const bool SUPPORTS_INET = env::LIBC && (env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID || env::OPENBSD); const bool SUPPORTS_INET = env::LIBC && (env::WIN32 || env::DARWIN || env::LINUX || env::ANDROID);
typedef AIFamily = CInt; typedef AIFamily = CInt;
typedef AIProtocol = CInt; typedef AIProtocol = CInt;
@@ -23,7 +23,7 @@ struct AddrInfo
ZString ai_canonname; ZString ai_canonname;
SockAddrPtr ai_addr; SockAddrPtr ai_addr;
} }
struct @if(env::LINUX || env::OPENBSD) struct @if(env::LINUX)
{ {
SockAddrPtr ai_addr; SockAddrPtr ai_addr;
ZString ai_canonname; ZString ai_canonname;
@@ -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 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); 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 || env::ANDROID || env::OPENBSD)); 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_INET6 = 0;
const AIFamily PLATFORM_AF_IPX = 0; const AIFamily PLATFORM_AF_IPX = 0;

View File

@@ -1,109 +0,0 @@
module std::net::os @if(env::OPENBSD);
import libc;
// for most of this, see OpenBSD /usr/include/sys/socket.h
const AIFlags AI_EXT = 0x8;
const AIFlags AI_NUMERICSERV = 0x10;
const AIFlags AI_FQDN = 0x20;
const AIFlags AI_ADDRCONFIG = 0x40;
const AIFamily PLATFORM_AF_LOCAL = AF_UNIX; // draft POSIX compatibility
const AIFamily PLATFORM_AF_IMPLINK = 3; // arpanet imp addresses
const AIFamily PLATFORM_AF_PUP = 4; // pup protocols: e.g. BSP
const AIFamily PLATFORM_AF_CHAOS = 5; // mit CHAOS protocols
const AIFamily PLATFORM_AF_NS = 6; // XEROX NS protocols
const AIFamily PLATFORM_AF_ISO = 7; // ISO protocols
const AIFamily PLATFORM_AF_OSI = PLATFORM_AF_ISO;
const AIFamily PLATFORM_AF_ECMA = 8; // european computer manufacturers
const AIFamily PLATFORM_AF_DATAKIT = 9; // datakit protocols
const AIFamily PLATFORM_AF_CCITT = 10; // CCITT protocols, X.25 etc
const AIFamily PLATFORM_AF_SNA = 11; // IBM SNA
const AIFamily PLATFORM_AF_DECNET = 12; // DECnet
const AIFamily PLATFORM_AF_DLI = 13; // DEC Direct data link interface
const AIFamily PLATFORM_AF_LAT = 14; // LAT
const AIFamily PLATFORM_AF_HYLINK = 15; // NSC Hyperchannel
const AIFamily PLATFORM_AF_APPLETALK = 16; // Apple Talk
const AIFamily PLATFORM_AF_ROUTE = 17; // Internal Routing Protocol
const AIFamily PLATFORM_AF_LINK = 18; // Link layer interface
const AIFamily PLATFORM_PSEUDO_AF_XTP = 19; // eXpress Transfer Protocol (no AF)
const AIFamily PLATFORM_AF_COIP = 20; // connection-oriented IP, aka ST II
const AIFamily PLATFORM_AF_CNT = 21; // Computer Network Technology
const AIFamily PLATFORM_PSEUDO_AF_RTIP = 22; // Help Identify RTIP packets
const AIFamily PLATFORM_AF_IPX = 23; // Novell Internet Protocol
const AIFamily PLATFORM_AF_INET6 = 24; // IPv6
const AIFamily PLATFORM_PSEUDO_AF_PIP = 25; // Help Identify PIP packets
const AIFamily PLATFORM_AF_ISDN = 26; // Integrated Services Digital Network*/
const AIFamily PLATFORM_AF_E164 = PLATFORM_AF_ISDN; // CCITT E.164 recommendation
const AIFamily PLATFORM_AF_NATM = 27; // native ATM access
const AIFamily PLATFORM_AF_ENCAP = 28;
const AIFamily PLATFORM_AF_SIP = 29; // Simple Internet Protocol
const AIFamily PLATFORM_AF_KEY = 30;
const AIFamily PLATFORM_PSEUDO_AF_HDRCMPLT = 31; // Used by BPF to not rewrite headers in interface output routine
const AIFamily PLATFORM_AF_BLUETOOTH = 32; // Bluetooth
const AIFamily PLATFORM_AF_MPLS = 33; // MPLS
const AIFamily PLATFORM_PSEUDO_AF_PFLOW = 34; // pflow
const AIFamily PLATFORM_PSEUDO_AF_PIPEX = 35; // PIPEX
const AIFamily PLATFORM_AF_FRAME = 36; // frame (Ethernet) sockets
const AIFamily PLATFORM_AF_MAX = 37;
// see: https://github.com/openbsd/src/blob/1ca4dc792538d17b4c2bd71550f68070505fd7b9/sys/sys/socket.h#L157
const int SOL_SOCKET = 0xFFFF;
const int SO_DEBUG = 0x0001; // turn on debugging info recording
const int SO_ACCEPTCONN = 0x0002; // socket has had listen()
const int SO_REUSEADDR = 0x0004; // allow local address reuse
const int SO_KEEPALIVE = 0x0008; // keep connections alive
const int SO_DONTROUTE = 0x0010; // just use interface addresses
const int SO_BROADCAST = 0x0020; // permit sending of broadcast msgs
const int SO_USELOOPBACK = 0x0040; // bypass hardware when possible
const int SO_LINGER = 0x0080; // linger on close if data present
const int SO_OOBINLINE = 0x0100; // leave received OOB data in line
const int SO_REUSEPORT = 0x0200; // allow local address & port reuse
const int SO_TIMESTAMP = 0x0800; // timestamp received dgram traffic
const int SO_BINDANY = 0x1000; // allow bind to any address
const int SO_ZEROIZE = 0x2000; // zero out all mbufs sent over socket
// additional
const int SO_SNDBUF = 0x1001; // send buffer size
const int SO_RCVBUF = 0x1002; // receive buffer size
const int SO_SNDLOWAT = 0x1003; // send low-water mark
const int SO_RCVLOWAT = 0x1004; // receive low-water mark
const int SO_SNDTIMEO = 0x1005; // send timeout
const int SO_RCVTIMEO = 0x1006; // receive timeout
const int SO_ERROR = 0x1007; // get error status and clear
const int SO_TYPE = 0x1008; // get socket type
const int SO_NETPROC = 0x1020; // multiplex; network processing
const int SO_RTABLE = 0x1021; // routing table to be used
const int SO_PEERCRED = 0x1022; // get connect-time credentials
const int SO_SPLICE = 0x1023; // splice data to other socket
const int SO_DOMAIN = 0x1024; // get socket domain
const int SO_PROTOCOL = 0x1025; // get socket protocol
// POLLIN through POLLNVAL are predefined by lib/std/net/os/posix.c3
const CUShort POLLRDNORM = 0x0040;
const CUShort POLLNORM = POLLRDNORM;
const CUShort POLLWRNORM = POLLOUT;
const CUShort POLLRDBAND = 0x0080;
const CUShort POLLWRBAND = 0x0100;
const CInt MSG_OOB = 0x1; // process out-of-band data
const CInt MSG_PEEK = 0x2; // peek at incoming message
const CInt MSG_DONTROUTE = 0x4; // send without using routing tables
const CInt MSG_EOR = 0x8; // data completes record
const CInt MSG_TRUNC = 0x10; // data discarded before delivery
const CInt MSG_CTRUNC = 0x20; // control data lost before delivery
const CInt MSG_WAITALL = 0x40; // wait for full request or error
const CInt MSG_DONTWAIT = 0x80; // this message should be nonblocking
const CInt MSG_BCAST = 0x100; // this message rec'd as broadcast
const CInt MSG_MCAST = 0x200; // this message rec'd as multicast
const CInt MSG_NOSIGNAL = 0x400; // do not send SIGPIPE
const CInt MSG_CMSG_CLOEXEC = 0x800; // set FD_CLOEXEC on received fds
const CInt MSG_WAITFORONE = 0x1000; // nonblocking but wait for one msg
// socket creation options
const SOCK_CLOEXEC = 0x8000; // set FD_CLOEXEC
const SOCK_NONBLOCK = 0x4000; // set O_NONBLOCK
const SOCK_NONBLOCK_INHERIT = 0x2000; // inherit O_NONBLOCK from listener
const SOCK_DNS = 0x1000; // set SS_DNS
const PLATFORM_O_NONBLOCK = SOCK_NONBLOCK;

View File

@@ -23,7 +23,7 @@ macro void @loop_over_ai(AddrInfo* ai; @body(NativeSocket fd, AddrInfo* ai))
} }
} }
const Duration POLL_FOREVER = (Duration)-1; const Duration POLL_FOREVER = -1;
typedef PollSubscribes = ushort; typedef PollSubscribes = ushort;
typedef PollEvents = ushort; typedef PollEvents = ushort;

View File

@@ -5,11 +5,11 @@ import std::time, libc;
typedef TcpSocket = inline Socket; typedef TcpSocket = inline Socket;
typedef TcpServerSocket = inline Socket; typedef TcpServerSocket = inline Socket;
fn TcpSocket? connect(String host, uint port, Duration timeout = time::DURATION_ZERO, SocketOption... options, IpProtocol ip_protocol = UNSPECIFIED) fn TcpSocket? connect(String host, uint port, Duration timeout = 0, SocketOption... options, IpProtocol ip_protocol = UNSPECIFIED)
{ {
AddrInfo* ai = net::addrinfo(host, port, ip_protocol.ai_family, os::SOCK_STREAM)!; AddrInfo* ai = net::addrinfo(host, port, ip_protocol.ai_family, os::SOCK_STREAM)!;
defer os::freeaddrinfo(ai); defer os::freeaddrinfo(ai);
if (timeout > time::DURATION_ZERO) if (timeout > 0)
{ {
return (TcpSocket)net::connect_with_timeout_from_addrinfo(ai, options, timeout)!; return (TcpSocket)net::connect_with_timeout_from_addrinfo(ai, options, timeout)!;
} }

View File

@@ -90,7 +90,6 @@ alias BacktraceList = List{Backtrace};
alias symbolize_backtrace @if(env::LINUX) = linux::symbolize_backtrace; alias symbolize_backtrace @if(env::LINUX) = linux::symbolize_backtrace;
alias symbolize_backtrace @if(env::WIN32) = win32::symbolize_backtrace; alias symbolize_backtrace @if(env::WIN32) = win32::symbolize_backtrace;
alias symbolize_backtrace @if(env::DARWIN) = darwin::symbolize_backtrace; alias symbolize_backtrace @if(env::DARWIN) = darwin::symbolize_backtrace;
alias symbolize_backtrace @if(env::OPENBSD) = openbsd::symbolize_backtrace;
fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace) @if(!env::NATIVE_STACKTRACE) fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace) @if(!env::NATIVE_STACKTRACE)
{ {

View File

@@ -79,7 +79,7 @@ fn String? get_home_dir(Allocator allocator)
<* <*
Returns the current user's config directory. Returns the current user's config directory.
*> *>
fn Path? get_config_dir(Allocator allocator) => @pool() fn Path? get_config_dir(Allocator allocator) => @pool()
{ {
@@ -87,15 +87,13 @@ fn Path? get_config_dir(Allocator allocator) => @pool()
return path::new(allocator, tget_var("AppData")); return path::new(allocator, tget_var("AppData"));
$else $else
$if env::DARWIN: $if env::DARWIN:
String home_dir = tget_var("HOME")!; String s = tget_var("HOME")!;
const DIR = "Library/Application Support"; const DIR = "Library/Application Support";
$else $else
String? config_path = tget_var("XDG_CONFIG_HOME"); String s = tget_var("XDG_CONFIG_HOME") ?? tget_var("HOME")!;
if (try config_path && config_path.len > 0) return path::new(allocator, config_path);
String home_dir = tget_var("HOME")!;
const DIR = ".config"; const DIR = ".config";
$endif $endif
return path::temp(home_dir).append(allocator, DIR); return path::temp(s).append(allocator, DIR);
$endif $endif
} }
@@ -117,10 +115,10 @@ fn bool clear_var(String name) => @pool()
$endswitch $endswitch
} }
fn String? executable_path() fn String? executable_path(Allocator allocator)
{ {
$if env::DARWIN: $if env::DARWIN:
return darwin::executable_path(); return darwin::executable_path(allocator);
$else $else
return NOT_FOUND?; return NOT_FOUND?;
$endif $endif

View File

@@ -89,35 +89,6 @@ struct Linux_Dl_info
void* dli_saddr; /* Address of nearest symbol */ void* dli_saddr; /* Address of nearest symbol */
} }
alias Dl_iterate_phdr_callback64 = fn CInt(Linux_dl_phdr_info_64*, usz, void*);
alias Dl_iterate_phdr_callback32 = fn CInt(Linux_dl_phdr_info_32*, usz, void*);
extern fn CInt dl_iterate_phdr64(Dl_iterate_phdr_callback64 callback, void* data);
extern fn CInt dl_iterate_phdr32(Dl_iterate_phdr_callback32 callback, void* data);
struct Linux_dl_phdr_info_64
{
Elf64_Addr dlpi_addr;
ZString dlpi_name;
Elf64_Phdr* dlpi_phdr;
Elf64_Half dlpi_phnum;
ulong dlpi_adds;
ulong dlpi_subs;
usz dlpi_tsl_modid;
void* dlpi_tls_data;
}
struct Linux_dl_phdr_info_32
{
Elf32_Addr dlpi_addr;
ZString dlpi_name;
Elf32_Phdr* dlpi_phdr;
Elf32_Half dlpi_phnum;
ulong dlpi_adds;
ulong dlpi_subs;
usz dlpi_tsl_modid;
void* dlpi_tls_data;
}
fn ulong? elf_module_image_base(String path) @local fn ulong? elf_module_image_base(String path) @local
{ {
File file = file::open(path, "rb")!; File file = file::open(path, "rb")!;
@@ -159,29 +130,28 @@ fn ulong? elf_module_image_base(String path) @local
fn void? backtrace_add_from_exec(Allocator allocator, BacktraceList* list, void* addr) @local fn void? backtrace_add_from_exec(Allocator allocator, BacktraceList* list, void* addr) @local
{ {
char[1024] buf @noinit; char[] buf = mem::talloc_array(char, 1024);
String exec_path = process::execute_stdout_to_buffer(&buf, {"realpath", "-e", string::bformat(&&(char[64]){}, "/proc/%d/exe", posix::getpid())})!;
String exec_path = process::execute_stdout_to_buffer(buf, {"realpath", "-e", string::tformat("/proc/%d/exe", posix::getpid())})!;
String obj_name = exec_path.copy(allocator); String obj_name = exec_path.copy(allocator);
String addr2line = process::execute_stdout_to_buffer(&buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", exec_path, string::bformat(&&(char[64]){}, "0x%x", addr)})!; String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", exec_path, string::tformat("0x%x", addr)})!;
return backtrace_add_addr2line(allocator, list, addr, addr2line, obj_name, "???"); return backtrace_add_addr2line(allocator, list, addr, addr2line, obj_name, "???");
} }
fn void? backtrace_add_from_dlinfo(Allocator allocator, BacktraceList* list, void* addr, Linux_Dl_info* info) @local fn void? backtrace_add_from_dlinfo(Allocator allocator, BacktraceList* list, void* addr, Linux_Dl_info* info) @local
{ {
char[1024] buf @noinit; char[] buf = mem::talloc_array(char, 1024);
void* obj_addr = addr - (uptr)info.dli_fbase + (uptr)elf_module_image_base(info.dli_fname.str_view())!; void* obj_addr = addr - (uptr)info.dli_fbase + (uptr)elf_module_image_base(info.dli_fname.str_view())!;
ZString obj_path = info.dli_fname; ZString obj_path = info.dli_fname;
String sname = info.dli_sname ? info.dli_sname.str_view() : "???"; String sname = info.dli_sname ? info.dli_sname.str_view() : "???";
String addr2line = process::execute_stdout_to_buffer(&buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::bformat(&&(char[64]){}, "0x%x", obj_addr - 1)})!; String addr2line = process::execute_stdout_to_buffer(buf, {"addr2line", "-p", "-i", "-C", "-f", "-e", obj_path.str_view(), string::tformat("0x%x", obj_addr - 1)})!;
return backtrace_add_addr2line(allocator, list, addr, addr2line, info.dli_fname.str_view(), sname); return backtrace_add_addr2line(allocator, list, addr, addr2line, info.dli_fname.str_view(), sname);
} }
fn Backtrace? backtrace_line_parse(Allocator allocator, String string, String obj_name, String func_name, bool is_inlined) fn Backtrace? backtrace_line_parse(Allocator allocator, String string, String obj_name, String func_name, bool is_inlined)
{ {
@stack_mem(256; Allocator mem) String[] parts = string.trim().tsplit(" at ");
{
String[] parts = string.trim().split(mem, " at ");
if (parts.len != 2) return NOT_FOUND?; if (parts.len != 2) return NOT_FOUND?;
uint line = 0; uint line = 0;
@@ -200,14 +170,10 @@ fn Backtrace? backtrace_line_parse(Allocator allocator, String string, String ob
.allocator = allocator, .allocator = allocator,
.is_inline = is_inlined .is_inline = is_inlined
}; };
};
} }
fn void? backtrace_add_addr2line(Allocator allocator, BacktraceList* list, void* addr, String addr2line, String obj_name, String func_name) @local fn void? backtrace_add_addr2line(Allocator allocator, BacktraceList* list, void* addr, String addr2line, String obj_name, String func_name) @local
{ {
@stack_mem(1024; Allocator mem) String[] inline_parts = addr2line.tsplit("(inlined by)");
{
String[] inline_parts = addr2line.split(mem, "(inlined by)");
usz last = inline_parts.len - 1; usz last = inline_parts.len - 1;
foreach (i, part : inline_parts) foreach (i, part : inline_parts)
{ {
@@ -228,7 +194,6 @@ fn void? backtrace_add_addr2line(Allocator allocator, BacktraceList* list, void*
} }
list.push(trace); list.push(trace);
} }
};
} }
fn void? backtrace_add_element(Allocator allocator, BacktraceList *list, void* addr) @local fn void? backtrace_add_element(Allocator allocator, BacktraceList *list, void* addr) @local
@@ -239,12 +204,15 @@ fn void? backtrace_add_element(Allocator allocator, BacktraceList *list, void* a
return; return;
} }
@pool()
{
Linux_Dl_info info; Linux_Dl_info info;
if (dladdr(addr, &info) == 0) if (dladdr(addr, &info) == 0)
{ {
return backtrace_add_from_exec(allocator, list, addr); return backtrace_add_from_exec(allocator, list, addr);
} }
return backtrace_add_from_dlinfo(allocator, list, addr, &info); return backtrace_add_from_dlinfo(allocator, list, addr, &info);
};
} }
fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace) fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
@@ -259,9 +227,12 @@ fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
} }
list.free(); list.free();
} }
@pool()
{
foreach (addr : backtrace) foreach (addr : backtrace)
{ {
backtrace_add_element(allocator, &list, addr)!; backtrace_add_element(allocator, &list, addr)!;
} }
};
return list; return list;
} }

View File

@@ -79,26 +79,19 @@ 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 void mach_timebase_info(Darwin_mach_timebase_info_data_t* timebase);
extern fn ulong mach_absolute_time(); extern fn ulong mach_absolute_time();
fn String? executable_path() fn String? executable_path(Allocator allocator)
{ {
static char[4096] path; char[4096] path;
static uint len = 0; uint len = path.len;
if (!len) if (darwin_NSGetExecutablePath(&path, &len) < 0) return NOT_FOUND?;
{ return ((ZString)&path).copy(allocator);
char[4096] buf;
uint temp_len = buf.len;
if (darwin_NSGetExecutablePath(&buf, &temp_len) < 0) return NOT_FOUND?;
path[:len] = buf[:len];
len = (int)((ZString)&buf).len();
}
return (String)path[:len];
} }
fn uptr? load_address() @local fn uptr? load_address() @local
{ {
Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT"); Darwin_segment_command_64* cmd = darwin::getsegbyname("__TEXT");
if (!cmd) return backtrace::SEGMENT_NOT_FOUND?; if (!cmd) return backtrace::SEGMENT_NOT_FOUND?;
String path = env::executable_path() ?? backtrace::EXECUTABLE_PATH_NOT_FOUND?!; String path = env::executable_path(tmem) ?? backtrace::EXECUTABLE_PATH_NOT_FOUND?!;
uint dyld_count = darwin::_dyld_image_count(); uint dyld_count = darwin::_dyld_image_count();
for (uint i = 0; i < dyld_count; i++) for (uint i = 0; i < dyld_count; i++)
{ {
@@ -110,22 +103,23 @@ fn uptr? load_address() @local
return backtrace::IMAGE_NOT_FOUND?; return backtrace::IMAGE_NOT_FOUND?;
} }
fn Backtrace? backtrace_load_element(Allocator allocator, String execpath, void* buffer, void* load_address) @local fn Backtrace? backtrace_load_element(Allocator allocator, String execpath, void* buffer, void* load_address) @local
{ {
@pool()
{
if (buffer) if (buffer)
{ {
char[1024] buf; char* buf = tmalloc(1024);
String s = process::execute_stdout_to_buffer(&buf, String s = process::execute_stdout_to_buffer(buf[:1024],
{ "atos", "-o", execpath, "-arch", env::AARCH64 ? "arm64" : "x86_64", "-l", { "atos", "-o", execpath, "-arch", env::AARCH64 ? "arm64" : "x86_64", "-l",
string::bformat(&&(char[64]){}, "%p", load_address), string::tformat("%p", load_address),
string::bformat(&&(char[64]){}, "%p", buffer - 1), string::tformat("%p", buffer - 1),
"-fullPath" })!; "-fullPath" })!;
@stack_mem(512; Allocator mem) String[] parts = s.tsplit(" ");
{
String[] parts = s.split(mem, " ", 5);
if (parts.len == 4) if (parts.len == 4)
{ {
String[] path_parts = parts[3].split(mem, ":"); String[] path_parts = parts[3].tsplit(":");
return { return {
.offset = (uptr)buffer, .offset = (uptr)buffer,
.function = parts[0].copy(allocator), .function = parts[0].copy(allocator),
@@ -135,7 +129,6 @@ fn Backtrace? backtrace_load_element(Allocator allocator, String execpath, void*
.allocator = allocator .allocator = allocator
}; };
} }
};
} }
Darwin_Dl_info info; Darwin_Dl_info info;
if (!buffer || !darwin::dladdr(buffer, &info)) return backtrace::BACKTRACE_UNKNOWN; if (!buffer || !darwin::dladdr(buffer, &info)) return backtrace::BACKTRACE_UNKNOWN;
@@ -147,6 +140,7 @@ fn Backtrace? backtrace_load_element(Allocator allocator, String execpath, void*
.line = 0, .line = 0,
.allocator = allocator .allocator = allocator
}; };
};
} }
fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace) fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
@@ -162,11 +156,14 @@ fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
} }
list.free(); list.free();
} }
String execpath = executable_path()!; @pool()
{
String execpath = executable_path(tmem)!;
foreach (addr : backtrace) foreach (addr : backtrace)
{ {
list.push(backtrace_load_element(allocator, execpath, addr, load_addr) ?? backtrace::BACKTRACE_UNKNOWN); list.push(backtrace_load_element(allocator, execpath, addr, load_addr) ?? backtrace::BACKTRACE_UNKNOWN);
} }
};
return list; return list;
} }

View File

@@ -1,74 +1,2 @@
module std::os::openbsd @if(env::OPENBSD); module std::os::openbsd @if(env::OPENBSD);
import libc, std::os, std::collections::list;
extern fn ZString* backtrace_symbols_fmt(void **addrlist, usz len, ZString fmt);
fn Backtrace? backtrace_line_parse(Allocator allocator, String obj, String addr2line) @local
{
@stack_mem(256; Allocator mem)
{
String[] parts = addr2line.trim().split(mem, " at ");
if (parts.len != 2) return NOT_FOUND?;
uint line = 0;
String source = "";
if (!parts[1].contains("?") && parts[1].contains(":"))
{
usz index = parts[1].rindex_of_char(':')!;
source = parts[1][:index];
line = parts[1][index + 1..].to_uint()!;
}
return {
.function = parts[0].copy(allocator),
.object_file = obj.copy(allocator),
.file = source.copy(allocator),
.line = line,
.allocator = allocator,
.is_inline = false,
};
};
}
fn void? backtrace_add_addr2line(Allocator allocator, BacktraceList* list, String obj, String addr2line) @local
{
list.push(backtrace_line_parse(allocator, obj, addr2line)!);
}
fn void? backtrace_add_from_exec(Allocator allocator, BacktraceList* list, String fun, String obj) @local
{
char[1024] buf @noinit;
String addr2line = process::execute_stdout_to_buffer(&buf, {"llvm-addr2line", "-fpe", obj, fun})!;
return backtrace_add_addr2line(allocator, list, obj, addr2line);
}
fn void? backtrace_add_element(Allocator allocator, BacktraceList *list, String fun, String obj) @local
{
return backtrace_add_from_exec(allocator, list, fun, obj);
}
fn BacktraceList? symbolize_backtrace(Allocator allocator, void*[] backtrace)
{
BacktraceList list;
list.init(allocator, backtrace.len);
defer catch
{
foreach (trace : list)
{
trace.free();
}
list.free();
}
ZString *strings = backtrace_symbols_fmt(backtrace.ptr, backtrace.len, "%n%D %f");
for (int i = 0; i < backtrace.len; i++) {
String full = strings[i].str_view();
Splitter iter = full.tokenize(" ");
String fun = iter.next()!;
String obj = iter.next()!;
backtrace_add_element(allocator, &list, fun, obj)!;
}
free(strings);
return list;
}

View File

@@ -1,8 +1,3 @@
module std::os::posix @if(env::POSIX); module std::os::posix @if(env::POSIX);
extern ZString* environ; extern ZString* environ;
extern fn CLong sysconf(CInt name);
const CInt _SC_PAGESIZE @if(env::LINUX) = 30;
const CInt _SC_PAGESIZE @if(env::DARWIN) = 29;

View File

@@ -1,28 +0,0 @@
module std::os::posix @if(env::POSIX);
import libc;
const PROT_NONE = 0x00; // no permissions
const PROT_READ = 0x01; // pages can be read
const PROT_WRITE = 0x02; // pages can be written
const PROT_EXEC = 0x04; // pages can be executed
const MAP_SHARED = 0x0001; // share changes
const MAP_PRIVATE = 0x0002; // changes are private
const MAP_FILE = 0x0000; // map from file (default)
const MAP_ANONYMOUS = 0x1000; // allocated from memory, swap space
const void* MAP_FAILED = (void *)(uptr)-1; // mmap failed
const MADV_NORMAL = 0; // no further special treatment
const MADV_RANDOM = 1; // expect random page refs
const MADV_SEQUENTIAL = 2; // expect sequential page refs
const MADV_WILLNEED = 3; // will need these pages
const MADV_DONTNEED = 4; // dont need these pages
extern fn void* mmap(void*, usz, CInt, CInt, CInt, Off_t);
extern fn CInt munmap(void*, usz);
extern fn CInt mprotect(void*, usz, CInt);
extern fn int madvise(void*, usz, CInt);
extern fn CInt getpagesize();

View File

@@ -58,9 +58,7 @@ const CInt WUNTRACES = 2;
JmpBuf backtrace_jmpbuf @local; JmpBuf backtrace_jmpbuf @local;
alias BacktraceFn = fn CInt(void** buffer, CInt size); alias BacktraceFn = fn CInt(void** buffer, CInt size);
extern fn CInt backtrace(void** buffer, CInt size) @if(env::OPENBSD); fn CInt backtrace(void** buffer, CInt size)
fn CInt backtrace(void** buffer, CInt size) @if(!env::OPENBSD)
{ {
if (size < 1) return 0; if (size < 1) return 0;
void* handle = libc::dlopen("libc.so.6", libc::RTLD_LAZY|libc::RTLD_NODELETE); void* handle = libc::dlopen("libc.so.6", libc::RTLD_LAZY|libc::RTLD_NODELETE);

View File

@@ -55,7 +55,9 @@ fn void? create_named_pipe_helper(void** rd, void **wr) @local @if(env::WIN32)
tlocal long index = 0; tlocal long index = 0;
long unique = index++; long unique = index++;
String s = string::bformat(&&(char[128]){}, `\\.\pipe\c3_subprocess.%08x.%08x.%d`, win32::getCurrentProcessId(), win32::getCurrentThreadId(), unique); @pool()
{
String s = string::tformat(`\\.\pipe\c3_subprocess.%08x.%08x.%d`, win32::getCurrentProcessId(), win32::getCurrentThreadId(), unique);
Win32_LPCSTR str = (Win32_LPCSTR)s.ptr; Win32_LPCSTR str = (Win32_LPCSTR)s.ptr;
*rd = win32::createNamedPipeA( *rd = win32::createNamedPipeA(
str, str,
@@ -67,6 +69,7 @@ fn void? create_named_pipe_helper(void** rd, void **wr) @local @if(env::WIN32)
str, win32::GENERIC_WRITE, 0, &sa_attr, str, win32::GENERIC_WRITE, 0, &sa_attr,
win32::OPEN_EXISTING, win32::FILE_ATTRIBUTE_NORMAL, null); win32::OPEN_EXISTING, win32::FILE_ATTRIBUTE_NORMAL, null);
if (win32::INVALID_HANDLE_VALUE == *wr) return FAILED_TO_CREATE_PIPE?; if (win32::INVALID_HANDLE_VALUE == *wr) return FAILED_TO_CREATE_PIPE?;
};
} }
fn WString convert_command_line_win32(String[] command_line) @inline @if(env::WIN32) @local fn WString convert_command_line_win32(String[] command_line) @inline @if(env::WIN32) @local
@@ -140,12 +143,12 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
if (!win32::setHandleInformation(wr, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?; if (!win32::setHandleInformation(wr, win32::HANDLE_FLAG_INHERIT, 0)) return FAILED_TO_CREATE_PIPE?;
} }
@stack_mem(2048; Allocator mem) @pool()
{ {
WString used_environment = null; WString used_environment = null;
if (!options.inherit_environment) if (!options.inherit_environment)
{ {
DString env = dstring::new_with_capacity(mem, 64); DString env = dstring::temp_with_capacity(64);
if (!environment.len) if (!environment.len)
{ {
env.append("\0"); env.append("\0");
@@ -156,7 +159,7 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
env.append("\0"); env.append("\0");
} }
env.append("\0"); env.append("\0");
used_environment = env.str_view().to_wstring(mem)!; used_environment = env.str_view().to_temp_wstring()!;
} }
// Handle stdin pipe if not inheriting // Handle stdin pipe if not inheriting
@@ -263,26 +266,26 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
<* <*
@require command_line.len > 0 @require command_line.len > 0
*> *>
fn ZString* copy_command_line(Allocator mem, String[] command_line) @local @inline @if(env::POSIX) fn ZString* tcopy_command_line(String[] command_line) @local @inline @if(env::POSIX)
{ {
ZString* copy = allocator::alloc_array(mem, ZString, command_line.len + 1); ZString* copy = mem::talloc_array(ZString, command_line.len + 1);
foreach (i, str : command_line) foreach (i, str : command_line)
{ {
copy[i] = str.zstr_copy(mem); copy[i] = str.zstr_tcopy();
} }
copy[command_line.len] = null; copy[command_line.len] = null;
return copy; return copy;
} }
const ZString[1] EMPTY_ENVIRONMENT @if(env::POSIX) = { null }; const ZString[1] EMPTY_ENVIRONMENT @if(env::POSIX) = { null };
fn ZString* copy_env(Allocator mem, String[] environment) @local @inline @if(env::POSIX) fn ZString* tcopy_env(String[] environment) @local @inline @if(env::POSIX)
{ {
if (!environment.len) return &EMPTY_ENVIRONMENT; if (!environment.len) return &EMPTY_ENVIRONMENT;
ZString* copy = allocator::alloc_array(mem, ZString, environment.len + 1); ZString* copy = mem::talloc_array(ZString, environment.len + 1);
copy[environment.len] = null; copy[environment.len] = null;
foreach (i, str : environment) foreach (i, str : environment)
{ {
copy[i] = str.zstr_copy(mem); copy[i] = str.zstr_tcopy();
} }
return copy; return copy;
} }
@@ -335,10 +338,10 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
} }
Pid_t child; Pid_t child;
@stack_mem(2048; Allocator mem) @pool()
{ {
ZString* command_line_copy = copy_command_line(mem, command_line); ZString* command_line_copy = tcopy_command_line(command_line);
ZString* used_environment = options.inherit_environment ? posix::environ : copy_env(mem, environment); ZString* used_environment = options.inherit_environment ? posix::environ : tcopy_env(environment);
if (options.search_user_path) if (options.search_user_path)
{ {
if (posix::spawnp(&child, command_line_copy[0], &actions, null, command_line_copy, used_environment)) return FAILED_TO_START_PROCESS?; if (posix::spawnp(&child, command_line_copy[0], &actions, null, command_line_copy, used_environment)) return FAILED_TO_START_PROCESS?;

View File

@@ -221,8 +221,6 @@ const Win32_DWORD ERROR_DEVICE_FEATURE_NOT_SUPPORTED = 0x13C;
const Win32_DWORD ERROR_MR_MID_NOT_FOUND = 0x13D; const Win32_DWORD ERROR_MR_MID_NOT_FOUND = 0x13D;
const Win32_DWORD ERROR_SCOPE_NOT_FOUND = 0x13E; const Win32_DWORD ERROR_SCOPE_NOT_FOUND = 0x13E;
const Win32_DWORD ERROR_UNDEFINED_SCOPE = 0x13F; const Win32_DWORD ERROR_UNDEFINED_SCOPE = 0x13F;
const Win32_DWORD ERROR_INVALID_ADDRESS = 0x1E7;
const Win32_DWORD ERROR_IO_INCOMPLETE = 0x3E4; 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; const Win32_DWORD ERROR_TIMEOUT = 0x5B4;
const Win32_DWORD ERROR_COMMITMENT_LIMIT = 0x5AF;

View File

@@ -1,52 +0,0 @@
module std::os::win32 @if(env::WIN32);
enum Win32_AllocationType : const Win32_DWORD
{
MEM_COMMIT = 0x00001000,
MEM_RESERVE = 0x00002000,
MEM_RESET = 0x00080000,
MEM_RESET_UNDO = 0x01000000,
MEM_LARGE_PAGES = 0x20000000,
MEM_PHYSICAL = 0x00400000,
MEM_TOP_DOWN = 0x00100000,
MEM_WRITE_WATCH = 0x00200000
}
enum Win32_Protect : const Win32_DWORD
{
PAGE_EXECUTE = 0x10,
PAGE_EXECUTE_READ = 0x20,
PAGE_EXECUTE_READWRITE = 0x40,
PAGE_EXECUTE_WRITECOPY = 0x80,
PAGE_NOACCESS = 0x01,
PAGE_READONLY = 0x02,
PAGE_READWRITE = 0x04,
PAGE_WRITECOPY = 0x08,
PAGE_TARGETS_INVALID = 0x40000000,
PAGE_TARGETS_NO_UPDATE = 0x40000000,
PAGE_GUARD = 0x100,
PAGE_NOCACHE = 0x200,
PAGE_WRITECOMBINE = 0x400,
}
enum Win32_FreeType : const Win32_DWORD
{
MEM_DECOMMIT = 0x00004000,
MEM_RELEASE = 0x00008000,
MEM_COALESCE_PLACEHOLDERS = 0x00000001,
MEM_PRESERVE_PLACEHOLDER = 0x00000002,
}
extern fn Win32_LPVOID virtualAlloc(Win32_LPVOID lpAddres, Win32_SIZE_T dwSize, Win32_AllocationType flAllocationType, Win32_Protect flProtect) @extern("VirtualAlloc");
extern fn Win32_PVOID virtualAlloc2(Win32_HANDLE process, Win32_PVOID baseAddress, Win32_SIZE_T size, Win32_AllocationType allocationType, Win32_ULONG pageProtection, Win32_MEM_EXTENDED_PARAMETER* extendedParameters, Win32_ULONG parameterCount) @extern("VirtualAlloc2");
extern fn Win32_BOOL virtualFree(Win32_LPVOID lpAddress, Win32_SIZE_T dwSize, Win32_FreeType dwFreeType) @extern("VirtualFree");
extern fn Win32_BOOL virtualProtect(Win32_LPVOID lpAddress, Win32_SIZE_T dwSize, Win32_Protect flNewProtect, Win32_Protect* lpflOldProtect) @extern("VirtualProtect");
fn usz allocation_granularity()
{
static usz granularity;
if (granularity) return granularity;
Win32_SYSTEM_INFO info;
win32::getSystemInfo(&info);
return granularity = (usz)info.dwAllocationGranularity;
}

View File

@@ -1,37 +0,0 @@
module std::os::win32 @if(env::WIN32);
enum Win32_MEM_EXTENDED_PARAMETER_TYPE : CInt
{
INVALID_TYPE,
ADDRESS_REQUIREMENTS,
NUMA_NODE,
PARTITION_HANDLE,
USER_PHYSICAL_HANDLE,
ATTRIBUTE_FLAGS,
IMAGE_MACHINE,
MAX
}
alias Win32_PMEM_EXTENDED_PARAMETER_TYPE = Win32_MEM_EXTENDED_PARAMETER_TYPE;
enum Win32_MEM_EXTENDED_PARAMETER_ATTRIBUTE : const Win32_DWORD64
{
NONPAGED = 0x02, // The allocation is non-pageable.
NONPAGED_LARGE = 0x08, // The allocation is mapped using large pages.
NONPAGED_HUGE = 0x10, // The allocation is mapped using huge pages.
EC_CODE = 0x40, // The allocation will contain emulation-compatible (EC) code.
}
struct Win32_MEM_EXTENDED_PARAMETER
{
Win32_MEM_EXTENDED_PARAMETER_TYPE type;
union
{
Win32_MEM_EXTENDED_PARAMETER_ATTRIBUTE attribute; // If type is ATTRIBUTE_FLAGS
Win32_DWORD64 nodeNumber; // If type is NUMA_NODE
Win32_PVOID pointer; // If type is ADDRESS_REQUIREMENTS
Win32_SIZE_T size;
Win32_HANDLE handle;
Win32_DWORD uLong;
}
}
alias Win32_PMEM_EXTENDED_PARAMETER = Win32_MEM_EXTENDED_PARAMETER*;

View File

@@ -32,7 +32,7 @@ alias Indexs @private = char[256];
alias ElementType = $typeof((Type){}[0]); alias ElementType = $typeof((Type){}[0]);
const bool NO_KEY_FN @private = types::is_same(KeyFn, EmptySlot); const bool NO_KEY_FN @private = types::is_same(KeyFn, EmptySlot);
const bool KEY_BY_VALUE @private = NO_KEY_FN ||| @assignable_to((ElementType){}, 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]); const bool LIST_HAS_REF @private = $defined(&(Type){}[0]);
alias KeyFnReturnType @if(!NO_KEY_FN) = $typefrom(KeyFn.returns) ; alias KeyFnReturnType @if(!NO_KEY_FN) = $typefrom(KeyFn.returns) ;

View File

@@ -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_cmp = @is_valid_macro_slot(comp);
var $has_context = @is_valid_macro_slot(context); var $has_context = @is_valid_macro_slot(context);
var $cmp_by_value = $has_cmp &&& @assignable_to(list[0], CmpFn.paramsof[0].type); var $cmp_by_value = $has_cmp &&& $assignable(list[0], CmpFn.paramsof[0].type);
var $has_get_ref = $defined(&list[0]); var $has_get_ref = $defined(&list[0]);
for (usz i = low; i < high; ++i) for (usz i = low; i < high; ++i)
{ {

View File

@@ -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_cmp = @is_valid_macro_slot(cmp);
var $has_context = @is_valid_macro_slot(context); var $has_context = @is_valid_macro_slot(context);
var $cmp_by_value = $has_cmp &&& @assignable_to(list[0], CmpFn.paramsof[0].type); var $cmp_by_value = $has_cmp &&& $assignable(list[0], CmpFn.paramsof[0].type);
ElementType pivot = list[l]; ElementType pivot = list[l];
while (l < h) while (l < h)

View File

@@ -39,7 +39,7 @@ macro int @sort_cmp(list, pos, cmp, ctx) @local
{ {
var $has_cmp = @is_valid_macro_slot(cmp); var $has_cmp = @is_valid_macro_slot(cmp);
var $has_context = @is_valid_macro_slot(ctx); var $has_context = @is_valid_macro_slot(ctx);
var $cmp_by_value = $has_cmp &&& @assignable_to(list[0], $typeof(cmp).paramsof[0].type); var $cmp_by_value = $has_cmp &&& $assignable(list[0], $typeof(cmp).paramsof[0].type);
var a = list[pos]; var a = list[pos];
var b = list[pos+1]; var b = list[pos+1];

View File

@@ -5,23 +5,3 @@ typedef NativeTimedMutex = int;
typedef NativeConditionVariable = int; typedef NativeConditionVariable = int;
typedef NativeOnceFlag = int; typedef NativeOnceFlag = int;
typedef NativeThread = int; typedef NativeThread = int;
fn void NativeOnceFlag.call_once(&flag, OnceFn func)
{
if (*flag == 0)
{
*flag = 1;
func();
}
}
fn void? NativeMutex.init(&mtx, MutexType type) => NOT_IMPLEMENTED?;
fn bool NativeMutex.is_initialized(&self)
{
return false;
}
macro void? NativeMutex.lock(&mutex) => NOT_IMPLEMENTED?;
macro bool NativeMutex.try_lock(&mutex) => NOT_IMPLEMENTED?;
macro void? NativeMutex.unlock(&mutex) => NOT_IMPLEMENTED?;

View File

@@ -139,32 +139,15 @@ fn void? NativeConditionVariable.wait(&cond, NativeMutex* mtx)
<* <*
@require mtx.is_initialized() @require mtx.is_initialized()
@return? thread::WAIT_TIMEOUT, thread::WAIT_FAILED
*> *>
fn void? NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms) fn void? NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms)
{ {
Time time = time::now() + time::ms(ms); TimeSpec now;
return cond.wait_until(mtx, time) @inline; if (libc::timespec_get(&now, libc::TIME_UTC) != libc::TIME_UTC) return thread::WAIT_FAILED?;
} now.ns += (CLong)((ms % 1000) * 1000_000);
now.s += (Time_t)(ms / 1000 + now.ns / 1000_000_000);
<* now.ns = now.ns % 1000_000_000;
@require mtx.is_initialized() switch (posix::pthread_cond_timedwait(cond, &mtx.mutex, &now))
@return? thread::WAIT_TIMEOUT, thread::WAIT_FAILED
*>
fn void? NativeConditionVariable.wait_timeout_duration(&cond, NativeMutex* mtx, Duration duration)
{
if (duration < time::DURATION_ZERO) return thread::WAIT_TIMEOUT?;
Time time = time::now() + duration;
return cond.wait_until(mtx, time) @inline;
}
<*
@require mtx.is_initialized()
@return? thread::WAIT_TIMEOUT, thread::WAIT_FAILED
*>
fn void? NativeConditionVariable.wait_until(&cond, NativeMutex* mtx, Time time)
{
switch (posix::pthread_cond_timedwait(cond, &mtx.mutex, &&time.to_timespec()))
{ {
case errno::ETIMEDOUT: case errno::ETIMEDOUT:
return thread::WAIT_TIMEOUT?; return thread::WAIT_TIMEOUT?;
@@ -233,7 +216,7 @@ fn void native_thread_yield()
fn void? native_sleep_nano(NanoDuration nano) fn void? native_sleep_nano(NanoDuration nano)
{ {
if (nano <= time::NANO_DURATION_ZERO) return; if (nano <= 0) return;
if (libc::nanosleep(&&nano.to_timespec(), null)) return thread::INTERRUPTED?; if (libc::nanosleep(&&nano.to_timespec(), null)) return thread::INTERRUPTED?;
} }

View File

@@ -15,11 +15,6 @@ struct NativeMutex
} }
} }
fn bool NativeMutex.is_initialized(&self)
{
return self.initialized;
}
struct NativeTimedMutex struct NativeTimedMutex
{ {
Win32_SRWLOCK srw_lock; Win32_SRWLOCK srw_lock;
@@ -203,7 +198,7 @@ fn void? NativeTimedMutex.lock_timeout(&mtx, ulong ms)
NanoDuration duration = time::ms(ms).to_nano(); NanoDuration duration = time::ms(ms).to_nano();
Clock start = clock::now(); Clock start = clock::now();
for (NanoDuration remaining = duration; remaining > time::NANO_DURATION_ZERO; remaining = duration - start.to_now()) for (NanoDuration remaining = duration; remaining > 0; remaining = duration - start.to_now())
{ {
ulong remaining_ms = remaining.to_ms(); ulong remaining_ms = remaining.to_ms();
if (remaining_ms > uint.max) remaining_ms = uint.max; if (remaining_ms > uint.max) remaining_ms = uint.max;
@@ -316,7 +311,6 @@ fn void? NativeConditionVariable.wait(&cond, NativeMutex* mtx) @inline
<* <*
@require mtx.initialized : "Mutex was not initialized" @require mtx.initialized : "Mutex was not initialized"
@return? thread::WAIT_TIMEOUT, thread::WAIT_FAILED
*> *>
fn void? NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms) @inline fn void? NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms) @inline
{ {
@@ -324,28 +318,6 @@ fn void? NativeConditionVariable.wait_timeout(&cond, NativeMutex* mtx, ulong ms)
return timedwait(cond, mtx, (uint)ms) @inline; return timedwait(cond, mtx, (uint)ms) @inline;
} }
<*
@require mtx.initialized : "Mutex was not initialized"
@return? thread::WAIT_TIMEOUT, thread::WAIT_FAILED
*>
fn void? NativeConditionVariable.wait_timeout_duration(&cond, NativeMutex* mtx, Duration duration) @inline
{
if (duration < time::DURATION_ZERO) return thread::WAIT_TIMEOUT?;
long ms = duration.to_ms();
if (ms > uint.max) ms = uint.max;
return timedwait(cond, mtx, (uint)ms) @inline;
}
<*
@require mtx.initialized : "Mutex was not initialized"
@return? thread::WAIT_TIMEOUT, thread::WAIT_FAILED
*>
fn void? NativeConditionVariable.wait_until(&cond, NativeMutex* mtx, Time time) @inline
{
Duration duration = time - time::now();
return cond.wait_timeout_duration(mtx, duration);
}
fn void? NativeThread.create(&thread, ThreadFn func, void* args) fn void? NativeThread.create(&thread, ThreadFn func, void* args)
{ {
if (!(*thread = (NativeThread)win32::createThread(null, 0, func, args, 0, null))) return thread::INIT_FAILED?; if (!(*thread = (NativeThread)win32::createThread(null, 0, func, args, 0, null))) return thread::INIT_FAILED?;

View File

@@ -34,7 +34,6 @@ faultdef
CHANNEL_CLOSED; CHANNEL_CLOSED;
macro void? Mutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, {}); macro void? Mutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, {});
macro bool Mutex.is_initialized(mutex) => ((NativeMutex*)&mutex).is_initialized();
macro void? RecursiveMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, {.recursive}); macro void? RecursiveMutex.init(&mutex) => NativeMutex.init((NativeMutex*)mutex, {.recursive});
macro void? Mutex.destroy(&mutex) => NativeMutex.destroy((NativeMutex*)mutex); macro void? Mutex.destroy(&mutex) => NativeMutex.destroy((NativeMutex*)mutex);
macro void? Mutex.lock(&mutex) => NativeMutex.lock((NativeMutex*)mutex); macro void? Mutex.lock(&mutex) => NativeMutex.lock((NativeMutex*)mutex);
@@ -49,11 +48,6 @@ macro void? TimedMutex.lock_timeout(&mutex, ulong ms) => NativeTimedMutex.lock_t
macro bool TimedMutex.try_lock(&mutex) => NativeTimedMutex.try_lock((NativeTimedMutex*)mutex); macro bool TimedMutex.try_lock(&mutex) => NativeTimedMutex.try_lock((NativeTimedMutex*)mutex);
macro void? TimedMutex.unlock(&mutex) => NativeTimedMutex.unlock((NativeTimedMutex*)mutex); macro void? TimedMutex.unlock(&mutex) => NativeTimedMutex.unlock((NativeTimedMutex*)mutex);
macro void fence(AtomicOrdering $ordering) @safemacro
{
$$fence($ordering.ordinal);
}
macro void Mutex.@in_lock(&mutex; @body) macro void Mutex.@in_lock(&mutex; @body)
{ {
(void)mutex.lock(); (void)mutex.lock();
@@ -69,37 +63,12 @@ macro void? ConditionVariable.wait(&cond, Mutex* mutex)
{ {
return NativeConditionVariable.wait((NativeConditionVariable*)cond, (NativeMutex*)mutex); return NativeConditionVariable.wait((NativeConditionVariable*)cond, (NativeMutex*)mutex);
} }
<* macro void? ConditionVariable.wait_timeout(&cond, Mutex* mutex, ulong ms)
@require @assignable_to(#ms_or_duration, Duration) || @assignable_to(#ms_or_duration, ulong)
@return? thread::WAIT_TIMEOUT, thread::WAIT_FAILED
*>
macro void? ConditionVariable.wait_timeout(&cond, Mutex* mutex, #ms_or_duration) @safemacro
{ {
$if @assignable_to(#ms_or_duration, Duration): return NativeConditionVariable.wait_timeout((NativeConditionVariable*)cond, (NativeMutex*)mutex, ms);
return NativeConditionVariable.wait_timeout_duration((NativeConditionVariable*)cond, (NativeMutex*)mutex, #ms_or_duration);
$else
return NativeConditionVariable.wait_timeout((NativeConditionVariable*)cond, (NativeMutex*)mutex, #ms_or_duration);
$endif
}
<*
@return? thread::WAIT_TIMEOUT, thread::WAIT_FAILED
*>
macro void? ConditionVariable.wait_until(&cond, Mutex* mutex, Time time)
{
return NativeConditionVariable.wait_until((NativeConditionVariable*)cond, (NativeMutex*)mutex, time);
}
<*
Create and start a thread.
@require thread_fn != null : "A non null thread function is required"
*>
macro void? Thread.create(&thread, ThreadFn thread_fn, void* arg)
{
return NativeThread.create(thread, thread_fn, arg);
} }
macro void? Thread.create(&thread, ThreadFn thread_fn, void* arg) => NativeThread.create(thread, thread_fn, arg);
macro void? Thread.detach(thread) => NativeThread.detach(thread); macro void? Thread.detach(thread) => NativeThread.detach(thread);
macro int? Thread.join(thread) => NativeThread.join(thread); macro int? Thread.join(thread) => NativeThread.join(thread);
macro bool Thread.equals(thread, Thread other) => NativeThread.equals(thread, other); macro bool Thread.equals(thread, Thread other) => NativeThread.equals(thread, other);

View File

@@ -112,11 +112,6 @@ fn TzDateTime TzDateTime.to_gmt_offset(self, int gmt_offset) {
return { dt, gmt_offset }; return { dt, gmt_offset };
} }
fn bool TzDateTime.eq(self, TzDateTime other) @operator(==) @inline
{
return self.to_gmt_offset(0).time == other.to_gmt_offset(0).time;
}
<* <*
@require day >= 1 && day < 32 @require day >= 1 && day < 32
@require hour >= 0 && hour < 24 @require hour >= 0 && hour < 24
@@ -262,5 +257,3 @@ fn Duration DateTime.diff_us(self, DateTime from) @operator(-)
{ {
return self.time.diff_us(from.time); return self.time.diff_us(from.time);
} }
fn bool DateTime.eq(self, DateTime other) @operator(==) @inline => self.time == other.time;

View File

@@ -12,10 +12,10 @@ fn Clock native_clock()
ulong mult = 0; ulong mult = 0;
if (!freq.quadPart) if (!freq.quadPart)
{ {
if (!win32::queryPerformanceFrequency(&freq)) return (Clock)0; if (!win32::queryPerformanceFrequency(&freq)) return 0;
} }
Win32_LARGE_INTEGER counter @noinit; Win32_LARGE_INTEGER counter @noinit;
if (!win32::queryPerformanceCounter(&counter)) return (Clock)0; if (!win32::queryPerformanceCounter(&counter)) return 0;
return (Clock)counter.quadPart.muldiv(1_000_000_000, freq.quadPart); return (Clock)counter.quadPart.muldiv(1_000_000_000, freq.quadPart);
} }

View File

@@ -1,15 +1,13 @@
module std::time; module std::time;
import std::io, std::time::os; import std::io, std::time::os;
typedef Time @structlike = long; typedef Time = long;
typedef Duration @structlike = long; typedef Duration = long;
typedef Clock @structlike = ulong; typedef Clock = ulong;
typedef NanoDuration (Printable) @structlike = long; typedef NanoDuration (Printable) = long;
const Time FAR_FUTURE = long.max; const Time FAR_FUTURE = long.max;
const Time FAR_PAST = long.min; const Time FAR_PAST = long.min;
const NanoDuration NANO_DURATION_ZERO = 0;
const Duration US = 1; const Duration US = 1;
const Duration MS = 1_000; const Duration MS = 1_000;
const Duration SEC = 1_000_000; const Duration SEC = 1_000_000;
@@ -20,7 +18,6 @@ const Duration WEEK = 7 * DAY;
const Duration MONTH = 30 * DAY; const Duration MONTH = 30 * DAY;
const Duration YEAR = 36525 * DAY / 100; const Duration YEAR = 36525 * DAY / 100;
const Duration FOREVER = long.max; const Duration FOREVER = long.max;
const Duration DURATION_ZERO = 0;
fn Duration us(long l) @inline => l * US; fn Duration us(long l) @inline => l * US;
fn Duration ms(long l) @inline => l * MS; fn Duration ms(long l) @inline => l * MS;
@@ -81,7 +78,7 @@ fn Time now()
$if $defined(native_timestamp): $if $defined(native_timestamp):
return os::native_timestamp(); return os::native_timestamp();
$else $else
return (Time)0; return 0;
$endif $endif
} }
@@ -117,31 +114,31 @@ macro Duration Duration.mult(#td, long #val) @operator_s(*) @safemacro => (Durat
fn usz? NanoDuration.to_format(&self, Formatter* formatter) @dynamic fn usz? NanoDuration.to_format(&self, Formatter* formatter) @dynamic
{ {
NanoDuration nd = *self; NanoDuration nd = *self;
if (nd == NANO_DURATION_ZERO) if (nd == 0)
{ {
return formatter.printf("0s")!; return formatter.printf("0s")!;
} }
bool neg = nd < NANO_DURATION_ZERO; bool neg = nd < 0;
if (neg) nd = -nd; if (neg) nd = -nd;
DString str = dstring::temp_with_capacity(64); DString str = dstring::temp_with_capacity(64);
if (nd < (NanoDuration)1_000_000_000) if (nd < 1_000_000_000)
{ {
// Less than 1s: print milliseconds, microseconds and nanoseconds. // Less than 1s: print milliseconds, microseconds and nanoseconds.
NanoDuration ms = nd / 1_000_000; NanoDuration ms = nd / 1_000_000;
if (ms > NANO_DURATION_ZERO) if (ms > 0)
{ {
str.appendf("%dms", ms); str.appendf("%dms", ms);
nd -= ms * 1_000_000; nd -= ms * 1_000_000;
} }
NanoDuration us = nd / 1000; NanoDuration us = nd / 1000;
if (us > NANO_DURATION_ZERO) if (us > 0)
{ {
str.appendf("%dµs", us); str.appendf("%dµs", us);
nd -= us * 1000; nd -= us * 1000;
} }
if (nd > NANO_DURATION_ZERO) if (nd > 0)
{ {
str.appendf("%dns", nd); str.appendf("%dns", nd);
} }
@@ -153,19 +150,19 @@ fn usz? NanoDuration.to_format(&self, Formatter* formatter) @dynamic
nd /= 1_000_000_000; nd /= 1_000_000_000;
NanoDuration hour = nd / 3600; NanoDuration hour = nd / 3600;
if (hour > NANO_DURATION_ZERO) if (hour > 0)
{ {
str.appendf("%dh", hour); str.appendf("%dh", hour);
nd -= hour * 3600; nd -= hour * 3600;
} }
NanoDuration min = nd / 60; NanoDuration min = nd / 60;
if (min > NANO_DURATION_ZERO) if (min > 0)
{ {
str.appendf("%dm", min); str.appendf("%dm", min);
nd -= min * 60; nd -= min * 60;
} }
NanoDuration sec = nd; NanoDuration sec = nd;
if (ms > NANO_DURATION_ZERO) if (ms > 0)
{ {
// Ignore trailing zeroes. // Ignore trailing zeroes.
while (ms / 10 * 10 == ms) ms /= 10; while (ms / 10 * 10 == ms) ms /= 10;

View File

@@ -141,7 +141,7 @@ resource = first(tools["localizedResources"], lambda x: x["language"] == "en-us"
license = resource["license"] license = resource["license"]
if not args.accept_license: if not args.accept_license:
accept = input(f"Do you accept Visual Studio license at {license} [Y/N] ?") accept = input(f"Do you accept Visual Studio license at {license}, and also confirm that you have a valid license Visual Studio license allowing you to download the VS Build Tools [Y/N] ?")
if not accept or accept[0].lower() != "y": if not accept or accept[0].lower() != "y":
exit(0) exit(0)

View File

@@ -1,148 +1,5 @@
# C3C Release Notes # C3C Release Notes
## 0.7.5 Change list
### Changes / improvements
- Support `alias foo = module std::io` module aliasing.
- Add compile-time `@intlog2` macro to math.
- Add compile-time `@clz` builtin. #2367
- Add `bitsizeof` macro builtins. #2376
### Fixes
- List.remove_at would incorrectly trigger ASAN.
- With avx512, passing a 512 bit vector in a union would be lowered incorrectly, causing an assert. #2362
- Codegen error in `if (try x = (true ? io::EOF? : 1))`, i.e. using if-try with a known Empty.
- Codegen error in `if (try x = (false ? io::EOF? : 1))`, i.e. using if-try with a CT known value.
- Reduce allocated Vmem for the compiler on 32 bit machines.
- Bug causing a compiler error when parsing a broken lambda inside of an expression.
- Fixed: regression in comments for `@deprecated` and `@pure`.
- Detect recursive creation of generics #2366.
- Compiler assertion when defining a function with return type untyped_list #2368.
- Compiler assert when using generic parameters list without any parameters. #2369
- Parsing difference between "0x00." and "0X00." literals #2371
- Fixed bug generating `$c += 1` when `$c` was derived from a pointer but behind a cast.
- Compiler segfault when using bitwise not on number literal cast to bitstruct #2373.
- Formatter did not properly handle "null" for any, and null for empty faults. #2375
- Bitstructs no longer overloadable with bitops. #2374
### Stdlib changes
- Add `==` to `Pair`, `Triple` and TzDateTime. Add print to `Pair` and `Triple`.
- Add OpenBSD to `env::INET_DEVICES` and add required socket constants.
- Added `FileMmap` to manage memory mapped files.
- Add `vm::mmap_file` to memory map a file.
- Updated hash functions in default hash methods.
- Added `FixedBlockPool` which is a memory pool for fixed size blocks.
- Added the experimental `std::core::log` for logging.
## 0.7.4 Change list
### Changes / improvements
- Added const enums: `enum Foo : const`. Behaves like C enums but may be any type.
- Casting to / from an enum is now possible again. No need to use `.ordinal` and `.from_ordinal`.
- Inline associated enum values are deprecated, use `--use-old-enums` to re-enable them.
- `$typeof` may return a compile time type.
- Improved error messages on missing qualifier on enum value. #2260
- Add `--echo-prefix` to edit the prefix with `$echo` statements. Supports {FILE} and {LINE}
- Catch accidental `foo == BAR;` where `foo = BAR;` was most likely intended. #2274
- Improve error message when doing a rethrow in a function that doesn't return an optional.
- Add `--list-asm` to view all supported `asm` instructions.
- Formatting option "%h" now supports pointers.
- Improve error on unsigned implicit conversion to signed.
- Update error message for struct initialization #2286
- Add SipHash family of keyed PRFs. #2287
- `$is_const` is deprecated in favour of `@is_const` based on `$defined`.
- Multiline contract comments #2113
- Removed the use of temp allocator in backtrace printing.
- `env::AUTHORS` and `env::AUTHOR_EMAILS` added.
- Suppress codegen of panic printing with when panic messages are set to "off".
- Implicit linking of libc math when libc math functions are used.
- Allow even smaller memory limits.
- Check unaligned array access.
- Add "@structlike" for typedefs.
- "poison" the current function early when a declaration can't be correctly resolved.
- Add komihash, a5hash, metrohash64, metrohash128, and wyhash2 variants with tests/benchmark. #2293
- '$assignable' is deprecated.
- Deprecate allocator::heap() and allocator::temp()
- Add `thread::fence` providing a thread fence.
- Place output in `out` by default for projects. Use temp folder for building at the command line.
- Allow absolute paths for `$embed`.
- Add `@try` and `@try_catch`.
- Assignment evaluation order now right->left, following C++17 and possibly C23.
### Fixes
- mkdir/rmdir would not work properly with substring paths on non-windows platforms.
- Hex string formatter check incorrectly rejected slices.
- Correctly reject interface methods `type` and `ptr`.
- Comparing a null ZString with a non-null ZString would crash.
- Switch case with const non-int / enum would be treated as ints and crash. #2263
- Missing bounds check on upper bound with const ranges `foo[1:3]`.
- Check up the hierarchy when considering if an interface cast is valid #2267.
- Fix issue with labelled break inside of a $switch.
- Non-const macros may not return untyped lists.
- `$for` ct-state not properly popped.
- Inline `r / complex` for complex numbers fixed.
- Const slice lengths were not always detected as constant.
- Const slice indexing was not bounds checked.
- Initialize pool correctly in print_backtrace.
- `--max-mem` now works correctly again.
- Casting a fault to a pointer would trigger an assert.
- Make `to_float` more tolerant to spaces.
- Fixes to thread local pointer handling.
- Fixes to JSON parsing and Object.
- Array indices are now using int64 internally.
- Bit shift operation fails with inline uint enum despite matching underlying type #2279.
- Fix to codegen when using a bitstruct constant defined using a cast with an operator #2248.
- Function pointers are now compile time constants.
- Splat 8 arguments can sometimes cause incorrect behaviour in the compiler. #2283
- Correctly poison the analysis after a failed $assert or $error. #2284
- `$foo` variables could be assigned non-compile time values.
- `$foo[0] = ...` was incorrectly requiring that the assigned values were compile time constants.
- "Inlined at" would sometimes show the current location.
- Fixed bug splatting constants into constants.
- Resize bug when resizing memory down in ArenaAllocator, DynamicArenaAllocator, BackedArenaAllocator.
- Error message for missing arg incorrect for methods with zero args #2296.
- Fix stringify of $vaexpr #2301.
- Segfault when failing to cast subexpression to 'isz' in pointer subtraction #2305.
- Fix unexpected display of macro definition when passing a poisoned expression #2305.
- `@links` on macros would not be added to calling functions.
- Fix `Formatter.print` returning incorrect size.
- A distinct type based on an array would yield .len == 0
- Overloading addition with a pointer would not work.
- Copying const enums and regular enums incorrect #2313.
- Regression: Chaining an optional together with contracts could in some cases lose the optional.
- `char[*] b = *(char[*]*)&a;` would crash the compiler if `a` was a slice. #2320
- Implicitly cast const int expressions would sometimes not be detected as compile time const.
- Using @noreturn in a short body macro would not work properly #2326.
- Bug when reporting error in a macro return would crash the compiler #2326.
- Short body return expression would not have the correct span.
- Fix issue where recursively creating a dir would be incorrectly marked as a failure the first time.
- `@format` did not work correctly with macros #2341.
- Crash when parsing recursive type declaration #2345.
- Remove unnecessary "ret" in naked functions #2344.
- Lambdas now properly follow its attributes #2346.
- Not setting android-ndk resulted in a "set ndk-path" error.
- Lambda deduplication would be incorrect when generated at the global scope.
- Disallow accessing parameters in a naked function, as well as `return`, this fixes #1955.
- Assigning string literal to char[<*>] stores pointer rather than characters. #2357
### Stdlib changes
- Improve contract for readline. #2280
- Added Whirlpool hash.
- Added Ed25519.
- Added string::bformat.
- Virtual memory library.
- New virtual emory arena allocator.
- Added `WString.len`.
- Added `@addr` macro.
- Add `ConditionVariable.wait_until` and `ConditionVariable.wait_for`
- Added readline_to_stream that takes a stream.
- Added `Ref` and `RefCounted` experimental functionality.
- Added `Volatile` generic type.
- Added `UnalignedRef` generic type.
- Added `HashSet` generic type.
- Added `LinkedHashSet` and `LinkedHashMap` generic types.
- Add String conversion functions snake_case -> PascalCase and vice versa.
## 0.7.3 Change list ## 0.7.3 Change list
### Changes / improvements ### Changes / improvements
@@ -173,7 +30,6 @@
- Compile-time comparison of constant vectors. #1575. - Compile-time comparison of constant vectors. #1575.
- $member.get supports bitstructs. - $member.get supports bitstructs.
- $member.set for setting members without the *& trick. - $member.set for setting members without the *& trick.
- Initial support for #1925, does not affect C compilation yet, and doesn't try to link etc. Using "--emit-only"
### Fixes ### Fixes
- `-2147483648`, MIN literals work correctly. - `-2147483648`, MIN literals work correctly.
@@ -223,7 +79,6 @@
- `is_array_or_slice_of_char` and `is_arrayptr_or_slice_of_char` are replaced by constant `@` variants. - `is_array_or_slice_of_char` and `is_arrayptr_or_slice_of_char` are replaced by constant `@` variants.
- `@pool` now has an optional `reserve` parameter, some minor changes to the temp_allocator API - `@pool` now has an optional `reserve` parameter, some minor changes to the temp_allocator API
- io::struct_to_format now supports bitstructs. - io::struct_to_format now supports bitstructs.
- Add `String.escape`, `String.unescape` for escaping and unescaping a string.
## 0.7.2 Change list ## 0.7.2 Change list
@@ -251,7 +106,6 @@
- Add deprecation for `@param foo "abc"`. - Add deprecation for `@param foo "abc"`.
- Add `--header-output` and `header-output` options for controlling header output folder. - Add `--header-output` and `header-output` options for controlling header output folder.
- Generic faults is disallowed. - Generic faults is disallowed.
- Detect when a slice on the stack is accidentally returned from a function.
### Fixes ### Fixes
- Assert triggered when casting from `int[2]` to `uint[2]` #2115 - Assert triggered when casting from `int[2]` to `uint[2]` #2115

View File

@@ -3,8 +3,6 @@ import std;
import bar; import bar;
import clib; import clib;
import clib2; import clib2;
import std::core::env;
fn int test_doubler(int x) @if(env::WIN32) => x * x; fn int test_doubler(int x) @if(env::WIN32) => x * x;
extern fn int test_doubler(int) @if(!env::WIN32); extern fn int test_doubler(int) @if(!env::WIN32);
@@ -13,11 +11,6 @@ extern fn void printf(char *, ...);
fn int main() fn int main()
{ {
printf("Hello World!\n"); printf("Hello World!\n");
printf("Authors:");
io::printn(env::AUTHORS);
printf("Author emails:");
io::printn(env::AUTHOR_EMAILS);
bar::test(); bar::test();
printf("Hello double: %d\n", test_doubler(11)); printf("Hello double: %d\n", test_doubler(11));
if ($feature(ABCD)) io::printn("ABCD"); if ($feature(ABCD)) io::printn("ABCD");

View File

@@ -484,7 +484,6 @@ typedef struct BuildOptions_
int api_version; int api_version;
} android; } android;
int build_threads; int build_threads;
const char *echo_prefix;
const char **libraries_to_fetch; const char **libraries_to_fetch;
const char **files; const char **files;
const char *test_filter; const char *test_filter;
@@ -567,7 +566,6 @@ typedef struct BuildOptions_
const char *header_out; const char *header_out;
const char *obj_out; const char *obj_out;
const char *script_dir; const char *script_dir;
const char **emit_only;
RelocModel reloc_model; RelocModel reloc_model;
X86VectorCapability x86_vector_capability; X86VectorCapability x86_vector_capability;
X86CpuSet x86_cpu_set; X86CpuSet x86_cpu_set;
@@ -594,17 +592,10 @@ typedef struct BuildOptions_
bool print_precedence; bool print_precedence;
bool print_linking; bool print_linking;
bool print_env; bool print_env;
bool print_asm;
bool benchmarking; bool benchmarking;
bool testing; bool testing;
} BuildOptions; } BuildOptions;
typedef struct
{
const char *author;
const char *email;
} AuthorEntry;
typedef struct typedef struct
{ {
struct Library__ *parent; struct Library__ *parent;
@@ -657,7 +648,6 @@ typedef struct
const char **linker_libdirs; const char **linker_libdirs;
const char **linker_libs; const char **linker_libs;
const char *cpu; const char *cpu;
const char *echo_prefix;
const char **link_args; const char **link_args;
const char *build_dir; const char *build_dir;
const char *object_file_dir; const char *object_file_dir;
@@ -667,7 +657,6 @@ typedef struct
const char *header_file_dir; const char *header_file_dir;
const char *script_dir; const char *script_dir;
const char *run_dir; const char *run_dir;
const char **emit_only;
bool is_non_project; bool is_non_project;
bool run_after_compile; bool run_after_compile;
bool delete_after_run; bool delete_after_run;
@@ -698,7 +687,6 @@ typedef struct
bool print_stats; bool print_stats;
bool old_slice_copy; bool old_slice_copy;
bool old_enums; bool old_enums;
bool single_threaded;
int build_threads; int build_threads;
TrustLevel trust_level; TrustLevel trust_level;
OptimizationSetting optsetting; OptimizationSetting optsetting;
@@ -735,7 +723,6 @@ typedef struct
const char **csources; const char **csources;
const char **cinclude_dirs; const char **cinclude_dirs;
const char **exec; const char **exec;
AuthorEntry *authors;
const char **feature_list; const char **feature_list;
const char *custom_linker_path; const char *custom_linker_path;
struct struct

View File

@@ -50,7 +50,7 @@ static const char *optsizes[3] = {
[SIZE_OPTIMIZATION_TINY] = "tiny", [SIZE_OPTIMIZATION_TINY] = "tiny",
}; };
static const char *linker_kind[3] = { static const char *linker[3] = {
[LINKER_TYPE_BUILTIN] = "builtin", [LINKER_TYPE_BUILTIN] = "builtin",
[LINKER_TYPE_CC] = "cc", [LINKER_TYPE_CC] = "cc",
[LINKER_TYPE_CUSTOM] = "custom" [LINKER_TYPE_CUSTOM] = "custom"

View File

@@ -106,7 +106,6 @@ static void usage(bool full)
print_opt("--path <dir>", "Use this as the base directory for the current command."); print_opt("--path <dir>", "Use this as the base directory for the current command.");
print_opt("--template <template>", "Select template for 'init': \"exe\", \"static-lib\", \"dynamic-lib\" or a path."); print_opt("--template <template>", "Select template for 'init': \"exe\", \"static-lib\", \"dynamic-lib\" or a path.");
print_opt("--symtab <value>", "Sets the preferred symtab size."); print_opt("--symtab <value>", "Sets the preferred symtab size.");
print_opt("--max-mem <value>", "Set the maximum memory size (in MB).");
print_opt("--run-once", "After running the output file, delete it immediately."); print_opt("--run-once", "After running the output file, delete it immediately.");
print_opt("--suppress-run", "Build but do not run on test/benchmark options."); print_opt("--suppress-run", "Build but do not run on test/benchmark options.");
print_opt("--trust=<option>", "Trust level: none (default), include ($include allowed), full ($exec / exec allowed)."); print_opt("--trust=<option>", "Trust level: none (default), include ($include allowed), full ($exec / exec allowed).");
@@ -141,7 +140,6 @@ static void usage(bool full)
{ {
PRINTF(""); PRINTF("");
print_opt("--ansi=<yes|no>", "Set colour output using ansi on/off, default is to try to detect it."); print_opt("--ansi=<yes|no>", "Set colour output using ansi on/off, default is to try to detect it.");
print_opt("--echo-prefix <arg>", "Sets the prefix for any output using '$echo', defaults to 'c3c:'.");
print_opt("--test-filter <arg>", "Set a filter when running tests, running only matching tests."); print_opt("--test-filter <arg>", "Set a filter when running tests, running only matching tests.");
print_opt("--test-breakpoint", "When running tests, trigger a breakpoint on failure."); print_opt("--test-breakpoint", "When running tests, trigger a breakpoint on failure.");
print_opt("--test-nosort", "Do not sort tests."); print_opt("--test-nosort", "Do not sort tests.");
@@ -161,7 +159,6 @@ static void usage(bool full)
print_opt("--use-stdlib=<yes|no>", "Include the standard library (default: yes)."); print_opt("--use-stdlib=<yes|no>", "Include the standard library (default: yes).");
print_opt("--link-libc=<yes|no>", "Link libc other default libraries (default: yes)."); print_opt("--link-libc=<yes|no>", "Link libc other default libraries (default: yes).");
print_opt("--emit-stdlib=<yes|no>", "Output files for the standard library. (default: yes)"); print_opt("--emit-stdlib=<yes|no>", "Output files for the standard library. (default: yes)");
print_opt("--emit-only <file>", "Output only the file matching <file>.");
print_opt("--panicfn <name>", "Override the panic function name."); print_opt("--panicfn <name>", "Override the panic function name.");
print_opt("--testfn <name>", "Override the test runner function name."); print_opt("--testfn <name>", "Override the test runner function name.");
print_opt("--benchfn <name>", "Override the benchmark runner function name."); print_opt("--benchfn <name>", "Override the benchmark runner function name.");
@@ -191,7 +188,6 @@ static void usage(bool full)
print_opt("--list-manifest-properties", "List all available keys used in manifest.json files."); print_opt("--list-manifest-properties", "List all available keys used in manifest.json files.");
print_opt("--list-targets", "List all architectures the compiler supports."); print_opt("--list-targets", "List all architectures the compiler supports.");
print_opt("--list-type-properties", "List all type properties."); print_opt("--list-type-properties", "List all type properties.");
print_opt("--list-asm", "List all asm instructions for the current target.");
PRINTF(""); PRINTF("");
print_opt("--print-output", "Print the object files created to stdout."); print_opt("--print-output", "Print the object files created to stdout.");
print_opt("--print-input", "Print inputted C3 files to stdout."); print_opt("--print-input", "Print inputted C3 files to stdout.");
@@ -728,18 +724,6 @@ static void parse_option(BuildOptions *options)
options->ansi = parse_opt_select(Ansi, argopt, on_off); options->ansi = parse_opt_select(Ansi, argopt, on_off);
return; return;
} }
if (match_longopt("max-mem"))
{
if (at_end() || next_is_opt())
{
FAIL_WITH_ERR_LONG("'--max-mem' expected a max memory.");
}
if (atoll(next_arg()) < 1)
{
FAIL_WITH_ERR_LONG("'--max-mem' expected a positive integer value.");
}
return;
}
if (match_longopt("sources")) if (match_longopt("sources"))
{ {
if (at_end() || next_is_opt()) if (at_end() || next_is_opt())
@@ -759,7 +743,7 @@ static void parse_option(BuildOptions *options)
options->old_slice_copy = true; options->old_slice_copy = true;
return; return;
} }
if (match_longopt("use-old-enums")) if (match_longopt("use-new-enums"))
{ {
options->old_enums = true; options->old_enums = true;
return; return;
@@ -878,7 +862,7 @@ static void parse_option(BuildOptions *options)
if ((argopt = match_argopt("linker"))) if ((argopt = match_argopt("linker")))
{ {
options->custom_linker_path = NULL; options->custom_linker_path = NULL;
options->linker_type = parse_opt_select(LinkerType, argopt, linker_kind); options->linker_type = parse_opt_select(LinkerType, argopt, linker);
if (options->linker_type == LINKER_TYPE_CUSTOM) if (options->linker_type == LINKER_TYPE_CUSTOM)
{ {
if (at_end() || next_is_opt()) error_exit("error: --linker=custom expects a valid linker name."); if (at_end() || next_is_opt()) error_exit("error: --linker=custom expects a valid linker name.");
@@ -901,12 +885,6 @@ static void parse_option(BuildOptions *options)
options->emit_stdlib = parse_opt_select(EmitStdlib, argopt, on_off); options->emit_stdlib = parse_opt_select(EmitStdlib, argopt, on_off);
return; return;
} }
if (match_longopt("emit-only"))
{
if (at_end() || next_is_opt()) error_exit("error: --emit-only expects an output name, e.g. 'foo', to only output 'foo.o'.");
vec_add(options->emit_only, next_arg());
return;
}
if ((argopt = match_argopt("use-stdlib"))) if ((argopt = match_argopt("use-stdlib")))
{ {
options->use_stdlib = parse_opt_select(UseStdlib, argopt, on_off); options->use_stdlib = parse_opt_select(UseStdlib, argopt, on_off);
@@ -937,10 +915,10 @@ static void parse_option(BuildOptions *options)
options->riscv_float_capability = parse_opt_select(RiscvFloatCapability, argopt, riscv_capability); options->riscv_float_capability = parse_opt_select(RiscvFloatCapability, argopt, riscv_capability);
return; return;
} }
if (match_longopt("max-vector-size")) if ((argopt = match_argopt("max-vector-size")))
{ {
int size = (at_end() || next_is_opt()) ? 0 : atoi(next_arg()); int size = atoi(next_arg());
if (size < 128) error_exit("Expected a valid positive integer >= 128 for --max-vector-size."); if (size < 128) error_exit("Expected a valid positive integer >= 128 or --max-vector-size.");
if (size > MAX_VECTOR_WIDTH) error_exit("Expected a valid positive integer <= %u for --max-vector-size.", (unsigned)MAX_VECTOR_WIDTH); if (size > MAX_VECTOR_WIDTH) error_exit("Expected a valid positive integer <= %u for --max-vector-size.", (unsigned)MAX_VECTOR_WIDTH);
if (size != next_highest_power_of_2(size)) if (size != next_highest_power_of_2(size))
{ {
@@ -1008,11 +986,6 @@ static void parse_option(BuildOptions *options)
options->command = COMMAND_PRINT_SYNTAX; options->command = COMMAND_PRINT_SYNTAX;
return; return;
} }
if (match_longopt("list-asm"))
{
options->print_asm = true;
return;
}
if (match_longopt("list-project-properties")) if (match_longopt("list-project-properties"))
{ {
options->print_project_properties = true; options->print_project_properties = true;
@@ -1047,12 +1020,6 @@ static void parse_option(BuildOptions *options)
options->build_threads = threads; options->build_threads = threads;
return; return;
} }
if (match_longopt("echo-prefix"))
{
if (at_end() || next_is_opt()) error_exit("error: --echo-prefix needs a prefix.");
options->echo_prefix = next_arg();
return;
}
if (match_longopt("target")) if (match_longopt("target"))
{ {
if (at_end() || next_is_opt()) error_exit("error: --target needs a arch+os definition."); if (at_end() || next_is_opt()) error_exit("error: --target needs a arch+os definition.");
@@ -1362,7 +1329,7 @@ static void parse_option(BuildOptions *options)
break; break;
} }
FAIL_WITH_ERR_LONG("Cannot process the unknown option \"%s\".", current_arg); FAIL_WITH_ERR("Cannot process the unknown option \"%s\".", current_arg);
} }
BuildOptions parse_arguments(int argc, const char *argv[]) BuildOptions parse_arguments(int argc, const char *argv[])
@@ -1473,7 +1440,7 @@ BuildOptions parse_arguments(int argc, const char *argv[])
const char *ndk_path = getenv("ANDROID_NDK"); const char *ndk_path = getenv("ANDROID_NDK");
if (!ndk_path) if (!ndk_path)
{ {
FAIL_WITH_ERR("Can't find Android NDK, please set --android-ndk."); FAIL_WITH_ERR("Can't find Android NDK, please set --ndk-path.");
} }
build_options.android.ndk_path = strdup(ndk_path); build_options.android.ndk_path = strdup(ndk_path);
} }

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