mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
Compare commits
123 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a3cdc01ec | ||
|
|
808ce6d605 | ||
|
|
09606a2998 | ||
|
|
0a21323652 | ||
|
|
c990b5aaf1 | ||
|
|
98e0985a10 | ||
|
|
5974bf6cd0 | ||
|
|
992890065a | ||
|
|
d4dbe27072 | ||
|
|
56a11ae3ca | ||
|
|
f1d03c194f | ||
|
|
0085fe73ad | ||
|
|
a238b62483 | ||
|
|
58022e7cca | ||
|
|
d2f046780d | ||
|
|
ec65c5761e | ||
|
|
0ed917cdc2 | ||
|
|
8373ab0509 | ||
|
|
8f7610345d | ||
|
|
8bd963ecaf | ||
|
|
dc52478c09 | ||
|
|
e1ec4b1235 | ||
|
|
7c68396c0d | ||
|
|
eae7d0c4a1 | ||
|
|
5055e86518 | ||
|
|
4b13ad692a | ||
|
|
7c81bb35ca | ||
|
|
5a82f672b5 | ||
|
|
0387d7666d | ||
|
|
392c78860d | ||
|
|
585c66100d | ||
|
|
8bb974829d | ||
|
|
6b3139940c | ||
|
|
9efb9b90d1 | ||
|
|
152558f5bc | ||
|
|
7ae4c5a1ab | ||
|
|
9fbf6bc213 | ||
|
|
143fa70f87 | ||
|
|
3ac701be0e | ||
|
|
8cb23cff29 | ||
|
|
ec6ba8e7ca | ||
|
|
df030ac51c | ||
|
|
4b03a84b00 | ||
|
|
5d0c41da6b | ||
|
|
990e9685d2 | ||
|
|
a7309b217e | ||
|
|
d7cf8fa9ab | ||
|
|
a78a169a17 | ||
|
|
d80a8629a6 | ||
|
|
14779bd467 | ||
|
|
eb80776988 | ||
|
|
fac9054f1b | ||
|
|
768d24d580 | ||
|
|
e299a4b630 | ||
|
|
bbf89815d6 | ||
|
|
cc6d552e3d | ||
|
|
27ceded331 | ||
|
|
3f279b2f1c | ||
|
|
acc4a900f5 | ||
|
|
f079fa82b2 | ||
|
|
7665720264 | ||
|
|
83705ab340 | ||
|
|
adea3dd83f | ||
|
|
9b52be9ba6 | ||
|
|
202349d88f | ||
|
|
18ab18958b | ||
|
|
2237d3b836 | ||
|
|
e2f17a770b | ||
|
|
40e6a2c4a3 | ||
|
|
a80e40a798 | ||
|
|
de8a733c77 | ||
|
|
c5d7a03859 | ||
|
|
0b82537700 | ||
|
|
7b8299c8dd | ||
|
|
dcbd0f8f2d | ||
|
|
402c00e230 | ||
|
|
0542c05b88 | ||
|
|
67e224c62f | ||
|
|
565b08846f | ||
|
|
80ad0e02ad | ||
|
|
b8ee5a4150 | ||
|
|
445dd155b7 | ||
|
|
abb3efca00 | ||
|
|
de0b317461 | ||
|
|
21758476d4 | ||
|
|
b69f78be30 | ||
|
|
89b9f52f1e | ||
|
|
02a67254cc | ||
|
|
120f56ac5f | ||
|
|
5b93212f43 | ||
|
|
c57415ea78 | ||
|
|
36dfbdff45 | ||
|
|
d07e454dfe | ||
|
|
40aa4d4dcd | ||
|
|
0be291e0d7 | ||
|
|
35d84390bd | ||
|
|
124efb2684 | ||
|
|
5c158e481b | ||
|
|
42b79d19c1 | ||
|
|
4521a25284 | ||
|
|
1933d47ba1 | ||
|
|
bb9e9b54cf | ||
|
|
eddbfb8ba3 | ||
|
|
2c55d6e220 | ||
|
|
472c49de25 | ||
|
|
016254d38c | ||
|
|
054fcd2deb | ||
|
|
d71aa10f62 | ||
|
|
1d0aef4522 | ||
|
|
71b673d241 | ||
|
|
dcf695c726 | ||
|
|
5e656603a5 | ||
|
|
b4426c095b | ||
|
|
5f32c97094 | ||
|
|
1c8cb7fa11 | ||
|
|
6bc606a9b1 | ||
|
|
d509b4caa1 | ||
|
|
12975d07ac | ||
|
|
3d512abaf7 | ||
|
|
a9d93c93d5 | ||
|
|
1b7601fdbb | ||
|
|
373013046d | ||
|
|
10d369d766 |
426
.github/workflows/main.yml
vendored
426
.github/workflows/main.yml
vendored
@@ -8,13 +8,13 @@ on:
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
LLVM_RELEASE_VERSION_WINDOWS: 21.1.8
|
||||
LLVM_RELEASE_VERSION_MAC: 21
|
||||
LLVM_RELEASE_VERSION_LINUX: 21
|
||||
LLVM_RELEASE_VERSION: 21.x
|
||||
LLVM_RELEASE_VERSION_LINUX_MUSL: 20
|
||||
LLVM_RELEASE_VERSION_OPENBSD: 20
|
||||
LLVM_RELEASE_VERSION_NETBSD: 19
|
||||
LLVM_DEV_VERSION: 22
|
||||
# CACHE_INVALIDATION_SEED: '%Y-%m-%d' # Daily
|
||||
CACHE_INVALIDATION_SEED: '%G-%V-1' # Weekly
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
- uses: actions/cache@v5
|
||||
with:
|
||||
path: build/_deps
|
||||
key: ${{ runner.os }}-llvm-${{ env.LLVM_RELEASE_VERSION_WINDOWS }}-${{ matrix.build_type }}-${{ hashFiles('CMakeLists.txt', '.github/workflows/main.yml') }}
|
||||
key: ${{ runner.os }}-llvm-${{ env.LLVM_RELEASE_VERSION }}-${{ matrix.build_type }}-${{ hashFiles('CMakeLists.txt', '.github/workflows/main.yml') }}
|
||||
|
||||
# set up the environment for Ninja
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
- name: CMake Build
|
||||
run: |
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DC3_FETCH_LLVM=ON
|
||||
cmake --build build --config ${{ matrix.build_type }}
|
||||
|
||||
# We remove the GNU link tool so C3C picks up the MSVC link.exe
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
build_type: [Debug]
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
@@ -89,16 +89,19 @@ jobs:
|
||||
with:
|
||||
msystem: MINGW64
|
||||
update: false
|
||||
cache: true
|
||||
install: git binutils mingw-w64-x86_64-clang mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-python mingw-w64-x86_64-llvm mingw-w64-x86_64-llvm-libs
|
||||
|
||||
|
||||
- name: Install LLD
|
||||
run: |
|
||||
echo "Server = https://mirror.msys2.org/mingw/mingw64" > /etc/pacman.d/mirrorlist.mingw64
|
||||
pacman -Sy --noconfirm --needed \
|
||||
mingw-w64-x86_64-llvm \
|
||||
mingw-w64-x86_64-llvm-libs \
|
||||
mingw-w64-x86_64-lld \
|
||||
mingw-w64-x86_64-clang
|
||||
for i in 1 2; do \
|
||||
pacman -Sy --noconfirm --needed \
|
||||
mingw-w64-x86_64-llvm \
|
||||
mingw-w64-x86_64-llvm-libs \
|
||||
mingw-w64-x86_64-lld \
|
||||
mingw-w64-x86_64-clang && break || sleep 5; \
|
||||
done
|
||||
|
||||
- name: CMake Build
|
||||
run: |
|
||||
@@ -113,7 +116,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
build_type: [Debug]
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
@@ -123,6 +126,7 @@ jobs:
|
||||
with:
|
||||
msystem: CLANG64
|
||||
update: false
|
||||
cache: true
|
||||
install: git binutils mingw-w64-clang-x86_64-cmake mingw-w64-clang-x86_64-toolchain mingw-w64-clang-x86_64-python
|
||||
- name: CMake Build
|
||||
run: |
|
||||
@@ -132,91 +136,111 @@ jobs:
|
||||
run: ./scripts/tools/ci_tests.sh "./build/c3c"
|
||||
|
||||
build-linux:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [17, 18, 19, 20, 21, 22]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- run: sudo apt-get update && sudo apt-get install -y zlib1g zlib1g-dev python3 ninja-build curl libcurl4-openssl-dev
|
||||
- name: Install Clang ${{matrix.llvm_version}}
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
REPO_URL="http://apt.llvm.org/focal/ llvm-toolchain-focal"
|
||||
if [[ "${{matrix.llvm_version}}" != "${{env.LLVM_DEV_VERSION}}" ]]; then REPO_URL="$REPO_URL-${{matrix.llvm_version}}"; fi
|
||||
sudo add-apt-repository "deb $REPO_URL main"
|
||||
sudo apt-get update
|
||||
PKGS="clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev libpolly-${{matrix.llvm_version}}-dev"
|
||||
if [[ "${{matrix.llvm_version}}" < 18 ]]; then PKGS="$PKGS libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools"; fi
|
||||
sudo apt-get install -y $PKGS
|
||||
- name: Get current date context
|
||||
id: date
|
||||
run: echo "date=$(date +'${{ env.CACHE_INVALIDATION_SEED }}')" >> $GITHUB_OUTPUT
|
||||
- name: Cache APT archives
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: apt-cache
|
||||
key: apt-cache-${{ runner.os }}-${{ steps.date.outputs.date }}
|
||||
- run: |
|
||||
mkdir -p apt-cache/partial
|
||||
for i in 1 2 3; do sudo apt-get update && break || sleep 2; done
|
||||
for i in 1 2 3; do sudo apt-get install -y -o dir::cache::archives="$(pwd)/apt-cache" --fix-missing zlib1g zlib1g-dev python3 ninja-build curl libcurl4-openssl-dev opensbi qemu-system-misc u-boot-qemu gcc-riscv64-unknown-elf && break || sleep 2; done
|
||||
sudo chown -R $USER:$USER apt-cache
|
||||
- name: CMake Build
|
||||
run: |
|
||||
C3_LLVM_VER=${{matrix.llvm_version}}
|
||||
if [[ "${{matrix.llvm_version}}" -ge 18 && "${{matrix.llvm_version}}" != "${{env.LLVM_DEV_VERSION}}" ]]; then C3_LLVM_VER="${{matrix.llvm_version}}.1"; fi
|
||||
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
|
||||
-DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \
|
||||
-DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \
|
||||
-DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \
|
||||
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
|
||||
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
|
||||
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
|
||||
-DLLVM_ENABLE_LIBXML2=OFF \
|
||||
-DC3_LLVM_VERSION=$C3_LLVM_VER
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DC3_FETCH_LLVM=ON
|
||||
cmake --build build
|
||||
- name: Run Unified Tests
|
||||
run: ./scripts/tools/ci_tests.sh "./build/c3c"
|
||||
|
||||
- name: Embedded/QEMU Tests
|
||||
run: |
|
||||
sudo apt-get install -y opensbi qemu-system-misc u-boot-qemu gcc-riscv64-unknown-elf
|
||||
# Added `opensbi qemu-system-misc u-boot-qemu gcc-riscv64-unknown-elf` to the apt-get install command above
|
||||
cd resources/examples/embedded/riscv-qemu
|
||||
make C3C_PATH=../../../../build/ run
|
||||
|
||||
- name: Bundle & Upload (Linux)
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX
|
||||
run: |
|
||||
bash ./scripts/tools/package_build.sh "./build/c3c" "c3-linux-${{matrix.build_type}}" "tar"
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX
|
||||
with:
|
||||
name: c3-linux-${{matrix.build_type}}
|
||||
path: c3-linux-${{matrix.build_type}}.tar.gz
|
||||
|
||||
build-linux-alpine:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: alpine:3.22
|
||||
image: alpine:3.23
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [18, 19, 20]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- run: apk update && apk add zlib-dev zlib-static python3 samurai cmake curl-dev curl-static openssl-dev bash
|
||||
- name: Install Clang
|
||||
run: apk add "llvm${{matrix.llvm_version}}-dev" "llvm${{matrix.llvm_version}}-static" "llvm${{matrix.llvm_version}}-gtest" "llvm${{matrix.llvm_version}}-linker-tools" "lld${{matrix.llvm_version}}-dev" "clang${{matrix.llvm_version}}-dev" "clang${{matrix.llvm_version}}-static"
|
||||
|
||||
- name: Get current date context
|
||||
id: date
|
||||
run: echo "date=$(date +'${{ env.CACHE_INVALIDATION_SEED }}')" >> $GITHUB_OUTPUT
|
||||
|
||||
#https://wiki.alpinelinux.org/wiki/Local_APK_cache
|
||||
- name: Prepare real apk cache directory + symlink
|
||||
run: |
|
||||
mkdir -p /var/cache/apk
|
||||
ln -sf /var/cache/apk /etc/apk/cache
|
||||
|
||||
- name: Cache APK downloaded packages (daily)
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: /var/cache/apk
|
||||
key: apk-pkgs-${{ runner.os }}-alpine-3.23-${{ steps.date.outputs.date }}
|
||||
restore-keys: |
|
||||
apk-pkgs-${{ runner.os }}-alpine-3.23-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
V=${{env.LLVM_RELEASE_VERSION_LINUX_MUSL}}
|
||||
for i in 1 2 3; do apk update && break || sleep 2; done
|
||||
for i in 1 2 3; do \
|
||||
apk add \
|
||||
build-base bash git cmake ninja python3 linux-headers \
|
||||
tar zstd \
|
||||
zlib-dev curl-dev libffi-dev \
|
||||
llvm${V}-dev \
|
||||
llvm${V}-static \
|
||||
llvm${V}-gtest \
|
||||
clang${V}-dev \
|
||||
clang${V}-static \
|
||||
lld${V}-dev \
|
||||
lld${V}-libs && break || sleep 2; \
|
||||
done
|
||||
|
||||
- name: CMake Build
|
||||
run: |
|
||||
C3_LLVM_VER=${{matrix.llvm_version}}
|
||||
if [[ "${{matrix.llvm_version}}" -ge 18 && "${{matrix.llvm_version}}" != "${{env.LLVM_DEV_VERSION}}" ]]; then C3_LLVM_VER="${{matrix.llvm_version}}.1"; fi
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} -DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} -DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} -DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} -DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} -DLLVM_ENABLE_LIBXML2=OFF -DC3_LINK_DYNAMIC=ON -DC3_LLVM_VERSION=$C3_LLVM_VER
|
||||
cmake -B build -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.build_type }} \
|
||||
-DC3_FETCH_LLVM=OFF \
|
||||
-DLLVM_ENABLE_LIBXML2=OFF \
|
||||
-DC3_LINK_DYNAMIC=ON
|
||||
cmake --build build
|
||||
|
||||
- name: Run Unified Tests
|
||||
run: ./scripts/tools/ci_tests.sh "./build/c3c"
|
||||
|
||||
- name: Bundle & Upload (Alpine)
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX_MUSL
|
||||
run: bash ./scripts/tools/package_build.sh "./build/c3c" "c3-linux-musl-${{matrix.build_type}}" "tar"
|
||||
run: bash ./scripts/tools/package_build.sh "./build/c3c" "c3-linux-musl-${{ matrix.build_type }}" "tar"
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX_MUSL
|
||||
with:
|
||||
name: c3-linux-musl-${{matrix.build_type}}
|
||||
path: c3-linux-musl-${{matrix.build_type}}.tar.gz
|
||||
name: c3-linux-musl-${{ matrix.build_type }}
|
||||
path: c3-linux-musl-${{ matrix.build_type }}.tar.gz
|
||||
|
||||
build-mac:
|
||||
runs-on: macos-latest
|
||||
@@ -224,58 +248,23 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [17, 18, 19, 20, 21]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Download LLVM
|
||||
run: |
|
||||
brew_install() {
|
||||
for pkg in "$@"; do
|
||||
brew list "$pkg" &>/dev/null || brew install "$pkg"
|
||||
done
|
||||
}
|
||||
if [[ "${{ matrix.llvm_version }}" == "21" ]]; then
|
||||
brew_install llvm lld ninja curl
|
||||
echo "/opt/homebrew/opt/llvm/bin" >> $GITHUB_PATH
|
||||
echo "/opt/homebrew/opt/lld/bin" >> $GITHUB_PATH
|
||||
else
|
||||
brew_install llvm@${{ matrix.llvm_version }} ninja curl
|
||||
echo "/opt/homebrew/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
|
||||
if [[ "${{ matrix.llvm_version }}" -ge 19 ]]; then
|
||||
brew_install lld@${{ matrix.llvm_version }}
|
||||
echo "/opt/homebrew/opt/lld@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
|
||||
fi
|
||||
fi
|
||||
echo "CPATH=$(xcrun --show-sdk-path)/user/include" >> $GITHUB_ENV
|
||||
- name: CMake Build
|
||||
run: |
|
||||
C3_LLVM_VER=${{matrix.llvm_version}}
|
||||
if [[ "${{matrix.llvm_version}}" -ge 18 ]]; then C3_LLVM_VER="${{matrix.llvm_version}}.1"; fi
|
||||
if [[ "${{ matrix.llvm_version }}" == "21" ]]; then
|
||||
C3_LLD_DIR="/opt/homebrew/opt/lld/lib"
|
||||
C3_LLD_INCLUDE_DIR="/opt/homebrew/opt/lld/include"
|
||||
elif [[ "${{ matrix.llvm_version }}" -ge 19 ]]; then
|
||||
C3_LLD_DIR="/opt/homebrew/opt/lld@${{ matrix.llvm_version }}/lib"
|
||||
C3_LLD_INCLUDE_DIR="/opt/homebrew/opt/lld@${{ matrix.llvm_version }}/include"
|
||||
else
|
||||
C3_LLD_DIR=""
|
||||
C3_LLD_INCLUDE_DIR=""
|
||||
fi
|
||||
cmake -B build -G Ninja -DC3_LLVM_VERSION=$C3_LLVM_VER -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DC3_LLD_DIR=$C3_LLD_DIR -DC3_LLD_INCLUDE_DIR=$C3_LLD_INCLUDE_DIR
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DC3_FETCH_LLVM=ON
|
||||
cmake --build build
|
||||
- name: Run Unified Tests
|
||||
run: ./scripts/tools/ci_tests.sh "./build/c3c"
|
||||
|
||||
|
||||
- name: Build Lib (Mac)
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib -vv --trust=full
|
||||
|
||||
- name: Bundle & Upload (Mac)
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC
|
||||
run: bash ./scripts/tools/package_build.sh "./build/c3c" "c3-macos-${{matrix.build_type}}" "zip"
|
||||
- uses: actions/upload-artifact@v6
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC
|
||||
with:
|
||||
name: c3-macos-${{matrix.build_type}}
|
||||
path: c3-macos-${{matrix.build_type}}.zip
|
||||
@@ -285,36 +274,54 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
build_type: [Debug]
|
||||
version: ['7.8']
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Build, Test and Package in OpenBSD
|
||||
uses: vmactions/openbsd-vm@v1
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
sync: rsync
|
||||
usesh: true
|
||||
release: ${{ matrix.version }}
|
||||
prepare: pkg_add ninja cmake llvm%${{ env.LLVM_RELEASE_VERSION_OPENBSD }} bash
|
||||
# Combine all logic here so rsync copyback triggers at the end
|
||||
operating_system: openbsd
|
||||
version: ${{ matrix.version }}
|
||||
cpu_count: 4
|
||||
memory: 6G
|
||||
run: |
|
||||
export MALLOC_OPTIONS=j # Disable junk filling (it's faster)
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
# Install dependencies
|
||||
for i in 1 2 3; do
|
||||
sudo pkg_add ninja cmake llvm%${{ env.LLVM_RELEASE_VERSION_OPENBSD }} bash && break || sleep 5
|
||||
done
|
||||
|
||||
export MALLOC_OPTIONS=j
|
||||
|
||||
# Setup RAM Disk
|
||||
sudo mkdir -p /mnt/ram
|
||||
sudo mount_mfs -s $((2 * 1024 * 1024 * 2)) swap /mnt/ram
|
||||
sudo chown -R $(id -u):$(id -g) /mnt/ram
|
||||
export TMPDIR=/mnt/ram
|
||||
|
||||
# Copy source to RAM disk and switch to it
|
||||
cp -rp . /mnt/ram/
|
||||
cd /mnt/ram
|
||||
|
||||
# Build
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{matrix.build_type}}
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build build
|
||||
|
||||
# Run Unified Tests
|
||||
chmod +x scripts/tools/ci_tests.sh
|
||||
ulimit -d unlimited
|
||||
ulimit -d $(ulimit -Hd) || true
|
||||
./scripts/tools/ci_tests.sh "./build/c3c"
|
||||
|
||||
# Package
|
||||
chmod +x scripts/tools/package_build.sh
|
||||
./scripts/tools/package_build.sh "./build/c3c" "c3-openbsd-${{matrix.build_type}}" "tar"
|
||||
|
||||
# Move results back to workspace for GitHub rsync/upload
|
||||
mkdir -p "$GITHUB_WORKSPACE"/build
|
||||
cp build/c3c "$GITHUB_WORKSPACE"/build/
|
||||
cp *.tar.gz "$GITHUB_WORKSPACE"/
|
||||
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: c3-openbsd-${{matrix.build_type}}
|
||||
@@ -325,34 +332,49 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
build_type: [Debug]
|
||||
version: ['10.1']
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Build, Test and Package in NetBSD
|
||||
uses: vmactions/netbsd-vm@v1
|
||||
uses: cross-platform-actions/action@master
|
||||
with:
|
||||
sync: rsync
|
||||
usesh: true
|
||||
release: ${{ matrix.version }}
|
||||
prepare: |
|
||||
export PATH="/usr/pkg/sbin:/usr/pkg/bin:$PATH"
|
||||
export PKG_PATH="https://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/$(uname -m)/$(uname -r)/All/"
|
||||
/usr/sbin/pkg_add pkgin
|
||||
pkgin -y update
|
||||
pkgin -y install cmake gcc14 ninja-build bash \
|
||||
'llvm>=${{ env.LLVM_RELEASE_VERSION_NETBSD }}' \
|
||||
'clang>=${{ env.LLVM_RELEASE_VERSION_NETBSD }}' \
|
||||
'lld>=${{ env.LLVM_RELEASE_VERSION_NETBSD }}'
|
||||
operating_system: netbsd
|
||||
version: ${{ matrix.version }}
|
||||
cpu_count: 4
|
||||
memory: 6G
|
||||
run: |
|
||||
# Install dependencies
|
||||
export PATH="/usr/pkg/sbin:/usr/pkg/bin:$PATH"
|
||||
export PKG_PATH="https://ftp.netbsd.org/pub/pkgsrc/packages/NetBSD/$(uname -m)/$(uname -r)/All/"
|
||||
|
||||
# Ensure pkgin is available and updated
|
||||
sudo /usr/sbin/pkg_add -U pkgin || true
|
||||
|
||||
for i in 1 2 3; do
|
||||
sudo pkgin -y update && \
|
||||
sudo pkgin -y install cmake gcc14 ninja-build bash \
|
||||
'llvm>=${{ env.LLVM_RELEASE_VERSION_NETBSD }}' \
|
||||
'clang>=${{ env.LLVM_RELEASE_VERSION_NETBSD }}' \
|
||||
'lld>=${{ env.LLVM_RELEASE_VERSION_NETBSD }}' && break || sleep 5
|
||||
done
|
||||
|
||||
export CC=clang
|
||||
export CXX=clang++
|
||||
cd "$GITHUB_WORKSPACE"
|
||||
|
||||
# Setup RAM Disk
|
||||
sudo mkdir -p /mnt/ram
|
||||
sudo mount_tmpfs -s 2G tmpfs /mnt/ram
|
||||
sudo chown -R $(id -u):$(id -g) /mnt/ram
|
||||
export TMPDIR=/mnt/ram
|
||||
|
||||
# Copy source to RAM disk and switch to it
|
||||
cp -rp . /mnt/ram/
|
||||
cd /mnt/ram
|
||||
|
||||
# Build
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{matrix.build_type}}
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build build
|
||||
|
||||
# Run Unified Tests
|
||||
@@ -363,29 +385,72 @@ jobs:
|
||||
chmod +x scripts/tools/package_build.sh
|
||||
./scripts/tools/package_build.sh "./build/c3c" "c3-netbsd-${{matrix.build_type}}" "tar"
|
||||
|
||||
# Move results back to workspace for GitHub rsync/upload
|
||||
mkdir -p "$GITHUB_WORKSPACE"/build
|
||||
cp build/c3c "$GITHUB_WORKSPACE"/build/
|
||||
cp *.tar.gz "$GITHUB_WORKSPACE"/
|
||||
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: c3-netbsd-${{matrix.build_type}}
|
||||
path: c3-netbsd-${{matrix.build_type}}.tar.gz
|
||||
|
||||
build-with-docker:
|
||||
runs-on: ubuntu-22.04
|
||||
name: LLVM ${{ matrix.llvm_version }} Compatibility
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
ubuntu_version: [20.04, 22.04]
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [17, 18, 19]
|
||||
llvm_version: [17, 18, 19, 20]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: docker/setup-buildx-action@v3
|
||||
- name: Build
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build and cache Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: ./docker
|
||||
build-args: |
|
||||
LLVM_VERSION=${{ matrix.llvm_version }}
|
||||
UBUNTU_VERSION=22.04
|
||||
CMAKE_VERSION=3.20.0
|
||||
tags: c3c-builder:latest
|
||||
outputs: type=docker,dest=/tmp/c3c-builder.tar
|
||||
cache-from: type=gha,scope=build-with-docker-${{ matrix.llvm_version }}
|
||||
cache-to: type=gha,mode=max,scope=build-with-docker-${{ matrix.llvm_version }}
|
||||
env:
|
||||
DOCKER_BUILD_SUMMARY: false
|
||||
|
||||
- name: Load Docker image
|
||||
run: docker load -i /tmp/c3c-builder.tar
|
||||
|
||||
- name: Build C3 in Docker
|
||||
run: |
|
||||
chmod +x ./build-with-docker.sh
|
||||
LLVM_VERSION=${{ matrix.llvm_version }} UBUNTU_VERSION=${{ matrix.ubuntu_version }} CMAKE_BUILD_TYPE=${{ matrix.build_type }} ./build-with-docker.sh
|
||||
- name: Run Unified Tests in Docker
|
||||
rm -rf build bin
|
||||
mkdir -p build bin
|
||||
chmod -R 777 build bin
|
||||
docker run --rm \
|
||||
-v "$(pwd):/home/c3c/source" \
|
||||
-w /home/c3c/source \
|
||||
c3c-builder:latest \
|
||||
bash -c "git config --global --add safe.directory /home/c3c/source && \
|
||||
cmake -S . -B build \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_C_COMPILER=clang-${{ matrix.llvm_version }} \
|
||||
-DCMAKE_CXX_COMPILER=clang++-${{ matrix.llvm_version }} \
|
||||
-DCMAKE_LINKER=lld-${{ matrix.llvm_version }} \
|
||||
-DCMAKE_OBJCOPY=llvm-objcopy-${{ matrix.llvm_version }} \
|
||||
-DCMAKE_STRIP=llvm-strip-${{ matrix.llvm_version }} \
|
||||
-DCMAKE_DLLTOOL=llvm-dlltool-${{ matrix.llvm_version }} \
|
||||
-DC3_LLVM_VERSION=auto && \
|
||||
cmake --build build && \
|
||||
cp -r build/c3c build/lib bin"
|
||||
|
||||
- name: Run Unified Tests
|
||||
run: |
|
||||
chmod +x ./scripts/tools/ci_tests.sh
|
||||
docker run --rm \
|
||||
-u 0 \
|
||||
-v "$(pwd):/home/c3c/source" \
|
||||
@@ -398,12 +463,12 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [ Release, Debug ]
|
||||
build_type: [ Debug ]
|
||||
nixpkgs: [ Lock, Latest ]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: cachix/install-nix-action@v31
|
||||
with:
|
||||
with:
|
||||
github_access_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Update flake
|
||||
if: matrix.nixpkgs == 'Latest'
|
||||
@@ -414,9 +479,90 @@ jobs:
|
||||
if [[ ${{ matrix.build_type }} = "Debug" ]]; then CHECK_NAME=".#c3c-debug-checks"; fi
|
||||
nix build -L "$CHECK_NAME"
|
||||
|
||||
build-android:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [ Release, Debug ]
|
||||
architecture: [ aarch64, x86_64 ]
|
||||
include:
|
||||
- architecture: aarch64
|
||||
runner: ubuntu-24.04-arm
|
||||
- architecture: x86_64
|
||||
runner: ubuntu-24.04
|
||||
runs-on: ${{matrix.runner}}
|
||||
container:
|
||||
image: termux/termux-docker:${{matrix.architecture}}
|
||||
volumes:
|
||||
- /tmp/node20:/__e/node20
|
||||
- /tmp/node24:/__e/node24
|
||||
env:
|
||||
TERMUX_MAIN_PACKAGE_FORMAT: debian
|
||||
ANDROID_ROOT: /system
|
||||
ANDROID_DATA: /data
|
||||
PREFIX: /data/data/com.termux/files/usr
|
||||
HOME: /data/data/com.termux/files/home
|
||||
PATH: /data/data/com.termux/files/usr/bin
|
||||
TMPDIR: /data/data/com.termux/files/usr/tmp
|
||||
LANG: en_US.UTF-8
|
||||
TZ: UTC
|
||||
steps:
|
||||
- name: prepare termux
|
||||
run: |
|
||||
ln -sf ${PREFIX}/etc/termux/mirrors/default ${PREFIX}/etc/termux/chosen_mirrors
|
||||
for i in 1 2; do /entrypoint.sh pkg update && break || sleep 5; done
|
||||
/entrypoint.sh bash -c "yes | pkg upgrade -y"
|
||||
chmod -R o+x ${PREFIX}/bin
|
||||
for i in 1 2; do /entrypoint.sh pkg install -y nodejs-lts tar git && break || sleep 5; done
|
||||
# Symlink Termux node to where GHA expects it
|
||||
mkdir -p /__e/node20/bin /__e/node24/bin || true
|
||||
ln -sf ${PREFIX}/bin/node /__e/node20/bin/node || true
|
||||
ln -sf ${PREFIX}/bin/node /__e/node24/bin/node || true
|
||||
|
||||
- name: Get current date context
|
||||
id: date
|
||||
run: echo "date=$(date +'${{ env.CACHE_INVALIDATION_SEED }}')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache Termux packages
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: /data/data/com.termux/cache/apt/archives/
|
||||
key: termux-cache-${{ matrix.architecture }}-${{ steps.date.outputs.date }}
|
||||
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: fix permissions after checkout
|
||||
run: chown -R 1000:1000 .
|
||||
|
||||
- name: setup
|
||||
run: |
|
||||
for i in 1 2; do \
|
||||
/entrypoint.sh pkg install -y llvm binutils libgc build-essential cmake git libedit zlib clang make mlir llvm-tools libpolly python libllvm-static && break || sleep 10; \
|
||||
done
|
||||
|
||||
- name: build
|
||||
run: |
|
||||
cmake -Bbuild -DCMAKE_BUILD_TYPE=${{matrix.build_type}}
|
||||
cmake --build build
|
||||
|
||||
- name: Run Unified Tests
|
||||
if: matrix.build_type == 'Debug'
|
||||
run: ./scripts/tools/ci_tests.sh "./build/c3c" "android"
|
||||
|
||||
- name: bundle_output
|
||||
run: |
|
||||
chmod +x ./scripts/tools/package_build.sh
|
||||
./scripts/tools/package_build.sh "./build/c3c" "c3-android-${{matrix.architecture}}-${{matrix.build_type}}" "tar"
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: c3-android-${{matrix.architecture}}-${{matrix.build_type}}
|
||||
path: c3-android-${{matrix.architecture}}-${{matrix.build_type}}.tar.gz
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-22.04
|
||||
needs: [build-msvc, build-linux, build-mac, build-linux-alpine, build-openbsd, build-netbsd]
|
||||
needs: [build-msvc, build-linux, build-mac, build-linux-alpine, build-openbsd, build-netbsd, build-android]
|
||||
if: github.ref == 'refs/heads/master'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -429,20 +575,24 @@ jobs:
|
||||
# --- Release Assets ---
|
||||
mv c3-windows-Release/c3-windows-Release.zip c3-windows.zip
|
||||
mv c3-macos-Release/c3-macos-Release.zip c3-macos.zip
|
||||
|
||||
|
||||
mv c3-linux-Release/c3-linux-Release.tar.gz c3-linux.tar.gz
|
||||
mv c3-linux-musl-Release/c3-linux-musl-Release.tar.gz c3-linux-musl.tar.gz
|
||||
mv c3-openbsd-Release/c3-openbsd-Release.tar.gz c3-openbsd.tar.gz || true
|
||||
mv c3-netbsd-Release/c3-netbsd-Release.tar.gz c3-netbsd.tar.gz || true
|
||||
mv c3-android-aarch64-Release/c3-android-aarch64-Release.tar.gz c3-android-aarch64.tar.gz
|
||||
mv c3-android-x86_64-Release/c3-android-x86_64-Release.tar.gz c3-android-x86_64.tar.gz
|
||||
|
||||
# --- Debug Assets ---
|
||||
mv c3-windows-Debug/c3-windows-Debug.zip c3-windows-debug.zip
|
||||
mv c3-macos-Debug/c3-macos-Debug.zip c3-macos-debug.zip
|
||||
|
||||
|
||||
mv c3-linux-Debug/c3-linux-Debug.tar.gz c3-linux-debug.tar.gz
|
||||
mv c3-linux-musl-Debug/c3-linux-musl-Debug.tar.gz c3-linux-musl-debug.tar.gz
|
||||
mv c3-openbsd-Debug/c3-openbsd-Debug.tar.gz c3-openbsd-debug.tar.gz || true
|
||||
mv c3-netbsd-Debug/c3-netbsd-Debug.tar.gz c3-netbsd-debug.tar.gz || true
|
||||
mv c3-android-aarch64-Debug/c3-android-aarch64-Debug.tar.gz c3-android-aarch64-debug.tar.gz
|
||||
mv c3-android-x86_64-Debug/c3-android-x86_64-Debug.tar.gz c3-android-x86_64-debug.tar.gz
|
||||
|
||||
- run: gh release delete latest-prerelease-tag --cleanup-tag -y || true
|
||||
- run: echo "RELEASE_NAME=latest-prerelease-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV
|
||||
@@ -459,6 +609,8 @@ jobs:
|
||||
c3-linux-musl.tar.gz
|
||||
c3-openbsd.tar.gz
|
||||
c3-netbsd.tar.gz
|
||||
c3-android-aarch64.tar.gz
|
||||
c3-android-x86_64.tar.gz
|
||||
# --- Debug Artifacts ---
|
||||
c3-windows-debug.zip
|
||||
c3-macos-debug.zip
|
||||
@@ -466,3 +618,5 @@ jobs:
|
||||
c3-linux-musl-debug.tar.gz
|
||||
c3-openbsd-debug.tar.gz
|
||||
c3-netbsd-debug.tar.gz
|
||||
c3-android-aarch64-debug.tar.gz
|
||||
c3-android-x86_64-debug.tar.gz
|
||||
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -73,6 +73,9 @@ out/
|
||||
/cmake-build-debug/
|
||||
/cmake-build-release/
|
||||
|
||||
CMakeFiles/cmake.check_cache
|
||||
CMakeCache.txt
|
||||
|
||||
# etags(Emacs), ctags, gtags
|
||||
TAGS
|
||||
GPATH
|
||||
@@ -94,8 +97,8 @@ result
|
||||
|
||||
# tests
|
||||
/test/tmp/*
|
||||
/test/testrun
|
||||
/test/test_suite_runner
|
||||
testrun
|
||||
test_suite_runner
|
||||
|
||||
# patches, originals and rejects
|
||||
*.patch
|
||||
|
||||
192
CMakeLists.txt
192
CMakeLists.txt
@@ -97,12 +97,16 @@ set(C3_USE_TB OFF CACHE BOOL "Use TB")
|
||||
set(C3_LLD_DIR "" CACHE STRING "Use custom LLD directory")
|
||||
set(C3_LLD_INCLUDE_DIR "" CACHE STRING "Use custom LLD include directory")
|
||||
set(C3_ENABLE_CLANGD_LSP OFF CACHE BOOL "Enable/Disable output of compile commands during generation")
|
||||
set(C3_FETCH_LLVM OFF CACHE BOOL "Automatically download LLVM artifacts")
|
||||
set(C3_LLVM_TAG "llvm_21.x" CACHE STRING "Tag/Branch to download LLVM from")
|
||||
set(LLVM_CRT_LIBRARY_DIR "" CACHE STRING "Use custom llvm's compiler-rt directory")
|
||||
set(TCC_LIB_PATH "/usr/lib/tcc/libtcc1.a" CACHE STRING "Use custom libtcc1.a path")
|
||||
|
||||
set(C3_OPTIONS
|
||||
C3_LINK_DYNAMIC
|
||||
C3_WITH_LLVM
|
||||
C3_FETCH_LLVM
|
||||
C3_LLVM_TAG
|
||||
C3_LLVM_VERSION
|
||||
C3_USE_MIMALLOC
|
||||
C3_MIMALLOC_TAG
|
||||
@@ -125,9 +129,6 @@ if(C3_USE_MIMALLOC)
|
||||
)
|
||||
FetchContent_MakeAvailable(mimalloc)
|
||||
endif()
|
||||
if (NOT WIN32)
|
||||
find_package(CURL)
|
||||
endif()
|
||||
|
||||
find_package(Git QUIET)
|
||||
if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
@@ -155,55 +156,83 @@ if(C3_ENABLE_CLANGD_LSP)
|
||||
endif(C3_ENABLE_CLANGD_LSP)
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
if (C3_LLVM_VERSION STREQUAL "auto")
|
||||
set(C3_LLVM_VERSION ${C3_LLVM_DEFAULT_VERSION})
|
||||
if(C3_FETCH_LLVM)
|
||||
# 1. Determine local platform ID
|
||||
if (WIN32)
|
||||
set(C3_LLVM_PLATFORM "windows-amd64")
|
||||
elseif (APPLE)
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
set(C3_LLVM_PLATFORM "darwin-aarch64")
|
||||
else()
|
||||
set(C3_LLVM_PLATFORM "darwin-amd64")
|
||||
endif()
|
||||
else() # Linux
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||
set(C3_LLVM_PLATFORM "linux-aarch64")
|
||||
elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "riscv64")
|
||||
set(C3_LLVM_PLATFORM "linux-riscv64")
|
||||
else()
|
||||
set(C3_LLVM_PLATFORM "linux-amd64")
|
||||
endif()
|
||||
endif()
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_21_1_8/llvm-21.1.8-windows-amd64-msvc17-libcmt.7z
|
||||
)
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows_debug
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_21_1_8/llvm-21.1.8-windows-amd64-msvc17-libcmt-dbg.7z
|
||||
)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message("Loading Windows LLVM debug libraries, this may take a while...")
|
||||
FetchContent_MakeAvailable(LLVM_Windows_debug)
|
||||
set(llvm_dir ${llvm_windows_debug_SOURCE_DIR})
|
||||
else()
|
||||
message("Loading Windows LLVM libraries, this may take a while...")
|
||||
FetchContent_MakeAvailable(LLVM_Windows)
|
||||
set(llvm_dir ${llvm_windows_SOURCE_DIR})
|
||||
endif()
|
||||
message("Loaded Windows LLVM libraries into ${llvm_dir}")
|
||||
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
|
||||
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
find_package(LLD REQUIRED CONFIG)
|
||||
# 2. Determine if we want Debug or Release
|
||||
set(C3_LLVM_SUFFIX "")
|
||||
set(C3_LLVM_TYPE "Release")
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(C3_LLVM_SUFFIX "-dbg")
|
||||
set(C3_LLVM_TYPE "Debug")
|
||||
endif()
|
||||
|
||||
# 3. Construct URL
|
||||
set(C3_LLVM_ARTIFACT_NAME "llvm-${C3_LLVM_PLATFORM}${C3_LLVM_SUFFIX}")
|
||||
#set(C3_LLVM_URL "https://github.com/c3lang/c3-llvm/releases/download/${C3_LLVM_TAG}/${C3_LLVM_ARTIFACT_NAME}.tar.gz")
|
||||
#set(C3_LLVM_URL "https://github.com//ManuLinares/llvm-custom-builds/releases/download/${C3_LLVM_TAG}/${C3_LLVM_ARTIFACT_NAME}.tar.gz")
|
||||
|
||||
# We could also just set "latest" here to always fetch the latest release
|
||||
set(C3_LLVM_URL "https://github.com/c3lang/llvm-for-c3/releases/latest/download/${C3_LLVM_ARTIFACT_NAME}.tar.gz")
|
||||
|
||||
message(STATUS "Fetching ${C3_LLVM_TYPE} LLVM artifact for ${C3_LLVM_PLATFORM}...")
|
||||
message(STATUS "URL: ${C3_LLVM_URL}")
|
||||
|
||||
FetchContent_Declare(
|
||||
LLVM_Artifact
|
||||
URL ${C3_LLVM_URL}
|
||||
)
|
||||
FetchContent_MakeAvailable(LLVM_Artifact)
|
||||
|
||||
# 4. Point CMake to the fetched location
|
||||
set(llvm_dir ${llvm_artifact_SOURCE_DIR})
|
||||
set(CMAKE_PREFIX_PATH ${llvm_dir} ${CMAKE_PREFIX_PATH})
|
||||
set(LLVM_DIR "${llvm_dir}/lib/cmake/llvm")
|
||||
set(LLD_DIR "${llvm_dir}/lib/cmake/lld")
|
||||
|
||||
# TEST: For Windows, we might need to add the bin dir to prefix path for find_package to work well
|
||||
if (WIN32)
|
||||
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
|
||||
endif()
|
||||
|
||||
find_package(LLVM REQUIRED CONFIG NO_DEFAULT_PATH)
|
||||
find_package(LLD REQUIRED CONFIG NO_DEFAULT_PATH)
|
||||
else()
|
||||
# Add paths for LLVM CMake files of version 19 and higher as they follow a new installation
|
||||
# layout and are now in /usr/lib/llvm/*/lib/cmake/llvm/ rather than /usr/lib/cmake/llvm/
|
||||
#
|
||||
# Because of CMAKE_FIND_PACKAGE_SORT_ORDER CMAKE_FIND_PACKAGE_SORT_DIRECTION,
|
||||
# the newest version will always be found first.
|
||||
c3_print_variables(CMAKE_PREFIX_PATH)
|
||||
if (DEFINED LLVM_DIR)
|
||||
message(STATUS "Looking for LLVM CMake files in user-specified directory ${LLVM_DIR}")
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
# Legacy MSVC path if someone explicitly disabled fetching but is on MSVC
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
find_package(LLD REQUIRED CONFIG)
|
||||
else()
|
||||
# Default system search
|
||||
file (GLOB LLVM_CMAKE_PATHS "/usr/lib/llvm/*/lib/cmake/llvm/")
|
||||
list (APPEND CMAKE_PREFIX_PATH ${LLVM_CMAKE_PATHS} "/usr/lib/")
|
||||
message(STATUS "No LLVM_DIR specified, searching default directories ${CMAKE_PREFIX_PATH}")
|
||||
endif()
|
||||
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
|
||||
else()
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
|
||||
else()
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (EXISTS /opt/homebrew/lib)
|
||||
if (NOT C3_FETCH_LLVM AND EXISTS /opt/homebrew/lib)
|
||||
list(APPEND LLVM_LIBRARY_DIRS /opt/homebrew/lib)
|
||||
endif()
|
||||
|
||||
@@ -279,16 +308,16 @@ if(C3_WITH_LLVM)
|
||||
message(STATUS "Looking for static lld libraries in ${LLVM_LIBRARY_DIRS}")
|
||||
|
||||
# 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)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_COFF NAMES lldCOFF.a liblldCOFF.a liblldCOFF.dylib lldCOFF.lib liblldCOFF.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_COMMON NAMES lldCommon.a liblldCommon.a liblldCommon.dylib lldCommon.lib liblldCommon.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_ELF NAMES lldELF.a liblldELF.a liblldELF.dylib lldELF.lib liblldELF.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
|
||||
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 lldMachO.a liblldMachO.a liblldMachO.dylib lldMachO.lib liblldMachO.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
else()
|
||||
set(LLD_MACHO "")
|
||||
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)
|
||||
find_library(LLD_MINGW NAMES lldMinGW.a liblldMinGW.a liblldMinGW.dylib lldMinGW.lib liblldMinGW.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_WASM NAMES lldWasm.a liblldWasm.a liblldWasm.dylib lldWasm.lib liblldWasm.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
else()
|
||||
message(STATUS "Looking for shared lld libraries in ${LLVM_LIBRARY_DIRS}")
|
||||
|
||||
@@ -297,7 +326,7 @@ if(C3_WITH_LLVM)
|
||||
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} REQUIRED)
|
||||
else()
|
||||
find_library(LLVM NAMES libLLVM.a LLVM.lib PATHS ${LLVM_LIBRARY_DIRS} REQUIRED)
|
||||
endif()
|
||||
endif()
|
||||
set(llvm_libs ${LLVM})
|
||||
|
||||
# These don't seem to be reliable on windows.
|
||||
@@ -325,17 +354,36 @@ if(C3_WITH_LLVM)
|
||||
|
||||
if (APPLE)
|
||||
set(lld_libs ${lld_libs} xar)
|
||||
find_file(RT_ASAN_DYNAMIC NAMES libclang_rt.asan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
|
||||
find_file(RT_TSAN_DYNAMIC NAMES libclang_rt.tsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
|
||||
find_file(RT_UBSAN_DYNAMIC NAMES libclang_rt.ubsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
|
||||
find_file(RT_LSAN_DYNAMIC NAMES libclang_rt.lsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
|
||||
set(sanitizer_runtime_libraries
|
||||
${RT_ASAN_DYNAMIC}
|
||||
${RT_TSAN_DYNAMIC}
|
||||
# Unused
|
||||
# ${RT_UBSAN_DYNAMIC}
|
||||
# ${RT_LSAN_DYNAMIC}
|
||||
)
|
||||
if (llvm_dir)
|
||||
file(GLOB_RECURSE RT_ASAN_DYNAMIC "${llvm_dir}/*libclang_rt.asan_osx_dynamic.dylib")
|
||||
file(GLOB_RECURSE RT_TSAN_DYNAMIC "${llvm_dir}/*libclang_rt.tsan_osx_dynamic.dylib")
|
||||
endif()
|
||||
|
||||
if (NOT RT_ASAN_DYNAMIC OR NOT RT_TSAN_DYNAMIC)
|
||||
# Fallback to searching in LLVM_LIBRARY_DIRS (for non-fetched LLVM)
|
||||
find_file(RT_ASAN_DYNAMIC_PATH NAMES libclang_rt.asan_osx_dynamic.dylib PATHS ${LLVM_LIBRARY_DIRS} PATH_SUFFIXES "clang/${LLVM_MAJOR_VERSION}/lib/darwin" "clang/${LLVM_PACKAGE_VERSION}/lib/darwin")
|
||||
find_file(RT_TSAN_DYNAMIC_PATH NAMES libclang_rt.tsan_osx_dynamic.dylib PATHS ${LLVM_LIBRARY_DIRS} PATH_SUFFIXES "clang/${LLVM_MAJOR_VERSION}/lib/darwin" "clang/${LLVM_PACKAGE_VERSION}/lib/darwin")
|
||||
if (RT_ASAN_DYNAMIC_PATH)
|
||||
set(RT_ASAN_DYNAMIC ${RT_ASAN_DYNAMIC_PATH})
|
||||
endif()
|
||||
if (RT_TSAN_DYNAMIC_PATH)
|
||||
set(RT_TSAN_DYNAMIC ${RT_TSAN_DYNAMIC_PATH})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (RT_ASAN_DYNAMIC)
|
||||
list(GET RT_ASAN_DYNAMIC 0 RT_ASAN_DYNAMIC)
|
||||
endif()
|
||||
if (RT_TSAN_DYNAMIC)
|
||||
list(GET RT_TSAN_DYNAMIC 0 RT_TSAN_DYNAMIC)
|
||||
endif()
|
||||
|
||||
if (RT_ASAN_DYNAMIC AND RT_TSAN_DYNAMIC)
|
||||
set(sanitizer_runtime_libraries
|
||||
${RT_ASAN_DYNAMIC}
|
||||
${RT_TSAN_DYNAMIC}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "Linking to llvm libs ${llvm_libs}")
|
||||
@@ -413,10 +461,12 @@ add_executable(c3c
|
||||
src/utils/whereami.c
|
||||
src/utils/cpus.c
|
||||
src/utils/unzipper.c
|
||||
src/utils/msi.c
|
||||
src/compiler/c_codegen.c
|
||||
src/compiler/decltable.c
|
||||
src/compiler/methodtable.c
|
||||
src/compiler/mac_support.c
|
||||
src/utils/fetch_msvc.c
|
||||
src/compiler/windows_support.c
|
||||
src/compiler/codegen_asm.c
|
||||
src/compiler/asm_target.c
|
||||
@@ -474,6 +524,7 @@ if(C3_WITH_LLVM)
|
||||
else()
|
||||
target_sources(c3c PRIVATE src/utils/hostinfo.c)
|
||||
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=0)
|
||||
target_link_libraries(c3c m)
|
||||
endif()
|
||||
|
||||
target_include_directories(c3c PRIVATE
|
||||
@@ -523,7 +574,7 @@ else()
|
||||
endif()
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
target_link_libraries(c3c ${llvm_libs} miniz c3c_wrappers ${lld_libs})
|
||||
target_link_libraries(c3c miniz c3c_wrappers)
|
||||
|
||||
target_include_directories(c3c PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/wrapper/include/")
|
||||
@@ -531,11 +582,11 @@ if(C3_WITH_LLVM)
|
||||
target_include_directories(c3c_wrappers PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/wrapper/include/")
|
||||
|
||||
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
|
||||
target_link_libraries(c3c_wrappers PUBLIC ${lld_libs} ${llvm_libs})
|
||||
|
||||
else()
|
||||
|
||||
target_link_libraries(c3c ${llvm_libs} miniz ${lld_libs})
|
||||
target_link_libraries(c3c miniz ${lld_libs} ${llvm_libs})
|
||||
|
||||
endif()
|
||||
|
||||
@@ -552,12 +603,11 @@ if(MINGW)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--stack,8388608")
|
||||
endif ()
|
||||
|
||||
if (CURL_FOUND)
|
||||
target_link_libraries(c3c ${CURL_LIBRARIES})
|
||||
target_include_directories(c3c PRIVATE ${CURL_INCLUDE_DIRS})
|
||||
target_compile_definitions(c3c PUBLIC CURL_FOUND=1)
|
||||
else()
|
||||
target_compile_definitions(c3c PUBLIC CURL_FOUND=0)
|
||||
if (NOT WIN32)
|
||||
# For dlopen support
|
||||
if (CMAKE_DL_LIBS)
|
||||
target_link_libraries(c3c ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -652,8 +702,10 @@ if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
|
||||
|
||||
if (APPLE)
|
||||
# Change LC_ID_DYLIB to be rpath-based instead of having an absolute path
|
||||
# We set DYLD_LIBRARY_PATH so the tools (which might be dynamic) can find libLLVM.dylib in the artifact
|
||||
string(REPLACE ";" ":" _dyld_path "${LLVM_LIBRARY_DIRS}")
|
||||
add_custom_command(TARGET c3c POST_BUILD
|
||||
COMMAND find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
|
||||
COMMAND ${CMAKE_COMMAND} -E env "DYLD_LIBRARY_PATH=${_dyld_path}:$ENV{DYLD_LIBRARY_PATH}" find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ fn void main()
|
||||
|
||||
### Current status
|
||||
|
||||
The current stable version of the compiler is **version 0.7.9**.
|
||||
The current stable version of the compiler is **version 0.7.10**.
|
||||
|
||||
The upcoming 0.7.x releases will focus on expanding the standard library,
|
||||
fixing bugs and improving compile time analysis.
|
||||
@@ -162,6 +162,8 @@ The compiler is currently verified to compile on Linux, OpenBSD, Windows and Mac
|
||||
| MacOS x64 | Yes | Yes + cross compilation | Yes | Yes | Yes | Yes* |
|
||||
| MacOS Aarch64 | Yes | Yes + cross compilation | Yes | Yes | Yes | Yes* |
|
||||
| iOS Aarch64 | No | Untested | Untested | Yes | Yes | Yes* |
|
||||
| Android Aarch64 | No | Untested | Untested | Untested | Untested | Yes* |
|
||||
| Android x64 | No | Untested | Untested | Untested | Untested | Yes* |
|
||||
| Linux x86 | Yes | Yes | Yes | Yes | Yes | Yes* |
|
||||
| Linux x64 | Yes | Yes | Yes | Yes | Yes | Yes* |
|
||||
| Linux Aarch64 | Yes | Yes | Yes | Yes | Yes | Yes* |
|
||||
@@ -195,7 +197,7 @@ More platforms will be supported in the future.
|
||||
- If you wish to contribute with ideas, please file issues or discuss on Discord.
|
||||
- Interested in contributing to the stdlib? Please get in touch on Discord.
|
||||
- Compilation instructions for other Linux and Unix variants are appreciated.
|
||||
- Would you like to contribute bindings to some library? It would be nice to have support for SDL, Raylib and more.
|
||||
- Would you like to contribute bindings to some library? It would be nice to have support for SDL3 and more. If you have created some bindings, please submit them to https://github.com/c3lang/vendor.
|
||||
- Build something with C3 and show it off and give feedback. The language is still open for significant tweaks.
|
||||
- Start work on the C -> C3 converter which takes C code and does a "best effort" to translate it to C3. The first version only needs to work on C headers.
|
||||
- Do you have some specific area you have deep knowledge of and could help make C3 even better at doing? File or comment on issues.
|
||||
|
||||
51
benchmarks/stdlib/compression/deflate.c3
Normal file
51
benchmarks/stdlib/compression/deflate.c3
Normal file
@@ -0,0 +1,51 @@
|
||||
module deflate_benchmarks;
|
||||
import std::compression::deflate;
|
||||
|
||||
const uint SMALL_ITERATIONS = 50000;
|
||||
const uint LARGE_ITERATIONS = 100;
|
||||
|
||||
// Data to compress
|
||||
const char[] SMALL_DATA = { [0..1023] = 'A' };
|
||||
const char[] LARGE_DATA = { [0..1048575] = 'B' };
|
||||
|
||||
char[] small_compressed;
|
||||
char[] large_compressed;
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
small_compressed = deflate::compress(mem, SMALL_DATA)!!;
|
||||
large_compressed = deflate::compress(mem, LARGE_DATA)!!;
|
||||
set_benchmark_warmup_iterations(2);
|
||||
set_benchmark_max_iterations(10);
|
||||
|
||||
set_benchmark_func_iterations($qnameof(deflate_compress_small), SMALL_ITERATIONS);
|
||||
set_benchmark_func_iterations($qnameof(deflate_decompress_small), SMALL_ITERATIONS);
|
||||
set_benchmark_func_iterations($qnameof(deflate_compress_large), LARGE_ITERATIONS);
|
||||
set_benchmark_func_iterations($qnameof(deflate_decompress_large), LARGE_ITERATIONS);
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
module deflate_benchmarks @benchmark;
|
||||
|
||||
import std::compression::deflate;
|
||||
import std::core::mem;
|
||||
|
||||
fn void deflate_compress_small() => @pool()
|
||||
{
|
||||
char[]? compressed = deflate::compress(tmem, SMALL_DATA);
|
||||
}
|
||||
|
||||
fn void deflate_decompress_small() => @pool()
|
||||
{
|
||||
char[]? decompressed = deflate::decompress(tmem, small_compressed);
|
||||
}
|
||||
|
||||
fn void deflate_compress_large() => @pool()
|
||||
{
|
||||
char[]? compressed = deflate::compress(tmem, LARGE_DATA);
|
||||
}
|
||||
|
||||
fn void deflate_decompress_large() => @pool()
|
||||
{
|
||||
char[]? decompressed = deflate::decompress(tmem, large_compressed);
|
||||
}
|
||||
@@ -32,8 +32,8 @@ fn void initialize_bench() @init
|
||||
$qnameof(sha1_16)[..^4],
|
||||
$qnameof(sha2_256_16)[..^4],
|
||||
$qnameof(sha2_512_16)[..^4],
|
||||
$qnameof(blake2s_256_16)[..^4],
|
||||
$qnameof(blake2b_256_16)[..^4],
|
||||
//$qnameof(blake2s_256_16)[..^4],
|
||||
//$qnameof(blake2b_256_16)[..^4],
|
||||
$qnameof(blake3_16)[..^4],
|
||||
$qnameof(ripemd_160_16)[..^4],
|
||||
$qnameof(whirlpool_16)[..^4],
|
||||
@@ -68,8 +68,8 @@ fn void md5_16() => md5::hash(common_16);
|
||||
fn void sha1_16() => sha1::hash(common_16);
|
||||
fn void sha2_256_16() => sha256::hash(common_16);
|
||||
fn void sha2_512_16() => sha512::hash(common_16);
|
||||
fn void blake2s_256_16() => blake2::s(256, common_16);
|
||||
fn void blake2b_256_16() => blake2::b(256, common_16);
|
||||
//fn void blake2s_256_16() => blake2::s(256, common_16);
|
||||
//fn void blake2b_256_16() => blake2::b(256, common_16);
|
||||
fn void blake3_16() => blake3::hash(common_16);
|
||||
fn void ripemd_160_16() => ripemd::hash{160}(common_16);
|
||||
fn void whirlpool_16() => whirlpool::hash(common_16);
|
||||
@@ -80,8 +80,8 @@ fn void md5_256() => md5::hash(common_256);
|
||||
fn void sha1_256() => sha1::hash(common_256);
|
||||
fn void sha2_256_256() => sha256::hash(common_256);
|
||||
fn void sha2_512_256() => sha512::hash(common_256);
|
||||
fn void blake2s_256_256() => blake2::s(256, common_256);
|
||||
fn void blake2b_256_256() => blake2::b(256, common_256);
|
||||
//fn void blake2s_256_256() => blake2::s(256, common_256);
|
||||
//fn void blake2b_256_256() => blake2::b(256, common_256);
|
||||
fn void blake3_256() => blake3::hash(common_256);
|
||||
fn void ripemd_160_256() => ripemd::hash{160}(common_256);
|
||||
fn void whirlpool_256() => whirlpool::hash(common_256);
|
||||
@@ -92,8 +92,8 @@ fn void md5_4kib() => md5::hash(common_4kib);
|
||||
fn void sha1_4kib() => sha1::hash(common_4kib);
|
||||
fn void sha2_256_4kib() => sha256::hash(common_4kib);
|
||||
fn void sha2_512_4kib() => sha512::hash(common_4kib);
|
||||
fn void blake2s_256_4kib() => blake2::s(256, common_4kib);
|
||||
fn void blake2b_256_4kib() => blake2::b(256, common_4kib);
|
||||
//fn void blake2s_256_4kib() => blake2::s(256, common_4kib);
|
||||
//fn void blake2b_256_4kib() => blake2::b(256, common_4kib);
|
||||
fn void blake3_4kib() => blake3::hash(common_4kib);
|
||||
fn void ripemd_160_4kib() => ripemd::hash{160}(common_4kib);
|
||||
fn void whirlpool_4kib() => whirlpool::hash(common_4kib);
|
||||
@@ -104,8 +104,8 @@ fn void md5_1mib() => md5::hash(common_1mib);
|
||||
fn void sha1_1mib() => sha1::hash(common_1mib);
|
||||
fn void sha2_256_1mib() => sha256::hash(common_1mib);
|
||||
fn void sha2_512_1mib() => sha512::hash(common_1mib);
|
||||
fn void blake2s_256_1mib() => blake2::s(256, common_1mib);
|
||||
fn void blake2b_256_1mib() => blake2::b(256, common_1mib);
|
||||
//fn void blake2s_256_1mib() => blake2::s(256, common_1mib);
|
||||
//fn void blake2b_256_1mib() => blake2::b(256, common_1mib);
|
||||
fn void blake3_1mib() => blake3::hash(common_1mib);
|
||||
fn void ripemd_160_1mib() => ripemd::hash{160}(common_1mib);
|
||||
fn void whirlpool_1mib() => whirlpool::hash(common_1mib);
|
||||
|
||||
57
benchmarks/stdlib/hash/blake3.c3
Normal file
57
benchmarks/stdlib/hash/blake3.c3
Normal file
@@ -0,0 +1,57 @@
|
||||
module blake3_bench;
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(3);
|
||||
set_benchmark_max_iterations(128);
|
||||
|
||||
input = mem::alloc_array(char, BUFSZ);
|
||||
input[:BUFSZ] = (char[]){ [0..BUFSZ-1] = 0xA5 }[..];
|
||||
input_slice = input[:BUFSZ];
|
||||
}
|
||||
|
||||
fn void teardown_bench() @finalizer
|
||||
{
|
||||
mem::free(input);
|
||||
input = null;
|
||||
}
|
||||
|
||||
char* input;
|
||||
char[] input_slice;
|
||||
const usz BUFSZ = 1024 * 1024;
|
||||
|
||||
module blake3_bench @benchmark;
|
||||
|
||||
import std::hash;
|
||||
|
||||
fn void blake3_hash()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = blake3::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_sha256()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = sha256::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_sha512()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = sha512::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_whirlpool()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = whirlpool::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
41
benchmarks/stdlib/hash/md5.c3
Normal file
41
benchmarks/stdlib/hash/md5.c3
Normal file
@@ -0,0 +1,41 @@
|
||||
module md5_bench;
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(3);
|
||||
set_benchmark_max_iterations(128);
|
||||
|
||||
input = mem::alloc_array(char, BUFSZ);
|
||||
input[:BUFSZ] = (char[]){ [0..BUFSZ-1] = 0xA5 }[..];
|
||||
input_slice = input[:BUFSZ];
|
||||
}
|
||||
|
||||
fn void teardown_bench() @finalizer
|
||||
{
|
||||
mem::free(input);
|
||||
input = null;
|
||||
}
|
||||
|
||||
char* input;
|
||||
char[] input_slice;
|
||||
const usz BUFSZ = 1024 * 1024;
|
||||
|
||||
module md5_bench @benchmark;
|
||||
|
||||
import std::hash;
|
||||
|
||||
fn void md5_hash()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = md5::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_sha256()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = sha256::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
57
benchmarks/stdlib/hash/whirlpool.c3
Normal file
57
benchmarks/stdlib/hash/whirlpool.c3
Normal file
@@ -0,0 +1,57 @@
|
||||
module whirlpool_bench;
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(3);
|
||||
set_benchmark_max_iterations(128);
|
||||
|
||||
input = mem::alloc_array(char, BUFSZ);
|
||||
input[:BUFSZ] = (char[]){ [0..BUFSZ-1] = 0xA5 }[..];
|
||||
input_slice = input[:BUFSZ];
|
||||
}
|
||||
|
||||
fn void teardown_bench() @finalizer
|
||||
{
|
||||
mem::free(input);
|
||||
input = null;
|
||||
}
|
||||
|
||||
char* input;
|
||||
char[] input_slice;
|
||||
const usz BUFSZ = 1024 * 1024;
|
||||
|
||||
module whirlpool_bench @benchmark;
|
||||
|
||||
import std::hash;
|
||||
|
||||
fn void whirlpool_hash()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = whirlpool::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_sha256()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = sha256::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_sha512()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = sha512::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_streebog_512()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = streebog::hash_512(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
@@ -30,7 +30,8 @@ chmod -R 777 build bin
|
||||
exec $DOCKER run -i --rm \
|
||||
-v "$PWD":/home/c3c/source \
|
||||
-w /home/c3c/source $IMAGE bash -c \
|
||||
"cmake -S . -B build \
|
||||
"git config --global --add safe.directory /home/c3c/source && \
|
||||
cmake -S . -B build \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \
|
||||
-DCMAKE_C_COMPILER=clang-$LLVM_VERSION \
|
||||
|
||||
@@ -2,48 +2,44 @@ ARG UBUNTU_VERSION=22.04
|
||||
FROM ubuntu:${UBUNTU_VERSION}
|
||||
|
||||
ARG LLVM_VERSION=18
|
||||
ENV LLVM_DEV_VERSION=20
|
||||
ARG CMAKE_VERSION=3.20.0
|
||||
|
||||
ARG CMAKE_VERSION=3.20
|
||||
# Prevent interactive prompts during apt install
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && apt-get install -y wget gnupg software-properties-common zlib1g zlib1g-dev python3 ninja-build curl g++ libcurl4-openssl-dev && \
|
||||
wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-$CMAKE_VERSION-linux-x86_64.sh && \
|
||||
RUN for i in 1 2 3; do apt-get update && break || sleep 2; done && \
|
||||
apt-get install -y --fix-missing \
|
||||
wget gnupg software-properties-common lsb-release \
|
||||
zlib1g zlib1g-dev python3 ninja-build curl g++ libcurl4-openssl-dev git && \
|
||||
CODENAME=$(lsb_release -cs) && \
|
||||
ARCH=$(uname -m) && \
|
||||
wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-${ARCH}.sh && \
|
||||
mkdir -p /opt/cmake && \
|
||||
sh cmake-${CMAKE_VERSION}-linux-x86_64.sh --prefix=/opt/cmake --skip-license && \
|
||||
rm cmake-${CMAKE_VERSION}-linux-x86_64.sh && \
|
||||
ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake
|
||||
sh cmake-${CMAKE_VERSION}-linux-${ARCH}.sh --prefix=/opt/cmake --skip-license && \
|
||||
ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake && \
|
||||
rm cmake-${CMAKE_VERSION}-linux-${ARCH}.sh
|
||||
|
||||
RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \
|
||||
if [ "${LLVM_VERSION}" -lt 18 ]; then \
|
||||
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_VERSION} main" && \
|
||||
apt-get update && \
|
||||
apt-get install -y -t llvm-toolchain-focal-${LLVM_VERSION} \
|
||||
libpolly-${LLVM_VERSION}-dev \
|
||||
clang-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
|
||||
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev libmlir-${LLVM_VERSION} \
|
||||
libmlir-${LLVM_VERSION}-dev mlir-${LLVM_VERSION}-tools; \
|
||||
elif [ "${LLVM_VERSION}" -lt "${LLVM_DEV_VERSION}" ]; then \
|
||||
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_VERSION} main" && \
|
||||
apt-get update && \
|
||||
apt-get install -y -t llvm-toolchain-focal-${LLVM_VERSION} \
|
||||
libpolly-${LLVM_VERSION}-dev \
|
||||
clang-${LLVM_VERSION} clang++-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
|
||||
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev; \
|
||||
RUN CODENAME=$(lsb_release -cs) && \
|
||||
for i in 1 2; do wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key && break || sleep 2; done | apt-key add - && \
|
||||
if [ "${LLVM_VERSION}" -ge 16 ]; then \
|
||||
add-apt-repository "deb http://apt.llvm.org/${CODENAME}/ llvm-toolchain-${CODENAME}-${LLVM_VERSION} main"; \
|
||||
else \
|
||||
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main" && \
|
||||
apt-get update && \
|
||||
apt-get install -y -t llvm-toolchain-focal \
|
||||
libpolly-${LLVM_VERSION}-dev \
|
||||
clang-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
|
||||
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev; \
|
||||
add-apt-repository "deb http://apt.llvm.org/${CODENAME}/ llvm-toolchain-${CODENAME} main"; \
|
||||
fi && \
|
||||
for i in 1 2 3; do apt-get update && break || sleep 2; done && \
|
||||
apt-get install -y --fix-missing \
|
||||
clang-${LLVM_VERSION} \
|
||||
clang++-${LLVM_VERSION} \
|
||||
llvm-${LLVM_VERSION} \
|
||||
llvm-${LLVM_VERSION}-dev \
|
||||
lld-${LLVM_VERSION} \
|
||||
liblld-${LLVM_VERSION}-dev \
|
||||
libpolly-${LLVM_VERSION}-dev && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN groupadd -g 1337 c3c && \
|
||||
useradd -m -u 1337 -g c3c c3c
|
||||
|
||||
# Add cmake to PATH for user c3c
|
||||
USER c3c
|
||||
ENV PATH="/opt/cmake/bin:${PATH}"
|
||||
|
||||
WORKDIR /home/c3c
|
||||
WORKDIR /home/c3c
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright (c) 2023-2025 Eduardo José Gómez Hernández. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::atomic::types <Type>;
|
||||
module std::atomic::types;
|
||||
|
||||
struct Atomic
|
||||
struct Atomic <Type>
|
||||
{
|
||||
Type data;
|
||||
}
|
||||
@@ -11,147 +11,87 @@ struct Atomic
|
||||
<*
|
||||
Loads data atomically, by default this uses SEQ_CONSISTENT ordering.
|
||||
|
||||
@param ordering : "The ordering, cannot be release or acquire-release."
|
||||
@require ordering != RELEASE && ordering != ACQUIRE_RELEASE : "Release and acquire-release are not valid for load"
|
||||
@param $ordering : "The ordering, cannot be release or acquire-release."
|
||||
@require $ordering != RELEASE && $ordering != ACQUIRE_RELEASE : "Release and acquire-release are not valid for load"
|
||||
*>
|
||||
macro Type Atomic.load(&self, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.load(&self, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
switch(ordering)
|
||||
{
|
||||
case NOT_ATOMIC: return $$atomic_load(data, false, AtomicOrdering.NOT_ATOMIC.ordinal);
|
||||
case UNORDERED: return $$atomic_load(data, false, AtomicOrdering.UNORDERED.ordinal);
|
||||
case RELAXED: return $$atomic_load(data, false, AtomicOrdering.RELAXED.ordinal);
|
||||
case ACQUIRE: return $$atomic_load(data, false, AtomicOrdering.ACQUIRE.ordinal);
|
||||
case SEQ_CONSISTENT: return $$atomic_load(data, false, AtomicOrdering.SEQ_CONSISTENT.ordinal);
|
||||
case ACQUIRE_RELEASE:
|
||||
case RELEASE: unreachable("Invalid ordering.");
|
||||
}
|
||||
return $$atomic_load(&self.data, false, $ordering.ordinal);
|
||||
}
|
||||
<*
|
||||
Stores data atomically, by default this uses SEQ_CONSISTENT ordering.
|
||||
|
||||
@param ordering : "The ordering, cannot be acquire or acquire-release."
|
||||
@require ordering != ACQUIRE && ordering != ACQUIRE_RELEASE : "Acquire and acquire-release are not valid for store"
|
||||
@param $ordering : "The ordering, cannot be acquire or acquire-release."
|
||||
@require $ordering != ACQUIRE && $ordering != ACQUIRE_RELEASE : "Acquire and acquire-release are not valid for store"
|
||||
*>
|
||||
macro void Atomic.store(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro void Atomic.store(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
switch(ordering)
|
||||
{
|
||||
case NOT_ATOMIC: $$atomic_store(data, value, false, AtomicOrdering.NOT_ATOMIC.ordinal);
|
||||
case UNORDERED: $$atomic_store(data, value, false, AtomicOrdering.UNORDERED.ordinal);
|
||||
case RELAXED: $$atomic_store(data, value, false, AtomicOrdering.RELAXED.ordinal);
|
||||
case RELEASE: $$atomic_store(data, value, false, AtomicOrdering.RELEASE.ordinal);
|
||||
case SEQ_CONSISTENT: $$atomic_store(data, value, false, AtomicOrdering.SEQ_CONSISTENT.ordinal);
|
||||
case ACQUIRE_RELEASE:
|
||||
case ACQUIRE: unreachable("Invalid ordering.");
|
||||
}
|
||||
$$atomic_store(&self.data, value, false, $ordering.ordinal);
|
||||
}
|
||||
|
||||
macro Type Atomic.add(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.add(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_add, data, value, ordering);
|
||||
return atomic::fetch_add(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.sub(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.sub(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_sub, data, value, ordering);
|
||||
return atomic::fetch_sub(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.mul(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.mul(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_mul, data, value, ordering);
|
||||
return atomic::fetch_mul(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.div(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.div(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_div, data, value, ordering);
|
||||
return atomic::fetch_div(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.max(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.max(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_max, data, value, ordering);
|
||||
return atomic::fetch_max(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.min(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.min(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_min, data, value, ordering);
|
||||
return atomic::fetch_min(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.or(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
macro Type Atomic.or(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_or, data, value, ordering);
|
||||
return atomic::fetch_or(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.xor(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
macro Type Atomic.xor(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_xor, data, value, ordering);
|
||||
return atomic::fetch_xor(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.and(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
macro Type Atomic.and(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_and, data, value, ordering);
|
||||
return atomic::fetch_and(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.shr(&self, Type amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
macro Type Atomic.shr(&self, Type amount, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_shift_right, data, amount, ordering);
|
||||
return atomic::fetch_shift_right(&self.data, amount, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.shl(&self, Type amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
macro Type Atomic.shl(&self, Type amount, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_shift_left, data, amount, ordering);
|
||||
return atomic::fetch_shift_left(&self.data, amount, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.set(&self, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
|
||||
macro Type Atomic.set(&self, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec_no_arg(atomic::flag_set, data, ordering);
|
||||
return atomic::flag_set(&self.data, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.clear(&self, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
|
||||
macro Type Atomic.clear(&self, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
|
||||
return @atomic_exec_no_arg(atomic::flag_clear, data, ordering);
|
||||
}
|
||||
|
||||
macro @atomic_exec(#func, data, value, ordering) @local
|
||||
{
|
||||
switch(ordering)
|
||||
{
|
||||
case RELAXED: return #func(data, value, RELAXED);
|
||||
case ACQUIRE: return #func(data, value, ACQUIRE);
|
||||
case RELEASE: return #func(data, value, RELEASE);
|
||||
case ACQUIRE_RELEASE: return #func(data, value, ACQUIRE_RELEASE);
|
||||
case SEQ_CONSISTENT: return #func(data, value, SEQ_CONSISTENT);
|
||||
default: unreachable("Ordering may not be non-atomic or unordered.");
|
||||
}
|
||||
}
|
||||
|
||||
macro @atomic_exec_no_arg(#func, data, ordering) @local
|
||||
{
|
||||
switch(ordering)
|
||||
{
|
||||
case RELAXED: return #func(data, RELAXED);
|
||||
case ACQUIRE: return #func(data, ACQUIRE);
|
||||
case RELEASE: return #func(data, RELEASE);
|
||||
case ACQUIRE_RELEASE: return #func(data, ACQUIRE_RELEASE);
|
||||
case SEQ_CONSISTENT: return #func(data, SEQ_CONSISTENT);
|
||||
default: unreachable("Ordering may not be non-atomic or unordered.");
|
||||
}
|
||||
return atomic::flag_clear(&self.data, $ordering);
|
||||
}
|
||||
|
||||
module std::atomic;
|
||||
@@ -175,8 +115,8 @@ macro bool is_native_atomic_type($Type)
|
||||
$case FLOAT:
|
||||
$case BOOL:
|
||||
return true;
|
||||
$case DISTINCT:
|
||||
$case CONST_ENUM:
|
||||
$case TYPEDEF:
|
||||
$case CONSTDEF:
|
||||
return is_native_atomic_type($Type.inner);
|
||||
$default:
|
||||
return false;
|
||||
@@ -272,7 +212,7 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
|
||||
@require $defined(*ptr) : "Expected a pointer"
|
||||
@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."
|
||||
*>
|
||||
macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
|
||||
@@ -150,7 +150,7 @@ fn bool HashMap.is_initialized(&map)
|
||||
fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map)
|
||||
{
|
||||
self.init(allocator, other_map.table.len, other_map.load_factor);
|
||||
self.put_all_for_create(other_map);
|
||||
hashmap_put_all_for_create(self, other_map);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
|
||||
if (e.hash == hash && equals(key, e.key)) return e.value;
|
||||
}
|
||||
Value val = #expr;
|
||||
map.add_entry(hash, key, val, index);
|
||||
hashmap_add_entry(map, hash, key, val, index);
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -269,13 +269,13 @@ fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
map.add_entry(hash, key, value, index);
|
||||
hashmap_add_entry(map, hash, key, value, index);
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void? HashMap.remove(&map, Key key) @maydiscard
|
||||
{
|
||||
if (!map.remove_entry_for_key(key)) return NOT_FOUND~;
|
||||
if (!hashmap_remove_entry_for_key(map, key)) return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn void HashMap.clear(&map)
|
||||
@@ -290,9 +290,9 @@ fn void HashMap.clear(&map)
|
||||
{
|
||||
Entry *to_delete = next;
|
||||
next = next.next;
|
||||
map.free_entry(to_delete);
|
||||
hashmap_free_entry(map, to_delete);
|
||||
}
|
||||
map.free_entry(entry);
|
||||
hashmap_free_entry(map, entry);
|
||||
*entry_ref = null;
|
||||
}
|
||||
map.count = 0;
|
||||
@@ -302,7 +302,7 @@ fn void HashMap.free(&map)
|
||||
{
|
||||
if (!map.is_initialized()) return;
|
||||
map.clear();
|
||||
map.free_internal(map.table.ptr);
|
||||
hashmap_free_internal(map, map.table.ptr);
|
||||
map.table = {};
|
||||
}
|
||||
|
||||
@@ -402,7 +402,7 @@ fn HashMapKeyIterator HashMap.key_iter(&self)
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||
fn void hashmap_add_entry(HashMap* map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||
{
|
||||
$if COPY_KEYS:
|
||||
key = key.copy(map.allocator);
|
||||
@@ -411,11 +411,11 @@ fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_ind
|
||||
map.table[bucket_index] = entry;
|
||||
if (map.count++ >= map.threshold)
|
||||
{
|
||||
map.resize(map.table.len * 2);
|
||||
hashmap_resize(map, map.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashMap.resize(&map, uint new_capacity) @private
|
||||
fn void hashmap_resize(HashMap* map, uint new_capacity) @private
|
||||
{
|
||||
Entry*[] old_table = map.table;
|
||||
uint old_capacity = old_table.len;
|
||||
@@ -425,9 +425,9 @@ fn void HashMap.resize(&map, uint new_capacity) @private
|
||||
return;
|
||||
}
|
||||
Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity);
|
||||
map.transfer(new_table);
|
||||
hashmap_transfer(map, new_table);
|
||||
map.table = new_table;
|
||||
map.free_internal(old_table.ptr);
|
||||
hashmap_free_internal(map, old_table.ptr);
|
||||
map.threshold = (uint)(new_capacity * map.load_factor);
|
||||
}
|
||||
|
||||
@@ -443,7 +443,7 @@ fn usz? HashMap.to_format(&self, Formatter* f) @dynamic
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
fn void HashMap.transfer(&map, Entry*[] new_table) @private
|
||||
fn void hashmap_transfer(HashMap* map, Entry*[] new_table) @private
|
||||
{
|
||||
Entry*[] src = map.table;
|
||||
uint new_capacity = new_table.len;
|
||||
@@ -462,20 +462,20 @@ fn void HashMap.transfer(&map, Entry*[] new_table) @private
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashMap.put_all_for_create(&map, HashMap* other_map) @private
|
||||
fn void hashmap_put_all_for_create(HashMap* map, HashMap* other_map) @private
|
||||
{
|
||||
if (!other_map.count) return;
|
||||
foreach (Entry *e : other_map.table)
|
||||
{
|
||||
while (e)
|
||||
{
|
||||
map.put_for_create(e.key, e.value);
|
||||
hashmap_put_for_create(map, e.key, e.value);
|
||||
e = e.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashMap.put_for_create(&map, Key key, Value value) @private
|
||||
fn void hashmap_put_for_create(HashMap* map, Key key, Value value) @private
|
||||
{
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
@@ -487,15 +487,15 @@ fn void HashMap.put_for_create(&map, Key key, Value value) @private
|
||||
return;
|
||||
}
|
||||
}
|
||||
map.create_entry(hash, key, value, i);
|
||||
hashmap_create_entry(map, hash, key, value, i);
|
||||
}
|
||||
|
||||
fn void HashMap.free_internal(&map, void* ptr) @inline @private
|
||||
fn void hashmap_free_internal(HashMap* map, void* ptr) @inline @private
|
||||
{
|
||||
allocator::free(map.allocator, ptr);
|
||||
}
|
||||
|
||||
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
|
||||
fn bool hashmap_remove_entry_for_key(HashMap* map, Key key) @private
|
||||
{
|
||||
if (!map.count) return false;
|
||||
uint hash = rehash(key.hash());
|
||||
@@ -516,7 +516,7 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private
|
||||
{
|
||||
prev.next = next;
|
||||
}
|
||||
map.free_entry(e);
|
||||
hashmap_free_entry(map, e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
@@ -525,7 +525,7 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
|
||||
fn void hashmap_create_entry(HashMap* map, uint hash, Key key, Value value, int bucket_index) @private
|
||||
{
|
||||
Entry *e = map.table[bucket_index];
|
||||
$if COPY_KEYS:
|
||||
@@ -536,12 +536,12 @@ fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_i
|
||||
map.count++;
|
||||
}
|
||||
|
||||
fn void HashMap.free_entry(&self, Entry *entry) @local
|
||||
fn void hashmap_free_entry(HashMap* map, Entry *entry) @local
|
||||
{
|
||||
$if COPY_KEYS:
|
||||
allocator::free(self.allocator, entry.key);
|
||||
allocator::free(map.allocator, entry.key);
|
||||
$endif
|
||||
self.free_internal(entry);
|
||||
hashmap_free_internal(map, entry);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ fn bool HashSet.is_initialized(&set)
|
||||
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);
|
||||
hashset_put_all_for_create(self, other_set);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ fn bool HashSet.add(&set, Value value)
|
||||
{
|
||||
if (e.hash == hash && equals(value, e.value)) return false;
|
||||
}
|
||||
set.add_entry(hash, value, index);
|
||||
hashset_add_entry(set, hash, value, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ fn bool HashSet.contains(&set, Value value)
|
||||
*>
|
||||
fn void? HashSet.remove(&set, Value value) @maydiscard
|
||||
{
|
||||
if (!set.remove_entry_for_value(value)) return NOT_FOUND~;
|
||||
if (!hashset_remove_entry_for_value(set, value)) return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn usz HashSet.remove_all(&set, Value[] values)
|
||||
@@ -266,7 +266,7 @@ fn usz HashSet.remove_all(&set, Value[] values)
|
||||
usz total;
|
||||
foreach (v : values)
|
||||
{
|
||||
if (set.remove_entry_for_value(v)) total++;
|
||||
if (hashset_remove_entry_for_value(set, v)) total++;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
@@ -279,7 +279,7 @@ fn usz HashSet.remove_all_from(&set, HashSet* other)
|
||||
usz total;
|
||||
other.@each(;Value val)
|
||||
{
|
||||
if (set.remove_entry_for_value(val)) total++;
|
||||
if (hashset_remove_entry_for_value(set, val)) total++;
|
||||
};
|
||||
return total;
|
||||
}
|
||||
@@ -291,7 +291,7 @@ fn void HashSet.free(&set)
|
||||
{
|
||||
if (!set.is_initialized()) return;
|
||||
set.clear();
|
||||
set.free_internal(set.table.ptr);
|
||||
hashset_free_internal(set, set.table.ptr);
|
||||
*set = {};
|
||||
}
|
||||
|
||||
@@ -314,10 +314,10 @@ fn void HashSet.clear(&set)
|
||||
{
|
||||
Entry *to_delete = next;
|
||||
next = next.next;
|
||||
set.free_entry(to_delete);
|
||||
hashset_free_entry(set, to_delete);
|
||||
}
|
||||
|
||||
set.free_entry(entry);
|
||||
hashset_free_entry(set, entry);
|
||||
*entry_ref = null;
|
||||
}
|
||||
set.count = 0;
|
||||
@@ -327,7 +327,7 @@ fn void HashSet.reserve(&set, usz capacity)
|
||||
{
|
||||
if (capacity > set.threshold)
|
||||
{
|
||||
set.resize(math::next_power_of_2(capacity));
|
||||
hashset_resize(set, math::next_power_of_2(capacity));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,17 +464,17 @@ fn bool HashSet.is_subset(&self, HashSet* other)
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void HashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
|
||||
fn void hashset_add_entry(HashSet* 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);
|
||||
hashset_resize(set, set.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashSet.resize(&self, usz new_capacity) @private
|
||||
fn void hashset_resize(HashSet* self, usz new_capacity) @private
|
||||
{
|
||||
Entry*[] old_table = self.table;
|
||||
usz old_capacity = old_table.len;
|
||||
@@ -484,9 +484,9 @@ fn void HashSet.resize(&self, usz new_capacity) @private
|
||||
return;
|
||||
}
|
||||
Entry*[] new_table = allocator::new_array(self.allocator, Entry*, new_capacity);
|
||||
self.transfer(new_table);
|
||||
hashset_transfer(self, new_table);
|
||||
self.table = new_table;
|
||||
self.free_internal(old_table.ptr);
|
||||
hashset_free_internal(self, old_table.ptr);
|
||||
self.threshold = (uint)(new_capacity * self.load_factor);
|
||||
}
|
||||
|
||||
@@ -502,7 +502,7 @@ fn usz? HashSet.to_format(&self, Formatter* f) @dynamic
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
fn void HashSet.transfer(&self, Entry*[] new_table) @private
|
||||
fn void hashset_transfer(HashSet* self, Entry*[] new_table) @private
|
||||
{
|
||||
Entry*[] src = self.table;
|
||||
uint new_capacity = new_table.len;
|
||||
@@ -521,20 +521,20 @@ fn void HashSet.transfer(&self, Entry*[] new_table) @private
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashSet.put_all_for_create(&set, HashSet* other_set) @private
|
||||
fn void hashset_put_all_for_create(HashSet* set, HashSet* other_set) @private
|
||||
{
|
||||
if (!other_set.count) return;
|
||||
foreach (Entry *e : other_set.table)
|
||||
{
|
||||
while (e)
|
||||
{
|
||||
set.put_for_create(e.value);
|
||||
hashset_put_for_create(set, e.value);
|
||||
e = e.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashSet.put_for_create(&set, Value value) @private
|
||||
fn void hashset_put_for_create(HashSet* set, Value value) @private
|
||||
{
|
||||
uint hash = rehash(value.hash());
|
||||
uint i = index_for(hash, set.table.len);
|
||||
@@ -546,15 +546,15 @@ fn void HashSet.put_for_create(&set, Value value) @private
|
||||
return;
|
||||
}
|
||||
}
|
||||
set.create_entry(hash, value, i);
|
||||
hashset_create_entry(set, hash, value, i);
|
||||
}
|
||||
|
||||
fn void HashSet.free_internal(&self, void* ptr) @inline @private
|
||||
fn void hashset_free_internal(HashSet* self, void* ptr) @inline @private
|
||||
{
|
||||
allocator::free(self.allocator, ptr);
|
||||
}
|
||||
|
||||
fn void HashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
|
||||
fn void hashset_create_entry(HashSet* set, uint hash, Value value, int bucket_index) @private
|
||||
{
|
||||
Entry* entry = allocator::new(set.allocator, Entry, {
|
||||
.hash = hash,
|
||||
@@ -569,7 +569,7 @@ fn void HashSet.create_entry(&set, uint hash, Value value, int bucket_index) @pr
|
||||
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
|
||||
fn bool hashset_remove_entry_for_value(HashSet* set, Value value) @private
|
||||
{
|
||||
if (!set.count) return false;
|
||||
uint hash = rehash(value.hash());
|
||||
@@ -590,7 +590,7 @@ fn bool HashSet.remove_entry_for_value(&set, Value value) @private
|
||||
{
|
||||
prev.next = next;
|
||||
}
|
||||
set.free_entry(e);
|
||||
hashset_free_entry(set, e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
@@ -600,7 +600,7 @@ fn bool HashSet.remove_entry_for_value(&set, Value value) @private
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void HashSet.free_entry(&set, Entry *entry) @private
|
||||
fn void hashset_free_entry(HashSet* set, Entry *entry) @private
|
||||
{
|
||||
allocator::free(set.allocator, entry);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ fn bool InterfaceList.is_initialized(&self) @inline => self.allocator != null;
|
||||
macro void InterfaceList.push(&self, element)
|
||||
{
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
self._append(allocator::clone(self.allocator, element));
|
||||
interfacelist_append(self, allocator::clone(self.allocator, element));
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -381,7 +381,7 @@ fn usz? InterfaceList.to_format(&self, Formatter* formatter) @dynamic
|
||||
*>
|
||||
fn usz InterfaceList.remove_if(&self, InterfacePredicate filter)
|
||||
{
|
||||
return self._remove_if(filter, false);
|
||||
return interfacelist_remove_if(self, filter, false);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -392,7 +392,7 @@ fn usz InterfaceList.remove_if(&self, InterfacePredicate filter)
|
||||
*>
|
||||
fn usz InterfaceList.retain_if(&self, InterfacePredicate selection)
|
||||
{
|
||||
return self._remove_if(selection, true);
|
||||
return interfacelist_remove_if(self, selection, true);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -404,7 +404,7 @@ fn usz InterfaceList.retain_if(&self, InterfacePredicate selection)
|
||||
*>
|
||||
fn usz InterfaceList.remove_using_test(&self, InterfaceTest filter, Type context)
|
||||
{
|
||||
return self._remove_using_test(filter, false, context);
|
||||
return interfacelist_remove_using_test(self, filter, false, context);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -416,7 +416,7 @@ fn usz InterfaceList.remove_using_test(&self, InterfaceTest filter, Type context
|
||||
*>
|
||||
fn usz InterfaceList.retain_using_test(&self, InterfaceTest selection, Type context)
|
||||
{
|
||||
return self._remove_using_test(selection, true, context);
|
||||
return interfacelist_remove_using_test(self, selection, true, context);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -455,7 +455,7 @@ macro void InterfaceList.set(&self, usz index, value)
|
||||
|
||||
// -- private
|
||||
|
||||
fn void InterfaceList.ensure_capacity(&self, usz added = 1) @inline @private
|
||||
fn void interfacelist_ensure_capacity(InterfaceList* self, usz added = 1) @inline @private
|
||||
{
|
||||
usz new_size = self.size + added;
|
||||
if (self.capacity >= new_size) return;
|
||||
@@ -466,18 +466,18 @@ fn void InterfaceList.ensure_capacity(&self, usz added = 1) @inline @private
|
||||
self.reserve(new_capacity);
|
||||
}
|
||||
|
||||
fn void InterfaceList._append(&self, Type element) @local
|
||||
fn void interfacelist_append(InterfaceList* self, Type element) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
interfacelist_ensure_capacity(self);
|
||||
self.entries[self.size++] = element;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void InterfaceList._insert_at(&self, usz index, Type value) @local
|
||||
fn void interfacelist_insert_at(InterfaceList* self, usz index, Type value) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
interfacelist_ensure_capacity(self);
|
||||
for (usz i = self.size; i > index; i--)
|
||||
{
|
||||
self.entries[i] = self.entries[i - 1];
|
||||
@@ -486,7 +486,7 @@ fn void InterfaceList._insert_at(&self, usz index, Type value) @local
|
||||
self.entries[index] = value;
|
||||
}
|
||||
|
||||
macro usz InterfaceList._remove_using_test(&self, InterfaceTest filter, bool $invert, ctx) @local
|
||||
macro usz interfacelist_remove_using_test(InterfaceList* self, InterfaceTest filter, bool $invert, ctx) @local
|
||||
{
|
||||
usz size = self.size;
|
||||
for (usz i = size, usz k = size; k > 0; k = i)
|
||||
@@ -512,7 +512,7 @@ macro usz InterfaceList._remove_using_test(&self, InterfaceTest filter, bool $in
|
||||
return size - self.size;
|
||||
}
|
||||
|
||||
macro usz InterfaceList._remove_if(&self, InterfacePredicate filter, bool $invert) @local
|
||||
macro usz interfacelist_remove_if(InterfaceList* self, InterfacePredicate filter, bool $invert) @local
|
||||
{
|
||||
usz size = self.size;
|
||||
for (usz i = size, usz k = size; k > 0; k = i)
|
||||
|
||||
@@ -75,7 +75,7 @@ fn void LinkedBlockingQueue.free(&self)
|
||||
self.not_full.destroy();
|
||||
}
|
||||
|
||||
fn void LinkedBlockingQueue.link_entry(&self, QueueEntry* entry) @private
|
||||
fn void linkedblockingqueue_link_entry(LinkedBlockingQueue* self, QueueEntry* entry) @private
|
||||
{
|
||||
entry.next = null;
|
||||
entry.prev = self.tail;
|
||||
@@ -95,7 +95,7 @@ fn void LinkedBlockingQueue.link_entry(&self, QueueEntry* entry) @private
|
||||
}
|
||||
|
||||
|
||||
fn QueueEntry* LinkedBlockingQueue.unlink_head(&self) @private
|
||||
fn QueueEntry* linkedblockingqueue_unlink_head(LinkedBlockingQueue* self) @private
|
||||
{
|
||||
if (self.head == null) return null;
|
||||
|
||||
@@ -134,7 +134,7 @@ fn void LinkedBlockingQueue.push(&self, Value value)
|
||||
.next = null,
|
||||
.prev = null
|
||||
});
|
||||
self.link_entry(entry);
|
||||
linkedblockingqueue_link_entry(self, entry);
|
||||
|
||||
// Signal that queue is no longer empty
|
||||
self.not_empty.signal();
|
||||
@@ -156,7 +156,7 @@ fn Value LinkedBlockingQueue.poll(&self)
|
||||
self.not_empty.wait(&self.lock);
|
||||
}
|
||||
|
||||
QueueEntry* entry = self.unlink_head();
|
||||
QueueEntry* entry = linkedblockingqueue_unlink_head(self);
|
||||
Value value = entry.value;
|
||||
allocator::free(self.allocator, entry);
|
||||
if (self.capacity > 0)
|
||||
@@ -180,7 +180,7 @@ fn Value? LinkedBlockingQueue.pop(&self)
|
||||
{
|
||||
if (self.count == 0) return NO_MORE_ELEMENT~;
|
||||
|
||||
QueueEntry* entry = self.unlink_head();
|
||||
QueueEntry* entry = linkedblockingqueue_unlink_head(self);
|
||||
Value value = entry.value;
|
||||
allocator::free(self.allocator, entry);
|
||||
|
||||
@@ -217,7 +217,7 @@ fn Value? LinkedBlockingQueue.poll_timeout(&self, Duration timeout)
|
||||
if (!self.count) return NO_MORE_ELEMENT~;
|
||||
}
|
||||
|
||||
QueueEntry* entry = self.unlink_head();
|
||||
QueueEntry* entry = linkedblockingqueue_unlink_head(self);
|
||||
Value value = entry.value;
|
||||
allocator::free(self.allocator, entry);
|
||||
|
||||
@@ -273,7 +273,7 @@ fn void? LinkedBlockingQueue.try_push(&self, Value value)
|
||||
.next = null,
|
||||
.prev = null
|
||||
});
|
||||
self.link_entry(entry);
|
||||
linkedblockingqueue_link_entry(self, entry);
|
||||
self.not_empty.signal();
|
||||
};
|
||||
}
|
||||
@@ -307,7 +307,7 @@ fn void? LinkedBlockingQueue.push_timeout(&self, Value value, Duration timeout)
|
||||
.next = null,
|
||||
.prev = null
|
||||
});
|
||||
self.link_entry(entry);
|
||||
linkedblockingqueue_link_entry(self, entry);
|
||||
self.not_empty.signal();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ fn bool LinkedHashMap.is_initialized(&map)
|
||||
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);
|
||||
linkedhashmap_put_all_for_create(self, other_map);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -242,13 +242,13 @@ fn bool LinkedHashMap.set(&map, Key key, Value value) @operator([]=)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
map.add_entry(hash, key, value, index);
|
||||
linkedhashmap_add_entry(map, hash, key, value, index);
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void? LinkedHashMap.remove(&map, Key key) @maydiscard
|
||||
{
|
||||
if (!map.remove_entry_for_key(key)) return NOT_FOUND~;
|
||||
if (!linkedhashmap_remove_entry_for_key(map, key)) return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.clear(&map)
|
||||
@@ -259,7 +259,7 @@ fn void LinkedHashMap.clear(&map)
|
||||
while (entry)
|
||||
{
|
||||
LinkedEntry* next = entry.after;
|
||||
map.free_entry(entry);
|
||||
linkedhashmap_free_entry(map, entry);
|
||||
entry = next;
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ fn void LinkedHashMap.free(&map)
|
||||
{
|
||||
if (!map.is_initialized()) return;
|
||||
map.clear();
|
||||
map.free_internal(map.table.ptr);
|
||||
linkedhashmap_free_internal(map, map.table.ptr);
|
||||
map.table = {};
|
||||
}
|
||||
|
||||
@@ -396,7 +396,7 @@ fn bool LinkedHashMapIterator.has_next(&self)
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||
fn void linkedhashmap_add_entry(LinkedHashMap* map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||
{
|
||||
$if COPY_KEYS:
|
||||
key = key.copy(map.allocator);
|
||||
@@ -428,11 +428,11 @@ fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint buck
|
||||
|
||||
if (map.count++ >= map.threshold)
|
||||
{
|
||||
map.resize(map.table.len * 2);
|
||||
linkedhashmap_resize(map, map.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.resize(&map, uint new_capacity) @private
|
||||
fn void linkedhashmap_resize(LinkedHashMap* map, uint new_capacity) @private
|
||||
{
|
||||
LinkedEntry*[] old_table = map.table;
|
||||
uint old_capacity = old_table.len;
|
||||
@@ -502,7 +502,7 @@ fn void LinkedHashMap.resize(&map, uint new_capacity) @private
|
||||
}
|
||||
}
|
||||
|
||||
map.free_internal(old_table.ptr);
|
||||
linkedhashmap_free_internal(map, old_table.ptr);
|
||||
}
|
||||
|
||||
fn usz? LinkedHashMap.to_format(&self, Formatter* f) @dynamic
|
||||
@@ -517,7 +517,7 @@ fn usz? LinkedHashMap.to_format(&self, Formatter* f) @dynamic
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.transfer(&map, LinkedEntry*[] new_table) @private
|
||||
fn void linkedhashmap_transfer(LinkedHashMap* map, LinkedEntry*[] new_table) @private
|
||||
{
|
||||
LinkedEntry*[] src = map.table;
|
||||
uint new_capacity = new_table.len;
|
||||
@@ -536,7 +536,7 @@ fn void LinkedHashMap.transfer(&map, LinkedEntry*[] new_table) @private
|
||||
}
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.put_all_for_create(&map, LinkedHashMap* other_map) @private
|
||||
fn void linkedhashmap_put_all_for_create(LinkedHashMap* map, LinkedHashMap* other_map) @private
|
||||
{
|
||||
if (!other_map.count) return;
|
||||
other_map.@each(; Key key, Value value) {
|
||||
@@ -544,7 +544,7 @@ fn void LinkedHashMap.put_all_for_create(&map, LinkedHashMap* other_map) @privat
|
||||
};
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.put_for_create(&map, Key key, Value value) @private
|
||||
fn void linkedhashmap_put_for_create(LinkedHashMap* map, Key key, Value value) @private
|
||||
{
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
@@ -556,15 +556,15 @@ fn void LinkedHashMap.put_for_create(&map, Key key, Value value) @private
|
||||
return;
|
||||
}
|
||||
}
|
||||
map.create_entry(hash, key, value, i);
|
||||
linkedhashmap_create_entry(map, hash, key, value, i);
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.free_internal(&map, void* ptr) @inline @private
|
||||
fn void linkedhashmap_free_internal(LinkedHashMap* map, void* ptr) @inline @private
|
||||
{
|
||||
allocator::free(map.allocator, ptr);
|
||||
}
|
||||
|
||||
fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
|
||||
fn bool linkedhashmap_remove_entry_for_key(LinkedHashMap* map, Key key) @private
|
||||
{
|
||||
if (!map.count) return false;
|
||||
|
||||
@@ -605,7 +605,7 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
|
||||
}
|
||||
|
||||
map.count--;
|
||||
map.free_entry(e);
|
||||
linkedhashmap_free_entry(map, e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
@@ -614,23 +614,23 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
|
||||
fn void linkedhashmap_create_entry(LinkedHashMap* 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);
|
||||
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
|
||||
fn void linkedhashmap_free_entry(LinkedHashMap* self, LinkedEntry *entry) @local
|
||||
{
|
||||
$if COPY_KEYS:
|
||||
allocator::free(self.allocator, entry.key);
|
||||
allocator::free(self.allocator, entry.key);
|
||||
$endif
|
||||
self.free_internal(entry);
|
||||
linkedhashmap_free_internal(self, entry);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ fn LinkedHashSet* LinkedHashSet.init_from_set(&self, Allocator allocator, Linked
|
||||
LinkedEntry* entry = other_set.head;
|
||||
while (entry) // Save insertion order
|
||||
{
|
||||
self.put_for_create(entry.value);
|
||||
linkedhashset_put_for_create(self, entry.value);
|
||||
entry = entry.after;
|
||||
}
|
||||
return self;
|
||||
@@ -223,7 +223,7 @@ fn bool LinkedHashSet.add(&set, Value value)
|
||||
{
|
||||
if (e.hash == hash && equals(value, e.value)) return false;
|
||||
}
|
||||
set.add_entry(hash, value, index);
|
||||
linkedhashset_add_entry(set, hash, value, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ fn bool LinkedHashSet.contains(&set, Value value)
|
||||
*>
|
||||
fn void? LinkedHashSet.remove(&set, Value value) @maydiscard
|
||||
{
|
||||
if (!set.remove_entry_for_value(value)) return NOT_FOUND~;
|
||||
if (!linkedhashset_remove_entry_for_value(set, value)) return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn usz LinkedHashSet.remove_all(&set, Value[] values)
|
||||
@@ -274,7 +274,7 @@ fn usz LinkedHashSet.remove_all(&set, Value[] values)
|
||||
usz total;
|
||||
foreach (v : values)
|
||||
{
|
||||
if (set.remove_entry_for_value(v)) total++;
|
||||
if (linkedhashset_remove_entry_for_value(set, v)) total++;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
@@ -287,7 +287,7 @@ fn usz LinkedHashSet.remove_all_from(&set, LinkedHashSet* other)
|
||||
usz total;
|
||||
other.@each(;Value val)
|
||||
{
|
||||
if (set.remove_entry_for_value(val)) total++;
|
||||
if (linkedhashset_remove_entry_for_value(set, val)) total++;
|
||||
};
|
||||
return total;
|
||||
}
|
||||
@@ -299,7 +299,7 @@ fn void LinkedHashSet.free(&set)
|
||||
{
|
||||
if (!set.is_initialized()) return;
|
||||
set.clear();
|
||||
set.free_internal(set.table.ptr);
|
||||
linkedhashset_free_internal(set, set.table.ptr);
|
||||
set.table = {};
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ fn void LinkedHashSet.clear(&set)
|
||||
while (entry)
|
||||
{
|
||||
LinkedEntry* next = entry.after;
|
||||
set.free_entry(entry);
|
||||
linkedhashset_free_entry(set, entry);
|
||||
entry = next;
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ fn void LinkedHashSet.reserve(&set, usz capacity)
|
||||
{
|
||||
if (capacity > set.threshold)
|
||||
{
|
||||
set.resize(math::next_power_of_2(capacity));
|
||||
linkedhashset_resize(set, math::next_power_of_2(capacity));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +451,7 @@ fn bool LinkedHashSet.is_subset(&self, LinkedHashSet* other)
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
|
||||
fn void linkedhashset_add_entry(LinkedHashSet* set, uint hash, Value value, uint bucket_index) @private
|
||||
{
|
||||
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
|
||||
.hash = hash,
|
||||
@@ -478,11 +478,11 @@ fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index)
|
||||
|
||||
if (set.count++ >= set.threshold)
|
||||
{
|
||||
set.resize(set.table.len * 2);
|
||||
linkedhashset_resize(set, set.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.resize(&set, usz new_capacity) @private
|
||||
fn void linkedhashset_resize(LinkedHashSet* set, usz new_capacity) @private
|
||||
{
|
||||
LinkedEntry*[] old_table = set.table;
|
||||
usz old_capacity = old_table.len;
|
||||
@@ -552,7 +552,7 @@ fn void LinkedHashSet.resize(&set, usz new_capacity) @private
|
||||
}
|
||||
}
|
||||
|
||||
set.free_internal(old_table.ptr);
|
||||
linkedhashset_free_internal(set, old_table.ptr);
|
||||
}
|
||||
|
||||
fn usz? LinkedHashSet.to_format(&self, Formatter* f) @dynamic
|
||||
@@ -567,7 +567,7 @@ fn usz? LinkedHashSet.to_format(&self, Formatter* f) @dynamic
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.transfer(&set, LinkedEntry*[] new_table) @private
|
||||
fn void linked_hashset_transfer(LinkedHashSet* set, LinkedEntry*[] new_table) @private
|
||||
{
|
||||
LinkedEntry*[] src = set.table;
|
||||
uint new_capacity = new_table.len;
|
||||
@@ -586,7 +586,7 @@ fn void LinkedHashSet.transfer(&set, LinkedEntry*[] new_table) @private
|
||||
}
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.put_for_create(&set, Value value) @private
|
||||
fn void linkedhashset_put_for_create(LinkedHashSet* set, Value value) @private
|
||||
{
|
||||
uint hash = rehash(value.hash());
|
||||
uint i = index_for(hash, set.table.len);
|
||||
@@ -598,15 +598,15 @@ fn void LinkedHashSet.put_for_create(&set, Value value) @private
|
||||
return;
|
||||
}
|
||||
}
|
||||
set.create_entry(hash, value, i);
|
||||
linkedhashset_create_entry(set, hash, value, i);
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.free_internal(&set, void* ptr) @inline @private
|
||||
fn void linkedhashset_free_internal(LinkedHashSet* set, void* ptr) @inline @private
|
||||
{
|
||||
allocator::free(set.allocator, ptr);
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
|
||||
fn void linkedhashset_create_entry(LinkedHashSet* set, uint hash, Value value, int bucket_index) @private
|
||||
{
|
||||
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
|
||||
.hash = hash,
|
||||
@@ -633,7 +633,7 @@ fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_inde
|
||||
set.count++;
|
||||
}
|
||||
|
||||
fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
|
||||
fn bool linkedhashset_remove_entry_for_value(LinkedHashSet* set, Value value) @private
|
||||
{
|
||||
if (!set.count) return false;
|
||||
|
||||
@@ -674,7 +674,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
|
||||
}
|
||||
|
||||
set.count--;
|
||||
set.free_entry(e);
|
||||
linkedhashset_free_entry(set, e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
@@ -683,7 +683,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.free_entry(&set, LinkedEntry *entry) @private
|
||||
fn void linkedhashset_free_entry(LinkedHashSet* set, LinkedEntry *entry) @private
|
||||
{
|
||||
allocator::free(set.allocator, entry);
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ fn usz? LinkedList.rindex_of(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
*>
|
||||
fn void LinkedList.remove_at(&self, usz index)
|
||||
{
|
||||
self.unlink(self.node_at_index(index));
|
||||
linked_list_unlink(self, self.node_at_index(index));
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -232,47 +232,47 @@ fn void LinkedList.insert_at(&self, usz index, Type element)
|
||||
case self.size:
|
||||
self.push(element);
|
||||
default:
|
||||
self.link_before(self.node_at_index(index), element);
|
||||
linked_list_link_before(self, self.node_at_index(index), element);
|
||||
}
|
||||
}
|
||||
<*
|
||||
@require succ != null
|
||||
*>
|
||||
fn void LinkedList.link_before(&self, Node *succ, Type value) @private
|
||||
fn void linked_list_link_before(LinkedList* l, Node *succ, Type value) @private
|
||||
{
|
||||
Node* pred = succ.prev;
|
||||
Node* new_node = self.alloc_node();
|
||||
Node* new_node = l.alloc_node();
|
||||
*new_node = { .prev = pred, .next = succ, .value = value };
|
||||
succ.prev = new_node;
|
||||
if (!pred)
|
||||
{
|
||||
self._first = new_node;
|
||||
l._first = new_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred.next = new_node;
|
||||
}
|
||||
self.size++;
|
||||
l.size++;
|
||||
}
|
||||
|
||||
<*
|
||||
@require self._first != null
|
||||
@require l._first != null
|
||||
*>
|
||||
fn void LinkedList.unlink_first(&self) @private
|
||||
fn void linked_list_unlink_first(LinkedList* l) @private
|
||||
{
|
||||
Node* f = self._first;
|
||||
Node* f = l._first;
|
||||
Node* next = f.next;
|
||||
self.free_node(f);
|
||||
self._first = next;
|
||||
l.free_node(f);
|
||||
l._first = next;
|
||||
if (!next)
|
||||
{
|
||||
self._last = null;
|
||||
l._last = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
next.prev = null;
|
||||
}
|
||||
self.size--;
|
||||
l.size--;
|
||||
}
|
||||
|
||||
fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
@@ -285,7 +285,7 @@ fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
case equals(node.value, t):
|
||||
Node* next = node.next;
|
||||
self.unlink(node);
|
||||
linked_list_unlink(self, node);
|
||||
node = next;
|
||||
default:
|
||||
node = node.next;
|
||||
@@ -297,7 +297,7 @@ fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
fn Type? LinkedList.pop(&self)
|
||||
{
|
||||
if (!self._last) return NO_MORE_ELEMENT~;
|
||||
defer self.unlink_last();
|
||||
defer linked_list_unlink_last(self);
|
||||
return self._last.value;
|
||||
}
|
||||
|
||||
@@ -309,20 +309,20 @@ fn bool LinkedList.is_empty(&self)
|
||||
fn Type? LinkedList.pop_front(&self)
|
||||
{
|
||||
if (!self._first) return NO_MORE_ELEMENT~;
|
||||
defer self.unlink_first();
|
||||
defer linked_list_unlink_first(self);
|
||||
return self._first.value;
|
||||
}
|
||||
|
||||
fn void? LinkedList.remove_last(&self) @maydiscard
|
||||
{
|
||||
if (!self._first) return NO_MORE_ELEMENT~;
|
||||
self.unlink_last();
|
||||
linked_list_unlink_last(self);
|
||||
}
|
||||
|
||||
fn void? LinkedList.remove_first(&self) @maydiscard
|
||||
{
|
||||
if (!self._first) return NO_MORE_ELEMENT~;
|
||||
self.unlink_first();
|
||||
linked_list_unlink_first(self);
|
||||
}
|
||||
|
||||
|
||||
@@ -332,7 +332,7 @@ fn bool LinkedList.remove_first_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
if (node.value == t)
|
||||
{
|
||||
self.unlink(node);
|
||||
linked_list_unlink(self, node);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -345,7 +345,7 @@ fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
if (node.value == t)
|
||||
{
|
||||
self.unlink(node);
|
||||
linked_list_unlink(self, node);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -354,7 +354,7 @@ fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
<*
|
||||
@require self._last != null
|
||||
*>
|
||||
fn void LinkedList.unlink_last(&self) @inline @private
|
||||
fn void linked_list_unlink_last(LinkedList* self) @inline @private
|
||||
{
|
||||
Node* l = self._last;
|
||||
Node* prev = l.prev;
|
||||
@@ -374,7 +374,7 @@ fn void LinkedList.unlink_last(&self) @inline @private
|
||||
<*
|
||||
@require x != null
|
||||
*>
|
||||
fn void LinkedList.unlink(&self, Node* x) @private
|
||||
fn void linked_list_unlink(LinkedList* self, Node* x) @private
|
||||
{
|
||||
Node* next = x.next;
|
||||
Node* prev = x.prev;
|
||||
|
||||
@@ -82,7 +82,7 @@ fn void List.init_wrapping_array(&self, Allocator allocator, Type[] types)
|
||||
self.allocator = allocator;
|
||||
self.capacity = types.len;
|
||||
self.entries = types.ptr;
|
||||
self.set_size(types.len);
|
||||
list_set_size(self, types.len);
|
||||
}
|
||||
|
||||
fn bool List.is_initialized(&self) @inline => self.allocator != null && self.allocator != (Allocator)&dummy;
|
||||
@@ -110,19 +110,19 @@ fn usz? List.to_format(&self, Formatter* formatter) @dynamic
|
||||
fn void List.push(&self, Type element) @inline
|
||||
{
|
||||
self.reserve(1);
|
||||
self.entries[self.set_size(self.size + 1)] = element;
|
||||
self.entries[list_set_size(self, self.size + 1)] = element;
|
||||
}
|
||||
|
||||
fn Type? List.pop(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
defer self.set_size(self.size - 1);
|
||||
defer list_set_size(self, self.size - 1);
|
||||
return self.entries[self.size - 1];
|
||||
}
|
||||
|
||||
fn void List.clear(&self)
|
||||
{
|
||||
self.set_size(0);
|
||||
list_set_size(self, 0);
|
||||
}
|
||||
|
||||
fn Type? List.pop_first(&self)
|
||||
@@ -138,7 +138,7 @@ fn Type? List.pop_first(&self)
|
||||
fn void List.remove_at(&self, usz index)
|
||||
{
|
||||
usz new_size = self.size - 1;
|
||||
defer self.set_size(new_size);
|
||||
defer list_set_size(self, new_size);
|
||||
if (!new_size || index == new_size) return;
|
||||
self.entries[index .. new_size - 1] = self.entries[index + 1 .. new_size];
|
||||
}
|
||||
@@ -147,7 +147,7 @@ fn void List.add_all(&self, List* other_list)
|
||||
{
|
||||
if (!other_list.size) return;
|
||||
self.reserve(other_list.size);
|
||||
usz index = self.set_size(self.size + other_list.size);
|
||||
usz index = list_set_size(self, self.size + other_list.size);
|
||||
foreach (&value : other_list)
|
||||
{
|
||||
self.entries[index++] = *value;
|
||||
@@ -203,7 +203,7 @@ fn void List.add_array(&self, Type[] array) @deprecated("Use push_all")
|
||||
{
|
||||
if (!array.len) return;
|
||||
self.reserve(array.len);
|
||||
usz index = self.set_size(self.size + array.len);
|
||||
usz index = list_set_size(self, self.size + array.len);
|
||||
self.entries[index : array.len] = array[..];
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ fn void List.push_all(&self, Type[] array)
|
||||
{
|
||||
if (!array.len) return;
|
||||
self.reserve(array.len);
|
||||
usz index = self.set_size(self.size + array.len);
|
||||
usz index = list_set_size(self, self.size + array.len);
|
||||
self.entries[index : array.len] = array[..];
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ fn void List.push_front(&self, Type type) @inline
|
||||
fn void List.insert_at(&self, usz index, Type type)
|
||||
{
|
||||
self.reserve(1);
|
||||
self.set_size(self.size + 1);
|
||||
list_set_size(self, self.size + 1);
|
||||
for (isz i = self.size - 1; i > index; i--)
|
||||
{
|
||||
self.entries[i] = self.entries[i - 1];
|
||||
@@ -251,7 +251,7 @@ fn void List.set_at(&self, usz index, Type type)
|
||||
fn void? List.remove_last(&self) @maydiscard
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
self.set_size(self.size - 1);
|
||||
list_set_size(self, self.size - 1);
|
||||
}
|
||||
|
||||
fn void? List.remove_first(&self) @maydiscard
|
||||
@@ -299,7 +299,7 @@ fn void List.free(&self)
|
||||
{
|
||||
if (!self.allocator || self.allocator.ptr == &dummy || !self.capacity) return;
|
||||
|
||||
self.pre_free(); // Remove sanitizer annotation
|
||||
list_pre_free(self); // Remove sanitizer annotation
|
||||
|
||||
$if type_is_overaligned():
|
||||
allocator::free_aligned(self.allocator, self.entries);
|
||||
@@ -358,7 +358,7 @@ fn usz List.retain_using_test(&self, ElementTest filter, any context)
|
||||
return list_common::list_remove_using_test(self, filter, true, context);
|
||||
}
|
||||
|
||||
fn void List.ensure_capacity(&self, usz min_capacity) @local
|
||||
fn void list_ensure_capacity(List* self, usz min_capacity) @local
|
||||
{
|
||||
if (!min_capacity) return;
|
||||
if (self.capacity >= min_capacity) return;
|
||||
@@ -374,7 +374,7 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
|
||||
break;
|
||||
}
|
||||
|
||||
self.pre_free(); // Remove sanitizer annotation
|
||||
list_pre_free(self); // Remove sanitizer annotation
|
||||
|
||||
min_capacity = math::next_power_of_2(min_capacity);
|
||||
$if type_is_overaligned():
|
||||
@@ -384,7 +384,7 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
|
||||
$endif;
|
||||
self.capacity = min_capacity;
|
||||
|
||||
self.post_alloc(); // Add sanitizer annotation
|
||||
list_post_alloc(self); // Add sanitizer annotation
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -419,7 +419,7 @@ fn void List.reserve(&self, usz added)
|
||||
assert(new_size < usz.max / 2U);
|
||||
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
|
||||
while (new_capacity < new_size) new_capacity *= 2U;
|
||||
self.ensure_capacity(new_capacity);
|
||||
list_ensure_capacity(self, new_capacity);
|
||||
}
|
||||
|
||||
fn void List._update_size_change(&self,usz old_size, usz new_size)
|
||||
@@ -436,7 +436,7 @@ fn void List._update_size_change(&self,usz old_size, usz new_size)
|
||||
<*
|
||||
@require new_size == 0 || self.capacity != 0
|
||||
*>
|
||||
fn usz List.set_size(&self, usz new_size) @inline @private
|
||||
fn usz list_set_size(List* self, usz new_size) @inline @private
|
||||
{
|
||||
usz old_size = self.size;
|
||||
self._update_size_change(old_size, new_size);
|
||||
@@ -444,7 +444,7 @@ fn usz List.set_size(&self, usz new_size) @inline @private
|
||||
return old_size;
|
||||
}
|
||||
|
||||
macro void List.pre_free(&self) @private
|
||||
macro void list_pre_free(List* self) @private
|
||||
{
|
||||
if (!self.capacity) return;
|
||||
self._update_size_change(self.size, self.capacity);
|
||||
@@ -453,7 +453,7 @@ macro void List.pre_free(&self) @private
|
||||
<*
|
||||
@require self.capacity > 0
|
||||
*>
|
||||
macro void List.post_alloc(&self) @private
|
||||
macro void list_post_alloc(List* self) @private
|
||||
{
|
||||
self._update_size_change(self.capacity, self.size);
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ fn bool Object.is_indexable(&self) => self.is_empty() || self.is_array();
|
||||
<*
|
||||
@require self.is_keyable()
|
||||
*>
|
||||
fn void Object.init_map_if_needed(&self) @private
|
||||
fn void object_init_map_if_needed(Object* self) @private
|
||||
{
|
||||
if (self.is_empty())
|
||||
{
|
||||
@@ -163,7 +163,7 @@ fn void Object.init_map_if_needed(&self) @private
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
*>
|
||||
fn void Object.init_array_if_needed(&self) @private
|
||||
fn void object_init_array_if_needed(Object* self) @private
|
||||
{
|
||||
if (self.is_empty())
|
||||
{
|
||||
@@ -175,9 +175,9 @@ fn void Object.init_array_if_needed(&self) @private
|
||||
<*
|
||||
@require self.is_keyable()
|
||||
*>
|
||||
fn void Object.set_object(&self, String key, Object* new_object) @private
|
||||
fn void object_set_object(Object* self, String key, Object* new_object) @private
|
||||
{
|
||||
self.init_map_if_needed();
|
||||
object_init_map_if_needed(self);
|
||||
Object*? val = self.map.get_entry(key).value;
|
||||
defer (void)val.free();
|
||||
self.map.set(key, new_object);
|
||||
@@ -214,7 +214,7 @@ macro Object* Object.object_from_value(&self, value) @private
|
||||
macro Object* Object.set(&self, String key, value)
|
||||
{
|
||||
Object* val = self.object_from_value(value);
|
||||
self.set_object(key, val);
|
||||
object_set_object(self, key, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ fn usz Object.get_len(&self)
|
||||
*>
|
||||
fn void Object.push_object(&self, Object* to_append)
|
||||
{
|
||||
self.init_array_if_needed();
|
||||
object_init_array_if_needed(self);
|
||||
self.array.push(to_append);
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ fn void Object.push_object(&self, Object* to_append)
|
||||
*>
|
||||
fn void Object.set_object_at(&self, usz index, Object* to_set)
|
||||
{
|
||||
self.init_array_if_needed();
|
||||
object_init_array_if_needed(self);
|
||||
while (self.array.len() < index)
|
||||
{
|
||||
self.array.push(&NULL_OBJECT);
|
||||
|
||||
1108
lib/std/compression/deflate.c3
Normal file
1108
lib/std/compression/deflate.c3
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ const uint PIXELS_MAX = 400000000;
|
||||
Purely informative. It will be saved to the file header,
|
||||
but does not affect how chunks are en-/decoded.
|
||||
*>
|
||||
enum QOIColorspace : const char
|
||||
constdef QOIColorspace : char
|
||||
{
|
||||
<* sRGB with linear alpha *>
|
||||
SRGB = 0,
|
||||
@@ -21,7 +21,7 @@ enum QOIColorspace : const char
|
||||
AUTO can be used when decoding to automatically determine
|
||||
the channels from the file's header.
|
||||
*>
|
||||
enum QOIChannels : const inline char
|
||||
constdef QOIChannels : inline char
|
||||
{
|
||||
AUTO = 0,
|
||||
RGB = 3,
|
||||
|
||||
1215
lib/std/compression/zip.c3
Normal file
1215
lib/std/compression/zip.c3
Normal file
File diff suppressed because it is too large
Load Diff
@@ -56,7 +56,7 @@ fn BackedArenaAllocator*? new_backed_allocator(usz size, Allocator allocator)
|
||||
fn void BackedArenaAllocator.destroy(&self)
|
||||
{
|
||||
self.reset(0);
|
||||
if (self.last_page) (void)self._free_page(self.last_page);
|
||||
if (self.last_page) (void)_free_page(self, self.last_page);
|
||||
allocator::free(self.backing_allocator, self);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ fn void BackedArenaAllocator.reset(&self, usz mark)
|
||||
self.used = last_page.mark;
|
||||
ExtraPage *to_free = last_page;
|
||||
last_page = last_page.prev_page;
|
||||
self._free_page(to_free)!!;
|
||||
_free_page(self, to_free)!!;
|
||||
}
|
||||
self.last_page = last_page;
|
||||
$if env::COMPILER_SAFE_MODE || env::ADDRESS_SANITIZER:
|
||||
@@ -98,13 +98,13 @@ fn void BackedArenaAllocator.reset(&self, usz mark)
|
||||
self.used = mark;
|
||||
}
|
||||
|
||||
fn void? BackedArenaAllocator._free_page(&self, ExtraPage* page) @inline @local
|
||||
fn void? _free_page(BackedArenaAllocator* self, ExtraPage* page) @inline @local
|
||||
{
|
||||
void* mem = page.start;
|
||||
return self.backing_allocator.release(mem, page.is_aligned());
|
||||
}
|
||||
|
||||
fn void*? BackedArenaAllocator._realloc_page(&self, ExtraPage* page, usz size, usz alignment) @inline @local
|
||||
fn void*? _realloc_page(BackedArenaAllocator* self, ExtraPage* page, usz size, usz alignment) @inline @local
|
||||
{
|
||||
// Then the actual start pointer:
|
||||
void* real_pointer = page.start;
|
||||
@@ -133,7 +133,7 @@ fn void*? BackedArenaAllocator.resize(&self, void* pointer, usz size, usz alignm
|
||||
assert(self.last_page, "Realloc of unrelated pointer");
|
||||
// First grab the page
|
||||
ExtraPage *page = pointer - ExtraPage.sizeof;
|
||||
return self._realloc_page(page, size, alignment);
|
||||
return _realloc_page(self, page, size, alignment);
|
||||
}
|
||||
|
||||
AllocChunk* data = self.acquire(size, NO_ZERO, alignment)!;
|
||||
|
||||
@@ -137,12 +137,67 @@ fn void DynamicArenaAllocator.reset(&self)
|
||||
self.page = page;
|
||||
}
|
||||
|
||||
|
||||
|
||||
<*
|
||||
@require size > 0 : `acquire expects size > 0`
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
|
||||
*>
|
||||
fn void*? DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
DynamicArenaPage* page = self.page;
|
||||
|
||||
void* ptr @noinit;
|
||||
do SET_DONE:
|
||||
{
|
||||
if (!page && self.unused_page)
|
||||
{
|
||||
self.page = page = self.unused_page;
|
||||
self.unused_page = page.prev_arena;
|
||||
page.prev_arena = null;
|
||||
}
|
||||
if (!page)
|
||||
{
|
||||
ptr = _alloc_new(self, size, alignment)!;
|
||||
break SET_DONE;
|
||||
}
|
||||
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
|
||||
usz new_used = start - page.memory + size;
|
||||
if ALLOCATE_NEW: (new_used > page.total)
|
||||
{
|
||||
if ((page = self.unused_page))
|
||||
{
|
||||
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
|
||||
new_used = start + size - page.memory;
|
||||
if (page.total >= new_used)
|
||||
{
|
||||
self.unused_page = page.prev_arena;
|
||||
page.prev_arena = self.page;
|
||||
self.page = page;
|
||||
break ALLOCATE_NEW;
|
||||
}
|
||||
}
|
||||
ptr = _alloc_new(self, size, alignment)!;
|
||||
break SET_DONE;
|
||||
}
|
||||
page.used = new_used;
|
||||
assert(start + size == page.memory + page.used);
|
||||
ptr = start;
|
||||
DynamicArenaChunk* chunk = (DynamicArenaChunk*)ptr - 1;
|
||||
chunk.size = size;
|
||||
};
|
||||
if (init_type == ZERO) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
<*
|
||||
@require math::is_power_of_2(alignment)
|
||||
@require size > 0
|
||||
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
|
||||
*>
|
||||
fn void*? DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local
|
||||
fn void*? _alloc_new(DynamicArenaAllocator* self, usz size, usz alignment) @local
|
||||
{
|
||||
// First, make sure that we can align it, extending the page size if needed.
|
||||
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + alignment, alignment));
|
||||
@@ -166,57 +221,4 @@ fn void*? DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @loca
|
||||
self.page = page;
|
||||
page.current_stack_ptr = mem_start;
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
<*
|
||||
@require size > 0 : `acquire expects size > 0`
|
||||
@require !alignment || math::is_power_of_2(alignment)
|
||||
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
|
||||
*>
|
||||
fn void*? DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
DynamicArenaPage* page = self.page;
|
||||
|
||||
void* ptr @noinit;
|
||||
do SET_DONE:
|
||||
{
|
||||
if (!page && self.unused_page)
|
||||
{
|
||||
self.page = page = self.unused_page;
|
||||
self.unused_page = page.prev_arena;
|
||||
page.prev_arena = null;
|
||||
}
|
||||
if (!page)
|
||||
{
|
||||
ptr = self._alloc_new(size, alignment)!;
|
||||
break SET_DONE;
|
||||
}
|
||||
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
|
||||
usz new_used = start - page.memory + size;
|
||||
if ALLOCATE_NEW: (new_used > page.total)
|
||||
{
|
||||
if ((page = self.unused_page))
|
||||
{
|
||||
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
|
||||
new_used = start + size - page.memory;
|
||||
if (page.total >= new_used)
|
||||
{
|
||||
self.unused_page = page.prev_arena;
|
||||
page.prev_arena = self.page;
|
||||
self.page = page;
|
||||
break ALLOCATE_NEW;
|
||||
}
|
||||
}
|
||||
ptr = self._alloc_new(size, alignment)!;
|
||||
break SET_DONE;
|
||||
}
|
||||
page.used = new_used;
|
||||
assert(start + size == page.memory + page.used);
|
||||
ptr = start;
|
||||
DynamicArenaChunk* chunk = (DynamicArenaChunk*)ptr - 1;
|
||||
chunk.size = size;
|
||||
};
|
||||
if (init_type == ZERO) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
@@ -32,58 +32,58 @@ fn void*? SimpleHeapAllocator.acquire(&self, usz size, AllocInitType init_type,
|
||||
{
|
||||
if (init_type == ZERO)
|
||||
{
|
||||
return alignment > 0 ? @aligned_alloc(self._calloc, size, alignment) : self._calloc(size);
|
||||
return alignment > 0 ? @aligned_alloc_fn(self, simple_alloc_calloc, size, alignment) : simple_alloc_calloc(self, size);
|
||||
}
|
||||
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : self._alloc(size);
|
||||
return alignment > 0 ? @aligned_alloc_fn(self, simple_alloc_alloc, size, alignment) : simple_alloc_alloc(self, size);
|
||||
}
|
||||
|
||||
fn void*? SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
|
||||
{
|
||||
return alignment > 0
|
||||
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment)
|
||||
: self._realloc(old_pointer, size);
|
||||
? @aligned_realloc_fn(self, simple_alloc_calloc, simple_alloc_free, old_pointer, size, alignment)
|
||||
: simple_alloc_realloc(self, old_pointer, size);
|
||||
}
|
||||
|
||||
fn void SimpleHeapAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
|
||||
{
|
||||
if (aligned)
|
||||
{
|
||||
@aligned_free(self._free, old_pointer)!!;
|
||||
@aligned_free_fn(self, simple_alloc_free, old_pointer)!!;
|
||||
}
|
||||
else
|
||||
{
|
||||
self._free(old_pointer);
|
||||
simple_alloc_free(self, old_pointer);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@require old_pointer && bytes > 0
|
||||
*>
|
||||
fn void*? SimpleHeapAllocator._realloc(&self, void* old_pointer, usz bytes) @local
|
||||
fn void*? simple_alloc_realloc(SimpleHeapAllocator* self, void* old_pointer, usz bytes) @local
|
||||
{
|
||||
// Find the block header.
|
||||
Header* block = (Header*)old_pointer - 1;
|
||||
if (block.size >= bytes) return old_pointer;
|
||||
void* new = self._alloc(bytes)!;
|
||||
void* new = simple_alloc_alloc(self, bytes)!;
|
||||
usz max_to_copy = math::min(block.size, bytes);
|
||||
mem::copy(new, old_pointer, max_to_copy);
|
||||
self._free(old_pointer);
|
||||
simple_alloc_free(self, old_pointer);
|
||||
return new;
|
||||
}
|
||||
|
||||
fn void*? SimpleHeapAllocator._calloc(&self, usz bytes) @local
|
||||
fn void*? simple_alloc_calloc(SimpleHeapAllocator* self, usz bytes) @local
|
||||
{
|
||||
void* data = self._alloc(bytes)!;
|
||||
void* data = simple_alloc_alloc(self, bytes)!;
|
||||
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return data;
|
||||
}
|
||||
|
||||
fn void*? SimpleHeapAllocator._alloc(&self, usz bytes) @local
|
||||
fn void*? simple_alloc_alloc(SimpleHeapAllocator* self, usz bytes) @local
|
||||
{
|
||||
usz aligned_bytes = mem::aligned_offset(bytes, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
if (!self.free_list)
|
||||
{
|
||||
self.add_block(aligned_bytes)!;
|
||||
simple_alloc_add_block(self, aligned_bytes)!;
|
||||
}
|
||||
|
||||
Header* current = self.free_list;
|
||||
@@ -123,22 +123,22 @@ fn void*? SimpleHeapAllocator._alloc(&self, usz bytes) @local
|
||||
current = current.next;
|
||||
}
|
||||
}
|
||||
self.add_block(aligned_bytes)!;
|
||||
return self._alloc(aligned_bytes);
|
||||
simple_alloc_add_block(self, aligned_bytes)!;
|
||||
return simple_alloc_alloc(self, aligned_bytes);
|
||||
}
|
||||
|
||||
fn void? SimpleHeapAllocator.add_block(&self, usz aligned_bytes) @local
|
||||
fn void? simple_alloc_add_block(SimpleHeapAllocator* self, usz aligned_bytes) @local
|
||||
{
|
||||
assert(mem::aligned_offset(aligned_bytes, mem::DEFAULT_MEM_ALIGNMENT) == aligned_bytes);
|
||||
char[] result = self.alloc_fn(aligned_bytes + Header.sizeof)!;
|
||||
Header* new_block = (Header*)result.ptr;
|
||||
new_block.size = result.len - Header.sizeof;
|
||||
new_block.next = null;
|
||||
self._free(new_block + 1);
|
||||
simple_alloc_free(self, new_block + 1);
|
||||
}
|
||||
|
||||
|
||||
fn void SimpleHeapAllocator._free(&self, void* ptr) @local
|
||||
fn void simple_alloc_free(SimpleHeapAllocator* self, void* ptr) @local
|
||||
{
|
||||
// Empty ptr -> do nothing.
|
||||
if (!ptr) return;
|
||||
|
||||
@@ -15,7 +15,6 @@ module std::core::mem::allocator @if(env::POSIX);
|
||||
import std::os;
|
||||
import libc;
|
||||
|
||||
|
||||
fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
|
||||
{
|
||||
if (init_type == ZERO)
|
||||
@@ -49,25 +48,32 @@ fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
|
||||
|
||||
fn void*? LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
|
||||
{
|
||||
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY~;
|
||||
void* new_ptr;
|
||||
if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return mem::OUT_OF_MEMORY~;
|
||||
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY~;
|
||||
|
||||
$switch:
|
||||
$case env::DARWIN:
|
||||
usz old_usable_size = darwin::malloc_size(old_ptr);
|
||||
$case env::LINUX:
|
||||
usz old_usable_size = linux::malloc_usable_size(old_ptr);
|
||||
$default:
|
||||
usz old_usable_size = new_bytes;
|
||||
$endswitch
|
||||
// Try realloc, even though it might be unaligned.
|
||||
void* new_ptr = libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY~!;
|
||||
|
||||
// If it's aligned then we're done!
|
||||
uptr ptr_val = (uptr)new_ptr;
|
||||
if (ptr_val & (alignment - 1) == 0) return new_ptr;
|
||||
|
||||
// We failed, so we need to use memalign
|
||||
// We will free new_ptr before we exit.
|
||||
defer libc::free(new_ptr);
|
||||
|
||||
// Create a pointer which is sure to be aligned.
|
||||
void* aligned_ptr;
|
||||
if (posix::posix_memalign(&aligned_ptr, alignment, new_bytes))
|
||||
{
|
||||
return mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
// Now it is safe to copy the full range of data.
|
||||
mem::copy(aligned_ptr, new_ptr, new_bytes, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return aligned_ptr;
|
||||
|
||||
usz copy_size = new_bytes < old_usable_size ? new_bytes : old_usable_size;
|
||||
mem::copy(new_ptr, old_ptr, copy_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
libc::free(old_ptr);
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
|
||||
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
|
||||
{
|
||||
libc::free(old_ptr);
|
||||
|
||||
@@ -127,7 +127,7 @@ fn void TempAllocator.reset(&self)
|
||||
{
|
||||
TempAllocator* old = child;
|
||||
child = old.derived;
|
||||
old.destroy();
|
||||
temp_allocator_destroy(old);
|
||||
}
|
||||
self.capacity = self.original_capacity;
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
@@ -142,17 +142,17 @@ fn void TempAllocator.reset(&self)
|
||||
fn void TempAllocator.free(&self)
|
||||
{
|
||||
self.reset();
|
||||
self.destroy();
|
||||
temp_allocator_destroy(self);
|
||||
}
|
||||
|
||||
fn void TempAllocator.destroy(&self) @local
|
||||
fn void temp_allocator_destroy(TempAllocator* self)
|
||||
{
|
||||
TempAllocatorPage *last_page = self.last_page;
|
||||
while (last_page)
|
||||
{
|
||||
TempAllocatorPage *to_free = last_page;
|
||||
last_page = last_page.prev_page;
|
||||
self._free_page(to_free)!!;
|
||||
_free_page(self, to_free)!!;
|
||||
}
|
||||
if (self.allocated)
|
||||
{
|
||||
@@ -179,33 +179,6 @@ fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
|
||||
}
|
||||
|
||||
|
||||
fn void? TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local
|
||||
{
|
||||
void* mem = page.start;
|
||||
return self.backing_allocator.release(mem, page.is_aligned());
|
||||
}
|
||||
|
||||
fn void*? TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
|
||||
{
|
||||
// Then the actual start pointer:
|
||||
void* real_pointer = page.start;
|
||||
|
||||
// Walk backwards to find the pointer to this page.
|
||||
TempAllocatorPage **pointer_to_prev = &self.last_page;
|
||||
// Remove the page from the list
|
||||
while (*pointer_to_prev != page)
|
||||
{
|
||||
pointer_to_prev = &((*pointer_to_prev).prev_page);
|
||||
}
|
||||
*pointer_to_prev = page.prev_page;
|
||||
usz page_size = page.pagesize();
|
||||
// Clear on size > original size.
|
||||
void* data = self.acquire(size, NO_ZERO, alignment)!;
|
||||
if (page_size > size) page_size = size;
|
||||
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
self.backing_allocator.release(real_pointer, page.is_aligned());
|
||||
return data;
|
||||
}
|
||||
|
||||
fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
|
||||
{
|
||||
@@ -215,7 +188,7 @@ fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @d
|
||||
assert(self.last_page, "Realloc of non temp pointer");
|
||||
// First grab the page
|
||||
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
|
||||
return self._realloc_page(page, size, alignment);
|
||||
return _realloc_page(self, page, size, alignment);
|
||||
}
|
||||
bool is_realloc_of_last = chunk.size + pointer == &self.data[self.used];
|
||||
if (is_realloc_of_last)
|
||||
@@ -326,9 +299,39 @@ fn void*? TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
|
||||
return &page.data[0];
|
||||
}
|
||||
|
||||
|
||||
fn void? _free_page(TempAllocator* self, TempAllocatorPage* page) @inline @local
|
||||
{
|
||||
void* mem = page.start;
|
||||
return self.backing_allocator.release(mem, page.is_aligned());
|
||||
}
|
||||
|
||||
fn void*? _realloc_page(TempAllocator* self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
|
||||
{
|
||||
// Then the actual start pointer:
|
||||
void* real_pointer = page.start;
|
||||
|
||||
// Walk backwards to find the pointer to this page.
|
||||
TempAllocatorPage **pointer_to_prev = &self.last_page;
|
||||
// Remove the page from the list
|
||||
while (*pointer_to_prev != page)
|
||||
{
|
||||
pointer_to_prev = &((*pointer_to_prev).prev_page);
|
||||
}
|
||||
*pointer_to_prev = page.prev_page;
|
||||
usz page_size = page.pagesize();
|
||||
// Clear on size > original size.
|
||||
void* data = self.acquire(size, NO_ZERO, alignment)!;
|
||||
if (page_size > size) page_size = size;
|
||||
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
self.backing_allocator.release(real_pointer, page.is_aligned());
|
||||
return data;
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -383,10 +386,10 @@ fn void TempAllocator.reset(&self)
|
||||
}
|
||||
fn void TempAllocator.free(&self)
|
||||
{
|
||||
self.destroy();
|
||||
_destroy(self);
|
||||
}
|
||||
|
||||
fn void TempAllocator.destroy(&self) @local
|
||||
fn void _destroy(TempAllocator* self) @local
|
||||
{
|
||||
TempAllocator* child = self.derived;
|
||||
if (!child) return;
|
||||
@@ -403,4 +406,4 @@ fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @d
|
||||
fn void TempAllocator.release(&self, void* old_pointer, bool b) @dynamic
|
||||
{
|
||||
self.vmem.release(old_pointer, b) @inline;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module std::core::string::ansi;
|
||||
import std::io;
|
||||
|
||||
enum Ansi : const inline String
|
||||
constdef Ansi : inline String
|
||||
{
|
||||
RESET = "\e[0m",
|
||||
BOLD = "\e[1m",
|
||||
|
||||
@@ -329,6 +329,72 @@ macro bool @all(array, #predicate)
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Extract a copy of all even-index elements from an input array.
|
||||
|
||||
@param [&inout] allocator : "The allocator used to create the return array."
|
||||
@param [in] array : "The array from which to extract all even elements."
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $defined(array[:lengthof(array)]) : "Expected a sliceable list"
|
||||
*>
|
||||
macro even(Allocator allocator, array)
|
||||
{
|
||||
return unlace_impl{$typeof(array[0])}(allocator, array[:lengthof(array)]);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Extract a copy of all odd-index elements from an input array.
|
||||
|
||||
@param [&inout] allocator : "The allocator used to create the return array."
|
||||
@param [in] array : "The array from which to extract all odd elements."
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $defined(array[:lengthof(array)]) : "Expected a sliceable list"
|
||||
*>
|
||||
macro odd(Allocator allocator, array)
|
||||
{
|
||||
return unlace_impl{$typeof(array[0])}(allocator, lengthof(array) > 1 ? array[1..] : ($typeof(array[0])[]){});
|
||||
}
|
||||
|
||||
<*
|
||||
Private implementation of `even` and `odd` macros, expecting a slice and returning one as well.
|
||||
This function always extracts the even elements of the input slice.
|
||||
|
||||
@param [&inout] allocator : "The allocator used to create the return array."
|
||||
@param [in] array : "The array from which to extract all odd elements."
|
||||
*>
|
||||
fn Type[] unlace_impl(Allocator allocator, Type[] array) <Type> @private
|
||||
{
|
||||
usz new_len = array.len / 2 + (array.len % 2 == 0 ? 0 : 1);
|
||||
if (new_len == 0) return (Type[]){};
|
||||
Type[] new_array = allocator::new_array(allocator, Type, new_len);
|
||||
foreach (x, &new : new_array) *new = types::implements_copy(Type) ??? array[x * 2].copy(allocator) : array[x * 2];
|
||||
return new_array[:new_len];
|
||||
}
|
||||
|
||||
<*
|
||||
Unlace or partition an input list into its component parts such that `[a, b, c, d, e]` becomes
|
||||
`[a, c, e]` and `[b, d]`. Returned arrays are allocated by the given allocator and are returned
|
||||
via two `out` parameters, `left` and `right`.
|
||||
|
||||
@param [&inout] allocator : "The allocator used to create the returned arrays."
|
||||
@param [in] array : "The input array to unlace."
|
||||
@param [out] left : "Stores a copy of all even-index array elements."
|
||||
@param [out] right : "Stores a copy of all odd-index array elements."
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $typeof(left) == $typeof(array[0])[]*
|
||||
@require $typeof(right) == $typeof(array[0])[]*
|
||||
*>
|
||||
macro unlace(Allocator allocator, array, left, right)
|
||||
{
|
||||
if (left) *left = even(allocator, array);
|
||||
if (right) *right = odd(allocator, array);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Zip together two separate arrays/slices into a single array of Pairs or return values. Values will
|
||||
be collected up to the length of the shorter array if `fill_with` is left undefined; otherwise, they
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::bitorder;
|
||||
|
||||
import std::bits;
|
||||
// This module contains types of different endianness.
|
||||
// *BE types represent big-endian types
|
||||
// *LE types represent little-endian types.
|
||||
@@ -87,6 +87,56 @@ bitstruct UInt128LE : uint128 @littleendian
|
||||
uint128 val : 0..127;
|
||||
}
|
||||
|
||||
<*
|
||||
@require $defined(*bytes) : "Pointer must be possible to dereference"
|
||||
@require types::is_intlike($typeof(*bytes)) : "Type must be an integer or int vector"
|
||||
*>
|
||||
macro load_be(bytes)
|
||||
{
|
||||
$if env::BIG_ENDIAN:
|
||||
return mem::load(bytes, $align: 1);
|
||||
$else
|
||||
return bswap(mem::load(bytes, $align: 1));
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require $defined(*bytes) : "Pointer must be possible to dereference"
|
||||
@require types::is_intlike($typeof(*bytes)) : "Type must be an integer or int vector"
|
||||
*>
|
||||
macro load_le(bytes)
|
||||
{
|
||||
$if env::BIG_ENDIAN:
|
||||
return bswap(mem::load(bytes, $align: 1));
|
||||
$else
|
||||
return mem::load(bytes, $align: 1);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require types::is_intlike($typeof(value)) : "Type must be an integer or int vector"
|
||||
*>
|
||||
macro void store_be(void* dst, value)
|
||||
{
|
||||
$if env::BIG_ENDIAN:
|
||||
mem::store(($typeof(value)*)dst, value, $align: 1);
|
||||
$else
|
||||
mem::store(($typeof(value)*)dst, bswap(value), $align: 1);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require types::is_intlike($typeof(value)) : "Type must be an integer or int vector"
|
||||
*>
|
||||
macro void store_le(void* dst, value)
|
||||
{
|
||||
$if env::BIG_ENDIAN:
|
||||
mem::store(($typeof(value)*)dst, bswap(value), $align: 1);
|
||||
$else
|
||||
mem::store(($typeof(value)*)dst, value, $align: 1);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_array_or_slice_of_char(bytes) : "argument must be an array, a pointer to an array or a slice of char"
|
||||
@require is_bitorder($Type) : "type must be a bitorder integer"
|
||||
|
||||
@@ -26,7 +26,7 @@ macro foo(a, #b = EMPTY_MACRO_SLOT)
|
||||
*>
|
||||
const EmptySlot EMPTY_MACRO_SLOT @builtin @deprecated("Use `#arg = ...` instead.") = null;
|
||||
|
||||
typedef EmptySlot = void*;
|
||||
typedef EmptySlot @constinit = void*;
|
||||
macro bool @is_empty_macro_slot(#arg) @const @builtin
|
||||
@deprecated("Use `#arg = ...` to define an optional macro slot, and `$defined(#arg)` to detect whether the argument is set.")
|
||||
=> $typeof(#arg) == EmptySlot;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module std::core::dstring;
|
||||
import std::io;
|
||||
import std::io, std::math;
|
||||
|
||||
<*
|
||||
The DString offers a dynamic string builder.
|
||||
@@ -331,14 +331,14 @@ fn Char32[] DString.copy_utf32(&self, Allocator allocator)
|
||||
}
|
||||
|
||||
<*
|
||||
@require $typeof(str) == String || $typeof(str) == DString : "Expected string or DString"
|
||||
@require $defined(String s = str) ||| $typeof(str) == DString : "Expected string or DString"
|
||||
*>
|
||||
macro void DString.append_string(&self, str)
|
||||
{
|
||||
$if $typeof(str) == String:
|
||||
self.append_bytes(str);
|
||||
$else
|
||||
$if $typeof(str) == DString:
|
||||
self.append_string_deprecated(str);
|
||||
$else
|
||||
self.append_bytes((String)str);
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -631,7 +631,7 @@ fn void DString.reverse(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn StringData* DString.data(self) @inline @private
|
||||
fn StringData* DString.data(self) @inline
|
||||
{
|
||||
return (StringData*)self;
|
||||
}
|
||||
@@ -648,7 +648,7 @@ fn void DString.reserve(&self, usz addition)
|
||||
if (data.capacity >= len) return;
|
||||
usz new_capacity = data.capacity * 2;
|
||||
if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY;
|
||||
while (new_capacity < len) new_capacity *= 2;
|
||||
if (new_capacity < len) new_capacity = math::next_power_of_2(len);
|
||||
data.capacity = new_capacity;
|
||||
*self = (DString)allocator::realloc(data.allocator, data, StringData.sizeof + new_capacity);
|
||||
}
|
||||
@@ -658,9 +658,10 @@ fn usz? DString.read_from_stream(&self, InStream reader)
|
||||
if (&reader.available)
|
||||
{
|
||||
usz total_read = 0;
|
||||
while (usz available = reader.available()!)
|
||||
while (ulong available = reader.available()!)
|
||||
{
|
||||
self.reserve(available);
|
||||
if (available > isz.max) available = (ulong)isz.max;
|
||||
self.reserve((usz)available);
|
||||
StringData* data = self.data();
|
||||
usz len = reader.read(data.chars[data.len..(data.capacity - 1)])!;
|
||||
total_read += len;
|
||||
|
||||
@@ -126,6 +126,7 @@ const bool ARCH_64_BIT = $$REGISTER_SIZE == 64;
|
||||
const bool LIBC = $$COMPILER_LIBC_AVAILABLE;
|
||||
const bool NO_LIBC = !LIBC && !CUSTOM_LIBC;
|
||||
const bool CUSTOM_LIBC = $$CUSTOM_LIBC;
|
||||
const bool OLD_IO = $feature(OLD_IO);
|
||||
const CompilerOptLevel COMPILER_OPT_LEVEL = CompilerOptLevel.from_ordinal($$COMPILER_OPT_LEVEL);
|
||||
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
|
||||
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
|
||||
|
||||
@@ -6,22 +6,22 @@ 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;
|
||||
const LogCategory CATEGORY_APPLICATION = (LogCategory)0;
|
||||
const LogCategory CATEGORY_SYSTEM = (LogCategory)1;
|
||||
const LogCategory CATEGORY_KERNEL = (LogCategory)2;
|
||||
const LogCategory CATEGORY_AUDIO = (LogCategory)3;
|
||||
const LogCategory CATEGORY_VIDEO = (LogCategory)4;
|
||||
const LogCategory CATEGORY_RENDER = (LogCategory)5;
|
||||
const LogCategory CATEGORY_INPUT = (LogCategory)6;
|
||||
const LogCategory CATEGORY_NETWORK = (LogCategory)7;
|
||||
const LogCategory CATEGORY_SOCKET = (LogCategory)8;
|
||||
const LogCategory CATEGORY_SECURITY = (LogCategory)9;
|
||||
const LogCategory CATEGORY_TEST = (LogCategory)10;
|
||||
const LogCategory CATEGORY_ERROR = (LogCategory)11;
|
||||
const LogCategory CATEGORY_ASSERT = (LogCategory)12;
|
||||
const LogCategory CATEGORY_CRASH = (LogCategory)13;
|
||||
const LogCategory CATEGORY_STATS = (LogCategory)14;
|
||||
const LogCategory CATEGORY_CUSTOM_START = (LogCategory)100;
|
||||
|
||||
tlocal LogCategory default_category = CATEGORY_APPLICATION;
|
||||
tlocal LogTag current_tag;
|
||||
|
||||
@@ -404,6 +404,28 @@ macro void*? @aligned_alloc(#alloc_fn, usz bytes, usz alignment)
|
||||
return mem;
|
||||
}
|
||||
|
||||
<*
|
||||
@require bytes > 0
|
||||
@require alignment > 0
|
||||
@require bytes <= isz.max
|
||||
*>
|
||||
macro void*? @aligned_alloc_fn(context, #alloc_fn, usz bytes, usz alignment)
|
||||
{
|
||||
if (alignment < void*.alignof) alignment = void*.alignof;
|
||||
usz header = AlignedBlock.sizeof + alignment;
|
||||
usz alignsize = bytes + header;
|
||||
$if $kindof(#alloc_fn(context, bytes)) == OPTIONAL:
|
||||
void* data = #alloc_fn(context, alignsize)!;
|
||||
$else
|
||||
void* data = #alloc_fn(context, alignsize);
|
||||
$endif
|
||||
void* mem = mem::aligned_pointer(data + AlignedBlock.sizeof, alignment);
|
||||
AlignedBlock* desc = (AlignedBlock*)mem - 1;
|
||||
assert(mem > data);
|
||||
*desc = { bytes, data };
|
||||
return mem;
|
||||
}
|
||||
|
||||
struct AlignedBlock
|
||||
{
|
||||
usz len;
|
||||
@@ -420,6 +442,16 @@ macro void? @aligned_free(#free_fn, void* old_pointer)
|
||||
$endif
|
||||
}
|
||||
|
||||
macro void? @aligned_free_fn(context, #free_fn, void* old_pointer)
|
||||
{
|
||||
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
||||
$if $kindof(#free_fn(context, desc.start)) == OPTIONAL:
|
||||
#free_fn(context, desc.start)!;
|
||||
$else
|
||||
#free_fn(context, desc.start);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require bytes > 0
|
||||
@require alignment > 0
|
||||
@@ -438,6 +470,23 @@ macro void*? @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes
|
||||
return new_data;
|
||||
}
|
||||
|
||||
<*
|
||||
@require bytes > 0
|
||||
@require alignment > 0
|
||||
*>
|
||||
macro void*? @aligned_realloc_fn(context, #calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment)
|
||||
{
|
||||
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
||||
void* data_start = desc.start;
|
||||
void* new_data = @aligned_alloc_fn(context, #calloc_fn, bytes, alignment)!;
|
||||
mem::copy(new_data, old_pointer, desc.len < bytes ? desc.len : bytes, 1, 1);
|
||||
$if $kindof(#free_fn(context, data_start)) == OPTIONAL:
|
||||
#free_fn(context, data_start)!;
|
||||
$else
|
||||
#free_fn(context, data_start);
|
||||
$endif
|
||||
return new_data;
|
||||
}
|
||||
|
||||
// All allocators
|
||||
alias mem @builtin = thread_allocator ;
|
||||
|
||||
@@ -54,7 +54,7 @@ struct FixedBlockPool
|
||||
@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)
|
||||
fn FixedBlockPool* FixedBlockPool.init(&self, Allocator allocator, usz block_size, usz capacity = INITIAL_CAPACITY, usz alignment = 0)
|
||||
{
|
||||
self.allocator = allocator;
|
||||
self.tail = &self.head;
|
||||
@@ -64,7 +64,7 @@ macro FixedBlockPool* FixedBlockPool.init(&self, Allocator allocator, usz block_
|
||||
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.buffer = fixedblockpool_allocate_page(self);
|
||||
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
|
||||
asan::poison_memory_region(self.head.buffer, self.page_size);
|
||||
$endif
|
||||
@@ -119,7 +119,7 @@ fn void FixedBlockPool.free(&self)
|
||||
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
|
||||
asan::unpoison_memory_region(self.head.buffer, self.page_size);
|
||||
$endif
|
||||
self.free_page(self.head.buffer);
|
||||
fixedblockpool_free_page(self, self.head.buffer);
|
||||
FixedBlockPoolNode* iter = self.head.next;
|
||||
|
||||
while (iter)
|
||||
@@ -127,7 +127,7 @@ fn void FixedBlockPool.free(&self)
|
||||
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
|
||||
asan::unpoison_memory_region(iter.buffer, self.page_size);
|
||||
$endif
|
||||
self.free_page(iter.buffer);
|
||||
fixedblockpool_free_page(self, iter.buffer);
|
||||
FixedBlockPoolNode* current = iter;
|
||||
iter = iter.next;
|
||||
allocator::free(self.allocator, current);
|
||||
@@ -158,7 +158,7 @@ fn void* FixedBlockPool.alloc(&self)
|
||||
}
|
||||
|
||||
void* end = self.tail.buffer + (self.tail.capacity * self.block_size);
|
||||
if (self.next_free >= end) self.new_node();
|
||||
if (self.next_free >= end) fixedblockpool_new_node(self);
|
||||
void* ptr = self.next_free;
|
||||
self.next_free += self.block_size;
|
||||
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
|
||||
@@ -172,7 +172,7 @@ fn void* FixedBlockPool.alloc(&self)
|
||||
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"
|
||||
@require fixedblockpool_check_ptr(self, ptr) : "The pointer should be part of the pool"
|
||||
*>
|
||||
fn void FixedBlockPool.dealloc(&self, void* ptr)
|
||||
{
|
||||
@@ -193,7 +193,7 @@ fn void FixedBlockPool.dealloc(&self, void* ptr)
|
||||
<*
|
||||
@require self.initialized : "The block pool must be initialized"
|
||||
*>
|
||||
fn bool FixedBlockPool.check_ptr(&self, void *ptr) @local
|
||||
fn bool fixedblockpool_check_ptr(FixedBlockPool* self, void *ptr) @local
|
||||
{
|
||||
FixedBlockPoolNode* iter = &self.head;
|
||||
|
||||
@@ -210,10 +210,10 @@ fn bool FixedBlockPool.check_ptr(&self, void *ptr) @local
|
||||
<*
|
||||
@require self.grow_capacity > 0 : "How many blocks will it store"
|
||||
*>
|
||||
fn void FixedBlockPool.new_node(&self) @local
|
||||
fn void fixedblockpool_new_node(FixedBlockPool* self) @local
|
||||
{
|
||||
FixedBlockPoolNode* node = allocator::new(self.allocator, FixedBlockPoolNode);
|
||||
node.buffer = self.allocate_page();
|
||||
node.buffer = fixedblockpool_allocate_page(self);
|
||||
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
|
||||
asan::poison_memory_region(node.buffer, self.page_size);
|
||||
$endif
|
||||
@@ -224,14 +224,14 @@ fn void FixedBlockPool.new_node(&self) @local
|
||||
self.allocated += node.capacity;
|
||||
}
|
||||
|
||||
macro void* FixedBlockPool.allocate_page(&self) @private
|
||||
macro void* fixedblockpool_allocate_page(FixedBlockPool* 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
|
||||
macro void fixedblockpool_free_page(FixedBlockPool* self, void* page) @private
|
||||
{
|
||||
if (self.alignment > mem::DEFAULT_MEM_ALIGNMENT)
|
||||
{
|
||||
|
||||
@@ -321,7 +321,7 @@ fn void? VirtualMemory.destroy(&self)
|
||||
return release(self.ptr, self.size);
|
||||
}
|
||||
|
||||
fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX) @private
|
||||
fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX)
|
||||
{
|
||||
switch (self)
|
||||
{
|
||||
@@ -336,7 +336,7 @@ fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX) @private
|
||||
}
|
||||
}
|
||||
|
||||
fn Win32_Protect VirtualMemoryAccess.to_win32(self) @if(env::WIN32) @private
|
||||
fn Win32_Protect VirtualMemoryAccess.to_win32(self) @if(env::WIN32)
|
||||
{
|
||||
switch (self)
|
||||
{
|
||||
|
||||
@@ -7,19 +7,26 @@ macro usz _strlen(ptr) @private
|
||||
return len;
|
||||
}
|
||||
|
||||
macro int @main_to_err_main(#m, int, char**)
|
||||
macro int @main_no_args(#m, int, char**)
|
||||
{
|
||||
if (catch #m()) return 1;
|
||||
return 0;
|
||||
$if $typeof(#m()) == void:
|
||||
#m();
|
||||
return 0;
|
||||
$else
|
||||
return #m();
|
||||
$endif
|
||||
}
|
||||
macro int @main_to_int_main(#m, int, char**)
|
||||
|
||||
macro int @main_args(#m, int argc, char** argv)
|
||||
{
|
||||
return #m();
|
||||
}
|
||||
macro int @main_to_void_main(#m, int, char**)
|
||||
{
|
||||
#m();
|
||||
return 0;
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
$if $typeof(#m(list)) == void:
|
||||
#m(list);
|
||||
return 0;
|
||||
$else
|
||||
return #m(list);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro String[] args_to_strings(int argc, char** argv) @private
|
||||
@@ -35,21 +42,6 @@ macro String[] args_to_strings(int argc, char** argv) @private
|
||||
}
|
||||
|
||||
|
||||
macro int @main_to_err_main_args(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
if (catch #m(list)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @main_to_int_main_args(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
return #m(list);
|
||||
}
|
||||
|
||||
macro int @_main_runner(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
@@ -57,14 +49,6 @@ macro int @_main_runner(#m, int argc, char** argv)
|
||||
return #m(list) ? 0 : 1;
|
||||
}
|
||||
|
||||
macro int @main_to_void_main_args(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
#m(list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
module std::core::main_stub @if(env::WIN32);
|
||||
import std::os::win32;
|
||||
|
||||
@@ -105,92 +89,66 @@ macro void release_wargs(String[] list) @private
|
||||
free(list.ptr);
|
||||
}
|
||||
|
||||
macro int @win_to_err_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
macro int @win_main_no_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
if (catch #m()) return 1;
|
||||
return 0;
|
||||
}
|
||||
macro int @win_to_int_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
return #m();
|
||||
$if $typeof(#m()) == void:
|
||||
#m();
|
||||
return 0;
|
||||
$else
|
||||
return #m();
|
||||
$endif
|
||||
}
|
||||
|
||||
macro int @win_to_void_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
#m();
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @win_to_err_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
macro int @win_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
if (catch #m(args)) return 1;
|
||||
return 0;
|
||||
$if $typeof(#m(args)) == void:
|
||||
#m(args);
|
||||
return 0;
|
||||
$else
|
||||
return #m(args);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro int @win_to_int_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
macro int @win_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
return #m(args);
|
||||
$if $typeof(#m(handle, prev_handle, args, show_cmd)) == void:
|
||||
#m(handle, prev_handle, args, show_cmd);
|
||||
return 0;
|
||||
$else
|
||||
return #m(handle, prev_handle, args, show_cmd);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro int @win_to_void_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
#m(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @win_to_err_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
if (catch #m(handle, prev_handle, args, show_cmd)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @win_to_int_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
return #m(handle, prev_handle, args, show_cmd);
|
||||
}
|
||||
|
||||
macro int @win_to_void_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
#m(handle, prev_handle, args, show_cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @wmain_to_err_main_args(#m, int argc, Char16** argv)
|
||||
macro int @wmain_main(#m, int argc, Char16** argv)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
if (catch #m(args)) return 1;
|
||||
return 1;
|
||||
$if $typeof(#m(args)) == void:
|
||||
#m(args);
|
||||
return 0;
|
||||
$else
|
||||
return #m(args);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)
|
||||
macro int @wmain_main_no_args(#m, int argc, Char16** argv)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
return #m(args);
|
||||
$if $typeof(#m()) == void:
|
||||
#m();
|
||||
return 0;
|
||||
$else
|
||||
return #m();
|
||||
$endif
|
||||
}
|
||||
|
||||
macro int @_wmain_runner(#m, int argc, Char16** argv)
|
||||
@@ -200,12 +158,3 @@ macro int @_wmain_runner(#m, int argc, Char16** argv)
|
||||
defer release_wargs(args);
|
||||
return #m(args) ? 0 : 1;
|
||||
}
|
||||
|
||||
macro int @wmain_to_void_main_args(#m, int argc, Char16** argv)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
#m(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -125,10 +125,11 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
|
||||
char[] perc_str = { [0..19] = ' ', [20] = 0 };
|
||||
int perc = 0;
|
||||
uint print_step = current_benchmark_iterations / 100;
|
||||
if (print_step == 0) print_step = 1;
|
||||
|
||||
for (this_iteration = 0; this_iteration < current_benchmark_iterations; ++this_iteration, benchmark_nano_seconds = {})
|
||||
{
|
||||
if (0 == this_iteration % print_step) // only print right about when the % will update
|
||||
if (this_iteration % print_step == 0) // only print right about when the % will update
|
||||
{
|
||||
perc_str[0..(uint)math::floor((this_iteration / (float)current_benchmark_iterations) * 20)] = '#';
|
||||
perc = (uint)math::ceil(100 * (this_iteration / (float)current_benchmark_iterations));
|
||||
|
||||
@@ -142,7 +142,7 @@ fn void mute_output() @local
|
||||
File* stderr = io::stderr();
|
||||
*stderr = test_context.fake_stdout;
|
||||
*stdout = test_context.fake_stdout;
|
||||
(void)test_context.fake_stdout.seek(0, Seek.SET)!!;
|
||||
(void)test_context.fake_stdout.set_cursor(0)!!;
|
||||
}
|
||||
|
||||
fn void unmute_output(bool has_error) @local
|
||||
@@ -155,7 +155,7 @@ fn void unmute_output(bool has_error) @local
|
||||
*stderr = test_context.stored.stderr;
|
||||
*stdout = test_context.stored.stdout;
|
||||
|
||||
usz log_size = test_context.fake_stdout.seek(0, Seek.CURSOR)!!;
|
||||
ulong log_size = test_context.fake_stdout.cursor()!!;
|
||||
if (has_error)
|
||||
{
|
||||
io::printn(test_context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]");
|
||||
@@ -165,7 +165,7 @@ fn void unmute_output(bool has_error) @local
|
||||
{
|
||||
test_context.fake_stdout.write_byte('\n')!!;
|
||||
test_context.fake_stdout.write_byte('\0')!!;
|
||||
(void)test_context.fake_stdout.seek(0, Seek.SET)!!;
|
||||
test_context.fake_stdout.set_cursor(0)!!;
|
||||
|
||||
io::printfn("\n========== TEST LOG ============");
|
||||
io::printfn("%s\n", test_context.current_test_name);
|
||||
|
||||
@@ -2,16 +2,16 @@ module std::core::sanitizer::tsan;
|
||||
|
||||
typedef MutexFlags = inline CUInt;
|
||||
|
||||
const MutexFlags MUTEX_LINKER_INIT = 1 << 0;
|
||||
const MutexFlags MUTEX_WRITE_REENTRANT = 1 << 1;
|
||||
const MutexFlags MUTEX_READ_REENTRANT = 1 << 2;
|
||||
const MutexFlags MUTEX_NOT_STATIC = 1 << 8;
|
||||
const MutexFlags MUTEX_READ_LOCK = 1 << 3;
|
||||
const MutexFlags MUTEX_TRY_LOCK = 1 << 4;
|
||||
const MutexFlags MUTEX_TRY_LOCK_FAILED = 1 << 5;
|
||||
const MutexFlags MUTEX_RECURSIVE_LOCK = 1 << 6;
|
||||
const MutexFlags MUTEX_RECURSIVE_UNLOCK = 1 << 7;
|
||||
const MutexFlags MUTEX_TRY_READ_LOCK = MUTEX_READ_LOCK | MUTEX_TRY_LOCK;
|
||||
const MutexFlags MUTEX_LINKER_INIT = (MutexFlags)1 << 0;
|
||||
const MutexFlags MUTEX_WRITE_REENTRANT = (MutexFlags)1 << 1;
|
||||
const MutexFlags MUTEX_READ_REENTRANT = (MutexFlags)1 << 2;
|
||||
const MutexFlags MUTEX_NOT_STATIC = (MutexFlags)1 << 8;
|
||||
const MutexFlags MUTEX_READ_LOCK = (MutexFlags)1 << 3;
|
||||
const MutexFlags MUTEX_TRY_LOCK = (MutexFlags)1 << 4;
|
||||
const MutexFlags MUTEX_TRY_LOCK_FAILED = (MutexFlags)1 << 5;
|
||||
const MutexFlags MUTEX_RECURSIVE_LOCK = (MutexFlags)1 << 6;
|
||||
const MutexFlags MUTEX_RECURSIVE_UNLOCK = (MutexFlags)1 << 7;
|
||||
const MutexFlags MUTEX_TRY_READ_LOCK = MUTEX_READ_LOCK | MUTEX_TRY_LOCK;
|
||||
const MutexFlags MUTEX_TRY_READ_LOCK_FAILED = MUTEX_TRY_READ_LOCK | MUTEX_TRY_LOCK_FAILED;
|
||||
|
||||
macro void mutex_create(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_create(addr, flags); $endif }
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
module std::core::string;
|
||||
import std::io, std::ascii;
|
||||
import std::core::mem::allocator;
|
||||
|
||||
|
||||
typedef String @if(!$defined(String)) = inline char[];
|
||||
|
||||
typedef String @constinit @if(!$defined(String)) = inline char[];
|
||||
<*
|
||||
ZString is a pointer to a zero terminated array of chars.
|
||||
|
||||
Use ZString when you need to interop with C zero terminated strings.
|
||||
*>
|
||||
typedef ZString = inline char*;
|
||||
typedef ZString @constinit = inline char*;
|
||||
|
||||
<*
|
||||
WString is a pointer to a zero terminated array of Char16.
|
||||
@@ -96,7 +98,8 @@ fn ZString tformat_zstr(String fmt, args...) @format(0)
|
||||
}
|
||||
|
||||
<*
|
||||
Return a new String created using the formatting function.
|
||||
Return a new String created using the formatting function, this function will implicitly
|
||||
use the temp allocator.
|
||||
|
||||
@param [inout] allocator : `The allocator to use`
|
||||
@param [in] fmt : `The formatting string`
|
||||
@@ -156,31 +159,44 @@ macro bool char_in_set(char c, String set)
|
||||
return false;
|
||||
}
|
||||
|
||||
<*
|
||||
Join together an array of strings via a "joiner" sequence, which is inserted between each element.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use."
|
||||
@param [in] s : "An array of strings to join in sequence."
|
||||
@param [in] joiner : "The string used to join each element of `s`."
|
||||
@return "A single string containing the result, allocated via `allocator`, safe to convert to a ZString."
|
||||
*>
|
||||
fn String join(Allocator allocator, String[] s, String joiner)
|
||||
{
|
||||
if (!s)
|
||||
{
|
||||
return (String)allocator::new_array(allocator, char, 2)[:0];
|
||||
}
|
||||
|
||||
usz total_size = joiner.len * s.len;
|
||||
usz joiner_len = joiner.len;
|
||||
usz total_size = joiner_len * (s.len - 1) + 1;
|
||||
foreach (String* &str : s)
|
||||
{
|
||||
total_size += str.len;
|
||||
}
|
||||
@pool()
|
||||
char[] data = allocator::alloc_array(allocator, char, total_size);
|
||||
usz offset = s[0].len;
|
||||
data[:offset] = s[0][:offset];
|
||||
foreach (String* &str : s[1..])
|
||||
{
|
||||
DString res = dstring::temp_with_capacity(total_size);
|
||||
res.append(s[0]);
|
||||
foreach (String* &str : s[1..])
|
||||
{
|
||||
res.append(joiner);
|
||||
res.append(*str);
|
||||
}
|
||||
return res.copy_str(allocator);
|
||||
data[offset:joiner_len] = joiner[:joiner_len];
|
||||
offset += joiner_len;
|
||||
usz len = str.len;
|
||||
data[offset:len] = str.[:len];
|
||||
offset += len;
|
||||
};
|
||||
data[offset] = 0;
|
||||
return (String)data[:offset];
|
||||
}
|
||||
|
||||
<* Alias for `string::join` using the temp allocator. *>
|
||||
macro String tjoin(String[] s, String joiner) => join(tmem, s, joiner);
|
||||
|
||||
<*
|
||||
Replace all instances of one substring with a different string.
|
||||
|
||||
|
||||
@@ -158,7 +158,7 @@ macro double? decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
if (rp % 9)
|
||||
{
|
||||
long rpm9 = rp >= 0 ? rp % 9 : rp % 9 + 9;
|
||||
int p10 = P10S[8 - rpm9];
|
||||
uint p10 = P10S[8 - rpm9];
|
||||
uint carry = 0;
|
||||
for (k = a; k != z; k++)
|
||||
{
|
||||
|
||||
@@ -97,8 +97,8 @@ macro bool is_subtype_of($Type, $OtherType)
|
||||
macro bool is_numerical($Type)
|
||||
{
|
||||
$switch $Type.kindof:
|
||||
$case DISTINCT:
|
||||
$case CONST_ENUM:
|
||||
$case TYPEDEF:
|
||||
$case CONSTDEF:
|
||||
return is_numerical($Type.inner);
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
@@ -112,7 +112,12 @@ macro bool is_numerical($Type)
|
||||
|
||||
fn bool TypeKind.is_int(kind) @inline
|
||||
{
|
||||
return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT;
|
||||
return kind == SIGNED_INT || kind == UNSIGNED_INT;
|
||||
}
|
||||
|
||||
macro bool TypeKind.@is_int($kind) @const
|
||||
{
|
||||
return $kind == SIGNED_INT ||| $kind == UNSIGNED_INT;
|
||||
}
|
||||
|
||||
macro bool is_slice_convertable($Type) @deprecated("Use is_slice_convertible") => is_slice_convertible($Type);
|
||||
@@ -165,7 +170,7 @@ macro bool is_unsigned($Type) @const
|
||||
|
||||
macro typeid flat_type($Type) @const
|
||||
{
|
||||
$if $Type.kindof == DISTINCT || $Type.kindof == CONST_ENUM:
|
||||
$if $Type.kindof == TYPEDEF || $Type.kindof == CONSTDEF:
|
||||
return flat_type($Type.inner);
|
||||
$else
|
||||
return $Type.typeid;
|
||||
@@ -174,7 +179,7 @@ macro typeid flat_type($Type) @const
|
||||
|
||||
macro TypeKind flat_kind($Type) @const
|
||||
{
|
||||
$if $Type.kindof == DISTINCT || $Type.kindof == CONST_ENUM:
|
||||
$if $Type.kindof == TYPEDEF || $Type.kindof == CONSTDEF:
|
||||
return flat_type($Type.inner);
|
||||
$else
|
||||
return $Type.kindof;
|
||||
@@ -198,8 +203,8 @@ macro bool is_flat_intlike($Type) @const
|
||||
$case UNSIGNED_INT:
|
||||
return true;
|
||||
$case VECTOR:
|
||||
$case DISTINCT:
|
||||
$case CONST_ENUM:
|
||||
$case TYPEDEF:
|
||||
$case CONSTDEF:
|
||||
return is_flat_intlike($Type.inner);
|
||||
$default:
|
||||
return false;
|
||||
@@ -225,7 +230,7 @@ macro bool is_underlying_int($Type) @const
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
return true;
|
||||
$case DISTINCT:
|
||||
$case TYPEDEF:
|
||||
return is_underlying_int($Type.inner);
|
||||
$default:
|
||||
return false;
|
||||
@@ -253,7 +258,7 @@ macro bool is_vector($Type) @const
|
||||
|
||||
macro typeid inner_type($Type) @const
|
||||
{
|
||||
$if $Type.kindof == DISTINCT || $Type.kindof == CONST_ENUM:
|
||||
$if $Type.kindof == TYPEDEF || $Type.kindof == CONSTDEF:
|
||||
return inner_type($Type.inner);
|
||||
$else
|
||||
return $Type.typeid;
|
||||
@@ -293,7 +298,7 @@ macro bool may_load_atomic($Type) @const
|
||||
$case POINTER:
|
||||
$case FLOAT:
|
||||
return true;
|
||||
$case DISTINCT:
|
||||
$case TYPEDEF:
|
||||
$case ENUM:
|
||||
return may_load_atomic($Type.inner);
|
||||
$default:
|
||||
@@ -308,8 +313,8 @@ macro lower_to_atomic_compatible_type($Type) @const
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
return $Type.typeid;
|
||||
$case DISTINCT:
|
||||
$case CONST_ENUM:
|
||||
$case TYPEDEF:
|
||||
$case CONSTDEF:
|
||||
return lower_to_atomic_compatible_type($Type.inner);
|
||||
$case FLOAT:
|
||||
$switch $Type:
|
||||
@@ -374,6 +379,9 @@ macro bool @comparable_value(#value) @const
|
||||
$endif
|
||||
}
|
||||
|
||||
const CONST_ENUM @builtin @deprecated("Use TypeKind.CONSTDEF instead") = TypeKind.CONSTDEF;
|
||||
const DISTINCT @builtin @deprecated("Use TypeKind.TYPEDEF instead") = TypeKind.TYPEDEF;
|
||||
|
||||
enum TypeKind : char
|
||||
{
|
||||
VOID,
|
||||
@@ -385,7 +393,7 @@ enum TypeKind : char
|
||||
FAULT,
|
||||
ANY,
|
||||
ENUM,
|
||||
CONST_ENUM,
|
||||
CONSTDEF,
|
||||
STRUCT,
|
||||
UNION,
|
||||
BITSTRUCT,
|
||||
@@ -394,7 +402,7 @@ enum TypeKind : char
|
||||
ARRAY,
|
||||
SLICE,
|
||||
VECTOR,
|
||||
DISTINCT,
|
||||
TYPEDEF,
|
||||
POINTER,
|
||||
INTERFACE,
|
||||
}
|
||||
|
||||
@@ -56,9 +56,9 @@ enum BlockMode
|
||||
<* AES type: 128, 192 or 256 bits *>
|
||||
enum AesType : (AesKey key)
|
||||
{
|
||||
AES128 = { 128, 16, 176, 4, 10 },
|
||||
AES192 = { 192, 24, 208, 6, 12 },
|
||||
AES256 = { 256, 32, 240, 8, 14 }
|
||||
AES128 {{ 128, 16, 176, 4, 10 }},
|
||||
AES192 {{ 192, 24, 208, 6, 12 }},
|
||||
AES256 {{ 256, 32, 240, 8, 14 }}
|
||||
}
|
||||
|
||||
struct AesKey
|
||||
|
||||
@@ -63,7 +63,7 @@ macro quarter_round(uint* x, int a, int b, int c, int d) @local
|
||||
}
|
||||
|
||||
<* Process the next (or final) chunk of ingested data. *>
|
||||
fn void ChaCha20.mutate_keystream(&self) @local @inline
|
||||
fn void chacha20_mutate_keystream(ChaCha20* self) @local @inline
|
||||
{
|
||||
self.key_stream[..] = self.state[..];
|
||||
|
||||
@@ -137,7 +137,7 @@ fn void ChaCha20.transform(&self, char[] data)
|
||||
|
||||
for (usz x = offset; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..], x = offset)
|
||||
{
|
||||
self.mutate_keystream();
|
||||
chacha20_mutate_keystream(self);
|
||||
if (offset) foreach (i, &b : data[:offset]) *b ^= key_stream[i];
|
||||
char[] aligned_data = data[offset..];
|
||||
for (; x <= (BLOCK_SIZE - usz.sizeof); x += usz.sizeof)
|
||||
@@ -150,7 +150,7 @@ fn void ChaCha20.transform(&self, char[] data)
|
||||
// 3. Process any remaining bytes.
|
||||
if (data.len > 0)
|
||||
{
|
||||
self.mutate_keystream();
|
||||
chacha20_mutate_keystream(self);
|
||||
for (usz i = 0; i < data.len; i++) data[i] ^= key_stream[i];
|
||||
self.position = data.len;
|
||||
}
|
||||
|
||||
@@ -332,7 +332,7 @@ fn Projection Projection.mul(&s, char[] n) @operator(*)
|
||||
|
||||
module std::crypto::ed25519 @private;
|
||||
|
||||
typedef F25519Int = inline char[32];
|
||||
typedef F25519Int @constinit = inline char[32];
|
||||
|
||||
const F25519Int ZERO = {};
|
||||
const F25519Int ONE = {[0] = 1};
|
||||
@@ -567,7 +567,7 @@ fn F25519Int F25519Int.inv(&s)
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn F25519Int F25519Int.pow_2523(&s) @local
|
||||
fn F25519Int pow_2523(F25519Int* s) @local
|
||||
{
|
||||
F25519Int r = *s;
|
||||
|
||||
@@ -587,7 +587,7 @@ fn F25519Int F25519Int.pow_2523(&s) @local
|
||||
fn F25519Int F25519Int.sqrt(&s)
|
||||
{
|
||||
F25519Int twice = s.mul_s(2);
|
||||
F25519Int pow = twice.pow_2523();
|
||||
F25519Int pow = pow_2523(&twice);
|
||||
|
||||
return (twice * pow * pow - ONE) * s * pow;
|
||||
}
|
||||
@@ -601,7 +601,7 @@ fn F25519Int F25519Int.sqrt(&s)
|
||||
module std::crypto::ed25519 @private;
|
||||
import std::math;
|
||||
|
||||
typedef FBaseInt = inline char[32];
|
||||
typedef FBaseInt @constinit = inline char[32];
|
||||
|
||||
// Order of the field : 2^252+0x14def9dea2f79cd65812631a5cf5d3ed
|
||||
const FBaseInt ORDER = x"edd3f55c1a631258 d69cf7a2def9de14 0000000000000000 0000000000000010";
|
||||
|
||||
@@ -242,7 +242,7 @@ const char INVALID @private = 0xff;
|
||||
const int STD_PADDING = '=';
|
||||
const int NO_PADDING = -1;
|
||||
|
||||
typedef Alphabet = char[32];
|
||||
typedef Alphabet @constinit = char[32];
|
||||
// Standard base32 Alphabet
|
||||
const Alphabet STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
// Extended Hex Alphabet
|
||||
|
||||
243
lib/std/encoding/codepage.c3
Normal file
243
lib/std/encoding/codepage.c3
Normal file
@@ -0,0 +1,243 @@
|
||||
// Copyright (c) 2026 Koni Marti. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license.
|
||||
<*
|
||||
Module providing generic single‑byte code page to UTF‑8 conversion.
|
||||
|
||||
This module implements a compact, table‑driven approach for single-byte
|
||||
(8‑bit) encodings (e.g. CP437, CP850, CP866, CP125x). It is designed so
|
||||
that each concrete code page only needs to supply a small, static
|
||||
mapping table; the conversion logic is shared.
|
||||
|
||||
The design has two main goals:
|
||||
|
||||
- Fast decode from code page to UTF‑8 with a single table lookup per byte.
|
||||
- Memory‑efficient encode from UTF‑8 to code page without a large
|
||||
Unicode‑to‑byte array (no 64k reverse map per code page).
|
||||
|
||||
The design of CodePageTable and the packed reverse mapping is conceptually
|
||||
similar to golang.org/x/text/encoding/charmap.
|
||||
|
||||
*>
|
||||
module std::encoding::codepage;
|
||||
import std::sort;
|
||||
|
||||
<*
|
||||
Default replacement byte used when encoding from UTF‑8 to a single‑byte
|
||||
code page and a Unicode scalar cannot be represented.
|
||||
|
||||
By convention, 0x1A is the ASCII/IBM SUB (substitute) control character.
|
||||
*>
|
||||
const char REPLACEMENT_CHAR = 0x1a;
|
||||
|
||||
<*
|
||||
CodePageTable contains the bidirectional mapping tables for a single‑byte code
|
||||
page in a compact packed form.
|
||||
|
||||
to_codepoint is the forward map from code‑page byte (0x00–0xFF) to its UTF‑8
|
||||
sequence. The array index is the raw byte value, each entry occupying 4 bytes:
|
||||
|
||||
- Byte 0 is the length of the UTF‑8 sequence (0–4)
|
||||
- Bytes 1:len are the UTF‑8 bytes for the mapped Unicode scalar
|
||||
|
||||
The table therefore uses 256 * 4 bytes and is stored as a flat
|
||||
char[1024] array, where entry i starts at offset i*4.
|
||||
|
||||
from_codepoint is the reverse map from Unicode scalar value to code‑page byte,
|
||||
also stored as a packed char[1024] array. It contains 256 entries of 4 bytes
|
||||
each, where each 4‑byte chunk is interpreted as a little‑endian uint
|
||||
with the following packing scheme:
|
||||
|
||||
high 8 bits = code‑page byte value (0x00–0xFF)
|
||||
low 24 bits = Unicode scalar value (code point)
|
||||
|
||||
In other words:
|
||||
|
||||
entry = (byte_value << 24) | codepoint;
|
||||
|
||||
Ordering:
|
||||
The 256 packed uint entries in from_codepoint are sorted by the low 24 bits
|
||||
(code points). This allows binary search over Unicode scalar values without
|
||||
a 64k reverse‑lookup table. For any given code page, there are at most
|
||||
256 mappings, so a log2(256) or 8 step search is sufficient.
|
||||
*>
|
||||
struct CodePageTable
|
||||
{
|
||||
char[1024] to_codepoint;
|
||||
char[1024] from_codepoint;
|
||||
}
|
||||
|
||||
enum CodePage : (String name, CodePageTable* table)
|
||||
{
|
||||
CP1250 { "cp1250", &codepage::CP1250 },
|
||||
CP1251 { "cp1251", &codepage::CP1251 },
|
||||
CP1252 { "cp1252", &codepage::CP1252 },
|
||||
CP1253 { "cp1253", &codepage::CP1253 },
|
||||
CP1254 { "cp1254", &codepage::CP1254 },
|
||||
CP1255 { "cp1255", &codepage::CP1255 },
|
||||
CP1256 { "cp1256", &codepage::CP1256 },
|
||||
CP1257 { "cp1257", &codepage::CP1257 },
|
||||
CP1258 { "cp1258", &codepage::CP1258 },
|
||||
CP437 { "cp437", &codepage::CP437 },
|
||||
CP737 { "cp737", &codepage::CP737 },
|
||||
CP775 { "cp775", &codepage::CP775 },
|
||||
CP850 { "cp850", &codepage::CP850 },
|
||||
CP852 { "cp852", &codepage::CP852 },
|
||||
CP855 { "cp855", &codepage::CP855 },
|
||||
CP857 { "cp857", &codepage::CP857 },
|
||||
CP860 { "cp860", &codepage::CP860 },
|
||||
CP861 { "cp861", &codepage::CP861 },
|
||||
CP862 { "cp862", &codepage::CP862 },
|
||||
CP863 { "cp863", &codepage::CP863 },
|
||||
CP864 { "cp864", &codepage::CP864 },
|
||||
CP865 { "cp865", &codepage::CP865 },
|
||||
CP866 { "cp866", &codepage::CP866 },
|
||||
CP869 { "cp869", &codepage::CP869 },
|
||||
CP874 { "cp874", &codepage::CP874 },
|
||||
ISO_8859_1 { "iso-8859-1", &codepage::ISO_8859_1 },
|
||||
ISO_8859_10 { "iso-8859-10", &codepage::ISO_8859_10 },
|
||||
ISO_8859_11 { "iso-8859-11", &codepage::ISO_8859_11 },
|
||||
ISO_8859_13 { "iso-8859-13", &codepage::ISO_8859_13 },
|
||||
ISO_8859_14 { "iso-8859-14", &codepage::ISO_8859_14 },
|
||||
ISO_8859_15 { "iso-8859-15", &codepage::ISO_8859_15 },
|
||||
ISO_8859_16 { "iso-8859-16", &codepage::ISO_8859_16 },
|
||||
ISO_8859_2 { "iso-8859-2", &codepage::ISO_8859_2 },
|
||||
ISO_8859_3 { "iso-8859-3", &codepage::ISO_8859_3 },
|
||||
ISO_8859_4 { "iso-8859-4", &codepage::ISO_8859_4 },
|
||||
ISO_8859_5 { "iso-8859-5", &codepage::ISO_8859_5 },
|
||||
ISO_8859_6 { "iso-8859-6", &codepage::ISO_8859_6 },
|
||||
ISO_8859_7 { "iso-8859-7", &codepage::ISO_8859_7 },
|
||||
ISO_8859_8 { "iso-8859-8", &codepage::ISO_8859_8 },
|
||||
ISO_8859_9 { "iso-8859-9", &codepage::ISO_8859_9 },
|
||||
US_ASCII { "us-ascii", &codepage::US_ASCII },
|
||||
}
|
||||
|
||||
<*
|
||||
Returns a CodePage for the given charset name.
|
||||
|
||||
@param [in] charset_name : "A name, case insensitive, using _ or - for separator"
|
||||
@return "The CodePage for the name"
|
||||
|
||||
@return? NOT_FOUND : "If the charset is unknown or unsupported"
|
||||
*>
|
||||
fn CodePage? by_name(String charset_name) => @pool()
|
||||
{
|
||||
String name = charset_name.treplace("_","-");
|
||||
name.convert_to_lower();
|
||||
foreach (page : CodePage.values)
|
||||
{
|
||||
if (page.name == charset_name) return page;
|
||||
}
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn String? decode(Allocator allocator, char[] src, CodePage code_page)
|
||||
{
|
||||
char[] dst = allocator::alloc_array(allocator, char, decode_len(src, code_page));
|
||||
return decode_buffer(src, dst, code_page);
|
||||
}
|
||||
|
||||
<*
|
||||
Decode a code-page byte buffer into a UTF‑8 string.
|
||||
|
||||
@param src : "Input byte array in the given code page."
|
||||
@param dst : "Destination output string in UTF-8."
|
||||
@param code_page : "Code page for this encoding."
|
||||
@return "String in UTF-8."
|
||||
*>
|
||||
fn String? decode_buffer(char[] src, char[] dst, CodePage code_page)
|
||||
{
|
||||
usz n = 0;
|
||||
CodePageTable *table = code_page.table;
|
||||
foreach (c: src)
|
||||
{
|
||||
usz pos = (usz)c * 4;
|
||||
char len = table.to_codepoint[pos];
|
||||
|
||||
dst[n:len] = table.to_codepoint[pos+1:len];
|
||||
n += len;
|
||||
}
|
||||
|
||||
return (String)dst[:n];
|
||||
}
|
||||
|
||||
fn char[]? encode(Allocator allocator, char[] src, CodePage code_page, char replacement = REPLACEMENT_CHAR)
|
||||
{
|
||||
char[] dst = allocator::alloc_array(allocator, char, encode_len(src));
|
||||
return encode_buffer(src, dst, code_page, replacement);
|
||||
}
|
||||
|
||||
const uint MASK @private = (1u << 24) - 1;
|
||||
|
||||
<*
|
||||
Encode a UTF‑8 string into a single‑byte code page.
|
||||
|
||||
@param src : "Input byte array in UTF-8"
|
||||
@param dst : "Destination output byte array in the target code page"
|
||||
@param code_page : "Code page for this encoding."
|
||||
@param replacement : "Byte to emit when Unicode scalar cannot be represented in the target code page."
|
||||
@return "Byte array in the given code page."
|
||||
*>
|
||||
fn char[]? encode_buffer(char[] src, char[] dst, CodePage code_page, char replacement = REPLACEMENT_CHAR)
|
||||
{
|
||||
// Unpack the packed reverse table once into a local uint[256] view.
|
||||
uint[256] from_map;
|
||||
CodePageTable *table = code_page.table;
|
||||
for (usz i = 0; i < 256; i++)
|
||||
{
|
||||
UIntLE *val = (UIntLE*)&table.from_codepoint[i * 4];
|
||||
from_map[i] = mem::load(val, 1).val;
|
||||
}
|
||||
|
||||
usz out = 0;
|
||||
usz n = src.len;
|
||||
for (usz i = 0; i < n; )
|
||||
{
|
||||
usz rem = n - i;
|
||||
if (rem > 4) rem = 4;
|
||||
|
||||
Char32 codepoint = conv::utf8_to_char32(&src[i], &rem)!;
|
||||
i += rem;
|
||||
|
||||
// Binary search for codepoint in low 24 bits of each entry.
|
||||
// Returned index is between [0..from_map.len).
|
||||
usz index = sort::binarysearch(from_map[..], (uint)codepoint, fn int(uint lhs, uint rhs) => (int)(lhs & MASK) - (int)(rhs & MASK));
|
||||
|
||||
uint entry = from_map[index];
|
||||
if ((entry & MASK) == (uint)codepoint)
|
||||
{
|
||||
char b = (char)(entry >> 24);
|
||||
dst[out++] = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[out++] = replacement;
|
||||
}
|
||||
}
|
||||
|
||||
return dst[:out];
|
||||
}
|
||||
|
||||
<*
|
||||
Compute the number of UTF‑8 bytes produced when decoding src with the given
|
||||
code page table.
|
||||
@param src : "Input byte array in the given code page."
|
||||
@param code_page : "Code page for this encoding."
|
||||
*>
|
||||
fn usz decode_len(char[] src, CodePage code_page) @inline
|
||||
{
|
||||
usz n;
|
||||
CodePageTable *table = code_page.table;
|
||||
foreach (usz c: src) n += table.to_codepoint[c *4 ];
|
||||
return n;
|
||||
}
|
||||
|
||||
<*
|
||||
Compute the number of output bytes produced when
|
||||
encoding src from UTF‑8 to a single‑byte code page.
|
||||
@param src : "Input byte array in UTF-8"
|
||||
*>
|
||||
fn usz encode_len(char[] src) @inline
|
||||
{
|
||||
return conv::utf8_codepoints((String)src);
|
||||
}
|
||||
|
||||
2234
lib/std/encoding/codepage_private.c3
Normal file
2234
lib/std/encoding/codepage_private.c3
Normal file
File diff suppressed because it is too large
Load Diff
355
lib/std/encoding/pem.c3
Normal file
355
lib/std/encoding/pem.c3
Normal file
@@ -0,0 +1,355 @@
|
||||
// Copyright (c) 2026 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.
|
||||
//
|
||||
// A module for encoding or decoding PEM blobs [mostly] in accordance with RFCs 1421-1424.
|
||||
// This implementation retains a lot of flexibility in parsing input PEM blobs.
|
||||
//
|
||||
module std::encoding::pem;
|
||||
|
||||
import std::collections, std::encoding::base64;
|
||||
|
||||
<* A safe, default tag to use per RFC 1421's rules. *>
|
||||
const String DEFAULT_TAG = "PRIVACY-ENHANCED MESSAGE";
|
||||
|
||||
<* The set of characters which are considered valid for PEM tags (which appear inside of Encapsulation Boundaries). *>
|
||||
const AsciiCharset TAG_SET @local = ascii::@combine_sets(ascii::ALPHA_UPPER_SET, ascii::NUMBER_SET, ascii::@create_set(" _-/+()"));
|
||||
<* The set of characters which are considered valid for optional PEM headers used. *>
|
||||
const AsciiCharset HEADER_KEY_SET @local = ascii::@combine_sets(ascii::ALPHANUMERIC_SET, ascii::@create_set("!#$%&'*+-.^_`|~"));
|
||||
|
||||
<* All PEM Encapsulation Boundaries must use this delimiter to demarcate the PEM from its surrounding content, if any. *>
|
||||
const String EB_DELIMITER @local = "-----";
|
||||
<* All PEM blobs will start with this Encapsulation Boundary prefix. *>
|
||||
const String PRE_EB_PREFIX @local = EB_DELIMITER +++ "BEGIN ";
|
||||
<* All PEM blobs will terminate with this Encapsulation Boundary prefix. *>
|
||||
const String POST_EB_PREFIX @local = EB_DELIMITER +++ "END ";
|
||||
|
||||
alias PemHeader = String[2];
|
||||
|
||||
<* Specify a set of possible PEM en/decoding faults. *>
|
||||
faultdef
|
||||
BODY_REQUIRED, // encoding: no body given (or too few of them)
|
||||
HEADERS_REQUIRED, // encoding: no headers given (or too few of them)
|
||||
HEADER_KEY_REQUIRED, // encoding: blank header keys are not allowed
|
||||
HEADER_VALUE_REQUIRED, // encoding: blank header values are not allowed
|
||||
INVALID_BODY, // decoding: invalid body, likely bad base64
|
||||
INVALID_FORMAT, // decoding: invalid input formatting - no pre-EB or just plain wrong
|
||||
INVALID_HEADER, // decoding: invalid headers
|
||||
INVALID_HEADER_KEY, // decoding: invalid or empty header key
|
||||
INVALID_PRE_EB, // decoding: invalid pre-EncapsBoundary BEFORE the PEM body
|
||||
INVALID_POST_EB, // decoding: invalid post-EncapsBoundary AFTER the PEM body
|
||||
INVALID_TAG, // decoding: invalid tag within an EB
|
||||
MISMATCHED_TAG, // decoding: the tag from the pre-EB doesn't match that of the post-EB
|
||||
MISSING_BODY, // decoding: missing PEM body base64
|
||||
MISSING_HEADER_KEY, // decoding: the header is missing its key
|
||||
MISSING_HEADER_VALUE, // decoding: the header is missing its value
|
||||
MISSING_POST_EB, // decoding: no post-EB was found to close off the PEM
|
||||
MISSING_TAG, // decoding: no tag was defined or parsed from the EB
|
||||
TAG_REQUIRED, // encoding: no/empty tag given (or too few of them)
|
||||
;
|
||||
|
||||
|
||||
<* Represents a PEM object in memory, with a reference to the body data, tag value, and optional headers. *>
|
||||
struct Pem
|
||||
{
|
||||
<* The allocator associated with the PEM's creation and destruction. *>
|
||||
Allocator allocator;
|
||||
<* A flexible 'tag' value used within the Encapsulation Boundary to denote the type of the PEM. *>
|
||||
String tag;
|
||||
<* A set of optional headers used to provide more context or information about the body of the PEM object. *>
|
||||
LinkedHashMap{String, String} headers;
|
||||
<* The core boy data of the PEM itself - the main values to be transmitted in this format. *>
|
||||
char[] data;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Create a new PEM object from a few inputs. Each input (i.e., tag, data, and headers) is copied to a new memory location.
|
||||
The PEM object itself is not allocated in-memory, but is a simple container that points to each value that _is_.
|
||||
|
||||
Key-Value pairs for headers are provided in sequence as variadic arguments: `"key", "value", "key2", "value2", ...`
|
||||
|
||||
Created PEMs that are not temporary should be destroyed with `Pem.free`.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use when copying the provided PEM object's fields."
|
||||
@param [in] data : "The body data of the PEM."
|
||||
@param [in] tag : "The tag value to use within the PEM's Encapsulation Boundary."
|
||||
|
||||
@return "A new PEM object."
|
||||
*>
|
||||
fn Pem create(Allocator allocator, char[] data, String tag, PemHeader... args)
|
||||
{
|
||||
Pem result = {
|
||||
.allocator = allocator,
|
||||
.tag = tag.copy(allocator),
|
||||
.data = allocator::clone_slice(allocator, data),
|
||||
};
|
||||
result.headers.init(allocator, capacity: max(args.len, 16));
|
||||
foreach (arg : args)
|
||||
{
|
||||
result.add_header(arg[0], arg[1]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Duplicate a `Pem` container and allocate copies of its members using the given allocator.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use when copying the `Pem` members."
|
||||
*>
|
||||
fn Pem Pem.copy(&self, Allocator allocator)
|
||||
{
|
||||
Pem result = create(allocator, self.data, self.tag);
|
||||
self.headers.@each(;String key, String value)
|
||||
{
|
||||
result.add_header(key, value);
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
<*
|
||||
Safely destroys a `Pem` and deallocate all of its members. This should always be explicitly called when not using `tmem`.
|
||||
*>
|
||||
fn void Pem.free(&self)
|
||||
{
|
||||
mem::zero_volatile(self.data);
|
||||
if (self.allocator != tmem)
|
||||
{
|
||||
self.headers.@each(;String key, String value)
|
||||
{
|
||||
allocator::free(self.allocator, value);
|
||||
};
|
||||
self.headers.free();
|
||||
self.tag.free(self.allocator);
|
||||
allocator::free(self.allocator, self.data);
|
||||
}
|
||||
mem::zero_volatile(@as_char_view(*self));
|
||||
}
|
||||
|
||||
fn void Pem.add_header(&self, String key, String value)
|
||||
{
|
||||
(void)self.headers[key].free(self.allocator);
|
||||
self.headers[key] = value.copy(self.allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Attempt to decode an input string into one or more `Pem` objects. If the input contains any non-PEM or otherwise
|
||||
invalid data, then this will throw an error. Ideally, this function is used to decode PEM files explicitly, lest
|
||||
the caller need to be sure they're only providing PEM data +/- some intermediate whitespace.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use when creating the `Pem` outputs and members."
|
||||
@param [in] input : "The string to parse one or more PEM blobs from."
|
||||
|
||||
@return "An array of decoded `Pem` objects, depending on how many were present in the input (separated optionally by whitespace)."
|
||||
*>
|
||||
fn Pem[]? decode(Allocator allocator, String input) => @pool()
|
||||
{
|
||||
List{Pem} pem_list;
|
||||
pem_list.tinit();
|
||||
|
||||
String[] lines = input.treplace("\r\n", "\n").tsplit("\n");
|
||||
foreach (&line : lines) *line = (*line).trim_right(); // remove any trailing whitespace as this can disrupt parsing (but shouldn't)
|
||||
while (lines.len > 0)
|
||||
{
|
||||
pem_list.push(_decode_single(allocator, &lines)!);
|
||||
while (lines.len > 0 && lines[0].trim().len == 0) lines = lines[1..]; // skip all empty lines in between or after PEM boundaries
|
||||
}
|
||||
return pem_list.to_array(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
INTERNAL ONLY: Decode one PEM at a time, from pre-EB to its discovered post-EB.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use during decoding to return the result."
|
||||
@param [&inout] lines_io : "A pointer to an input slice to modify as the single PEM is parsed from it."
|
||||
|
||||
*>
|
||||
fn Pem? _decode_single(Allocator allocator, String[]* lines_io) @local
|
||||
{
|
||||
String[] lines = *lines_io; // copy to local var
|
||||
Pem result = { .allocator = allocator };
|
||||
result.headers.init(allocator);
|
||||
defer catch result.free();
|
||||
|
||||
// Remove any preceding whitespace-only lines.
|
||||
while (lines[0].trim().len == 0) lines = lines[1..];
|
||||
|
||||
if (lines.len < 3) return INVALID_FORMAT~; // at least 3 lines (pre-EB, body, post-EB) are always required
|
||||
|
||||
// The Pre-Encapsulation-Boundary must be of the format: -----BEGIN TAG-----, where "TAG" can be any upper-case identifier [A-Z_ -/]
|
||||
String pre_eb = lines[0];
|
||||
if (pre_eb[0:11] != PRE_EB_PREFIX || pre_eb[^5..] != EB_DELIMITER) return INVALID_PRE_EB~;
|
||||
String tag = pre_eb[PRE_EB_PREFIX.len..^6];
|
||||
if (!tag.len || !tag.trim().len) return MISSING_TAG~;
|
||||
foreach (c : tag) if (!TAG_SET.contains(c)) return INVALID_TAG~;
|
||||
result.tag = tag.copy(allocator);
|
||||
|
||||
// The Post-Encapsulation-Boundary is the same, but uses "END", and the extracted tag must match.
|
||||
// Since the input might contain more than one PEM unit, we need to search for the ending encapsulation boundary dynamically.
|
||||
String post_eb;
|
||||
usz endl;
|
||||
for SEARCH_EB: (endl = 1; endl < lines.len; endl++)
|
||||
{
|
||||
if (lines[endl].len > POST_EB_PREFIX.len && lines[endl][0:EB_DELIMITER.len] == EB_DELIMITER)
|
||||
{
|
||||
post_eb = lines[endl];
|
||||
break SEARCH_EB;
|
||||
}
|
||||
}
|
||||
if (!post_eb.len) return MISSING_POST_EB~;
|
||||
if (post_eb[0:9] != POST_EB_PREFIX || post_eb[^5..] != EB_DELIMITER) return INVALID_POST_EB~;
|
||||
String post_tag = post_eb[POST_EB_PREFIX.len..^6];
|
||||
if (post_tag.len != tag.len || post_tag != tag) return MISMATCHED_TAG~;
|
||||
|
||||
// Now that the inner portion is decapsulated, tag is, strip off the boundaries.
|
||||
*lines_io = lines[endl+1..]; // update the iterated slice of lines from the calling context - see: `decode`
|
||||
lines = lines[1:endl-1];
|
||||
|
||||
// while there's a colon+space in the current line, we should assume that this is a key-value header pair
|
||||
while (lines[0].contains(": "))
|
||||
{
|
||||
if (!HEADER_KEY_SET.contains(lines[0][0])) return INVALID_HEADER~; // not a multiline header? error out if the first char is not appropriate
|
||||
String[] marker = lines; // temporary marker
|
||||
usz span = 1; // how many lines this header spans
|
||||
|
||||
// Search for multi-line key-value pairs, indicated by a whitespace character beginning the current line.
|
||||
for (lines = lines[1..]; lines[0].len > 0 && ascii::WHITESPACE_SET.contains(lines[0][0]); lines = lines[1..], span++);
|
||||
foreach (&line : marker[:span]) *line = (*line).trim(); // always trim on both sides
|
||||
|
||||
String full_header = string::tjoin(marker[:span], " "); // join the lines with a single space
|
||||
if (!full_header.contains(": ")) return INVALID_HEADER~; // reassert the presence of this
|
||||
|
||||
// Extract the key and value from the message, then validate.
|
||||
// The header name should match a valid set of characters, but the value doesn't need to conform to anything other than existing
|
||||
String[] kv = full_header.tsplit(": ", max: 2);
|
||||
if (!kv[0].len) return MISSING_HEADER_KEY~;
|
||||
if (!kv[1].len) return MISSING_HEADER_VALUE~;
|
||||
foreach (c : kv[0]) if (!HEADER_KEY_SET.contains(c)) return INVALID_HEADER_KEY~;
|
||||
|
||||
result.add_header(kv[0], kv[1]); // finally, push the values
|
||||
}
|
||||
|
||||
// if any headers were present, the line after the headers MUST BE EMPTY
|
||||
if (result.headers.len() > 0)
|
||||
{
|
||||
if (lines[0].trim().len > 0) return INVALID_FORMAT~; // but we are forgiving about whitespace here
|
||||
lines = lines[1..];
|
||||
}
|
||||
|
||||
// Here, we assume lines[0] is the start of base64 data. This means there must be at least 1 line, of course.
|
||||
if (lines.len < 1) return MISSING_BODY~;
|
||||
|
||||
// ... While the PEM format specifies a 64-character width on all but the last line of the base64 body,
|
||||
// this parser doesn't need to be particular about that as long as the base64 is ok
|
||||
// In this case, the rest of the lines in the set should be base64 and should decode accordingly
|
||||
String to_decode = string::tjoin(lines, "");
|
||||
if (!to_decode.len) return MISSING_BODY~; // paranoia
|
||||
result.data = (base64::decode(allocator, to_decode) ?? INVALID_BODY~)!;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Encodes a single `Pem` object into a new PEM-formatted string.
|
||||
|
||||
@param pem : "The pem object to encode"
|
||||
@param [&inout] allocator : "The allocator to use for allocating the final encoded string."
|
||||
*>
|
||||
fn String? encode_pem(Pem pem, Allocator allocator, bool use_crlf = false)
|
||||
{
|
||||
if (!pem.data.len) return BODY_REQUIRED~;
|
||||
if (!pem.tag.len) return TAG_REQUIRED~;
|
||||
|
||||
DString out;
|
||||
out.tinit();
|
||||
String line_ending = use_crlf ? "\r\n" : "\n";
|
||||
@pool()
|
||||
{
|
||||
out.appendf(PRE_EB_PREFIX +++ "%s" +++ EB_DELIMITER +++ "%s", pem.tag, line_ending);
|
||||
foreach KEY_ITER: (key : pem.headers.tkeys())
|
||||
{
|
||||
if (!key.len) return HEADER_KEY_REQUIRED~;
|
||||
String value = pem.headers[key]!!;
|
||||
if (!value.len) return HEADER_VALUE_REQUIRED~;
|
||||
usz first_line_length = 64 - 2 - key.len;
|
||||
if (value.len <= first_line_length)
|
||||
{
|
||||
out.appendf("%s: %s%s", key, value, line_ending);
|
||||
continue KEY_ITER;
|
||||
}
|
||||
out.appendf("%s: %s%s", key, value[:first_line_length].trim(), line_ending);
|
||||
value = value[first_line_length..];
|
||||
while (value.len > 0)
|
||||
{
|
||||
out.appendf(" %s%s", (value.len >= 63 ? value[:63] : value[..]).trim(), line_ending);
|
||||
value = value.len >= 63 ? value[63..] : {};
|
||||
}
|
||||
}
|
||||
if (pem.headers.len() > 0) out.append(line_ending);
|
||||
String body = base64::tencode(pem.data);
|
||||
while (body.len > 0)
|
||||
{
|
||||
out.appendf("%s%s", body.len >= 64 ? body[:64] : body[..], line_ending);
|
||||
body = body.len >= 64 ? body[64..] : {};
|
||||
}
|
||||
out.appendf(POST_EB_PREFIX +++ "%s" +++ EB_DELIMITER +++ "%s", pem.tag, line_ending);
|
||||
};
|
||||
|
||||
return allocator == tmem ? out.str_view() : out.copy_str(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Encodes a set of input data into a `String` containing the PEM-encoded data.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use when creating the final output string."
|
||||
@param [in] data : "The body data for the output PEM."
|
||||
@param [in] tag : "The tag "
|
||||
*>
|
||||
fn String? encode(Allocator allocator, char[] data, String tag, PemHeader... headers, bool use_crlf = false) => @pool()
|
||||
{
|
||||
if (!data.len) return BODY_REQUIRED~;
|
||||
return encode_pem(create(tmem, data, tag, ...headers), allocator, use_crlf);
|
||||
}
|
||||
|
||||
<*
|
||||
Encode many inputs to a single output string that represents chained/sequential PEM objects in the order they were provided.
|
||||
The length of the `bodies` and `tags` array must be equal.
|
||||
If headers are provided, they must be arrays of String objects, matching both the number of tags and the number of bodies.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use when creating the final output string."
|
||||
@param [in] bodies : "An ordered array of binary arrays, each representing the body of a single PEM."
|
||||
@param [in] tags : "An ordered array of tag strings, each representing the tag of a single PEM."
|
||||
|
||||
@return "A new `String`, allocated with `allocator`, that contains all PEM objects in the order they were given."
|
||||
*>
|
||||
fn String? encode_many(Allocator allocator, char[][] bodies, String[] tags, PemHeader[]... pem_headers, bool use_crlf = false)
|
||||
{
|
||||
usz entries = max(bodies.len, tags.len, pem_headers.len);
|
||||
switch
|
||||
{
|
||||
case bodies.len < entries: return BODY_REQUIRED~;
|
||||
case tags.len < entries: return TAG_REQUIRED~;
|
||||
case pem_headers.len > 0 && pem_headers.len < entries: return HEADERS_REQUIRED~;
|
||||
}
|
||||
|
||||
DString out;
|
||||
out.tinit();
|
||||
|
||||
if (!pem_headers.len)
|
||||
{
|
||||
foreach (x, body : bodies) @pool()
|
||||
{
|
||||
out.append(encode(tmem, body, tags[x], use_crlf: use_crlf)!);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (i, headers : pem_headers) @pool()
|
||||
{
|
||||
out.append(encode(tmem, bodies[i], tags[i], ...headers, use_crlf: use_crlf)!);
|
||||
};
|
||||
}
|
||||
return allocator == tmem ? out.str_view() : out.copy_str(allocator);
|
||||
}
|
||||
@@ -7,7 +7,7 @@ struct DelayedSchedulerEvent @local
|
||||
Clock execution_time;
|
||||
}
|
||||
|
||||
fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other) @local
|
||||
fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other)
|
||||
{
|
||||
switch
|
||||
{
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
module std::hash::adler32;
|
||||
|
||||
const uint ADLER_CONST @private = 65521;
|
||||
const uint ADLER32_CONST @private = 65521;
|
||||
|
||||
|
||||
struct Adler32
|
||||
{
|
||||
@@ -19,19 +20,79 @@ fn void Adler32.init(&self)
|
||||
|
||||
fn void Adler32.updatec(&self, char c)
|
||||
{
|
||||
self.a = (self.a + c) % ADLER_CONST;
|
||||
self.b = (self.b + self.a) % ADLER_CONST;
|
||||
self.a = (self.a + c) % ADLER32_CONST;
|
||||
self.b = (self.b + self.a) % ADLER32_CONST;
|
||||
}
|
||||
|
||||
fn void Adler32.update(&self, char[] data)
|
||||
{
|
||||
// Safe chunking constant which is optimized for L1 cache on most systems 32768 (32 KB).
|
||||
// 0x8000 ~ (2^32 / 65521 / 2).
|
||||
// The division is done so that we are guarenteed to never overflow.
|
||||
const uint SAFE_CHUNKING_SIZE = 0x8000;
|
||||
|
||||
// In order
|
||||
const uint UNROLL_SIZE = 8;
|
||||
|
||||
uint a = self.a;
|
||||
uint b = self.b;
|
||||
foreach (char x : data)
|
||||
|
||||
char* buf = data;
|
||||
usz len = data.len;
|
||||
|
||||
// Align pointer traversing buffer pointer to the unroll alignment size.
|
||||
if (len % UNROLL_SIZE != 0)
|
||||
{
|
||||
a = (a + x) % ADLER_CONST;
|
||||
b = (b + a) % ADLER_CONST;
|
||||
do
|
||||
{
|
||||
a += *buf;
|
||||
b += a;
|
||||
|
||||
buf++;
|
||||
len--;
|
||||
} while (len % UNROLL_SIZE != 0);
|
||||
|
||||
if (a >= ADLER32_CONST)
|
||||
{
|
||||
a -= ADLER32_CONST;
|
||||
}
|
||||
|
||||
b %= ADLER32_CONST;
|
||||
}
|
||||
|
||||
// Calculate rest of adler32 checksum.
|
||||
while (len > 0)
|
||||
{
|
||||
$for var $i = 0; $i < UNROLL_SIZE; $i++:
|
||||
a += buf[$i]; b += a;
|
||||
$endfor
|
||||
|
||||
len -= UNROLL_SIZE;
|
||||
buf += UNROLL_SIZE;
|
||||
|
||||
// Even with 8 max value (0xFF) bytes being additioned to a (0xFF * 8 = 2040 for worst case).
|
||||
// There is no chance that a will be > 2 * ADLER32_CONST, so modulo is not needed here.
|
||||
// So its more performant to use subtraction.
|
||||
if (a >= ADLER32_CONST)
|
||||
{
|
||||
a -= ADLER32_CONST;
|
||||
}
|
||||
|
||||
// We need to periodically chunk b because it accumulates a which is a sum, so it grows rapidly.
|
||||
// So every 4K of bytes we modulo in order to prevent uint integer overflow.
|
||||
if (len % SAFE_CHUNKING_SIZE == 0)
|
||||
{
|
||||
b %= ADLER32_CONST;
|
||||
}
|
||||
}
|
||||
|
||||
// No need to explicitely modulo after loop end with ADLER32_CONST.
|
||||
// As a and b are guarenteed to be under ADLER32_CONST.
|
||||
|
||||
// Do assert on debug.
|
||||
assert(a < ADLER32_CONST);
|
||||
assert(b < ADLER32_CONST);
|
||||
|
||||
*self = { a, b };
|
||||
}
|
||||
|
||||
@@ -42,12 +103,10 @@ fn uint Adler32.final(&self)
|
||||
|
||||
fn uint hash(char[] data)
|
||||
{
|
||||
uint a = 1;
|
||||
uint b = 0;
|
||||
foreach (char x : data)
|
||||
{
|
||||
a = (a + x) % ADLER_CONST;
|
||||
b = (b + a) % ADLER_CONST;
|
||||
}
|
||||
return (b << 16) | a;
|
||||
Adler32 adler;
|
||||
adler.init();
|
||||
|
||||
adler.update(data);
|
||||
|
||||
return adler.final();
|
||||
}
|
||||
@@ -49,43 +49,43 @@ macro @round(r, m, $s, #v) @local
|
||||
@g(r, m, $s, 7, #v[ 3], #v[ 4], #v[ 9], #v[14]);
|
||||
}
|
||||
|
||||
macro @common_compress(#instance, $rounds, $iv, $sigma, block) @local
|
||||
macro common_compress(instance, $rounds, $iv, $sigma, block) @local
|
||||
{
|
||||
$typeof(#instance.h[0])[16] m, v;
|
||||
$typeof(instance.h[0])[16] m, v;
|
||||
|
||||
((char*)&m)[:$sizeof(block)] = block[..];
|
||||
|
||||
v[:8] = #instance.h[..];
|
||||
v[:8] = instance.h[..];
|
||||
v[ 8] = $iv[0];
|
||||
v[ 9] = $iv[1];
|
||||
v[10] = $iv[2];
|
||||
v[11] = $iv[3];
|
||||
v[12] = $iv[4] ^ #instance.t[0];
|
||||
v[13] = $iv[5] ^ #instance.t[1];
|
||||
v[14] = $iv[6] ^ #instance.f[0];
|
||||
v[15] = $iv[7] ^ #instance.f[1];
|
||||
v[12] = $iv[4] ^ instance.t[0];
|
||||
v[13] = $iv[5] ^ instance.t[1];
|
||||
v[14] = $iv[6] ^ instance.f[0];
|
||||
v[15] = $iv[7] ^ instance.f[1];
|
||||
|
||||
$for usz $i = 0; $i < $rounds; $i++:
|
||||
@round($i, m, $sigma, v);
|
||||
$endfor
|
||||
|
||||
$for usz $i = 0; $i < 8; $i++:
|
||||
#instance.h[$i] ^= v[$i] ^ v[$i + 8];
|
||||
instance.h[$i] ^= v[$i] ^ v[$i + 8];
|
||||
$endfor
|
||||
}
|
||||
|
||||
macro @add_ctr(#instance, usz amount) @local
|
||||
macro _add_ctr(instance, usz amount) @local
|
||||
{
|
||||
#instance.t[0] += ($typeof(#instance.t[0]))amount;
|
||||
#instance.t[1] += ($typeof(#instance.t[0]))(#instance.t[0] < amount); // adds 1 on overflow of [0]
|
||||
instance.t[0] += ($typeof(instance.t[0]))amount;
|
||||
instance.t[1] += ($typeof(instance.t[0]))(instance.t[0] < amount); // adds 1 on overflow of [0]
|
||||
}
|
||||
|
||||
macro @common_init(#instance, $ParamType, $iv, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {}) @local
|
||||
macro common_init(instance, $ParamType, $iv, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {}) @local
|
||||
{
|
||||
mem::zero_volatile(@as_char_view(*#instance)); // explicitly because habits around hash init usually involve @noinit
|
||||
mem::zero_volatile(@as_char_view(*instance)); // explicitly because habits around hash init usually involve @noinit
|
||||
|
||||
#instance.h[..] = $iv[..];
|
||||
#instance.outlen = out_len;
|
||||
instance.h[..] = $iv[..];
|
||||
instance.outlen = out_len;
|
||||
|
||||
$ParamType p = {
|
||||
.digest_length = (char)out_len,
|
||||
@@ -96,59 +96,60 @@ macro @common_init(#instance, $ParamType, $iv, usz out_len, char[] key = {}, cha
|
||||
if (salt.len) p.salt[:salt.len] = salt[..];
|
||||
if (personal.len) p.personal[:personal.len] = personal[..];
|
||||
|
||||
array::@zip_into(((char*)&#instance.h)[:$sizeof(p)], ((char*)&p)[:$sizeof(p)], fn (a, b) => a ^ b); // bytes(self.h) ^= bytes(p)
|
||||
array::@zip_into(((char*)&instance.h)[:$sizeof(p)], ((char*)&p)[:$sizeof(p)], fn (a, b) => a ^ b); // bytes(self.h) ^= bytes(p)
|
||||
|
||||
if (key.len)
|
||||
{
|
||||
char[$sizeof($iv[0])*16] dummy = {};
|
||||
dummy[:key.len] = key[..];
|
||||
#instance.update(dummy[..]); // consume a FULL block
|
||||
instance.update(dummy[..]); // consume a FULL block
|
||||
mem::zero_volatile(dummy[..]); // do not optimize clearing this from the stack
|
||||
}
|
||||
}
|
||||
|
||||
macro @common_update(#instance, $block_size, char[] data) @local
|
||||
macro common_update(instance, $block_size, char[] data) @local
|
||||
{
|
||||
if (@unlikely(!data.len)) return;
|
||||
|
||||
usz fill = $block_size - #instance.buflen;
|
||||
usz fill = $block_size - instance.buflen;
|
||||
if (data.len > fill)
|
||||
{
|
||||
#instance.buf[#instance.buflen:fill] = data[:fill];
|
||||
#instance.buflen = 0;
|
||||
instance.buf[instance.buflen:fill] = data[:fill];
|
||||
instance.buflen = 0;
|
||||
|
||||
@add_ctr(#instance, $block_size);
|
||||
#instance.compress(#instance.buf);
|
||||
_add_ctr(instance, $block_size);
|
||||
instance._compress(instance.buf);
|
||||
|
||||
data = data[fill..];
|
||||
|
||||
for (; data.len > $block_size; data = data[$block_size..])
|
||||
{
|
||||
@add_ctr(#instance, $block_size);
|
||||
#instance.compress(data[:$block_size]);
|
||||
_add_ctr(instance, $block_size);
|
||||
instance._compress(data[:$block_size]);
|
||||
}
|
||||
}
|
||||
#instance.buf[#instance.buflen:data.len] = data[..];
|
||||
#instance.buflen += data.len;
|
||||
instance.buf[instance.buflen:data.len] = data[..];
|
||||
instance.buflen += data.len;
|
||||
}
|
||||
|
||||
macro @common_final(#instance, $output_length) @local
|
||||
macro common_final(instance, $output_length) @local
|
||||
{
|
||||
char[$output_length] result = {};
|
||||
if ($output_length != #instance.outlen) return result;
|
||||
if ($output_length != instance.outlen) return result;
|
||||
|
||||
@add_ctr(#instance, #instance.buflen);
|
||||
if (#instance.f[0]) return result; // technically an error return
|
||||
_add_ctr(instance, instance.buflen);
|
||||
if (instance.f[0]) return result; // technically an error return
|
||||
|
||||
if (#instance.last_node) #instance.f[1] = $typeof(#instance.h[0]).max;
|
||||
#instance.f[0] = $typeof(#instance.h[0]).max;
|
||||
var $max = $typeof(instance.h[0]).max;
|
||||
if (instance.last_node) instance.f[1] = $max;
|
||||
instance.f[0] = $max;
|
||||
|
||||
mem::zero_volatile(#instance.buf[#instance.buflen..]); // pad buffer with zeroes
|
||||
#instance.compress(#instance.buf);
|
||||
mem::zero_volatile(instance.buf[instance.buflen..]); // pad buffer with zeroes
|
||||
instance._compress(instance.buf);
|
||||
|
||||
defer mem::zero_volatile(@as_char_view(*#instance)); // destroy the current context implicitly
|
||||
defer mem::zero_volatile(@as_char_view(*instance)); // destroy the current context implicitly
|
||||
|
||||
result[:#instance.outlen] = @as_char_view(#instance.h)[:#instance.outlen];
|
||||
result[:instance.outlen] = @as_char_view(instance.h)[:instance.outlen];
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -244,13 +245,13 @@ alias b_512 = blake2b_512;
|
||||
@require !personal.ptr || (personal.len > 0 && personal.len <= BLAKE2B_PERSONALBYTES) : "A specified personalization's length must be within the proper range."
|
||||
*>
|
||||
fn void Blake2b.init(&self, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {})
|
||||
=> @common_init(self, Blake2bParam, BLAKE2B_IV, out_len, key, salt, personal);
|
||||
=> common_init(self, Blake2bParam, BLAKE2B_IV, out_len, key, salt, personal);
|
||||
|
||||
<*
|
||||
Core compression inline function for Blake2b.
|
||||
*>
|
||||
fn void Blake2b.compress(&self, char[BLAKE2B_BLOCKBYTES] block) @local @inline
|
||||
=> @common_compress(self, 12, BLAKE2B_IV, BLAKE2B_SIGMA, block);
|
||||
fn void Blake2b._compress(&self, char[BLAKE2B_BLOCKBYTES] block) @inline
|
||||
=> common_compress(self, 12, BLAKE2B_IV, BLAKE2B_SIGMA, block);
|
||||
|
||||
<*
|
||||
Add more data to the hash context or stream.
|
||||
@@ -258,7 +259,7 @@ fn void Blake2b.compress(&self, char[BLAKE2B_BLOCKBYTES] block) @local @inline
|
||||
@param[in] data : "The data to ingest into the hash context."
|
||||
*>
|
||||
fn void Blake2b.update(&self, char[] data)
|
||||
=> @common_update(self, BLAKE2B_BLOCKBYTES, data);
|
||||
=> common_update(self, BLAKE2B_BLOCKBYTES, data);
|
||||
|
||||
<*
|
||||
Finalize the hash context and return the hash result at the given size.
|
||||
@@ -267,9 +268,12 @@ fn void Blake2b.update(&self, char[] data)
|
||||
|
||||
@require $output_length == self.outlen : "The specified compile-time output size MUST be equal to the initialized output size."
|
||||
*>
|
||||
macro char[*] Blake2b.final(&self, $output_length)
|
||||
=> @common_final(self, $output_length);
|
||||
macro char[*] Blake2b.final(&self, $output_length) => _blake2b_final{$output_length}(self);
|
||||
|
||||
fn char[OUTPUT_LENGTH] _blake2b_final(Blake2b* self) <OUTPUT_LENGTH> @local
|
||||
{
|
||||
return common_final(self, OUTPUT_LENGTH);
|
||||
}
|
||||
|
||||
// ======================================================================================
|
||||
// BEGIN Blake2s contents. Do not separate this from Blake2b: there's not really a point.
|
||||
@@ -356,13 +360,13 @@ alias s_256 = blake2s_256;
|
||||
@require !personal.ptr || (personal.len > 0 && personal.len <= BLAKE2B_PERSONALBYTES) : "A specified personalization's length must be within the proper range."
|
||||
*>
|
||||
fn void Blake2s.init(&self, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {})
|
||||
=> @common_init(self, Blake2sParam, BLAKE2S_IV, out_len, key, salt, personal);
|
||||
=> common_init(self, Blake2sParam, BLAKE2S_IV, out_len, key, salt, personal);
|
||||
|
||||
<*
|
||||
Core compression inline function for Blake2s.
|
||||
*>
|
||||
fn void Blake2s.compress(&self, char[BLAKE2S_BLOCKBYTES] block) @local @inline
|
||||
=> @common_compress(self, 10, BLAKE2S_IV, BLAKE2S_SIGMA, block);
|
||||
fn void Blake2s._compress(&self, char[BLAKE2S_BLOCKBYTES] block) @inline
|
||||
=> common_compress(self, 10, BLAKE2S_IV, BLAKE2S_SIGMA, block);
|
||||
|
||||
<*
|
||||
Add more data to the hash context or stream.
|
||||
@@ -370,7 +374,7 @@ fn void Blake2s.compress(&self, char[BLAKE2S_BLOCKBYTES] block) @local @inline
|
||||
@param[in] data : "The data to ingest into the hash context."
|
||||
*>
|
||||
fn void Blake2s.update(&self, char[] data)
|
||||
=> @common_update(self, BLAKE2S_BLOCKBYTES, data);
|
||||
=> common_update(self, BLAKE2S_BLOCKBYTES, data);
|
||||
|
||||
<*
|
||||
Finalize the hash context and return the hash result at the given size.
|
||||
@@ -380,4 +384,4 @@ fn void Blake2s.update(&self, char[] data)
|
||||
@require $output_length == self.outlen : "The specified compile-time output size MUST be equal to the initialized output size."
|
||||
*>
|
||||
macro char[*] Blake2s.final(&self, $output_length)
|
||||
=> @common_final(self, $output_length);
|
||||
=> common_final(self, $output_length);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// https://github.com/BLAKE3-team/BLAKE3/blob/master
|
||||
//
|
||||
module std::hash::blake3;
|
||||
|
||||
import std::thread;
|
||||
|
||||
const BLOCK_SIZE = 64;
|
||||
const CHUNK_SIZE = 1024;
|
||||
@@ -55,12 +55,15 @@ bool cpuinfo_initd @local = false;
|
||||
<*
|
||||
Cache some information at runtime about the current processor and platform, as needed for optimizations.
|
||||
*>
|
||||
fn void init_blake3() @local @init
|
||||
fn void init_blake3() @local
|
||||
{
|
||||
$if IS_X86:
|
||||
cpudetect::x86_initialize_cpu_features(); // query all x86 feature flags, one time
|
||||
$endif
|
||||
cpuinfo_initd = true;
|
||||
static OnceFlag run_once;
|
||||
run_once.call(fn() {
|
||||
$if IS_X86:
|
||||
cpudetect::x86_initialize_cpu_features(); // query all x86 feature flags, one time
|
||||
$endif
|
||||
cpuinfo_initd = true;
|
||||
});
|
||||
}
|
||||
|
||||
<* Check whether a given CPU flag is set (x86/x86_64 only). *>
|
||||
@@ -88,7 +91,7 @@ macro @simd_degree() @local
|
||||
}
|
||||
|
||||
<* Flags used during hash computation based on its state. *>
|
||||
enum Blake3Flags : const inline char
|
||||
constdef Blake3Flags : inline char
|
||||
{
|
||||
CHUNK_START = 1 << 0,
|
||||
CHUNK_END = 1 << 1,
|
||||
@@ -243,7 +246,7 @@ fn void Blake3.init(&self, char[] key = {}, char explicit_flags = 0)
|
||||
<*
|
||||
Reset the state of the hashing context, in case it should be reused without reloading the key value.
|
||||
*>
|
||||
fn void Blake3.reset(&self) @local @inline
|
||||
fn void _reset(Blake3* self) @local @inline
|
||||
{
|
||||
self.chunk.reset(self.key[..], 0);
|
||||
self.cv_stack_len = 0;
|
||||
@@ -252,7 +255,7 @@ fn void Blake3.reset(&self) @local @inline
|
||||
<*
|
||||
Private function to merge tree results.
|
||||
*>
|
||||
fn void Blake3.merge_cv_stack(&self, ulong total_len) @local @inline
|
||||
fn void _merge_cv_stack(Blake3* self, ulong total_len) @local @inline
|
||||
{
|
||||
usz post_merge_stack_len = (usz)@popcnt(total_len);
|
||||
for (; self.cv_stack_len > post_merge_stack_len; self.cv_stack_len--)
|
||||
@@ -266,9 +269,9 @@ fn void Blake3.merge_cv_stack(&self, ulong total_len) @local @inline
|
||||
<*
|
||||
Private function to add a new tree onto the stack.
|
||||
*>
|
||||
fn void Blake3.push_cv(&self, char* new_cv, ulong chunk_counter) @local @inline
|
||||
fn void Blake3.push_cv(&self, char* new_cv, ulong chunk_counter) @inline
|
||||
{
|
||||
self.merge_cv_stack(chunk_counter);
|
||||
_merge_cv_stack(self, chunk_counter);
|
||||
self.cv_stack[self.cv_stack_len * OUT_SIZE : OUT_SIZE] = new_cv[:OUT_SIZE];
|
||||
self.cv_stack_len++;
|
||||
}
|
||||
@@ -331,7 +334,7 @@ fn void Blake3.update(&self, char[] input, bool use_tbb = false)
|
||||
if (input.len > 0)
|
||||
{
|
||||
self.chunk.update(input);
|
||||
self.merge_cv_stack(self.chunk.chunk_counter);
|
||||
_merge_cv_stack(self, self.chunk.chunk_counter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,7 +400,7 @@ fn void Blake3.destroy(&self) @inline
|
||||
@param [in] key
|
||||
@param flags
|
||||
*>
|
||||
fn void Blake3ChunkState.init(&self, uint[] key, char flags) @local @inline
|
||||
fn void Blake3ChunkState.init(&self, uint[] key, char flags) @inline
|
||||
{
|
||||
mem::zero_volatile(@as_char_view(*self));
|
||||
self.cv[..] = key[..];
|
||||
@@ -410,7 +413,7 @@ fn void Blake3ChunkState.init(&self, uint[] key, char flags) @local @inline
|
||||
@param [in] key
|
||||
@param chunk_counter
|
||||
*>
|
||||
fn void Blake3ChunkState.reset(&self, uint[] key, ulong chunk_counter) @local @inline
|
||||
fn void Blake3ChunkState.reset(&self, uint[] key, ulong chunk_counter) @inline
|
||||
{
|
||||
self.init(key, self.flags); // maintain its own flags
|
||||
self.chunk_counter = chunk_counter; // update chunk counter
|
||||
@@ -419,7 +422,7 @@ fn void Blake3ChunkState.reset(&self, uint[] key, ulong chunk_counter) @local @i
|
||||
<*
|
||||
Get bytes length of consumed data.
|
||||
*>
|
||||
fn usz Blake3ChunkState.len(&self) @operator(len) @local @inline
|
||||
fn usz Blake3ChunkState.len(&self) @operator(len) @inline
|
||||
=> (BLOCK_SIZE * (usz)self.blocks_compressed) + (usz)self.buf_len;
|
||||
|
||||
<*
|
||||
@@ -427,7 +430,7 @@ fn usz Blake3ChunkState.len(&self) @operator(len) @local @inline
|
||||
|
||||
@param [in] data : "Data to ingest."
|
||||
*>
|
||||
fn usz Blake3ChunkState.fill_buf(&self, char[] data) @local @inline
|
||||
fn usz Blake3ChunkState.fill_buf(&self, char[] data) @inline
|
||||
{
|
||||
usz take = min(BLOCK_SIZE - (usz)self.buf_len, data.len);
|
||||
self.buf[self.buf_len:take] = data[:take];
|
||||
@@ -438,7 +441,7 @@ fn usz Blake3ChunkState.fill_buf(&self, char[] data) @local @inline
|
||||
<*
|
||||
Determine whether to set the CHUNK_START flag.
|
||||
*>
|
||||
fn char Blake3ChunkState.maybe_start_flag(&self) @local @inline
|
||||
fn char Blake3ChunkState.maybe_start_flag(&self) @inline
|
||||
=> !self.blocks_compressed ? Blake3Flags.CHUNK_START : 0;
|
||||
|
||||
<*
|
||||
@@ -446,7 +449,7 @@ fn char Blake3ChunkState.maybe_start_flag(&self) @local @inline
|
||||
|
||||
@param [in] input : "Incoming bytes to update with."
|
||||
*>
|
||||
fn void Blake3ChunkState.update(&self, char[] input) @local
|
||||
fn void Blake3ChunkState.update(&self, char[] input)
|
||||
{
|
||||
if (self.buf_len)
|
||||
{
|
||||
@@ -470,7 +473,7 @@ fn void Blake3ChunkState.update(&self, char[] input) @local
|
||||
<*
|
||||
Convert the chunk state to an "output" type with the right flags.
|
||||
*>
|
||||
fn Blake3Output Blake3ChunkState.output(&self) @local @inline
|
||||
fn Blake3Output Blake3ChunkState.output(&self) @inline
|
||||
=> make_output(self.cv[..], &self.buf, self.buf_len, self.chunk_counter, self.flags | self.maybe_start_flag() | Blake3Flags.CHUNK_END);
|
||||
|
||||
<*
|
||||
@@ -508,7 +511,7 @@ macro Blake3Output parent_output(char* block, uint[] key, char flags) @local
|
||||
|
||||
@param [&inout] cv
|
||||
*>
|
||||
macro void Blake3Output.chaining_value(&self, char* cv) @local
|
||||
macro void Blake3Output.chaining_value(&self, char* cv)
|
||||
{
|
||||
uint[KEY_SIZE_WORDS] cv_words;
|
||||
cv_words[..] = self.input_cv[..];
|
||||
@@ -522,7 +525,7 @@ macro void Blake3Output.chaining_value(&self, char* cv) @local
|
||||
@param seek
|
||||
@param [inout] into
|
||||
*>
|
||||
fn void Blake3Output.root_bytes(&self, usz seek, char[] into) @local
|
||||
fn void Blake3Output.root_bytes(&self, usz seek, char[] into)
|
||||
{
|
||||
if (!into.len) return;
|
||||
|
||||
@@ -606,13 +609,11 @@ fn void compress_pre(uint[] state, uint[] cv, char[BLOCK_SIZE] block, usz block_
|
||||
state[13] = (uint)(counter >> 32);
|
||||
state[14] = (uint)block_len;
|
||||
state[15] = (uint)flags;
|
||||
@round(state, &block_words[0], 0);
|
||||
@round(state, &block_words[0], 1);
|
||||
@round(state, &block_words[0], 2);
|
||||
@round(state, &block_words[0], 3);
|
||||
@round(state, &block_words[0], 4);
|
||||
@round(state, &block_words[0], 5);
|
||||
@round(state, &block_words[0], 6);
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
@round(state, &block_words[0], (usz)i);
|
||||
}
|
||||
}
|
||||
|
||||
macro compress_in_place(uint[] cv, char[BLOCK_SIZE] block, usz block_len, ulong counter, char flags) @local
|
||||
|
||||
@@ -5,8 +5,8 @@ module std::hash::fnv32a;
|
||||
|
||||
typedef Fnv32a = uint;
|
||||
|
||||
const FNV32A_START @private = 0x811c9dc5;
|
||||
const FNV32A_MUL @private = 0x01000193;
|
||||
const FNV32A_START @private = (Fnv32a)0x811c9dc5;
|
||||
const FNV32A_MUL @private = (Fnv32a)0x01000193;
|
||||
|
||||
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV32A_MUL;
|
||||
|
||||
@@ -32,10 +32,10 @@ macro void Fnv32a.update_char(&self, char c)
|
||||
|
||||
fn uint hash(char[] data)
|
||||
{
|
||||
uint h = FNV32A_START;
|
||||
Fnv32a h = FNV32A_START;
|
||||
foreach (char x : data)
|
||||
{
|
||||
update(&h, x);
|
||||
}
|
||||
return h;
|
||||
return (uint)h;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ module std::hash::fnv64a;
|
||||
|
||||
typedef Fnv64a = ulong;
|
||||
|
||||
const FNV64A_START @private = 0xcbf29ce484222325;
|
||||
const FNV64A_MUL @private = 0x00000100000001b3;
|
||||
const Fnv64a FNV64A_START @private = (Fnv64a)0xcbf29ce484222325;
|
||||
const Fnv64a FNV64A_MUL @private = (Fnv64a)0x00000100000001b3;
|
||||
|
||||
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV64A_MUL;
|
||||
macro Fnv64a update(Fnv64a h, char x) @nodiscard @private => (h ^ (Fnv64a)x) * FNV64A_MUL;
|
||||
|
||||
fn void Fnv64a.init(&self)
|
||||
{
|
||||
@@ -20,22 +20,22 @@ fn void Fnv64a.update(&self, char[] data)
|
||||
Fnv64a h = *self;
|
||||
foreach (char x : data)
|
||||
{
|
||||
update(&h, x);
|
||||
h = update(h, x);
|
||||
}
|
||||
*self = h;
|
||||
}
|
||||
|
||||
macro void Fnv64a.update_char(&self, char c)
|
||||
{
|
||||
update(self, c);
|
||||
*self = update(*self, c);
|
||||
}
|
||||
|
||||
fn ulong hash(char[] data)
|
||||
{
|
||||
ulong h = FNV64A_START;
|
||||
Fnv64a h = FNV64A_START;
|
||||
foreach (char x : data)
|
||||
{
|
||||
update(&h, x);
|
||||
h = update(h, x);
|
||||
}
|
||||
return h;
|
||||
return (ulong)h;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
module std::hash::streebog;
|
||||
|
||||
|
||||
enum StreebogLength : const inline uint
|
||||
constdef StreebogLength : inline uint
|
||||
{
|
||||
SIZE_256 = 32,
|
||||
SIZE_512 = 64,
|
||||
@@ -147,11 +147,19 @@ fn void Streebog.update(&self, char[] data)
|
||||
macro char[*] Streebog.final(&self, StreebogLength $hash_size)
|
||||
{
|
||||
char[$hash_size] result;
|
||||
streebog_final_private(self);
|
||||
defer mem::zero_volatile(@as_char_view(*self)); // implicitly clear the structure when finalized
|
||||
result[..] = @as_char_view(self.h)[(BLOCK_SIZE - $hash_size)..];
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void streebog_final_private(Streebog* self) @local
|
||||
{
|
||||
ulong[8] unprocessed_bits_count;
|
||||
usz index = self.index >> 3;
|
||||
usz shift = (self.index & 0b111) * 8;
|
||||
|
||||
unprocessed_bits_count[0] = self.index * 8;
|
||||
unprocessed_bits_count[0] = self.index * 8ul;
|
||||
self.message[index] &= ~(ulong.max << shift);
|
||||
self.message[index++] ^= 1ul << shift;
|
||||
if (index < 8) self.message[index..] = {};
|
||||
@@ -161,9 +169,4 @@ macro char[*] Streebog.final(&self, StreebogLength $hash_size)
|
||||
@add_512(self.s, self.message);
|
||||
@g_n(ZERO_512, self.h, self.n);
|
||||
@g_n(ZERO_512, self.h, self.s);
|
||||
|
||||
defer mem::zero_volatile(@as_char_view(*self)); // implicitly clear the structure when finalized
|
||||
|
||||
result[..] = @as_char_view(self.h)[(BLOCK_SIZE - $hash_size)..];
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,32 @@ fn char[HASH_BYTES] Md5.final(&ctx)
|
||||
|
||||
module std::hash::md5 @private;
|
||||
|
||||
const uint[64] MD5_T @private = {
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
|
||||
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
||||
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
||||
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
||||
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
||||
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
||||
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
|
||||
};
|
||||
|
||||
const int[16] MD5_S @private = {
|
||||
7, 12, 17, 22,
|
||||
5, 9, 14, 20,
|
||||
4, 11, 16, 23,
|
||||
6, 10, 15, 21
|
||||
};
|
||||
|
||||
// Implementation
|
||||
macro @f(x, y, z) => z ^ (x & (y ^ z));
|
||||
macro @g(x, y, z) => y ^ (z & (x ^ y));
|
||||
@@ -116,93 +142,54 @@ macro void @step(#f, a, b, c, d, ptr, n, t, s)
|
||||
|
||||
fn char* body(Md5* ctx, void* data, usz size)
|
||||
{
|
||||
char* ptr;
|
||||
uint a, b, c, d;
|
||||
uint saved_a, saved_b, saved_c, saved_d;
|
||||
ptr = data;
|
||||
a = ctx.a;
|
||||
b = ctx.b;
|
||||
c = ctx.c;
|
||||
d = ctx.d;
|
||||
char* ptr = data;
|
||||
uint a = ctx.a;
|
||||
uint b = ctx.b;
|
||||
uint c = ctx.c;
|
||||
uint d = ctx.d;
|
||||
|
||||
do
|
||||
{
|
||||
saved_a = a;
|
||||
saved_b = b;
|
||||
saved_c = c;
|
||||
saved_d = d;
|
||||
uint saved_a = a;
|
||||
uint saved_b = b;
|
||||
uint saved_c = c;
|
||||
uint saved_d = d;
|
||||
|
||||
/* Round 1 */
|
||||
@step(@f, &a, b, c, d, ptr, 0, 0xd76aa478, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 1, 0xe8c7b756, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 2, 0x242070db, 17) ;
|
||||
@step(@f, &b, c, d, a, ptr, 3, 0xc1bdceee, 22) ;
|
||||
@step(@f, &a, b, c, d, ptr, 4, 0xf57c0faf, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 5, 0x4787c62a, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 6, 0xa8304613, 17) ;
|
||||
@step(@f, &b, c, d, a, ptr, 7, 0xfd469501, 22) ;
|
||||
@step(@f, &a, b, c, d, ptr, 8, 0x698098d8, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 9, 0x8b44f7af, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 10, 0xffff5bb1, 17);
|
||||
@step(@f, &b, c, d, a, ptr, 11, 0x895cd7be, 22);
|
||||
@step(@f, &a, b, c, d, ptr, 12, 0x6b901122, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 13, 0xfd987193, 12);
|
||||
@step(@f, &c, d, a, b, ptr, 14, 0xa679438e, 17);
|
||||
@step(@f, &b, c, d, a, ptr, 15, 0x49b40821, 22);
|
||||
/* Round 1 */
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
{
|
||||
@step(@f, &a, b, c, d, ptr, i + 0, MD5_T[i + 0], MD5_S[0]);
|
||||
@step(@f, &d, a, b, c, ptr, i + 1, MD5_T[i + 1], MD5_S[1]);
|
||||
@step(@f, &c, d, a, b, ptr, i + 2, MD5_T[i + 2], MD5_S[2]);
|
||||
@step(@f, &b, c, d, a, ptr, i + 3, MD5_T[i + 3], MD5_S[3]);
|
||||
}
|
||||
|
||||
/* Round 2 */
|
||||
@step(@g, &a, b, c, d, ptr, 1, 0xf61e2562, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 6, 0xc040b340, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 11, 0x265e5a51, 14);
|
||||
@step(@g, &b, c, d, a, ptr, 0, 0xe9b6c7aa, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 5, 0xd62f105d, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 10, 0x02441453, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 15, 0xd8a1e681, 14);
|
||||
@step(@g, &b, c, d, a, ptr, 4, 0xe7d3fbc8, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 9, 0x21e1cde6, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 14, 0xc33707d6, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 3, 0xf4d50d87, 14) ;
|
||||
@step(@g, &b, c, d, a, ptr, 8, 0x455a14ed, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 13, 0xa9e3e905, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 2, 0xfcefa3f8, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 7, 0x676f02d9, 14) ;
|
||||
@step(@g, &b, c, d, a, ptr, 12, 0x8d2a4c8a, 20);
|
||||
/* Round 2 */
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
{
|
||||
@step(@g, &a, b, c, d, ptr, (1 + 5 * (i + 0)) % 16, MD5_T[16 + i + 0], MD5_S[4]);
|
||||
@step(@g, &d, a, b, c, ptr, (1 + 5 * (i + 1)) % 16, MD5_T[16 + i + 1], MD5_S[5]);
|
||||
@step(@g, &c, d, a, b, ptr, (1 + 5 * (i + 2)) % 16, MD5_T[16 + i + 2], MD5_S[6]);
|
||||
@step(@g, &b, c, d, a, ptr, (1 + 5 * (i + 3)) % 16, MD5_T[16 + i + 3], MD5_S[7]);
|
||||
}
|
||||
|
||||
/* Round 3 */
|
||||
@step(@h, &a, b, c, d, ptr, 5, 0xfffa3942, 4);
|
||||
@step(@h2, &d, a, b, c, ptr, 8, 0x8771f681, 11);
|
||||
@step(@h, &c, d, a, b, ptr, 11, 0x6d9d6122, 16);
|
||||
@step(@h2, &b, c, d, a, ptr, 14, 0xfde5380c, 23);
|
||||
@step(@h, &a, b, c, d, ptr, 1, 0xa4beea44, 4);
|
||||
@step(@h2, &d, a, b, c, ptr, 4, 0x4bdecfa9, 11);
|
||||
@step(@h, &c, d, a, b, ptr, 7, 0xf6bb4b60, 16);
|
||||
@step(@h2, &b, c, d, a, ptr, 10, 0xbebfbc70, 23);
|
||||
@step(@h, &a, b, c, d, ptr, 13, 0x289b7ec6, 4) ;
|
||||
@step(@h2, &d, a, b, c, ptr, 0, 0xeaa127fa, 11) ;
|
||||
@step(@h, &c, d, a, b, ptr, 3, 0xd4ef3085, 16) ;
|
||||
@step(@h2, &b, c, d, a, ptr, 6, 0x04881d05, 23) ;
|
||||
@step(@h, &a, b, c, d, ptr, 9, 0xd9d4d039, 4) ;
|
||||
@step(@h2, &d, a, b, c, ptr, 12, 0xe6db99e5, 11) ;
|
||||
@step(@h, &c, d, a, b, ptr, 15, 0x1fa27cf8, 16) ;
|
||||
@step(@h2, &b, c, d, a, ptr, 2, 0xc4ac5665, 23) ;
|
||||
/* Round 3 */
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
{
|
||||
@step(@h, &a, b, c, d, ptr, (5 + 3 * (i + 0)) % 16, MD5_T[32 + i + 0], MD5_S[8]);
|
||||
@step(@h, &d, a, b, c, ptr, (5 + 3 * (i + 1)) % 16, MD5_T[32 + i + 1], MD5_S[9]);
|
||||
@step(@h, &c, d, a, b, ptr, (5 + 3 * (i + 2)) % 16, MD5_T[32 + i + 2], MD5_S[10]);
|
||||
@step(@h, &b, c, d, a, ptr, (5 + 3 * (i + 3)) % 16, MD5_T[32 + i + 3], MD5_S[11]);
|
||||
}
|
||||
|
||||
/* Round 4 */
|
||||
@step(@i, &a, b, c, d, ptr, 0, 0xf4292244, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 7, 0x432aff97, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 14, 0xab9423a7, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 5, 0xfc93a039, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 12, 0x655b59c3, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 3, 0x8f0ccc92, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 10, 0xffeff47d, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 1, 0x85845dd1, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 8, 0x6fa87e4f, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 15, 0xfe2ce6e0, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 6, 0xa3014314, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 13, 0x4e0811a1, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 4, 0xf7537e82, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 11, 0xbd3af235, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 2, 0x2ad7d2bb, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 9, 0xeb86d391, 21) ;
|
||||
/* Round 4 */
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
{
|
||||
@step(@i, &a, b, c, d, ptr, (7 * (i + 0)) % 16, MD5_T[48 + i + 0], MD5_S[12]);
|
||||
@step(@i, &d, a, b, c, ptr, (7 * (i + 1)) % 16, MD5_T[48 + i + 1], MD5_S[13]);
|
||||
@step(@i, &c, d, a, b, ptr, (7 * (i + 2)) % 16, MD5_T[48 + i + 2], MD5_S[14]);
|
||||
@step(@i, &b, c, d, a, ptr, (7 * (i + 3)) % 16, MD5_T[48 + i + 3], MD5_S[15]);
|
||||
}
|
||||
|
||||
a += saved_a;
|
||||
b += saved_b;
|
||||
|
||||
243
lib/std/hash/murmur.c3
Normal file
243
lib/std/hash/murmur.c3
Normal file
@@ -0,0 +1,243 @@
|
||||
module std::hash::murmur3;
|
||||
|
||||
<*
|
||||
@param [in] data : "The data to hash"
|
||||
@param seed : "The seed to use for hashing"
|
||||
@require (data.len / 4) <= int.max : "Too much data"
|
||||
*>
|
||||
fn uint hash32(char[] data, uint seed)
|
||||
{
|
||||
int nblocks = (int)data.len / 4;
|
||||
uint h1 = seed;
|
||||
|
||||
const uint C1 = 0xcc9e2d51;
|
||||
const uint C2 = 0x1b873593;
|
||||
|
||||
uint* blocks = (uint *)(data.ptr + nblocks * 4);
|
||||
|
||||
for (int i = -nblocks; i != 0; i++)
|
||||
{
|
||||
uint k1 = getblock32(blocks, i);
|
||||
|
||||
k1 *= C1;
|
||||
k1 = k1.rotl(15);
|
||||
k1 *= C2;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = h1.rotl(13);
|
||||
h1 = h1 * 5U + 0xe6546b64;
|
||||
}
|
||||
|
||||
char* tail = data.ptr + nblocks * 4;
|
||||
|
||||
uint k1;
|
||||
|
||||
switch (data.len & 3)
|
||||
{
|
||||
case 3: k1 ^= tail[2] << 16; nextcase;
|
||||
case 2: k1 ^= tail[1] << 8; nextcase;
|
||||
case 1: k1 ^= tail[0]; k1 *= C1; k1 = k1.rotl(15); k1 *= C2; h1 ^= k1;
|
||||
}
|
||||
|
||||
h1 ^= (uint)data.len;
|
||||
|
||||
h1 = fmix32(h1);
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] data : "The data to hash"
|
||||
@param seed : "The seed to use for hashing"
|
||||
@require (data.len / 16) <= int.max : "Too much data"
|
||||
|
||||
*>
|
||||
fn uint128 hash128_64(char[] data, uint seed)
|
||||
{
|
||||
ulong len = data.len;
|
||||
int nblocks = (int)(len / 16);
|
||||
|
||||
ulong h1 = seed;
|
||||
ulong h2 = seed;
|
||||
|
||||
const ulong C1 = 0x87c37b91114253d5UL;
|
||||
const ulong C2 = 0x4cf5ad432745937fUL;
|
||||
|
||||
ulong* blocks = (ulong*)data.ptr; // Unaligned!
|
||||
|
||||
for (int i = 0; i < nblocks; i++)
|
||||
{
|
||||
ulong k1 = getblock64(blocks, i * 2 + 0);
|
||||
ulong k2 = getblock64(blocks, i * 2 + 1);
|
||||
|
||||
k1 *= C1; k1 = k1.rotl(31); k1 *= C2; h1 ^= k1;
|
||||
|
||||
h1 = h1.rotl(27); h1 += h2; h1 = h1 * 5U + 0x52dce729;
|
||||
|
||||
k2 *= C2; k2 = k2.rotl(33); k2 *= C1; h2 ^= k2;
|
||||
|
||||
h2 = h2.rotl(31); h2 += h1; h2 = h2 * 5U + 0x38495ab5;
|
||||
}
|
||||
|
||||
char* tail = data.ptr + nblocks * 16;
|
||||
ulong k1, k2;
|
||||
|
||||
switch (len & 15)
|
||||
{
|
||||
case 15: k2 ^= ((ulong)tail[14]) << 48; nextcase;
|
||||
case 14: k2 ^= ((ulong)tail[13]) << 40; nextcase;
|
||||
case 13: k2 ^= ((ulong)tail[12]) << 32; nextcase;
|
||||
case 12: k2 ^= ((ulong)tail[11]) << 24; nextcase;
|
||||
case 11: k2 ^= ((ulong)tail[10]) << 16; nextcase;
|
||||
case 10: k2 ^= ((ulong)tail[ 9]) << 8; nextcase;
|
||||
case 9: k2 ^= ((ulong)tail[ 8]) << 0;
|
||||
k2 *= C2; k2 = k2.rotl(33); k2 *= C1; h2 ^= k2;
|
||||
nextcase;
|
||||
case 8: k1 ^= ((ulong)tail[ 7]) << 56; nextcase;
|
||||
case 7: k1 ^= ((ulong)tail[ 6]) << 48; nextcase;
|
||||
case 6: k1 ^= ((ulong)tail[ 5]) << 40; nextcase;
|
||||
case 5: k1 ^= ((ulong)tail[ 4]) << 32; nextcase;
|
||||
case 4: k1 ^= ((ulong)tail[ 3]) << 24; nextcase;
|
||||
case 3: k1 ^= ((ulong)tail[ 2]) << 16; nextcase;
|
||||
case 2: k1 ^= ((ulong)tail[ 1]) << 8; nextcase;
|
||||
case 1: k1 ^= ((ulong)tail[ 0]) << 0;
|
||||
k1 *= C1; k1 = k1.rotl(31); k1 *= C2; h1 ^= k1;
|
||||
}
|
||||
|
||||
|
||||
h1 ^= len;
|
||||
h2 ^= len;
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
h1 = fmix64(h1);
|
||||
h2 = fmix64(h2);
|
||||
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
return h1 + (uint128)h2 << 64U;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] data : "The data to hash"
|
||||
@param seed : "The seed to use for hashing"
|
||||
@require data.len <= uint.max : "Too much data"
|
||||
*>
|
||||
fn uint128 hash128_32(char[] data, uint seed)
|
||||
{
|
||||
uint len = data.len;
|
||||
int nblocks = (int)(len / 16);
|
||||
|
||||
uint h1 = seed;
|
||||
uint h2 = seed;
|
||||
uint h3 = seed;
|
||||
uint h4 = seed;
|
||||
|
||||
const uint C1 = 0x239b961b;
|
||||
const uint C2 = 0xab0e9789;
|
||||
const uint C3 = 0x38b34ae5;
|
||||
const uint C4 = 0xa1e38b93;
|
||||
|
||||
uint* blocks = (uint *)(data.ptr + nblocks * 16);
|
||||
|
||||
for (int i = -nblocks; i != 0; i++)
|
||||
{
|
||||
uint k1 = getblock32(blocks, i * 4 + 0);
|
||||
uint k2 = getblock32(blocks, i * 4 + 1);
|
||||
uint k3 = getblock32(blocks, i * 4 + 2);
|
||||
uint k4 = getblock32(blocks, i * 4 + 3);
|
||||
|
||||
k1 *= C1; k1 = k1.rotl(15); k1 *= C2; h1 ^= k1;
|
||||
h1 = h1.rotl(19); h1 += h2; h1 = h1 * 5U + 0x561ccd1b;
|
||||
k2 *= C2; k2 = k2.rotl(16); k2 *= C3; h2 ^= k2;
|
||||
h2 = h2.rotl(17); h2 += h3; h2 = h2 * 5U + 0x0bcaa747;
|
||||
k3 *= C3; k3 = k3.rotl(17); k3 *= C4; h3 ^= k3;
|
||||
h3 = h3.rotl(15); h3 += h4; h3 = h3 * 5U + 0x96cd1c35;
|
||||
k4 *= C4; k4 = k4.rotl(18); k4 *= C1; h4 ^= k4;
|
||||
h4 = h4.rotl(13); h4 += h1; h4 = h4 * 5U + 0x32ac3b17;
|
||||
}
|
||||
|
||||
char* tail = data.ptr + nblocks * 16;
|
||||
|
||||
uint k1, k2, k3, k4;
|
||||
|
||||
switch (len & 15)
|
||||
{
|
||||
case 15: k4 ^= tail[14] << 16; nextcase;
|
||||
case 14: k4 ^= tail[13] << 8; nextcase;
|
||||
case 13: k4 ^= tail[12] << 0;
|
||||
k4 *= C4; k4 = k4.rotl(18); k4 *= C1; h4 ^= k4;
|
||||
nextcase;
|
||||
case 12: k3 ^= tail[11] << 24; nextcase;
|
||||
case 11: k3 ^= tail[10] << 16; nextcase;
|
||||
case 10: k3 ^= tail[ 9] << 8; nextcase;
|
||||
case 9: k3 ^= tail[ 8] << 0;
|
||||
k3 *= C3; k3 = k3.rotl(17); k3 *= C4; h3 ^= k3;
|
||||
nextcase;
|
||||
case 8: k2 ^= tail[ 7] << 24; nextcase;
|
||||
case 7: k2 ^= tail[ 6] << 16; nextcase;
|
||||
case 6: k2 ^= tail[ 5] << 8; nextcase;
|
||||
case 5: k2 ^= tail[ 4] << 0;
|
||||
k2 *= C2; k2 = k2.rotl(16); k2 *= C3; h2 ^= k2;
|
||||
nextcase;
|
||||
case 4: k1 ^= tail[ 3] << 24; nextcase;
|
||||
case 3: k1 ^= tail[ 2] << 16; nextcase;
|
||||
case 2: k1 ^= tail[ 1] << 8; nextcase;
|
||||
case 1: k1 ^= tail[ 0] << 0;
|
||||
k1 *= C1; k1 = k1.rotl(15); k1 *= C2; h1 ^= k1;
|
||||
}
|
||||
|
||||
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
h1 = fmix32(h1);
|
||||
h2 = fmix32(h2);
|
||||
h3 = fmix32(h3);
|
||||
h4 = fmix32(h4);
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
return h1 + (uint128)h2 << 32U + (uint128)h3 << 64U + (uint128)h4 << 96U;
|
||||
}
|
||||
|
||||
macro uint getblock32(uint* p, int i) @local
|
||||
{
|
||||
UIntLE* p_le = (UIntLE*)p + i;
|
||||
return mem::load(p_le, 1).val;
|
||||
}
|
||||
|
||||
macro ulong getblock64(ulong* p, int i) @local
|
||||
{
|
||||
ULongLE* p_le = (ULongLE*)p + i;
|
||||
return mem::load(p_le, 1).val;
|
||||
}
|
||||
|
||||
macro uint fmix32(uint h) @local
|
||||
{
|
||||
h ^= h >> 16UL;
|
||||
h *= 0x85ebca6b;
|
||||
h ^= h >> 13UL;
|
||||
h *= 0xc2b2ae35;
|
||||
h ^= h >> 16UL;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
macro ulong fmix64(ulong k) @local
|
||||
{
|
||||
k ^= k >> 33U;
|
||||
k *= 0xff51afd7ed558ccd;
|
||||
k ^= k >> 33U;
|
||||
k *= 0xc4ceb9fe1a85ec53;
|
||||
k ^= k >> 33U;
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
@@ -86,14 +86,14 @@ fn void Poly1305.update(&self, char[] input)
|
||||
}
|
||||
// ingest up to a block size to finish the partial, then advance the slice ptr
|
||||
self.temp[self.num:rem] = input[:rem];
|
||||
self.blocks(self.temp[..]);
|
||||
_blocks(self, self.temp[..]);
|
||||
input = input[rem..];
|
||||
}
|
||||
|
||||
usz even_length = input.len - (input.len % BLOCK_SIZE);
|
||||
if (even_length >= BLOCK_SIZE)
|
||||
{
|
||||
self.blocks(input[:even_length]); // consume blocks
|
||||
_blocks(self, input[:even_length]); // consume blocks
|
||||
input = input[even_length..]; // scroll to end (remainder)
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ fn char[TAG_SIZE] Poly1305.final(&self)
|
||||
{
|
||||
self.temp[self.num++] = 1; // partial blocks must end with 0x01
|
||||
self.temp[self.num..] = {}; // explicit zeros on the rest
|
||||
self.blocks(self.temp[..], 0); // chomp
|
||||
_blocks(self, self.temp[..], 0); // chomp
|
||||
}
|
||||
|
||||
uint128 t = (uint128)self.h[0] + 5;
|
||||
@@ -135,7 +135,7 @@ fn char[TAG_SIZE] Poly1305.final(&self)
|
||||
}
|
||||
|
||||
|
||||
fn void Poly1305.blocks(&self, char[] input, ulong pad_bit = 1) @local
|
||||
fn void _blocks(Poly1305* self, char[] input, ulong pad_bit = 1) @local
|
||||
{
|
||||
for (; input.len >= BLOCK_SIZE; input = input[BLOCK_SIZE..])
|
||||
{
|
||||
|
||||
@@ -68,18 +68,18 @@ fn void Sha256.update(&self, char[] data)
|
||||
usz len = min(BLOCK_SIZE - buffer_pos, data.len);
|
||||
self.buffer[buffer_pos:len] = data[:len];
|
||||
data = data[len..];
|
||||
if (buffer_pos + len == BLOCK_SIZE) self.transform();
|
||||
if (buffer_pos + len == BLOCK_SIZE) _transform(self);
|
||||
}
|
||||
|
||||
// When the data pointer is aligned, we can disregard unaligned loading in the `transform` macro.
|
||||
// We do this here from the outer call to reduce the expense of checking alignment on every single block.
|
||||
if (0 == (usz)data.ptr % usz.sizeof)
|
||||
{
|
||||
for (; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..]) self.transform((uint*)data.ptr);
|
||||
for (; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..]) _transform(self, (uint*)data.ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..]) self.transform_unaligned((uint*)data.ptr);
|
||||
for (; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..]) _transform_unaligned(self, (uint*)data.ptr);
|
||||
}
|
||||
|
||||
// Leftover data just gets stored away for the next update or final.
|
||||
@@ -102,7 +102,7 @@ fn char[HASH_SIZE] Sha256.final(&self)
|
||||
if (i > BLOCK_SIZE - 8)
|
||||
{
|
||||
self.buffer[i..] = 0x00;
|
||||
self.transform();
|
||||
_transform(self);
|
||||
i = 0; // Reset buffer index after transformation
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ fn char[HASH_SIZE] Sha256.final(&self)
|
||||
// Append the bitcount in big-endian format
|
||||
*(ulong*)(&self.buffer[BLOCK_SIZE - 8]) = env::BIG_ENDIAN ??? self.bitcount : bswap(self.bitcount);
|
||||
|
||||
self.transform();
|
||||
_transform(self);
|
||||
|
||||
// Convert state to the final hash
|
||||
foreach (x, s : self.state) *(uint*)(&hash[x * uint.sizeof]) = env::BIG_ENDIAN ??? s : bswap(s);
|
||||
@@ -121,10 +121,10 @@ fn char[HASH_SIZE] Sha256.final(&self)
|
||||
|
||||
// These wrappers are necessary to significantly reduce code generation from macro expansions.
|
||||
// Note that transformations on `self.buffer` (when incoming == null) should always be aligned.
|
||||
fn void Sha256.transform(&self, uint* incoming = null) @local @noinline => self.do_transform(incoming, true);
|
||||
fn void Sha256.transform_unaligned(&self, uint* incoming = null) @local @noinline => self.do_transform(incoming, false);
|
||||
fn void _transform(Sha256* self, uint* incoming = null) @local @noinline => _do_transform(self, incoming, true);
|
||||
fn void _transform_unaligned(Sha256* self, uint* incoming = null) @local @noinline => _do_transform(self, incoming, false);
|
||||
|
||||
macro Sha256.do_transform(&self, uint* incoming = null, bool $aligned = true) @local
|
||||
macro _do_transform(Sha256* self, uint* incoming = null, bool $aligned = true) @local
|
||||
{
|
||||
uint a, b, c, d, e, f, g, h, t1, t2 @noinit;
|
||||
uint[64] m @noinit;
|
||||
|
||||
@@ -17,9 +17,9 @@ const HASH_SIZE = 64;
|
||||
|
||||
struct Sha512
|
||||
{
|
||||
ulong length;
|
||||
ulong[8] hash_state;
|
||||
char[BLOCK_SIZE] buffer;
|
||||
ulong length;
|
||||
ulong[8] hash_state;
|
||||
char[BLOCK_SIZE] buffer;
|
||||
}
|
||||
|
||||
alias HmacSha512 = Hmac{Sha512, HASH_SIZE, BLOCK_SIZE};
|
||||
@@ -36,26 +36,26 @@ macro ulong r0(ulong x) @local => (ror(x, 1) ^ ror(x, 8) ^ (x >> 7));
|
||||
macro ulong r1(ulong x) @local => (ror(x, 19) ^ ror(x, 61) ^ (x >> 6));
|
||||
|
||||
const ulong[80] K @local = {
|
||||
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
|
||||
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
|
||||
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
|
||||
0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
|
||||
0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
|
||||
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
|
||||
0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
|
||||
0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
|
||||
0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
|
||||
0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
|
||||
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
|
||||
0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
|
||||
0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
|
||||
0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
|
||||
0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
|
||||
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
|
||||
0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
|
||||
0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
|
||||
0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
|
||||
0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
|
||||
0x428a2f98d728ae22, 0x7137449123ef65cd, 0xb5c0fbcfec4d3b2f, 0xe9b5dba58189dbbc,
|
||||
0x3956c25bf348b538, 0x59f111f1b605d019, 0x923f82a4af194f9b, 0xab1c5ed5da6d8118,
|
||||
0xd807aa98a3030242, 0x12835b0145706fbe, 0x243185be4ee4b28c, 0x550c7dc3d5ffb4e2,
|
||||
0x72be5d74f27b896f, 0x80deb1fe3b1696b1, 0x9bdc06a725c71235, 0xc19bf174cf692694,
|
||||
0xe49b69c19ef14ad2, 0xefbe4786384f25e3, 0x0fc19dc68b8cd5b5, 0x240ca1cc77ac9c65,
|
||||
0x2de92c6f592b0275, 0x4a7484aa6ea6e483, 0x5cb0a9dcbd41fbd4, 0x76f988da831153b5,
|
||||
0x983e5152ee66dfab, 0xa831c66d2db43210, 0xb00327c898fb213f, 0xbf597fc7beef0ee4,
|
||||
0xc6e00bf33da88fc2, 0xd5a79147930aa725, 0x06ca6351e003826f, 0x142929670a0e6e70,
|
||||
0x27b70a8546d22ffc, 0x2e1b21385c26c926, 0x4d2c6dfc5ac42aed, 0x53380d139d95b3df,
|
||||
0x650a73548baf63de, 0x766a0abb3c77b2a8, 0x81c2c92e47edaee6, 0x92722c851482353b,
|
||||
0xa2bfe8a14cf10364, 0xa81a664bbc423001, 0xc24b8b70d0f89791, 0xc76c51a30654be30,
|
||||
0xd192e819d6ef5218, 0xd69906245565a910, 0xf40e35855771202a, 0x106aa07032bbd1b8,
|
||||
0x19a4c116b8d2d0c8, 0x1e376c085141ab53, 0x2748774cdf8eeb99, 0x34b0bcb5e19b48a8,
|
||||
0x391c0cb3c5c95a63, 0x4ed8aa4ae3418acb, 0x5b9cca4f7763e373, 0x682e6ff3d6b2b8a3,
|
||||
0x748f82ee5defb2fc, 0x78a5636f43172f60, 0x84c87814a1f0ab72, 0x8cc702081a6439ec,
|
||||
0x90befffa23631e28, 0xa4506cebde82bde9, 0xbef9a3f7b2c67915, 0xc67178f2e372532b,
|
||||
0xca273eceea26619c, 0xd186b8c721c0c207, 0xeada7dd6cde0eb1e, 0xf57d4f7fee6ed178,
|
||||
0x06f067aa72176fba, 0x0a637dc5a2c898a6, 0x113f9804bef90dae, 0x1b710b35131c471b,
|
||||
0x28db77f523047d84, 0x32caab7b40c72493, 0x3c9ebe0a15c9bebc, 0x431d67c49c100d4c,
|
||||
0x4cc5d4becb3e42b6, 0x597f299cfc657e2a, 0x5fcb6fab3ad6faec, 0x6c44198c4a475817
|
||||
};
|
||||
|
||||
|
||||
@@ -63,219 +63,211 @@ const ulong[80] K @local = {
|
||||
// All truncation types are simple to add onto the base SHA512 implementation at a (near) future time.
|
||||
enum HashTruncationType : uint (uint truncation_width, ulong[8] initial_state)
|
||||
{
|
||||
SHA512 = {
|
||||
512,
|
||||
{
|
||||
0x6a09e667f3bcc908,
|
||||
0xbb67ae8584caa73b,
|
||||
0x3c6ef372fe94f82b,
|
||||
0xa54ff53a5f1d36f1,
|
||||
0x510e527fade682d1,
|
||||
0x9b05688c2b3e6c1f,
|
||||
0x1f83d9abfb41bd6b,
|
||||
0x5be0cd19137e2179
|
||||
}
|
||||
},
|
||||
SHA384 = {
|
||||
384,
|
||||
{
|
||||
0xcbbb9d5dc1059ed8,
|
||||
0x629a292a367cd507,
|
||||
0x9159015a3070dd17,
|
||||
0x152fecd8f70e5939,
|
||||
0x67332667ffc00b31,
|
||||
0x8eb44a8768581511,
|
||||
0xdb0c2e0d64f98fa7,
|
||||
0x47b5481dbefa4fa4
|
||||
}
|
||||
},
|
||||
SHA512_224 = {
|
||||
224,
|
||||
{
|
||||
0x8C3D37C819544DA2,
|
||||
0x73E1996689DCD4D6,
|
||||
0x1DFAB7AE32FF9C82,
|
||||
0x679DD514582F9FCF,
|
||||
0x0F6D2B697BD44DA8,
|
||||
0x77E36F7304C48942,
|
||||
0x3F9D85A86A1D36C8,
|
||||
0x1112E6AD91D692A1
|
||||
}
|
||||
},
|
||||
SHA512_256 = {
|
||||
256,
|
||||
{
|
||||
0x22312194FC2BF72C,
|
||||
0x9F555FA3C84C64C2,
|
||||
0x2393B86B6F53B151,
|
||||
0x963877195940EABD,
|
||||
0x96283EE2A88EFFE3,
|
||||
0xBE5E1E2553863992,
|
||||
0x2B0199FC2C85B8AA,
|
||||
0x0EB72DDC81C52CA2
|
||||
}
|
||||
},
|
||||
SHA512 { 512,
|
||||
{
|
||||
0x6a09e667f3bcc908,
|
||||
0xbb67ae8584caa73b,
|
||||
0x3c6ef372fe94f82b,
|
||||
0xa54ff53a5f1d36f1,
|
||||
0x510e527fade682d1,
|
||||
0x9b05688c2b3e6c1f,
|
||||
0x1f83d9abfb41bd6b,
|
||||
0x5be0cd19137e2179
|
||||
}},
|
||||
SHA384 { 384,
|
||||
{
|
||||
0xcbbb9d5dc1059ed8,
|
||||
0x629a292a367cd507,
|
||||
0x9159015a3070dd17,
|
||||
0x152fecd8f70e5939,
|
||||
0x67332667ffc00b31,
|
||||
0x8eb44a8768581511,
|
||||
0xdb0c2e0d64f98fa7,
|
||||
0x47b5481dbefa4fa4
|
||||
}},
|
||||
SHA512_224 { 224,
|
||||
{
|
||||
0x8C3D37C819544DA2,
|
||||
0x73E1996689DCD4D6,
|
||||
0x1DFAB7AE32FF9C82,
|
||||
0x679DD514582F9FCF,
|
||||
0x0F6D2B697BD44DA8,
|
||||
0x77E36F7304C48942,
|
||||
0x3F9D85A86A1D36C8,
|
||||
0x1112E6AD91D692A1
|
||||
}},
|
||||
SHA512_256 { 256,
|
||||
{
|
||||
0x22312194FC2BF72C,
|
||||
0x9F555FA3C84C64C2,
|
||||
0x2393B86B6F53B151,
|
||||
0x963877195940EABD,
|
||||
0x96283EE2A88EFFE3,
|
||||
0xBE5E1E2553863992,
|
||||
0x2B0199FC2C85B8AA,
|
||||
0x0EB72DDC81C52CA2
|
||||
}},
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@param [in] data
|
||||
@param [in] data
|
||||
*>
|
||||
fn char[HASH_SIZE] hash(char[] data)
|
||||
{
|
||||
Sha512 s @noinit;
|
||||
s.init();
|
||||
s.update(data);
|
||||
return s.final();
|
||||
Sha512 s @noinit;
|
||||
s.init();
|
||||
s.update(data);
|
||||
return s.final();
|
||||
}
|
||||
|
||||
|
||||
fn void Sha512.init(&self)
|
||||
{
|
||||
*self = {
|
||||
.hash_state = HashTruncationType.SHA512.initial_state
|
||||
};
|
||||
*self = {
|
||||
.hash_state = HashTruncationType.SHA512.initial_state
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@param [in] data
|
||||
@require data.len <= ulong.max
|
||||
@param [in] data
|
||||
@require data.len <= ulong.max
|
||||
*>
|
||||
fn void Sha512.update(&self, char[] data)
|
||||
{
|
||||
char* p = data.ptr;
|
||||
ulong len = data.len;
|
||||
ulong l;
|
||||
ulong r = self.length % 128;
|
||||
char* p = data.ptr;
|
||||
ulong len = data.len;
|
||||
ulong l;
|
||||
ulong r = self.length % 128;
|
||||
|
||||
self.length += len;
|
||||
self.length += len;
|
||||
|
||||
if (r)
|
||||
{
|
||||
if (len < (128 - r))
|
||||
{
|
||||
for (l = 0; l < len; ++l) self.buffer[r+l] = p[l];
|
||||
if (r)
|
||||
{
|
||||
if (len < (128 - r))
|
||||
{
|
||||
for (l = 0; l < len; ++l) self.buffer[r+l] = p[l];
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (l = 0; l < 128 - r; ++l) self.buffer[r+l] = p[l];
|
||||
for (l = 0; l < 128 - r; ++l) self.buffer[r+l] = p[l];
|
||||
|
||||
len -= (128 - r);
|
||||
p = &p[128 - r];
|
||||
len -= (128 - r);
|
||||
p = &p[128 - r];
|
||||
|
||||
sha512_transform(&self.hash_state, &self.buffer);
|
||||
}
|
||||
sha512_transform(&self.hash_state, &self.buffer);
|
||||
}
|
||||
|
||||
for (; len >= 128; len -= 128, p = &p[128]) sha512_transform(&self.hash_state, p);
|
||||
for (; len >= 128; len -= 128, p = &p[128]) sha512_transform(&self.hash_state, p);
|
||||
|
||||
for (l = 0; l < len; ++l) self.buffer[l] = p[l];
|
||||
for (l = 0; l < len; ++l) self.buffer[l] = p[l];
|
||||
}
|
||||
|
||||
|
||||
fn char[HASH_SIZE] Sha512.final(&self)
|
||||
{
|
||||
char[HASH_SIZE] hash;
|
||||
char[HASH_SIZE] hash;
|
||||
|
||||
int i;
|
||||
ulong r = self.length % 128;
|
||||
int i;
|
||||
ulong r = self.length % 128;
|
||||
|
||||
self.buffer[r++] = 0x80;
|
||||
self.buffer[r++] = 0x80;
|
||||
|
||||
if (r > 112)
|
||||
{
|
||||
for (i = 0; i < 128 - r; ++i) self.buffer[r+i] = 0;
|
||||
if (r > 112)
|
||||
{
|
||||
for (i = 0; i < 128 - r; ++i) self.buffer[r+i] = 0;
|
||||
|
||||
r = 0;
|
||||
r = 0;
|
||||
|
||||
sha512_transform(&self.hash_state, &self.buffer);
|
||||
}
|
||||
sha512_transform(&self.hash_state, &self.buffer);
|
||||
}
|
||||
|
||||
for (i = 0; i < 120 - r; ++i) self.buffer[r+i] = 0;
|
||||
for (i = 0; i < 120 - r; ++i) self.buffer[r+i] = 0;
|
||||
|
||||
self.length *= 8;
|
||||
self.length *= 8;
|
||||
|
||||
self.buffer[120] = (char)(self.length >> 56);
|
||||
self.buffer[121] = (char)(self.length >> 48);
|
||||
self.buffer[122] = (char)(self.length >> 40);
|
||||
self.buffer[123] = (char)(self.length >> 32);
|
||||
self.buffer[124] = (char)(self.length >> 24);
|
||||
self.buffer[125] = (char)(self.length >> 16);
|
||||
self.buffer[126] = (char)(self.length >> 8);
|
||||
self.buffer[127] = (char)(self.length);
|
||||
self.buffer[120] = (char)(self.length >> 56);
|
||||
self.buffer[121] = (char)(self.length >> 48);
|
||||
self.buffer[122] = (char)(self.length >> 40);
|
||||
self.buffer[123] = (char)(self.length >> 32);
|
||||
self.buffer[124] = (char)(self.length >> 24);
|
||||
self.buffer[125] = (char)(self.length >> 16);
|
||||
self.buffer[126] = (char)(self.length >> 8);
|
||||
self.buffer[127] = (char)(self.length);
|
||||
|
||||
sha512_transform(&self.hash_state, &self.buffer);
|
||||
sha512_transform(&self.hash_state, &self.buffer);
|
||||
|
||||
for (i = 0; i < 8; ++i)
|
||||
{
|
||||
hash[(8 * i)] = (char)(self.hash_state[i] >> 56);
|
||||
hash[(8 * i) + 1] = (char)(self.hash_state[i] >> 48);
|
||||
hash[(8 * i) + 2] = (char)(self.hash_state[i] >> 40);
|
||||
hash[(8 * i) + 3] = (char)(self.hash_state[i] >> 32);
|
||||
hash[(8 * i) + 4] = (char)(self.hash_state[i] >> 24);
|
||||
hash[(8 * i) + 5] = (char)(self.hash_state[i] >> 16);
|
||||
hash[(8 * i) + 6] = (char)(self.hash_state[i] >> 8);
|
||||
hash[(8 * i) + 7] = (char)(self.hash_state[i]);
|
||||
}
|
||||
for (i = 0; i < 8; ++i)
|
||||
{
|
||||
hash[(8 * i)] = (char)(self.hash_state[i] >> 56);
|
||||
hash[(8 * i) + 1] = (char)(self.hash_state[i] >> 48);
|
||||
hash[(8 * i) + 2] = (char)(self.hash_state[i] >> 40);
|
||||
hash[(8 * i) + 3] = (char)(self.hash_state[i] >> 32);
|
||||
hash[(8 * i) + 4] = (char)(self.hash_state[i] >> 24);
|
||||
hash[(8 * i) + 5] = (char)(self.hash_state[i] >> 16);
|
||||
hash[(8 * i) + 6] = (char)(self.hash_state[i] >> 8);
|
||||
hash[(8 * i) + 7] = (char)(self.hash_state[i]);
|
||||
}
|
||||
|
||||
return hash;
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@param [&inout] state
|
||||
@param [&in] buf
|
||||
@param [&inout] state
|
||||
@param [&in] buf
|
||||
*>
|
||||
fn void sha512_transform(ulong *state, char *buf) @local
|
||||
{
|
||||
ulong t1, t2, a, b, c, d, e, f, g, h;
|
||||
ulong[80] w;
|
||||
int i;
|
||||
ulong t1, t2, a, b, c, d, e, f, g, h;
|
||||
ulong[80] w;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; ++i)
|
||||
{
|
||||
w[i] = (ulong)buf[(8 * i)] << 56;
|
||||
w[i] |= (ulong)buf[(8 * i) + 1] << 48;
|
||||
w[i] |= (ulong)buf[(8 * i) + 2] << 40;
|
||||
w[i] |= (ulong)buf[(8 * i) + 3] << 32;
|
||||
w[i] |= (ulong)buf[(8 * i) + 4] << 24;
|
||||
w[i] |= (ulong)buf[(8 * i) + 5] << 16;
|
||||
w[i] |= (ulong)buf[(8 * i) + 6] << 8;
|
||||
w[i] |= buf[(8 * i) + 7];
|
||||
}
|
||||
for (i = 0; i < 16; ++i)
|
||||
{
|
||||
w[i] = (ulong)buf[(8 * i)] << 56;
|
||||
w[i] |= (ulong)buf[(8 * i) + 1] << 48;
|
||||
w[i] |= (ulong)buf[(8 * i) + 2] << 40;
|
||||
w[i] |= (ulong)buf[(8 * i) + 3] << 32;
|
||||
w[i] |= (ulong)buf[(8 * i) + 4] << 24;
|
||||
w[i] |= (ulong)buf[(8 * i) + 5] << 16;
|
||||
w[i] |= (ulong)buf[(8 * i) + 6] << 8;
|
||||
w[i] |= buf[(8 * i) + 7];
|
||||
}
|
||||
|
||||
for (; i < 80; ++i) w[i] = r1(w[i - 2]) + w[i - 7] + r0(w[i - 15]) + w[i - 16];
|
||||
for (; i < 80; ++i) w[i] = r1(w[i - 2]) + w[i - 7] + r0(w[i - 15]) + w[i - 16];
|
||||
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
f = state[5];
|
||||
g = state[6];
|
||||
h = state[7];
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
f = state[5];
|
||||
g = state[6];
|
||||
h = state[7];
|
||||
|
||||
for (i = 0; i < 80; ++i)
|
||||
{
|
||||
t1 = h + s1(e) + ch(e, f, g) + K[i] + w[i];
|
||||
t2 = s0(a) + maj(a, b, c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + t1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1 + t2;
|
||||
}
|
||||
for (i = 0; i < 80; ++i)
|
||||
{
|
||||
t1 = h + s1(e) + ch(e, f, g) + K[i] + w[i];
|
||||
t2 = s0(a) + maj(a, b, c);
|
||||
h = g;
|
||||
g = f;
|
||||
f = e;
|
||||
e = d + t1;
|
||||
d = c;
|
||||
c = b;
|
||||
b = a;
|
||||
a = t1 + t2;
|
||||
}
|
||||
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
state[5] += f;
|
||||
state[6] += g;
|
||||
state[7] += h;
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
state[5] += f;
|
||||
state[6] += g;
|
||||
state[7] += h;
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ fn void SipHash.update(&self, char[] data)
|
||||
self.v[3] ^= self.m;
|
||||
|
||||
$for var $i = 0; $i < BLOCK_ROUNDS; ++$i : // unrolled loop
|
||||
self.round();
|
||||
siphash_round(self);
|
||||
$endfor
|
||||
|
||||
self.v[0] ^= self.m;
|
||||
@@ -123,7 +123,7 @@ fn OutType SipHash.final(&self)
|
||||
$endif
|
||||
|
||||
$for var $i = 0; $i < FINALIZE_ROUNDS; ++$i : // unrolled loop
|
||||
self.round();
|
||||
siphash_round(self);
|
||||
$endfor
|
||||
|
||||
$if OutType.typeid == ulong.typeid :
|
||||
@@ -134,7 +134,7 @@ fn OutType SipHash.final(&self)
|
||||
self.v[1] ^= 0xDD;
|
||||
|
||||
$for var $i = 0; $i < FINALIZE_ROUNDS; ++$i : // unrolled loop
|
||||
self.round();
|
||||
siphash_round(self);
|
||||
$endfor
|
||||
|
||||
return lo | ((uint128)(self.v[0] ^ self.v[1] ^ self.v[2] ^ self.v[3]) << 64);
|
||||
@@ -142,7 +142,7 @@ fn OutType SipHash.final(&self)
|
||||
}
|
||||
|
||||
|
||||
fn void SipHash.round(&self) @local
|
||||
fn void siphash_round(SipHash* self) @local
|
||||
{
|
||||
self.v[0] += self.v[1];
|
||||
self.v[1] = self.v[1].rotl(13);
|
||||
|
||||
@@ -66,14 +66,14 @@ fn void Whirlpool.update(&self, char[] data)
|
||||
// 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);
|
||||
_process_block(self, &self.block);
|
||||
data = data[to_pad..];
|
||||
}
|
||||
|
||||
// Digest blocks wholesale.
|
||||
while (data.len >= BLOCK_SIZE)
|
||||
{
|
||||
self.process_block(data);
|
||||
_process_block(self, data);
|
||||
|
||||
data = (data.len > BLOCK_SIZE) ? data[BLOCK_SIZE..] : {};
|
||||
}
|
||||
@@ -100,7 +100,7 @@ fn char[HASH_SIZE] Whirlpool.final(&self)
|
||||
{
|
||||
if (remainder < 64) self.block[remainder..63] = 0x00;
|
||||
|
||||
self.process_block(&self.block);
|
||||
_process_block(self, &self.block);
|
||||
remainder = 0;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ fn char[HASH_SIZE] Whirlpool.final(&self)
|
||||
self.block_128[3] = $$bswap(self.counter_low << 3);
|
||||
|
||||
// Process the final block.
|
||||
self.process_block(&self.block);
|
||||
_process_block(self, &self.block);
|
||||
|
||||
// Each ulong in the resultant hash should be bit-swapped before the final return.
|
||||
char[HASH_SIZE] hash @align(ulong.alignof);
|
||||
@@ -128,15 +128,15 @@ fn char[HASH_SIZE] Whirlpool.final(&self)
|
||||
}
|
||||
|
||||
|
||||
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];
|
||||
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 = {
|
||||
@@ -153,31 +153,38 @@ const ulong[10] RC @private = {
|
||||
};
|
||||
const ROUNDS = 10;
|
||||
|
||||
fn void Whirlpool.process_block(&self, char* block) @local
|
||||
fn void _process_block(Whirlpool* 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(mem::load((ulong*)block + $round, 1)) ^ self.hash[$round];
|
||||
self.hash[$round] = state[$round];
|
||||
// NOTE: These loops are kept as $for to ensure initial setup is unrolled.
|
||||
$for var $i = 0; $i < 8; $i++:
|
||||
k[$i] = self.hash[$i];
|
||||
state[$i] = $$bswap(mem::load((ulong*)block + $i, 1)) ^ self.hash[$i];
|
||||
self.hash[$i] = state[$i];
|
||||
$endfor
|
||||
|
||||
$for var $round = 0; $round < ROUNDS; ++$round :
|
||||
var $m = $round % 2;
|
||||
// Use regular for loops for the rounds to avoid massive code bloat. 80K less instructions.
|
||||
for (int round = 0; round < ROUNDS; ++round)
|
||||
{
|
||||
int m = round % 2;
|
||||
int next_m = m ^ 1;
|
||||
ulong* pk = &k[m * 8];
|
||||
ulong* nk = &k[next_m * 8];
|
||||
ulong* ps = &state[m * 8];
|
||||
ulong* ns = &state[next_m * 8];
|
||||
|
||||
k[(($m ^ 1) * 8) + 0] = @w_op((&k[$m * 8]), 0) ^ RC[$round];
|
||||
nk[0] = @w_op(pk, 0) ^ RC[round];
|
||||
|
||||
$for var $i = 1; $i < 8; $i++ :
|
||||
k[(($m ^ 1) * 8) + $i] = @w_op((&k[$m * 8]), $i);
|
||||
nk[$i] = @w_op(pk, $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];
|
||||
ns[$i] = @w_op(ps, $i) ^ nk[$i];
|
||||
$endfor
|
||||
$endfor
|
||||
}
|
||||
|
||||
$for var $x = 0; $x < 8; $x++:
|
||||
self.hash[$x] ^= state[$x];
|
||||
|
||||
@@ -39,11 +39,16 @@ fn bool is_dir(String path)
|
||||
return os::native_is_dir(path);
|
||||
}
|
||||
|
||||
fn usz? get_size(String path)
|
||||
fn ulong? get_size(String path)
|
||||
{
|
||||
return os::native_file_size(path);
|
||||
}
|
||||
|
||||
fn void? set_modified_time(String path, Time_t time)
|
||||
{
|
||||
return os::native_set_modified_time(path, time);
|
||||
}
|
||||
|
||||
fn void? delete(String filename)
|
||||
{
|
||||
return os::native_remove(filename) @inline;
|
||||
@@ -63,10 +68,25 @@ fn void? File.reopen(&self, String filename, String mode)
|
||||
*>
|
||||
fn usz? File.seek(&self, isz offset, Seek seek_mode = Seek.SET) @dynamic
|
||||
{
|
||||
os::native_fseek(self.file, offset, seek_mode)!;
|
||||
return os::native_ftell(self.file);
|
||||
os::native_fseek(self.file, offset, (SeekOrigin)seek_mode.ordinal)!;
|
||||
return (usz)os::native_ftell(self.file);
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.file != null
|
||||
*>
|
||||
fn void? File.set_cursor(&self, long offset, SeekOrigin whence = FROM_START) @dynamic
|
||||
{
|
||||
return os::native_fseek(self.file, offset, whence);
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.file != null
|
||||
*>
|
||||
fn long? File.cursor(&self) @dynamic
|
||||
{
|
||||
return os::native_ftell(self.file);
|
||||
}
|
||||
|
||||
/*
|
||||
Implement later
|
||||
@@ -118,6 +138,14 @@ fn void? File.close(&self) @inline @dynamic
|
||||
self.file = null;
|
||||
}
|
||||
|
||||
fn ulong? File.size(&self) @dynamic
|
||||
{
|
||||
long curr = self.cursor()!;
|
||||
defer (void)self.set_cursor(curr);
|
||||
self.set_cursor(0, FROM_END)!;
|
||||
return self.cursor()!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.file != null
|
||||
*>
|
||||
@@ -171,9 +199,8 @@ fn char[]? load_buffer(String filename, char[] buffer)
|
||||
{
|
||||
File file = open(filename, "rb")!;
|
||||
defer (void)file.close();
|
||||
usz len = file.seek(0, END)!;
|
||||
long len = file.size()!;
|
||||
if (len > buffer.len) return io::OVERFLOW~;
|
||||
file.seek(0, SET)!;
|
||||
usz read = 0;
|
||||
while (read < len)
|
||||
{
|
||||
@@ -187,16 +214,16 @@ fn char[]? load(Allocator allocator, String filename)
|
||||
{
|
||||
File file = open(filename, "rb")!;
|
||||
defer (void)file.close();
|
||||
usz len = file.seek(0, END)!;
|
||||
file.seek(0, SET)!;
|
||||
char* data = allocator::malloc_try(allocator, len)!;
|
||||
ulong len = file.size()!;
|
||||
if (len > usz.max) return io::OUT_OF_SPACE~;
|
||||
char* data = allocator::malloc_try(allocator, (usz)len)!;
|
||||
defer catch allocator::free(allocator, data);
|
||||
usz read = 0;
|
||||
while (read < len)
|
||||
while (read < (usz)len)
|
||||
{
|
||||
read += file.read(data[read:len - read])!;
|
||||
read += file.read(data[read:(usz)len - read])!;
|
||||
}
|
||||
return data[:len];
|
||||
return data[:(usz)len];
|
||||
}
|
||||
|
||||
fn char[]? load_path(Allocator allocator, Path path) => load(allocator, path.str_view());
|
||||
|
||||
@@ -45,10 +45,9 @@ fn FileMmap? mmap_file(File file, usz offset = 0, usz len = 0, VirtualMemoryAcce
|
||||
{
|
||||
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;
|
||||
ulong new_len = file.size()! - offset;
|
||||
if (new_len > (ulong)isz.max) return mem::OUT_OF_MEMORY~;
|
||||
len = (usz)new_len;
|
||||
}
|
||||
|
||||
// get the page size
|
||||
|
||||
@@ -13,8 +13,7 @@ interface Printable
|
||||
fn usz? to_format(Formatter* formatter) @optional;
|
||||
}
|
||||
|
||||
faultdef BUFFER_EXCEEDED, INTERNAL_BUFFER_EXCEEDED, INVALID_FORMAT,
|
||||
NOT_ENOUGH_ARGUMENTS, INVALID_ARGUMENT;
|
||||
faultdef BUFFER_EXCEEDED, INTERNAL_BUFFER_EXCEEDED, INVALID_FORMAT, NOT_ENOUGH_ARGUMENTS, INVALID_ARGUMENT;
|
||||
|
||||
alias OutputFn = fn void?(void* buffer, char c);
|
||||
alias FloatType = double;
|
||||
@@ -34,14 +33,14 @@ macro bool is_struct_with_default_print($Type)
|
||||
*>
|
||||
macro usz? struct_to_format(value, Formatter* f, bool $force_dump)
|
||||
{
|
||||
var $Type = $typeof(value);
|
||||
usz total = f.print("{ ")!;
|
||||
$foreach $i, $member : $Type.membersof:
|
||||
$if $i > 0:
|
||||
total += f.print(", ")!;
|
||||
$endif
|
||||
$if $member.nameof != "":
|
||||
total += f.printf("%s: ", $member.nameof)!;
|
||||
var $Type = $typeof(value);
|
||||
usz total = f.print("{ ")!;
|
||||
$foreach $i, $member : $Type.membersof:
|
||||
$if $i > 0:
|
||||
total += f.print(", ")!;
|
||||
$endif
|
||||
$if $member.nameof != "":
|
||||
total += f.printf("%s: ", $member.nameof)!;
|
||||
$endif
|
||||
$if ($force_dump &&& ($member.typeid.kindof == STRUCT || $member.typeid.kindof == BITSTRUCT)) |||
|
||||
is_struct_with_default_print($member.typeid):
|
||||
@@ -92,16 +91,7 @@ fn void Formatter.init(&self, OutputFn out_fn, void* data = null)
|
||||
*self = { .data = data, .out_fn = out_fn};
|
||||
}
|
||||
|
||||
fn usz? Formatter.out(&self, char c) @private
|
||||
{
|
||||
if (catch err = self.out_fn(self.data, c))
|
||||
{
|
||||
if (self.first_fault) return self.first_fault~;
|
||||
self.first_fault = err;
|
||||
return err~;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
fn usz? Formatter.print_with_function(&self, Printable arg)
|
||||
{
|
||||
@@ -116,7 +106,7 @@ fn usz? Formatter.print_with_function(&self, Printable arg)
|
||||
self.width = old_width;
|
||||
self.prec = old_prec;
|
||||
}
|
||||
if (!arg) return self.out_substr("(null)");
|
||||
if (!arg) return formatter_out_substr(self, "(null)");
|
||||
return arg.to_format(self);
|
||||
}
|
||||
if (&arg.to_constant_string)
|
||||
@@ -130,208 +120,12 @@ fn usz? Formatter.print_with_function(&self, Printable arg)
|
||||
self.width = old_width;
|
||||
self.prec = old_prec;
|
||||
}
|
||||
if (!arg) return self.out_substr("(null)");
|
||||
return self.out_substr(arg.to_constant_string());
|
||||
if (!arg) return formatter_out_substr(self, "(null)");
|
||||
return formatter_out_substr(self, arg.to_constant_string());
|
||||
}
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn usz? Formatter.out_unknown(&self, String category, any arg) @private
|
||||
{
|
||||
return self.out_substr("<") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr(">");
|
||||
}
|
||||
fn usz? Formatter.out_str(&self, any arg) @private
|
||||
{
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case VOID:
|
||||
return self.out_substr("void");
|
||||
case FAULT:
|
||||
fault f = *(fault*)arg.ptr;
|
||||
return self.out_substr(f ? f.nameof : "(empty-fault)");
|
||||
case INTERFACE:
|
||||
any a = *(any*)arg;
|
||||
return a ? self.out_str(a) : self.out_substr("(empty-interface)");
|
||||
case ANY:
|
||||
any a = *(any*)arg;
|
||||
return a ? self.out_str(a) : self.out_substr("(empty-any)");
|
||||
case OPTIONAL:
|
||||
unreachable();
|
||||
case SIGNED_INT:
|
||||
case UNSIGNED_INT:
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
return self.ntoa_any(arg, 10) ?? self.out_substr("<INVALID>");
|
||||
case FLOAT:
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
return self.ftoa(float_from_any(arg)) ?? self.out_substr("ERR");
|
||||
case BOOL:
|
||||
return self.out_substr(*(bool*)arg.ptr ? "true" : "false");
|
||||
default:
|
||||
}
|
||||
usz? n = self.print_with_function((Printable)arg);
|
||||
if (try n) return n;
|
||||
if (@catch(n) != NOT_FOUND) n!;
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case TYPEID:
|
||||
return self.out_substr("typeid[")! + self.ntoa((iptr)*(typeid*)arg, false, 16)! + self.out_substr("]")!;
|
||||
case ENUM:
|
||||
usz i = types::any_to_enum_ordinal(arg, usz)!!;
|
||||
assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i);
|
||||
return self.out_substr(arg.type.names[i]);
|
||||
case STRUCT:
|
||||
return self.out_unknown("struct", arg);
|
||||
case UNION:
|
||||
return self.out_unknown("union", arg);
|
||||
case BITSTRUCT:
|
||||
return self.out_unknown("bitstruct", arg);
|
||||
case FUNC:
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.width = 0;
|
||||
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
|
||||
case CONST_ENUM:
|
||||
case DISTINCT:
|
||||
if (arg.type == String.typeid)
|
||||
{
|
||||
return self.out_substr(*(String*)arg);
|
||||
}
|
||||
if (arg.type == ZString.typeid)
|
||||
{
|
||||
return self.out_substr(*(ZString*)arg ? ((ZString*)arg).str_view() : "(null)");
|
||||
}
|
||||
if (arg.type == DString.typeid)
|
||||
{
|
||||
return self.out_substr(*(DString*)arg ? ((DString*)arg).str_view() : "(null)");
|
||||
}
|
||||
return self.out_str(arg.as_inner());
|
||||
case POINTER:
|
||||
typeid inner = arg.type.inner;
|
||||
void** pointer = arg.ptr;
|
||||
if (arg.type.inner != void.typeid)
|
||||
{
|
||||
any deref = any_make(*pointer, inner);
|
||||
n = self.print_with_function((Printable)deref);
|
||||
if (try n) return n;
|
||||
if (@catch(n) != NOT_FOUND) n!;
|
||||
}
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.width = 0;
|
||||
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
|
||||
case ARRAY:
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
typeid inner = arg.type.inner;
|
||||
usz size = inner.sizeof;
|
||||
usz alen = arg.type.len;
|
||||
// Pretend this is a String
|
||||
void* ptr = (void*)arg.ptr;
|
||||
usz len = self.out('[')!;
|
||||
for (usz i = 0; i < alen; i++)
|
||||
{
|
||||
if (i != 0) len += self.out_substr(", ")!;
|
||||
len += self.out_str(any_make(ptr, inner))!;
|
||||
ptr += size;
|
||||
}
|
||||
len += self.out(']')!;
|
||||
return len;
|
||||
case VECTOR:
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
usz size = inner.sizeof;
|
||||
usz vlen = arg.type.len;
|
||||
// Pretend this is a String
|
||||
void* ptr = (void*)arg.ptr;
|
||||
usz len = self.out_substr("[<")!;
|
||||
for (usz i = 0; i < vlen; i++)
|
||||
{
|
||||
if (i != 0) len += self.out_substr(", ")!;
|
||||
len += self.out_str(any_make(ptr, inner))!;
|
||||
ptr += size;
|
||||
}
|
||||
len += self.out_substr(">]")!;
|
||||
return len;
|
||||
case SLICE:
|
||||
// this is SomeType[] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
if (inner == void.typeid) inner = char.typeid;
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
usz size = inner.sizeof;
|
||||
// Pretend this is a String
|
||||
String* temp = (void*)arg.ptr;
|
||||
void* ptr = (void*)temp.ptr;
|
||||
usz slen = temp.len;
|
||||
usz len = self.out('[')!;
|
||||
for (usz i = 0; i < slen; i++)
|
||||
{
|
||||
if (i != 0) len += self.out_substr(", ")!;
|
||||
len += self.out_str(any_make(ptr, inner))!;
|
||||
ptr += size;
|
||||
}
|
||||
len += self.out(']')!;
|
||||
return len;
|
||||
case ANY:
|
||||
case INTERFACE:
|
||||
unreachable("Already handled");
|
||||
default:
|
||||
}
|
||||
return self.out_substr("Invalid type");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
fn void? out_null_fn(void* data @unused, char c @unused) @private
|
||||
{
|
||||
@@ -339,7 +133,7 @@ fn void? out_null_fn(void* data @unused, char c @unused) @private
|
||||
|
||||
macro usz? @report_fault(Formatter* f, $fault)
|
||||
{
|
||||
(void)f.out_substr($fault);
|
||||
(void)formatter_out_substr(f, $fault);
|
||||
return INVALID_FORMAT~;
|
||||
}
|
||||
|
||||
@@ -351,17 +145,19 @@ macro usz? @wrap_bad(Formatter* f, #action)
|
||||
switch (err)
|
||||
{
|
||||
case BUFFER_EXCEEDED:
|
||||
case INTERNAL_BUFFER_EXCEEDED:
|
||||
return f.first_err(err)~;
|
||||
default:
|
||||
case INTERNAL_BUFFER_EXCEEDED:
|
||||
return f.first_err(err)~;
|
||||
default:
|
||||
err = f.first_err(INVALID_ARGUMENT);
|
||||
f.out_substr("<INVALID>")!;
|
||||
return err~;
|
||||
formatter_out_substr(f, "<INVALID>")!;
|
||||
return err~;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
{
|
||||
self.first_fault = {};
|
||||
@@ -380,7 +176,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
if (c != '%')
|
||||
{
|
||||
// no
|
||||
total_len += self.out(c)!;
|
||||
total_len += self.print_char(c)!;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
@@ -388,7 +184,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
c = format[i];
|
||||
if (c == '%')
|
||||
{
|
||||
total_len += self.out(c)!;
|
||||
total_len += self.print_char(c)!;
|
||||
continue;
|
||||
}
|
||||
// evaluate flags
|
||||
@@ -416,7 +212,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
self.flags.left = true;
|
||||
w = -w;
|
||||
}
|
||||
self.width = w;
|
||||
self.width = (uint)w;
|
||||
// evaluate precision field
|
||||
self.prec = 0;
|
||||
if (c == '.')
|
||||
@@ -425,7 +221,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
if (++i >= format_len) return @report_fault(self, "<BAD FORMAT>");
|
||||
int? prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i);
|
||||
if (catch prec) return @report_fault(self, "<BAD FORMAT>");
|
||||
self.prec = prec < 0 ? 0 : prec;
|
||||
self.prec = (uint)(prec < 0 ? 0 : prec);
|
||||
c = format[i];
|
||||
}
|
||||
|
||||
@@ -434,7 +230,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
if (variant_index >= anys.len)
|
||||
{
|
||||
self.first_err(NOT_ENOUGH_ARGUMENTS);
|
||||
total_len += self.out_substr("<MISSING>")!;
|
||||
total_len += formatter_out_substr(self, "<MISSING>")!;
|
||||
continue;
|
||||
}
|
||||
any current = anys[variant_index++];
|
||||
@@ -462,83 +258,37 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
self.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'a':
|
||||
total_len += @wrap_bad(self, self.atoa(float_from_any(current)))!;
|
||||
total_len += @wrap_bad(self, formatter_atoa(self, float_from_any(current)))!;
|
||||
continue;
|
||||
case 'F' :
|
||||
self.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'f':
|
||||
total_len += @wrap_bad(self, self.ftoa(float_from_any(current)))!;
|
||||
total_len += @wrap_bad(self, formatter_ftoa(self, float_from_any(current)))!;
|
||||
continue;
|
||||
case 'E':
|
||||
self.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'e':
|
||||
total_len += @wrap_bad(self, self.etoa(float_from_any(current)))!;
|
||||
total_len += @wrap_bad(self, formatter_etoa(self, float_from_any(current)))!;
|
||||
continue;
|
||||
case 'G':
|
||||
self.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'g':
|
||||
total_len += @wrap_bad(self, self.gtoa(float_from_any(current)))!;
|
||||
total_len += @wrap_bad(self, formatter_gtoa(self, float_from_any(current)))!;
|
||||
continue;
|
||||
case 'c':
|
||||
total_len += self.out_char(current)!;
|
||||
total_len += formatter_out_char(self, current)!;
|
||||
continue;
|
||||
case 'H':
|
||||
self.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'h':
|
||||
char[] out @noinit;
|
||||
switch (current.type)
|
||||
{
|
||||
case char[]:
|
||||
case ichar[]:
|
||||
out = *(char[]*)current;
|
||||
default:
|
||||
if (current.type.kindof == ARRAY && (current.type.inner == char.typeid || current.type.inner == ichar.typeid))
|
||||
{
|
||||
out = ((char*)current.ptr)[:current.type.sizeof];
|
||||
break;
|
||||
}
|
||||
if (current.type.kindof == POINTER)
|
||||
{
|
||||
out = ((*(char**)current.ptr))[:current.type.inner.sizeof];
|
||||
break;
|
||||
}
|
||||
total_len += self.out_substr("<INVALID>")!;
|
||||
continue;
|
||||
}
|
||||
if (self.flags.left)
|
||||
{
|
||||
usz len = print_hex_chars(self, out, self.flags.uppercase)!;
|
||||
total_len += len;
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
continue;
|
||||
}
|
||||
if (self.width)
|
||||
{
|
||||
total_len += self.pad(' ', self.width, out.len * 2)!;
|
||||
}
|
||||
total_len += print_hex_chars(self, out, self.flags.uppercase)!;
|
||||
total_len += formatter_out_hex_buffer(self, current)!;
|
||||
continue;
|
||||
case 's':
|
||||
if (self.flags.left)
|
||||
{
|
||||
usz len = self.out_str(current)!;
|
||||
total_len += len;
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
continue;
|
||||
}
|
||||
if (self.width)
|
||||
{
|
||||
OutputFn out_fn = self.out_fn;
|
||||
self.out_fn = (OutputFn)&out_null_fn;
|
||||
usz len = self.out_str(current)!;
|
||||
self.out_fn = out_fn;
|
||||
total_len += self.pad(' ', self.width, len)!;
|
||||
}
|
||||
total_len += self.out_str(current)!;
|
||||
total_len += formatter_out_str_pad(self, current)!;
|
||||
continue;
|
||||
case 'p':
|
||||
self.flags.zeropad = true;
|
||||
@@ -546,7 +296,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
base = 16;
|
||||
default:
|
||||
self.first_err(INVALID_FORMAT);
|
||||
total_len += self.out_substr("<BAD FORMAT>")!;
|
||||
total_len += formatter_out_substr(self, "<BAD FORMAT>")!;
|
||||
continue;
|
||||
}
|
||||
if (base != 10)
|
||||
@@ -558,16 +308,25 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
|
||||
if (self.flags.precision) self.flags.zeropad = false;
|
||||
|
||||
bool is_neg;
|
||||
total_len += @wrap_bad(self, self.ntoa(int_from_any(current, &is_neg), is_neg, base))!;
|
||||
total_len += @wrap_bad(self, formatter_ntoa(self, int_from_any(current, &is_neg), is_neg, base))!;
|
||||
}
|
||||
// termination
|
||||
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
||||
|
||||
// return written chars without terminating \0
|
||||
if (self.first_fault) return self.first_fault~;
|
||||
return total_len;
|
||||
}
|
||||
|
||||
fn usz? Formatter.out(&self, char c) @deprecated("Use print_char") => self.print_char(c);
|
||||
|
||||
fn usz? Formatter.print_char(&self, char c)
|
||||
{
|
||||
if (catch err = self.out_fn(self.data, c))
|
||||
{
|
||||
if (self.first_fault) return self.first_fault~;
|
||||
self.first_fault = err;
|
||||
return err~;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
fn usz? Formatter.print(&self, String str)
|
||||
{
|
||||
@@ -576,6 +335,6 @@ fn usz? Formatter.print(&self, String str)
|
||||
// use null output function
|
||||
self.out_fn = &out_null_fn;
|
||||
}
|
||||
foreach (c : str) self.out(c)!;
|
||||
foreach (c : str) self.print_char(c)!;
|
||||
return str.len;
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ fn usz? print_hex_chars(Formatter* f, char[] out, bool uppercase) @inline
|
||||
foreach (c : out)
|
||||
{
|
||||
char digit = c >> 4;
|
||||
f.out(digit + (digit < 10 ? '0' : past_10))!;
|
||||
f.print_char(digit + (digit < 10 ? '0' : past_10))!;
|
||||
len++;
|
||||
digit = c & 0xf;
|
||||
f.out(digit + (digit < 10 ? '0' : past_10))!;
|
||||
f.print_char(digit + (digit < 10 ? '0' : past_10))!;
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
@@ -29,10 +29,10 @@ macro fault Formatter.first_err(&self, fault f)
|
||||
return f;
|
||||
}
|
||||
|
||||
fn usz? Formatter.adjust(&self, usz len) @local
|
||||
fn usz? formatter_adjust(Formatter* f, usz len) @local
|
||||
{
|
||||
if (!self.flags.left) return 0;
|
||||
return self.pad(' ', self.width, len);
|
||||
if (!f.flags.left) return 0;
|
||||
return formatter_pad(f, ' ', f.width, len);
|
||||
}
|
||||
|
||||
fn uint128? int_from_any(any arg, bool *is_neg) @private
|
||||
@@ -43,8 +43,8 @@ fn uint128? int_from_any(any arg, bool *is_neg) @private
|
||||
case POINTER:
|
||||
*is_neg = false;
|
||||
return (uint128)(uptr)*(void**)arg.ptr;
|
||||
case DISTINCT:
|
||||
case CONST_ENUM:
|
||||
case TYPEDEF:
|
||||
case CONSTDEF:
|
||||
return int_from_any(arg.as_inner(), is_neg);
|
||||
default:
|
||||
break;
|
||||
@@ -95,7 +95,7 @@ fn FloatType? float_from_any(any arg) @private
|
||||
$if env::F128_SUPPORT:
|
||||
if (arg.type == float128.typeid) return (FloatType)*((float128*)arg.ptr);
|
||||
$endif
|
||||
if (arg.type.kindof == DISTINCT || arg.type.kindof == CONST_ENUM)
|
||||
if (arg.type.kindof == TYPEDEF || arg.type.kindof == CONSTDEF)
|
||||
{
|
||||
return float_from_any(arg.as_inner());
|
||||
}
|
||||
@@ -156,11 +156,11 @@ fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline @private
|
||||
return i;
|
||||
}
|
||||
|
||||
fn usz? Formatter.out_substr(&self, String str) @private
|
||||
fn usz? formatter_out_substr(Formatter* f, String str) @private
|
||||
{
|
||||
usz l = conv::utf8_codepoints(str);
|
||||
uint prec = self.prec;
|
||||
if (self.flags.precision && l < prec) l = prec;
|
||||
uint prec = f.prec;
|
||||
if (f.flags.precision && l < prec) l = prec;
|
||||
usz index = 0;
|
||||
usz chars = str.len;
|
||||
char* ptr = str.ptr;
|
||||
@@ -168,17 +168,17 @@ fn usz? Formatter.out_substr(&self, String str) @private
|
||||
{
|
||||
char c = ptr[index];
|
||||
// Break if we have precision set and we ran out...
|
||||
if (c & 0xC0 != 0x80 && self.flags.precision && !prec--) break;
|
||||
self.out(c)!;
|
||||
if (c & 0xC0 != 0x80 && f.flags.precision && !prec--) break;
|
||||
f.print_char(c)!;
|
||||
index++;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
fn usz? Formatter.pad(&self, char c, isz width, isz len) @inline
|
||||
fn usz? formatter_pad(Formatter* f, char c, isz width, isz len) @inline
|
||||
{
|
||||
isz delta = width - len;
|
||||
for (isz i = 0; i < delta; i++) self.out(c)!;
|
||||
for (isz i = 0; i < delta; i++) f.print_char(c)!;
|
||||
return max(0, delta);
|
||||
}
|
||||
|
||||
@@ -191,7 +191,7 @@ fn char* fmt_u(uint128 x, char* s)
|
||||
|
||||
fn usz? Formatter.out_chars(&self, char[] s)
|
||||
{
|
||||
foreach (c : s) self.out(c)!;
|
||||
foreach (c : s) self.print_char(c)!;
|
||||
return s.len;
|
||||
}
|
||||
|
||||
@@ -203,12 +203,76 @@ enum FloatFormatting
|
||||
HEX
|
||||
}
|
||||
|
||||
fn usz? Formatter.etoa(&self, double y) => self.floatformat(EXPONENTIAL, y);
|
||||
fn usz? Formatter.ftoa(&self, double y) => self.floatformat(FLOAT, y);
|
||||
fn usz? Formatter.gtoa(&self, double y) => self.floatformat(ADAPTIVE, y);
|
||||
fn usz? Formatter.atoa(&self, double y) => self.floatformat(HEX, y);
|
||||
fn usz? formatter_etoa(Formatter* self, double y) => formatter_floatformat(self, EXPONENTIAL, y);
|
||||
fn usz? formatter_ftoa(Formatter* self, double y) => formatter_floatformat(self, FLOAT, y);
|
||||
fn usz? formatter_gtoa(Formatter* self, double y) => formatter_floatformat(self, ADAPTIVE, y);
|
||||
fn usz? formatter_atoa(Formatter* self, double y) => formatter_floatformat(self, HEX, y);
|
||||
|
||||
fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @private
|
||||
fn usz? formatter_floatformat_hex(Formatter* self, double y, bool is_neg, isz pl, isz p) @private @inline
|
||||
{
|
||||
double round = 8.0;
|
||||
// 0x / 0X
|
||||
pl += 2;
|
||||
if (p > 0 && p < math::DOUBLE_MANT_DIG / 4 - 1)
|
||||
{
|
||||
int re = math::DOUBLE_MANT_DIG / 4 - 1 - (int)p;
|
||||
round *= 1 << (math::DOUBLE_MANT_DIG % 4);
|
||||
while (re--) round *= 16;
|
||||
if (is_neg)
|
||||
{
|
||||
y = -y;
|
||||
y -= round;
|
||||
y += round;
|
||||
y = -y;
|
||||
}
|
||||
else
|
||||
{
|
||||
y += round;
|
||||
y -= round;
|
||||
}
|
||||
}
|
||||
int e2;
|
||||
y = math::frexp(y, &e2) * 2;
|
||||
if (y) e2--;
|
||||
char[12] ebuf0;
|
||||
char* ebuf = &ebuf0[0] + 12;
|
||||
char[9 + math::DOUBLE_MANT_DIG / 4] buf_array;
|
||||
char* buf = &buf_array[0];
|
||||
|
||||
// Reverse print
|
||||
char* estr = fmt_u(e2 < 0 ? (int128)-e2 : (int128)e2, ebuf);
|
||||
if (estr == ebuf) *--estr = '0';
|
||||
*--estr = (e2 < 0 ? '-' : '+');
|
||||
*--estr = self.flags.uppercase ? 'P' : 'p';
|
||||
char* s = buf;
|
||||
char* xdigits = self.flags.uppercase ? &XDIGITS_H : &XDIGITS_L;
|
||||
do
|
||||
{
|
||||
int x = (int)y;
|
||||
*s++ = xdigits[x];
|
||||
y = 16 * (y - x);
|
||||
if (s - buf == 1 && (y || p > 0 || self.flags.hash)) *s++ = '.';
|
||||
if (p >= 0 && (s - buf) >= (2 + p)) break;
|
||||
} while (y);
|
||||
isz outlen = s - buf;
|
||||
isz explen = ebuf - estr;
|
||||
if (p > int.max - 2 - explen - pl) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
usz len;
|
||||
usz l = (usz)(p && outlen - 2 < p
|
||||
? p + 2 + explen
|
||||
: outlen + explen);
|
||||
if (!self.flags.left && !self.flags.zeropad) len += formatter_pad(self, ' ', self.width, pl + l)!;
|
||||
if (is_neg || self.flags.plus) len += self.print_char(is_neg ? '-' : '+')!;
|
||||
len += self.out_chars(self.flags.uppercase ? "0X" : "0x")!;
|
||||
if (self.flags.zeropad) len += formatter_pad(self, '0', self.width, pl + l)!;
|
||||
len += self.out_chars(buf[:outlen])!;
|
||||
len += formatter_pad(self, '0', (isz)l - outlen - explen, 0)!;
|
||||
len += self.out_chars(estr[:explen])!;
|
||||
if (self.flags.left) len += formatter_pad(self, ' ', self.width, pl + (isz)l)!;
|
||||
return len;
|
||||
}
|
||||
|
||||
fn usz? formatter_floatformat(Formatter* self, FloatFormatting formatting, double y) @private
|
||||
{
|
||||
// This code is heavily based on musl's printf code
|
||||
const BUF_SIZE = (math::DOUBLE_MANT_DIG + 28) / 29 + 1
|
||||
@@ -226,78 +290,23 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
{
|
||||
usz len;
|
||||
// Add padding
|
||||
if (!self.flags.left) len += self.pad(' ', self.width, 3 + pl)!;
|
||||
if (!self.flags.left) len += formatter_pad(self, ' ', self.width, 3 + pl)!;
|
||||
String s = self.flags.uppercase ? "INF" : "inf";
|
||||
if (math::is_nan(y)) s = self.flags.uppercase ? "NAN" : "nan";
|
||||
if (pl) len += self.out(is_neg ? '-' : '+')!;
|
||||
if (pl) len += self.print_char(is_neg ? '-' : '+')!;
|
||||
len += self.out_chars(s)!;
|
||||
if (self.flags.left) len += self.pad(' ', self.width, 3 + pl)!;
|
||||
if (self.flags.left) len += formatter_pad(self, ' ', self.width, 3 + pl)!;
|
||||
return len;
|
||||
}
|
||||
|
||||
isz p = self.flags.precision ? self.prec : -1;
|
||||
if (formatting == HEX) return formatter_floatformat_hex(self, y, is_neg, pl, p);
|
||||
|
||||
// Rescale
|
||||
int e2;
|
||||
|
||||
y = math::frexp(y, &e2) * 2;
|
||||
if (y) e2--;
|
||||
char[12] ebuf0;
|
||||
char* ebuf = 12 + (char*)&ebuf0;
|
||||
char[9 + math::DOUBLE_MANT_DIG / 4] buf_array;
|
||||
char* buf = &buf_array;
|
||||
isz p = self.flags.precision ? self.prec : -1;
|
||||
if (formatting == HEX)
|
||||
{
|
||||
double round = 8.0;
|
||||
// 0x / 0X
|
||||
pl += 2;
|
||||
if (p > 0 && p < math::DOUBLE_MANT_DIG / 4 - 1)
|
||||
{
|
||||
int re = math::DOUBLE_MANT_DIG / 4 - 1 - (int)p;
|
||||
round *= 1 << (math::DOUBLE_MANT_DIG % 4);
|
||||
while (re--) round *= 16;
|
||||
if (is_neg)
|
||||
{
|
||||
y = -y;
|
||||
y -= round;
|
||||
y += round;
|
||||
y = -y;
|
||||
}
|
||||
else
|
||||
{
|
||||
y += round;
|
||||
y -= round;
|
||||
}
|
||||
}
|
||||
// Reverse print
|
||||
char* estr = fmt_u(e2 < 0 ? (int128)-e2 : (int128)e2, ebuf);
|
||||
if (estr == ebuf) *--estr = '0';
|
||||
*--estr = (e2 < 0 ? '-' : '+');
|
||||
*--estr = self.flags.uppercase ? 'P' : 'p';
|
||||
char* s = buf;
|
||||
char* xdigits = self.flags.uppercase ? &XDIGITS_H : &XDIGITS_L;
|
||||
do
|
||||
{
|
||||
int x = (int)y;
|
||||
*s++ = xdigits[x];
|
||||
y = 16 * (y - x);
|
||||
if (s - buf == 1 && (y || p > 0 || self.flags.hash)) *s++ = '.';
|
||||
} while (y);
|
||||
isz outlen = s - buf;
|
||||
isz explen = ebuf - estr;
|
||||
if (p > int.max - 2 - explen - pl) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
usz len;
|
||||
usz l = p && outlen - 2 < p
|
||||
? p + 2 + explen
|
||||
: outlen + explen;
|
||||
if (!self.flags.left && !self.flags.zeropad) len += self.pad(' ', self.width, pl + l)!;
|
||||
if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!;
|
||||
len += self.out_chars(self.flags.uppercase ? "0X" : "0x")!;
|
||||
if (self.flags.zeropad) len += self.pad('0', self.width, pl + l)!;
|
||||
len += self.out_chars(buf[:outlen])!;
|
||||
len += self.pad('0', l - outlen - explen, 0)!;
|
||||
len += self.out_chars(estr[:explen])!;
|
||||
if (self.flags.left) len += self.pad(' ', self.width, pl + l)!;
|
||||
return len;
|
||||
}
|
||||
|
||||
if (p < 0) p = 6;
|
||||
if (y)
|
||||
{
|
||||
@@ -344,7 +353,6 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
int need = (int)(1 + (p + math::DOUBLE_MANT_DIG / 3u + 8) / 9);
|
||||
for (uint* d = a; d < z; d++)
|
||||
{
|
||||
// CHECK THIS
|
||||
uint rm = *d & ((1 << sh) - 1);
|
||||
*d = (*d >> sh) + carry;
|
||||
carry = (1000000000 >> sh) * rm;
|
||||
@@ -377,13 +385,13 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
j %= 9;
|
||||
int i;
|
||||
for (i = 10, j++; j < 9; i *= 10, j++);
|
||||
x = *d % i;
|
||||
x = *d % (uint)i;
|
||||
// Are there any significant digits past j?
|
||||
if (x || (d + 1) != z)
|
||||
{
|
||||
double round = 2 / math::DOUBLE_EPSILON;
|
||||
double small;
|
||||
if (((*d / i) & 1) || (i == 1000000000 && d > a && (d[-1] & 1)))
|
||||
if (((*d / (uint)i) & 1) || (i == 1000000000 && d > a && (d[-1] & 1)))
|
||||
{
|
||||
round += 2;
|
||||
}
|
||||
@@ -437,7 +445,7 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
// Count trailing zeros in last place
|
||||
if (z > a && z[-1])
|
||||
{
|
||||
for (int i = 10, j = 0; z[-1] % i == 0; i *= 10, j++);
|
||||
for (int i = 10, j = 0; z[-1] % (uint)i == 0; i *= 10, j++);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -456,6 +464,8 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
}
|
||||
if (p > int.max - 1 - (isz)(p || self.flags.hash)) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
int l = (int)(1 + p + (isz)(p || self.flags.hash));
|
||||
char[12] ebuf0;
|
||||
char* ebuf = &ebuf0[0] + 12;
|
||||
char* estr @noinit;
|
||||
if (formatting == FLOAT)
|
||||
{
|
||||
@@ -473,9 +483,13 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
}
|
||||
if (l > int.max - pl) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
usz len;
|
||||
if (!self.flags.left && !self.flags.zeropad) len += self.pad(' ', self.width, pl + l)!;
|
||||
if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!;
|
||||
if (self.flags.zeropad) len += self.pad('0', self.width, pl + l)!;
|
||||
if (!self.flags.left && !self.flags.zeropad) len += formatter_pad(self, ' ', self.width, pl + l)!;
|
||||
if (is_neg || self.flags.plus) len += self.print_char(is_neg ? '-' : '+')!;
|
||||
if (self.flags.zeropad) len += formatter_pad(self, '0', self.width, pl + l)!;
|
||||
|
||||
char[9] buf_array;
|
||||
char* buf = &buf_array[0];
|
||||
|
||||
if (formatting == FLOAT)
|
||||
{
|
||||
if (a > r) a = r;
|
||||
@@ -492,14 +506,14 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
}
|
||||
len += self.out_chars(s[:buf + 9 - s])!;
|
||||
}
|
||||
if (p || self.flags.hash) len += self.out('.')!;
|
||||
if (p || self.flags.hash) len += self.print_char('.')!;
|
||||
for (; d < z && p > 0; d++, p -= 9)
|
||||
{
|
||||
char* s = fmt_u(*d, buf + 9);
|
||||
while (s > buf) *--s = '0';
|
||||
len += self.out_chars(s[:math::min((isz)9, p)])!;
|
||||
}
|
||||
len += self.pad('0', p + 9, 9)!;
|
||||
len += formatter_pad(self, '0', p + 9, 9)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -514,21 +528,42 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
|
||||
}
|
||||
else
|
||||
{
|
||||
len += self.out(s++[0])!;
|
||||
if (p > 0 || self.flags.hash) len += self.out('.')!;
|
||||
len += self.print_char(s++[0])!;
|
||||
if (p > 0 || self.flags.hash) len += self.print_char('.')!;
|
||||
}
|
||||
len += self.out_chars(s[:math::min(buf + 9 - s, p)])!;
|
||||
p -= buf + 9 - s;
|
||||
}
|
||||
len += self.pad('0', p + 18, 18)!;
|
||||
len += formatter_pad(self, '0', p + 18, 18)!;
|
||||
len += self.out_chars(estr[:ebuf - estr])!;
|
||||
}
|
||||
|
||||
if (self.flags.left) len += self.pad(' ', self.width, pl + l)!;
|
||||
if (self.flags.left) len += formatter_pad(self, ' ', self.width, pl + l)!;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
fn usz? formatter_out_str_pad(Formatter* self, any arg) @private @inline
|
||||
{
|
||||
usz total;
|
||||
if (self.width && !self.flags.left)
|
||||
{
|
||||
OutputFn out_fn = self.out_fn;
|
||||
self.out_fn = (OutputFn)&out_null_fn;
|
||||
usz len = formatter_out_str(self, arg)!;
|
||||
self.out_fn = out_fn;
|
||||
total += formatter_pad(self, ' ', self.width, (isz)len)!;
|
||||
}
|
||||
usz len = formatter_out_str(self, arg)!;
|
||||
total += len;
|
||||
if (self.flags.left)
|
||||
{
|
||||
total += formatter_pad(self, ' ', self.width, (isz)len)!;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
const char[201] DIGIT_PAIRS @private =
|
||||
"00102030405060708090"
|
||||
"01112131415161718191"
|
||||
@@ -541,19 +576,18 @@ const char[201] DIGIT_PAIRS @private =
|
||||
"08182838485868788898"
|
||||
"09192939495969798999";
|
||||
|
||||
|
||||
fn usz? Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
|
||||
fn usz? formatter_ntoa(Formatter* f, uint128 value, bool negative, uint base) @private
|
||||
{
|
||||
char[PRINTF_NTOA_BUFFER_SIZE] buf @noinit;
|
||||
usz len;
|
||||
|
||||
// no hash for 0 values
|
||||
if (!value) self.flags.hash = false;
|
||||
if (!value) f.flags.hash = false;
|
||||
|
||||
// write if precision != 0 or value is != 0
|
||||
if (!self.flags.precision || value)
|
||||
if (!f.flags.precision || value)
|
||||
{
|
||||
char past_10 = (self.flags.uppercase ? 'A' : 'a') - 10;
|
||||
char past_10 = (f.flags.uppercase ? 'A' : 'a') - 10;
|
||||
switch (base)
|
||||
{
|
||||
case 2:
|
||||
@@ -574,10 +608,10 @@ fn usz? Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
|
||||
while (value >= 10)
|
||||
{
|
||||
if (len + 1 >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
char digit = (char)(value % 100);
|
||||
char digit = (char)(value % 100U);
|
||||
buf[len:2] = DIGIT_PAIRS[2 * digit:2];
|
||||
len += 2;
|
||||
value /= 100;
|
||||
value /= 100U;
|
||||
}
|
||||
if (value > 0)
|
||||
{
|
||||
@@ -593,33 +627,32 @@ fn usz? Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
|
||||
value >>= 4;
|
||||
}
|
||||
while (value);
|
||||
case 8:
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
buf[len++] = '0' + (char)value & 0x7;
|
||||
value >>= 3;
|
||||
}
|
||||
while (value);
|
||||
default:
|
||||
unreachable();
|
||||
case 8:
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
buf[len++] = '0' + (char)value & 0x7;
|
||||
value >>= 3;
|
||||
} while (value);
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
return self.ntoa_format((String)buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
|
||||
return formatter_ntoa_format(f, (String)buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
|
||||
}
|
||||
|
||||
fn usz? Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint base) @private
|
||||
fn usz? formatter_ntoa_format(Formatter* f, String buf, usz len, bool negative, uint base) @private
|
||||
{
|
||||
// pad leading zeros
|
||||
if (!self.flags.left)
|
||||
if (!f.flags.left)
|
||||
{
|
||||
if (self.width && self.flags.zeropad && (negative || self.flags.plus || self.flags.space)) self.width--;
|
||||
while (len < self.prec)
|
||||
if (f.width && f.flags.zeropad && (negative || f.flags.plus || f.flags.space)) f.width--;
|
||||
while (len < f.prec)
|
||||
{
|
||||
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
while (self.flags.zeropad && len < self.width)
|
||||
while (f.flags.zeropad && len < f.width)
|
||||
{
|
||||
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
buf[len++] = '0';
|
||||
@@ -627,9 +660,9 @@ fn usz? Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
|
||||
}
|
||||
|
||||
// handle hash
|
||||
if (self.flags.hash && base != 10)
|
||||
if (f.flags.hash && base != 10)
|
||||
{
|
||||
if (!self.flags.precision && len && len == self.prec && len == self.width)
|
||||
if (!f.flags.precision && len && len == f.prec && len == f.width)
|
||||
{
|
||||
len--;
|
||||
if (len) len--;
|
||||
@@ -640,11 +673,11 @@ fn usz? Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
|
||||
switch (base)
|
||||
{
|
||||
case 16:
|
||||
buf[len++] = self.flags.uppercase ? 'X' : 'x';
|
||||
buf[len++] = f.flags.uppercase ? 'X' : 'x';
|
||||
case 8:
|
||||
buf[len++] = self.flags.uppercase ? 'O' : 'o';
|
||||
buf[len++] = f.flags.uppercase ? 'O' : 'o';
|
||||
case 2:
|
||||
buf[len++] = self.flags.uppercase ? 'B' : 'b';
|
||||
buf[len++] = f.flags.uppercase ? 'B' : 'b';
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
@@ -657,78 +690,78 @@ fn usz? Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
|
||||
case negative:
|
||||
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
buf[len++] = '-';
|
||||
case self.flags.plus:
|
||||
case f.flags.plus:
|
||||
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
buf[len++] = '+';
|
||||
case self.flags.space:
|
||||
case f.flags.space:
|
||||
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
|
||||
buf[len++] = ' ';
|
||||
}
|
||||
if (len) self.out_reverse(buf[:len])!;
|
||||
if (len) formatter_out_reverse(f, buf[:len])!;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
fn usz? Formatter.ntoa_any(&self, any arg, uint base) @private
|
||||
fn usz? formatter_ntoa_any(Formatter* f, any arg, uint base) @private
|
||||
{
|
||||
bool is_neg;
|
||||
return self.ntoa(int_from_any(arg, &is_neg)!!, is_neg, base) @inline;
|
||||
return formatter_ntoa(f, int_from_any(arg, &is_neg)!!, is_neg, base) @inline;
|
||||
}
|
||||
|
||||
fn usz? Formatter.out_char(&self, any arg) @private
|
||||
fn usz? formatter_out_char(Formatter* f, any arg) @private
|
||||
{
|
||||
if (!arg.type.kindof.is_int())
|
||||
{
|
||||
return self.out_substr("<NOT CHAR>");
|
||||
return formatter_out_substr(f, "<NOT CHAR>");
|
||||
}
|
||||
usz len = 1;
|
||||
// pre padding
|
||||
if (!self.flags.left)
|
||||
{
|
||||
len += self.pad(' ', self.width, len)!;
|
||||
}
|
||||
if (!f.flags.left)
|
||||
{
|
||||
len += formatter_pad(f, ' ', f.width, len)!;
|
||||
}
|
||||
|
||||
// char output
|
||||
Char32 c = types::any_to_int(arg, uint) ?? 0xFFFD;
|
||||
switch (true)
|
||||
{
|
||||
case c < 0x7f:
|
||||
self.out((char)c)!;
|
||||
f.print_char((char)c)!;
|
||||
case c < 0x7ff:
|
||||
self.out((char)(0xC0 | c >> 6))!;
|
||||
self.out((char)(0x80 | (c & 0x3F)))!;
|
||||
f.print_char((char)(0xC0 | c >> 6))!;
|
||||
f.print_char((char)(0x80 | (c & 0x3F)))!;
|
||||
case c < 0xffff:
|
||||
self.out((char)(0xE0 | c >> 12))!;
|
||||
self.out((char)(0x80 | (c >> 6 & 0x3F)))!;
|
||||
self.out((char)(0x80 | (c & 0x3F)))!;
|
||||
f.print_char((char)(0xE0 | c >> 12))!;
|
||||
f.print_char((char)(0x80 | (c >> 6 & 0x3F)))!;
|
||||
f.print_char((char)(0x80 | (c & 0x3F)))!;
|
||||
default:
|
||||
self.out((char)(0xF0 | c >> 18))!;
|
||||
self.out((char)(0x80 | (c >> 12 & 0x3F)))!;
|
||||
self.out((char)(0x80 | (c >> 6 & 0x3F)))!;
|
||||
self.out((char)(0x80 | (c & 0x3F)))!;
|
||||
f.print_char((char)(0xF0 | c >> 18))!;
|
||||
f.print_char((char)(0x80 | (c >> 12 & 0x3F)))!;
|
||||
f.print_char((char)(0x80 | (c >> 6 & 0x3F)))!;
|
||||
f.print_char((char)(0x80 | (c & 0x3F)))!;
|
||||
}
|
||||
if (self.flags.left)
|
||||
{
|
||||
len += self.pad(' ', self.width, len)!;
|
||||
if (f.flags.left)
|
||||
{
|
||||
len += formatter_pad(f, ' ', f.width, len)!;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
fn usz? Formatter.out_reverse(&self, char[] buf) @private
|
||||
fn usz? formatter_out_reverse(Formatter* f, char[] buf) @private
|
||||
{
|
||||
usz n;
|
||||
usz len = buf.len;
|
||||
// pad spaces up to given width
|
||||
if (!self.flags.zeropad && !self.flags.left)
|
||||
if (!f.flags.zeropad && !f.flags.left)
|
||||
{
|
||||
n += self.pad(' ', self.width, len)!;
|
||||
n += formatter_pad(f, ' ', f.width, len)!;
|
||||
}
|
||||
// reverse string
|
||||
while (len) n += self.out(buf[--len])!;
|
||||
while (len) n += f.print_char(buf[--len])!;
|
||||
|
||||
// append pad spaces up to given width
|
||||
n += self.adjust(n)!;
|
||||
n += formatter_adjust(f, n)!;
|
||||
return n;
|
||||
}
|
||||
|
||||
@@ -748,3 +781,197 @@ fn int? printf_parse_format_field(
|
||||
uint? intval = types::any_to_int(val, int);
|
||||
return intval ?? BAD_FORMAT~;
|
||||
}
|
||||
|
||||
fn usz? formatter_out_hex_buffer(Formatter* self, any arg) @private @inline
|
||||
{
|
||||
char[] out @noinit;
|
||||
switch (arg.type)
|
||||
{
|
||||
case char[]:
|
||||
case ichar[]:
|
||||
out = *(char[]*)arg;
|
||||
default:
|
||||
if (arg.type.kindof == ARRAY && (arg.type.inner == char.typeid || arg.type.inner == ichar.typeid))
|
||||
{
|
||||
out = ((char*)arg.ptr)[:arg.type.sizeof];
|
||||
break;
|
||||
}
|
||||
if (arg.type.kindof == POINTER)
|
||||
{
|
||||
// Maybe there is a more idiomatic way here
|
||||
out = ((*(char**)arg.ptr))[:arg.type.inner.sizeof];
|
||||
break;
|
||||
}
|
||||
return formatter_out_substr(self, "<INVALID>");
|
||||
}
|
||||
usz len = out.len * 2;
|
||||
usz total;
|
||||
if (self.flags.left)
|
||||
{
|
||||
total += print_hex_chars(self, out, self.flags.uppercase)!;
|
||||
total += formatter_pad(self, ' ', self.width, (isz)total)!;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self.width) total += formatter_pad(self, ' ', self.width, (isz)len)!;
|
||||
total += print_hex_chars(self, out, self.flags.uppercase)!;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
fn usz? formatter_out_unknown(Formatter* self, String category, any arg) @private
|
||||
{
|
||||
return formatter_out_substr(self, "<")
|
||||
+ formatter_out_substr(self, category)
|
||||
+ formatter_out_substr(self, " type:")
|
||||
+ formatter_ntoa(self, (iptr)arg.type, false, 16)
|
||||
+ formatter_out_substr(self, ", addr:")
|
||||
+ formatter_ntoa(self, (iptr)arg.ptr, false, 16)
|
||||
+ formatter_out_substr(self, ">");
|
||||
}
|
||||
|
||||
fn usz? formatter_out_collection(Formatter* self, any arg, String open, String close) @private
|
||||
{
|
||||
typeid inner = arg.type.inner;
|
||||
if (inner == void.typeid) inner = char.typeid;
|
||||
usz size = inner.sizeof;
|
||||
|
||||
usz alen;
|
||||
void* data_ptr;
|
||||
if (arg.type.kindof == SLICE)
|
||||
{
|
||||
String* temp = arg.ptr;
|
||||
data_ptr = temp.ptr;
|
||||
alen = temp.len;
|
||||
}
|
||||
else
|
||||
{
|
||||
data_ptr = arg.ptr;
|
||||
alen = arg.type.len;
|
||||
}
|
||||
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
|
||||
usz len = formatter_out_substr(self, open)!;
|
||||
for (usz i = 0; i < alen; i++)
|
||||
{
|
||||
if (i != 0) len += formatter_out_substr(self, ", ")!;
|
||||
len += formatter_out_str(self, any_make(data_ptr, inner))!;
|
||||
data_ptr += size;
|
||||
}
|
||||
len += formatter_out_substr(self, close)!;
|
||||
return len;
|
||||
}
|
||||
|
||||
fn usz? formatter_out_str(Formatter* self, any arg) @private
|
||||
{
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case VOID:
|
||||
return formatter_out_substr(self, "void");
|
||||
case FAULT:
|
||||
fault f = *(fault*)arg.ptr;
|
||||
return formatter_out_substr(self, f ? f.nameof : "(empty-fault)");
|
||||
case INTERFACE:
|
||||
any a = *(any*)arg;
|
||||
return a ? formatter_out_str(self, a) : formatter_out_substr(self, "(empty-interface)");
|
||||
case ANY:
|
||||
any a = *(any*)arg;
|
||||
return a ? formatter_out_str(self, a) : formatter_out_substr(self, "(empty-any)");
|
||||
case OPTIONAL:
|
||||
unreachable();
|
||||
case SIGNED_INT:
|
||||
case UNSIGNED_INT:
|
||||
case FLOAT:
|
||||
case FUNC:
|
||||
case POINTER:
|
||||
PrintFlags flags = self.flags;
|
||||
uint width = self.width;
|
||||
defer
|
||||
{
|
||||
self.flags = flags;
|
||||
self.width = width;
|
||||
}
|
||||
self.flags = {};
|
||||
self.width = 0;
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case SIGNED_INT:
|
||||
case UNSIGNED_INT:
|
||||
return formatter_ntoa_any(self, arg, 10) ?? formatter_out_substr(self, "<INVALID>");
|
||||
case FLOAT:
|
||||
return formatter_ftoa(self, float_from_any(arg)) ?? formatter_out_substr(self, "ERR");
|
||||
case FUNC:
|
||||
case POINTER:
|
||||
if (arg.type.kindof == POINTER && arg.type.inner != void.typeid)
|
||||
{
|
||||
void** pointer = arg.ptr;
|
||||
any deref = any_make(*pointer, arg.type.inner);
|
||||
usz? n = self.print_with_function((Printable)deref);
|
||||
if (try n) return n;
|
||||
if (@catch(n) != NOT_FOUND) n!;
|
||||
}
|
||||
return formatter_out_substr(self, "0x")! + formatter_ntoa_any(self, arg, 16);
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
case BOOL:
|
||||
return formatter_out_substr(self, *(bool*)arg.ptr ? "true" : "false");
|
||||
default:
|
||||
}
|
||||
usz? n = self.print_with_function((Printable)arg);
|
||||
if (try n) return n;
|
||||
if (@catch(n) != NOT_FOUND) n!;
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case TYPEID:
|
||||
return formatter_out_substr(self, "typeid[")! + formatter_ntoa(self, (iptr)*(typeid*)arg, false, 16)! + formatter_out_substr(self, "]")!;
|
||||
case ENUM:
|
||||
usz i = types::any_to_enum_ordinal(arg, usz)!!;
|
||||
assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i);
|
||||
return formatter_out_substr(self, arg.type.names[i]);
|
||||
case STRUCT:
|
||||
return formatter_out_unknown(self, "struct", arg);
|
||||
case UNION:
|
||||
return formatter_out_unknown(self, "union", arg);
|
||||
case BITSTRUCT:
|
||||
return formatter_out_unknown(self, "bitstruct", arg);
|
||||
case CONSTDEF:
|
||||
case TYPEDEF:
|
||||
if (arg.type == String.typeid)
|
||||
{
|
||||
return formatter_out_substr(self, *(String*)arg);
|
||||
}
|
||||
if (arg.type == ZString.typeid)
|
||||
{
|
||||
return formatter_out_substr(self, *(ZString*)arg ? ((ZString*)arg).str_view() : "(null)");
|
||||
}
|
||||
if (arg.type == DString.typeid)
|
||||
{
|
||||
return formatter_out_substr(self, *(DString*)arg ? ((DString*)arg).str_view() : "(null)");
|
||||
}
|
||||
return formatter_out_str(self, arg.as_inner());
|
||||
case ARRAY:
|
||||
return formatter_out_collection(self, arg, "[", "]");
|
||||
case VECTOR:
|
||||
return formatter_out_collection(self, arg, "[<", ">]");
|
||||
case SLICE:
|
||||
return formatter_out_collection(self, arg, "[", "]");
|
||||
case ANY:
|
||||
case INTERFACE:
|
||||
unreachable("Already handled");
|
||||
default:
|
||||
}
|
||||
return formatter_out_substr(self, "Invalid type");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,14 @@ enum Seek
|
||||
END
|
||||
}
|
||||
|
||||
enum SeekOrigin
|
||||
{
|
||||
FROM_START,
|
||||
FROM_CURSOR,
|
||||
FROM_END
|
||||
}
|
||||
|
||||
|
||||
faultdef
|
||||
ALREADY_EXISTS,
|
||||
BUSY,
|
||||
@@ -50,31 +58,17 @@ faultdef
|
||||
or to the end of the stream, whatever comes first.
|
||||
"\r" will be filtered from the String.
|
||||
|
||||
@param [&inout] allocator : "The allocator used to allocate the read string."
|
||||
@param stream : `The stream to read from.`
|
||||
@param limit : `Optionally limits the amount of bytes to read in a single line. Will NOT discard the remaining line data.`
|
||||
@require @is_not_instream_if_ptr(stream) : "The value for 'stream' should have been passed as a pointer and not as a value, please add '&'."
|
||||
@require @is_instream(stream) : `Make sure that the stream is actually an InStream.`
|
||||
@param [inout] allocator : `the allocator to use.`
|
||||
@return `The string containing the data read.`
|
||||
*>
|
||||
macro String? readline(Allocator allocator, stream = io::stdin())
|
||||
macro String? readline(Allocator allocator, stream = io::stdin(), usz limit = 0)
|
||||
{
|
||||
return readline_impl{$typeof(stream)}(allocator, stream);
|
||||
}
|
||||
|
||||
fn String? readline_impl(Allocator allocator, Stream stream) <Stream> @private
|
||||
{
|
||||
if (allocator == tmem)
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(256);
|
||||
readline_to_stream(&str, stream)!;
|
||||
return str.str_view();
|
||||
}
|
||||
@pool()
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(256);
|
||||
readline_to_stream(&str, stream)!;
|
||||
return str.copy_str(allocator);
|
||||
};
|
||||
return readline_impl{$typeof(stream)}(allocator, stream, limit);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -82,13 +76,30 @@ fn String? readline_impl(Allocator allocator, Stream stream) <Stream> @private
|
||||
on the temporary allocator and does not need to be freed.
|
||||
|
||||
@param stream : `The stream to read from.`
|
||||
@param limit : `Optionally limits the amount of bytes to read in a single line. Will NOT discard the remaining line data.`
|
||||
@require @is_not_instream_if_ptr(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.`
|
||||
@return `The temporary string containing the data read.`
|
||||
*>
|
||||
macro String? treadline(stream = io::stdin())
|
||||
macro String? treadline(stream = io::stdin(), usz limit = 0)
|
||||
{
|
||||
return readline(tmem, stream) @inline;
|
||||
return readline(tmem, stream, limit) @inline;
|
||||
}
|
||||
|
||||
fn String? readline_impl(Allocator allocator, Stream stream, usz limit) <Stream> @private
|
||||
{
|
||||
if (allocator == tmem)
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(256);
|
||||
readline_to_stream(&str, stream, limit)!;
|
||||
return str.str_view();
|
||||
}
|
||||
@pool()
|
||||
{
|
||||
DString str = dstring::temp_with_capacity(256);
|
||||
readline_to_stream(&str, stream, limit)!;
|
||||
return str.copy_str(allocator);
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -96,18 +107,19 @@ macro String? treadline(stream = io::stdin())
|
||||
|
||||
@param out_stream : `The stream to write to`
|
||||
@param in_stream : `The stream to read from.`
|
||||
@param limit : `Optionally limits the byte-length of the allocated output string.`
|
||||
@require @is_not_instream_if_ptr(in_stream) : "The value for 'in_stream' should have been passed as a pointer and not as a value, please add '&'."
|
||||
@require @is_not_outstream_if_ptr(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`
|
||||
@return `The number of bytes written. When a 'limit' is provided and the return value is equal to it, there may be more to read on the current line.`
|
||||
*>
|
||||
macro usz? readline_to_stream(out_stream, in_stream = io::stdin())
|
||||
macro usz? readline_to_stream(out_stream, in_stream = io::stdin(), usz limit = 0)
|
||||
{
|
||||
return readline_to_stream_impl{$typeof(in_stream), $typeof(out_stream)}(out_stream, in_stream);
|
||||
return readline_to_stream_impl{$typeof(in_stream), $typeof(out_stream)}(out_stream, in_stream, limit);
|
||||
}
|
||||
|
||||
fn usz? readline_to_stream_impl(OStream out_stream, IStream in_stream) <IStream, OStream> @private
|
||||
fn usz? readline_to_stream_impl(OStream out_stream, IStream in_stream, usz limit) <IStream, OStream> @private
|
||||
{
|
||||
bool $is_stream = IStream == InStream;
|
||||
$if $is_stream:
|
||||
@@ -132,7 +144,7 @@ fn usz? readline_to_stream_impl(OStream out_stream, IStream in_stream) <IStream,
|
||||
$endif
|
||||
len++;
|
||||
}
|
||||
while (1)
|
||||
while (!limit || len < limit)
|
||||
{
|
||||
$if $is_stream:
|
||||
char? c = func((void*)in_stream);
|
||||
@@ -354,7 +366,7 @@ fn usz? printfn(String format, args...) @format(0) @maydiscard
|
||||
PutcharBuffer buff;
|
||||
formatter.init(&out_putchar_buffer_fn, &buff);
|
||||
usz? len = formatter.vprintf(format, args);
|
||||
formatter.out('\n')!;
|
||||
formatter.print_char('\n')!;
|
||||
write_putchar_buffer(&buff, true)!;
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
@@ -5,22 +5,22 @@ import libc;
|
||||
@require mode.len > 0
|
||||
@require filename.len > 0
|
||||
*>
|
||||
fn void*? native_fopen(String filename, String mode) @inline => @pool()
|
||||
fn void*? native_fopen(String filename, String mode) @inline => @stack_mem(256; Allocator smem)
|
||||
{
|
||||
$if env::WIN32:
|
||||
void* file = libc::_wfopen(filename.to_temp_wstring(), mode.to_temp_wstring())!;
|
||||
void* file = libc::_wfopen(filename.to_wstring(smem), mode.to_wstring(smem))!;
|
||||
$else
|
||||
void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy());
|
||||
void* file = libc::fopen(filename.zstr_copy(smem), mode.zstr_copy(smem));
|
||||
$endif
|
||||
return file ?: file_open_errno()~;
|
||||
return file ?: file_open_errno()~;
|
||||
}
|
||||
|
||||
fn void? native_remove(String filename) => @pool()
|
||||
fn void? native_remove(String filename) => @stack_mem(256; Allocator smem)
|
||||
{
|
||||
$if env::WIN32:
|
||||
CInt result = libc::_wremove(filename.to_temp_wstring())!;
|
||||
CInt result = libc::_wremove(filename.to_wstring(smem))!;
|
||||
$else
|
||||
CInt result = libc::remove(filename.zstr_tcopy());
|
||||
CInt result = libc::remove(filename.zstr_copy(smem));
|
||||
$endif
|
||||
if (result)
|
||||
{
|
||||
@@ -39,26 +39,26 @@ fn void? native_remove(String filename) => @pool()
|
||||
@require mode.len > 0
|
||||
@require filename.len > 0
|
||||
*>
|
||||
fn void*? native_freopen(void* file, String filename, String mode) @inline => @pool()
|
||||
fn void*? native_freopen(void* file, String filename, String mode) @inline => @stack_mem(256; Allocator smem)
|
||||
{
|
||||
$if env::WIN32:
|
||||
file = libc::_wfreopen(filename.to_temp_wstring(), mode.to_temp_wstring(), file)!;
|
||||
file = libc::_wfreopen(filename.to_wstring(smem), mode.to_wstring(smem), file)!;
|
||||
$else
|
||||
file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file);
|
||||
file = libc::freopen(filename.zstr_copy(smem), mode.zstr_copy(smem), file);
|
||||
$endif
|
||||
return file ?: file_open_errno()~;
|
||||
}
|
||||
|
||||
fn void? native_fseek(void* file, isz offset, Seek seek_mode) @inline
|
||||
fn void? native_fseek(void* file, long offset, SeekOrigin seek_mode) @inline
|
||||
{
|
||||
if (libc::fseek(file, (SeekIndex)offset, seek_mode.ordinal)) return file_seek_errno()~;
|
||||
}
|
||||
|
||||
|
||||
fn usz? native_ftell(CFile file) @inline
|
||||
fn long? native_ftell(CFile file) @inline
|
||||
{
|
||||
long index = libc::ftell(file);
|
||||
return index >= 0 ? (usz)index : file_seek_errno()~;
|
||||
return index >= 0 ? index : file_seek_errno()~;
|
||||
}
|
||||
|
||||
fn usz? native_fwrite(CFile file, char[] buffer) @inline
|
||||
@@ -76,6 +76,11 @@ fn usz? native_fread(CFile file, char[] buffer) @inline
|
||||
return libc::fread(buffer.ptr, 1, buffer.len, file);
|
||||
}
|
||||
|
||||
fn void? native_fflush(CFile file) @inline @maydiscard
|
||||
{
|
||||
if (libc::fflush(file) != 0) return io::GENERAL_ERROR~;
|
||||
}
|
||||
|
||||
macro fault file_open_errno() @local
|
||||
{
|
||||
switch (libc::errno())
|
||||
@@ -123,3 +128,22 @@ macro fault file_seek_errno() @local
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct Utimbuf
|
||||
{
|
||||
Time_t actime;
|
||||
Time_t modtime;
|
||||
}
|
||||
|
||||
extern fn int utime(char* filename, void* times) @if(!env::WIN32);
|
||||
extern fn int _wutime(WChar* filename, void* times) @if(env::WIN32);
|
||||
|
||||
fn void? native_set_modified_time(String filename, libc::Time_t time) => @stack_mem(256; Allocator smem)
|
||||
{
|
||||
Utimbuf times = { time, time };
|
||||
$if env::WIN32:
|
||||
if (_wutime(filename.to_wstring(smem)!, ×)) return io::GENERAL_ERROR~;
|
||||
$else
|
||||
if (utime(filename.zstr_copy(smem), ×)) return io::GENERAL_ERROR~;
|
||||
$endif
|
||||
}
|
||||
|
||||
@@ -4,12 +4,14 @@ import libc;
|
||||
alias FopenFn = fn void*?(String, String);
|
||||
alias FreopenFn = fn void*?(void*, String, String);
|
||||
alias FcloseFn = fn void?(void*);
|
||||
alias FseekFn = fn void?(void*, isz, Seek);
|
||||
alias FtellFn = fn usz?(void*);
|
||||
alias FseekFn = fn void?(void*, long, SeekOrigin);
|
||||
alias FtellFn = fn long?(void*);
|
||||
alias FwriteFn = fn usz?(void*, char[] buffer);
|
||||
alias FreadFn = fn usz?(void*, char[] buffer);
|
||||
alias FflushFn = fn void?(void*);
|
||||
alias RemoveFn = fn void?(String);
|
||||
alias FputcFn = fn void?(int, void*);
|
||||
alias SetModifiedTimeFn = fn void?(String, libc::Time_t);
|
||||
|
||||
FopenFn native_fopen_fn @weak @if(!$defined(native_fopen_fn));
|
||||
FcloseFn native_fclose_fn @weak @if(!$defined(native_fclose_fn));
|
||||
@@ -18,8 +20,10 @@ FseekFn native_fseek_fn @weak @if(!$defined(native_fseek_fn));
|
||||
FtellFn native_ftell_fn @weak @if(!$defined(native_ftell_fn));
|
||||
FwriteFn native_fwrite_fn @weak @if(!$defined(native_fwrite_fn));
|
||||
FreadFn native_fread_fn @weak @if(!$defined(native_fread_fn));
|
||||
FflushFn native_fflush_fn @weak @if(!$defined(native_fflush_fn));
|
||||
RemoveFn native_remove_fn @weak @if(!$defined(native_remove_fn));
|
||||
FputcFn native_fputc_fn @weak @if(!$defined(native_fputc_fn));
|
||||
SetModifiedTimeFn native_set_modified_time_fn @weak @if(!$defined(native_set_modified_time_fn));
|
||||
|
||||
<*
|
||||
@require mode.len > 0
|
||||
@@ -52,13 +56,13 @@ fn void*? native_freopen(void* file, String filename, String mode) @inline
|
||||
return io::UNSUPPORTED_OPERATION~;
|
||||
}
|
||||
|
||||
fn void? native_fseek(void* file, isz offset, Seek seek_mode) @inline
|
||||
fn void? native_fseek(void* file, long offset, SeekOrigin whence) @inline
|
||||
{
|
||||
if (native_fseek_fn) return native_fseek_fn(file, offset, seek_mode);
|
||||
if (native_fseek_fn) return native_fseek_fn(file, offset, whence);
|
||||
return io::UNSUPPORTED_OPERATION~;
|
||||
}
|
||||
|
||||
fn usz? native_ftell(CFile file) @inline
|
||||
fn ulong? native_ftell(CFile file) @inline
|
||||
{
|
||||
if (native_ftell_fn) return native_ftell_fn(file);
|
||||
return io::UNSUPPORTED_OPERATION~;
|
||||
@@ -76,8 +80,20 @@ fn usz? native_fread(CFile file, char[] buffer) @inline
|
||||
return io::UNSUPPORTED_OPERATION~;
|
||||
}
|
||||
|
||||
fn void? native_fflush(CFile file) @inline @maydiscard
|
||||
{
|
||||
if (native_fflush_fn) return native_fflush_fn(file);
|
||||
return io::UNSUPPORTED_OPERATION~;
|
||||
}
|
||||
|
||||
fn void? native_fputc(CInt c, CFile stream) @inline
|
||||
{
|
||||
if (native_fputc_fn) return native_fputc_fn(c, stream);
|
||||
return io::UNSUPPORTED_OPERATION~;
|
||||
}
|
||||
|
||||
fn void? native_set_modified_time(String filename, libc::Time_t time) @inline
|
||||
{
|
||||
if (native_set_modified_time_fn) return native_set_modified_time_fn(filename, time);
|
||||
return io::UNSUPPORTED_OPERATION~;
|
||||
}
|
||||
|
||||
@@ -47,14 +47,15 @@ fn usz? native_file_size(String path) @if(env::WIN32) => @pool()
|
||||
return (usz)size.quadPart;
|
||||
}
|
||||
|
||||
fn usz? native_file_size(String path) @if(!env::WIN32 && !env::DARWIN && !env::LINUX && !env::ANDROID && !env::BSD_FAMILY)
|
||||
fn ulong? native_file_size(String path) @if(!env::WIN32 && !env::DARWIN && !env::LINUX && !env::ANDROID && !env::BSD_FAMILY)
|
||||
{
|
||||
File f = file::open(path, "r")!;
|
||||
defer (void)f.close();
|
||||
return f.seek(0, Seek.END)!;
|
||||
f.set_cursor(0, FROM_END)!;
|
||||
return f.cursor();
|
||||
}
|
||||
|
||||
fn usz? native_file_size(String path) @if(env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY)
|
||||
fn ulong? native_file_size(String path) @if(env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY)
|
||||
{
|
||||
Stat stat;
|
||||
native_stat(&stat, path)!;
|
||||
|
||||
@@ -36,7 +36,7 @@ fn Path? cwd(Allocator allocator)
|
||||
|
||||
fn bool is_dir(Path path) => os::native_is_dir(path.str_view());
|
||||
fn bool is_file(Path path) => os::native_is_file(path.str_view());
|
||||
fn usz? file_size(Path path) => os::native_file_size(path.str_view());
|
||||
fn ulong? file_size(Path path) => os::native_file_size(path.str_view());
|
||||
fn bool exists(Path path) => os::native_file_or_dir_exists(path.str_view());
|
||||
fn Path? tcwd() => cwd(tmem) @inline;
|
||||
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
module std::io;
|
||||
import std::math;
|
||||
|
||||
|
||||
|
||||
alias SetCursorFn = fn void?(void*, long offset, SeekOrigin whence = START);
|
||||
|
||||
|
||||
interface InStream
|
||||
{
|
||||
fn void? close() @optional;
|
||||
fn long? cursor() @optional;
|
||||
fn void? set_cursor(long offset, SeekOrigin whence = FROM_START) @optional;
|
||||
fn usz? seek(isz offset, Seek seek) @optional;
|
||||
fn usz len() @optional;
|
||||
fn usz? available() @optional;
|
||||
fn ulong? size() @optional;
|
||||
fn ulong? available() @optional;
|
||||
fn usz? read(char[] buffer);
|
||||
fn char? read_byte();
|
||||
fn usz? write_to(OutStream out) @optional;
|
||||
@@ -24,15 +32,23 @@ interface OutStream
|
||||
fn usz? read_to(InStream in) @optional;
|
||||
}
|
||||
|
||||
fn usz? available(InStream s)
|
||||
fn ulong? available(InStream s)
|
||||
{
|
||||
if (&s.available) return s.available();
|
||||
if (&s.set_cursor && &s.cursor)
|
||||
{
|
||||
long curr = s.cursor()!;
|
||||
s.set_cursor(0, FROM_END)!;
|
||||
ulong len = s.cursor()!;
|
||||
s.set_cursor(curr)!;
|
||||
return len - curr;
|
||||
}
|
||||
if (&s.seek)
|
||||
{
|
||||
usz curr = s.seek(0, Seek.CURSOR)!;
|
||||
usz len = s.seek(0, Seek.END)!;
|
||||
s.seek(curr, Seek.SET)!;
|
||||
return len - curr;
|
||||
return (ulong)len - (ulong)curr;
|
||||
}
|
||||
return io::UNSUPPORTED_OPERATION~;
|
||||
}
|
||||
@@ -177,6 +193,11 @@ macro usz? write_using_write_byte(s, char[] bytes)
|
||||
|
||||
macro void? pushback_using_seek(s)
|
||||
{
|
||||
if (&s.set_cursor)
|
||||
{
|
||||
s.set_cursor(-1, FROM_CURSOR)!;
|
||||
return;
|
||||
}
|
||||
s.seek(-1, CURSOR)!;
|
||||
}
|
||||
|
||||
@@ -407,11 +428,11 @@ macro ulong? read_le_ulong(stream)
|
||||
{
|
||||
ulong val = (ulong)stream.read_byte()!;
|
||||
val += (ulong)stream.read_byte()! << 8;
|
||||
val += (ulong)stream.read_byte()! << 16;
|
||||
val += (ulong)stream.read_byte()! << 16;
|
||||
val += (ulong)stream.read_byte()! << 24;
|
||||
val += (ulong)stream.read_byte()! << 32;
|
||||
val += (ulong)stream.read_byte()! << 40;
|
||||
val += (ulong)stream.read_byte()! << 48;
|
||||
val += (ulong)stream.read_byte()! << 48;
|
||||
return val + (ulong)stream.read_byte()! << 56;
|
||||
}
|
||||
|
||||
@@ -621,24 +642,30 @@ macro void? skip(stream, usz bytes)
|
||||
{
|
||||
if (!bytes) return;
|
||||
$switch:
|
||||
$case !$defined(stream.seek):
|
||||
for (usz i = 0; i < bytes; i++)
|
||||
{
|
||||
stream.read()!;
|
||||
}
|
||||
return;
|
||||
$case $typeof(stream) == InStream:
|
||||
if (!&stream.seek)
|
||||
{
|
||||
for (usz i = 0; i < bytes; i++)
|
||||
{
|
||||
stream.read()!;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!&stream.seek && !&stream.set_cursor)
|
||||
{
|
||||
for (usz i = 0; i < bytes; i++)
|
||||
{
|
||||
stream.read()!;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!&stream.set_cursor)
|
||||
{
|
||||
stream.seek(bytes, CURSOR)!;
|
||||
return;
|
||||
}
|
||||
stream.set_cursor(bytes, FROM_CURSOR)!;
|
||||
$case $defined(stream.set_cursor):
|
||||
stream.set_cursor(bytes, FROM_CURSOR)!;
|
||||
$case $defined(stream.seek):
|
||||
stream.seek(bytes, CURSOR)!;
|
||||
$default:
|
||||
stream.seek(bytes, CURSOR)!;
|
||||
for (usz i = 0; i < bytes; i++)
|
||||
{
|
||||
stream.read()!;
|
||||
}
|
||||
$endswitch
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ fn usz? ReadBuffer.read(&self, char[] bytes) @dynamic
|
||||
// Read directly into the input buffer.
|
||||
return self.wrapped_stream.read(bytes)!;
|
||||
}
|
||||
self.refill()!;
|
||||
readbuffer_refill(self)!;
|
||||
}
|
||||
usz n = min(self.write_idx - self.read_idx, bytes.len);
|
||||
bytes[:n] = self.bytes[self.read_idx:n];
|
||||
@@ -48,14 +48,14 @@ fn usz? ReadBuffer.read(&self, char[] bytes) @dynamic
|
||||
|
||||
fn char? ReadBuffer.read_byte(&self) @dynamic
|
||||
{
|
||||
if (self.read_idx == self.write_idx) self.refill()!;
|
||||
if (self.read_idx == self.write_idx) readbuffer_refill(self)!;
|
||||
if (self.read_idx == self.write_idx) return io::EOF~;
|
||||
char c = self.bytes[self.read_idx];
|
||||
self.read_idx++;
|
||||
return c;
|
||||
}
|
||||
|
||||
fn void? ReadBuffer.refill(&self) @local @inline
|
||||
fn void? readbuffer_refill(ReadBuffer* self) @local @inline
|
||||
{
|
||||
self.read_idx = 0;
|
||||
self.write_idx = self.wrapped_stream.read(self.bytes)!;
|
||||
@@ -92,7 +92,7 @@ fn void? WriteBuffer.close(&self) @dynamic
|
||||
|
||||
fn void? WriteBuffer.flush(&self) @dynamic
|
||||
{
|
||||
self.write_pending()!;
|
||||
write_buffer_write_pending(self)!;
|
||||
if (&self.wrapped_stream.flush) self.wrapped_stream.flush()!;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ fn usz? WriteBuffer.write(&self, char[] bytes) @dynamic
|
||||
self.index += bytes.len;
|
||||
return bytes.len;
|
||||
}
|
||||
self.write_pending()!;
|
||||
write_buffer_write_pending(self)!;
|
||||
if (bytes.len >= self.bytes.len)
|
||||
{
|
||||
// Write directly to the stream.
|
||||
@@ -123,13 +123,13 @@ fn void? WriteBuffer.write_byte(&self, char c) @dynamic
|
||||
usz n = self.bytes.len - self.index;
|
||||
if (n == 0)
|
||||
{
|
||||
self.write_pending()!;
|
||||
write_buffer_write_pending(self)!;
|
||||
}
|
||||
self.bytes[self.index] = c;
|
||||
self.index += 1;
|
||||
}
|
||||
|
||||
fn void? WriteBuffer.write_pending(&self) @local
|
||||
fn void? write_buffer_write_pending(WriteBuffer* self) @local
|
||||
{
|
||||
self.index -= self.wrapped_stream.write(self.bytes[:self.index])!;
|
||||
if (self.index != 0) return INCOMPLETE_WRITE~;
|
||||
|
||||
@@ -104,28 +104,37 @@ fn void? ByteBuffer.pushback_byte(&self) @dynamic
|
||||
self.has_last = false;
|
||||
}
|
||||
|
||||
fn usz? ByteBuffer.seek(&self, isz offset, Seek seek) @dynamic
|
||||
fn long? ByteBuffer.cursor(&self) @dynamic
|
||||
{
|
||||
switch (seek)
|
||||
{
|
||||
case SET:
|
||||
if (offset < 0 || offset > self.write_idx) return INVALID_POSITION~;
|
||||
self.read_idx = offset;
|
||||
return offset;
|
||||
case CURSOR:
|
||||
if ((offset < 0 && self.read_idx < -offset) ||
|
||||
(offset > 0 && self.read_idx + offset > self.write_idx)) return INVALID_POSITION~;
|
||||
self.read_idx += offset;
|
||||
case END:
|
||||
if (offset < 0 || offset > self.write_idx) return INVALID_POSITION~;
|
||||
self.read_idx = self.write_idx - offset;
|
||||
}
|
||||
return self.read_idx;
|
||||
}
|
||||
|
||||
fn usz? ByteBuffer.available(&self) @inline @dynamic
|
||||
fn void? ByteBuffer.set_cursor(&self, long offset, SeekOrigin whence = FROM_START) @dynamic
|
||||
{
|
||||
return self.write_idx - self.read_idx;
|
||||
switch (whence)
|
||||
{
|
||||
case FROM_START:
|
||||
if (offset < 0 || offset > self.write_idx) return INVALID_POSITION~;
|
||||
self.read_idx = (usz)offset;
|
||||
case FROM_CURSOR:
|
||||
if ((offset < 0 && self.read_idx < -offset) ||
|
||||
(offset > 0 && self.read_idx + offset > self.write_idx)) return INVALID_POSITION~;
|
||||
self.read_idx += (usz)offset;
|
||||
case FROM_END:
|
||||
if (offset < 0 || offset > self.write_idx) return INVALID_POSITION~;
|
||||
self.read_idx = self.write_idx - (usz)offset;
|
||||
}
|
||||
}
|
||||
|
||||
fn usz? ByteBuffer.seek(&self, isz offset, Seek seek) @dynamic
|
||||
{
|
||||
self.set_cursor(offset, (SeekOrigin)seek.ordinal)!;
|
||||
return (usz)self.cursor();
|
||||
}
|
||||
|
||||
fn ulong? ByteBuffer.available(&self) @inline @dynamic
|
||||
{
|
||||
return (ulong)self.write_idx - self.read_idx;
|
||||
}
|
||||
|
||||
fn void ByteBuffer.grow(&self, usz n)
|
||||
|
||||
@@ -41,16 +41,26 @@ fn void? ByteReader.pushback_byte(&self) @dynamic
|
||||
|
||||
fn usz? ByteReader.seek(&self, isz offset, Seek seek) @dynamic
|
||||
{
|
||||
isz new_index;
|
||||
switch (seek)
|
||||
self.set_cursor((long)offset, (SeekOrigin)seek.ordinal)!;
|
||||
return (usz)self.cursor();
|
||||
}
|
||||
|
||||
fn long? ByteReader.cursor(&self) @dynamic
|
||||
{
|
||||
return self.index;
|
||||
}
|
||||
|
||||
fn void? ByteReader.set_cursor(&self, long offset, SeekOrigin whence = FROM_START) @dynamic
|
||||
{
|
||||
long new_index;
|
||||
switch (whence)
|
||||
{
|
||||
case SET: new_index = offset;
|
||||
case CURSOR: new_index = self.index + offset;
|
||||
case END: new_index = self.bytes.len + offset;
|
||||
case FROM_START: new_index = offset;
|
||||
case FROM_CURSOR: new_index = self.index + offset;
|
||||
case FROM_END: new_index = self.bytes.len + offset;
|
||||
}
|
||||
if (new_index < 0) return INVALID_POSITION~;
|
||||
self.index = new_index;
|
||||
return new_index;
|
||||
if (new_index < 0 || new_index > self.bytes.len) return INVALID_POSITION~;
|
||||
self.index = (usz)new_index;
|
||||
}
|
||||
|
||||
fn usz? ByteReader.write_to(&self, OutStream writer) @dynamic
|
||||
@@ -62,7 +72,7 @@ fn usz? ByteReader.write_to(&self, OutStream writer) @dynamic
|
||||
return written;
|
||||
}
|
||||
|
||||
fn usz? ByteReader.available(&self) @inline @dynamic
|
||||
fn ulong? ByteReader.available(&self) @inline @dynamic
|
||||
{
|
||||
return max(0, self.bytes.len - self.index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,9 +86,10 @@ fn usz? ByteWriter.read_from(&self, InStream reader) @dynamic
|
||||
usz start_index = self.index;
|
||||
if (&reader.available)
|
||||
{
|
||||
while (usz available = reader.available()!)
|
||||
while (ulong available = reader.available()!)
|
||||
{
|
||||
self.ensure_capacity(self.index + available)!;
|
||||
if (available > usz.max) return OUT_OF_SPACE~;
|
||||
self.ensure_capacity(self.index + (usz)available)!;
|
||||
usz read = reader.read(self.bytes[self.index..])!;
|
||||
self.index += read;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ fn char? LimitReader.read_byte(&self) @dynamic
|
||||
return self.wrapped_stream.read_byte();
|
||||
}
|
||||
|
||||
fn usz? LimitReader.available(&self) @inline @dynamic
|
||||
fn ulong? LimitReader.available(&self) @inline @dynamic
|
||||
{
|
||||
return self.limit;
|
||||
}
|
||||
@@ -49,7 +49,7 @@ fn void errno_set(Errno e)
|
||||
os::errno_set((int)e);
|
||||
}
|
||||
|
||||
typedef Errno = inline CInt;
|
||||
typedef Errno @constinit = inline CInt;
|
||||
alias TerminateFunction = fn void();
|
||||
alias CompareFunction = fn int(void*, void*);
|
||||
alias JmpBuf = uptr[$$JMP_BUF_SIZE];
|
||||
@@ -126,6 +126,7 @@ extern fn CInt memcmp(void* buf1, void* buf2, usz count);
|
||||
extern fn void* memcpy(void* dest, void* src, usz n);
|
||||
extern fn void* memmove(void* dest, void* src, usz n);
|
||||
extern fn void* memset(void* dest, CInt value, usz n);
|
||||
extern fn ZString mktemp(char* template) @cname(env::WIN32 ??? "_mktemp" : "mktemp");
|
||||
extern fn Time_t* mktime(Tm* time) @if(!env::WIN32);
|
||||
extern fn void perror(ZString string);
|
||||
extern fn CInt printf(ZString format, ...);
|
||||
@@ -177,6 +178,7 @@ extern fn CLong strtol(char* str, char** endptr, CInt base);
|
||||
extern fn CULong strtoul(char* str, char** endptr, CInt base);
|
||||
extern fn usz strxfrm(char* dest, ZString src, usz n);
|
||||
extern fn CInt system(ZString str);
|
||||
extern fn ZString tempnam(ZString dir, ZString prefix) @cname(env::WIN32 ??? "_tempnam" : "tempnam") @nodiscard;
|
||||
extern fn Time_t timegm(Tm* timeptr) @if(!env::WIN32 && !env::NETBSD);
|
||||
extern fn ZString tmpnam(char *buffer);
|
||||
extern fn CFile tmpfile();
|
||||
|
||||
@@ -22,18 +22,20 @@ alias Pid_t = int;
|
||||
alias Uid_t = uint;
|
||||
alias Gid_t = uint;
|
||||
|
||||
const CUInt SA_ONSTACK = env::LINUX ? 0x08000000 : 0x0001;
|
||||
const CUInt SA_RESTART = env::LINUX ? 0x10000000 : 0x0002;
|
||||
const CUInt SA_RESETHAND = env::LINUX ? 0x80000000 : 0x0004;
|
||||
const CUInt SA_SIGINFO = env::LINUX ? 0x00000004 : 0x0040;
|
||||
const CUInt SA_ONSTACK = env::LINUX || env::ANDROID ? 0x08000000 : 0x0001;
|
||||
const CUInt SA_RESTART = env::LINUX || env::ANDROID ? 0x10000000 : 0x0002;
|
||||
const CUInt SA_RESETHAND = env::LINUX || env::ANDROID ? 0x80000000 : 0x0004;
|
||||
const CUInt SA_SIGINFO = env::LINUX || env::ANDROID ? 0x00000004 : 0x0040;
|
||||
|
||||
alias Sigset_t @if((env::DARWIN || env::BSD_FAMILY) && !env::NETBSD) = uint;
|
||||
alias Sigset_t @if(env::NETBSD) = uint[4];
|
||||
alias Sigset_t @if(env::LINUX) = ulong[16];
|
||||
alias Sigset_t @if(env::ANDROID) = ulong;
|
||||
alias SigActionFunction = fn void(CInt, void*, void*);
|
||||
|
||||
struct Sigaction
|
||||
{
|
||||
CInt sa_flags @if(env::ANDROID);
|
||||
union
|
||||
{
|
||||
SignalFunction sa_handler;
|
||||
@@ -43,20 +45,20 @@ struct Sigaction
|
||||
CInt sa_flags @if(env::DARWIN || env::BSD_FAMILY );
|
||||
Sigset_t sa_mask @if(env::DARWIN || env::BSD_FAMILY);
|
||||
|
||||
Sigset_t sa_mask @if(env::LINUX);
|
||||
Sigset_t sa_mask @if(env::LINUX || env::ANDROID);
|
||||
CInt sa_flags @if(env::LINUX);
|
||||
void* sa_restorer @if(env::LINUX);
|
||||
void* sa_restorer @if(env::LINUX || env::ANDROID);
|
||||
}
|
||||
|
||||
struct Stack_t
|
||||
{
|
||||
void* ss_sp;
|
||||
struct @if(!env::LINUX)
|
||||
struct @if(!env::LINUX && !env::ANDROID)
|
||||
{
|
||||
usz ss_size;
|
||||
CInt ss_flags;
|
||||
}
|
||||
struct @if(env::LINUX)
|
||||
struct @if(env::LINUX || env::ANDROID)
|
||||
{
|
||||
CInt ss_flags;
|
||||
usz ss_size;
|
||||
@@ -166,13 +168,13 @@ bitstruct Tc_lflags : CUInt
|
||||
bool extproc : 16;
|
||||
}
|
||||
|
||||
enum T_nldly : const char
|
||||
constdef T_nldly : char
|
||||
{
|
||||
NL0 = 0b0,
|
||||
NL1 = 0b1,
|
||||
}
|
||||
|
||||
enum T_crdly : const char
|
||||
constdef T_crdly : char
|
||||
{
|
||||
CR0 = 0b00,
|
||||
CR1 = 0b01,
|
||||
@@ -180,7 +182,7 @@ enum T_crdly : const char
|
||||
CR3 = 0b11,
|
||||
}
|
||||
|
||||
enum T_tabdly : const char
|
||||
constdef T_tabdly : char
|
||||
{
|
||||
TAB0 = 0b00,
|
||||
TAB1 = 0b01,
|
||||
@@ -189,25 +191,25 @@ enum T_tabdly : const char
|
||||
XTABS = TAB3,
|
||||
}
|
||||
|
||||
enum T_bsdly : const char
|
||||
constdef T_bsdly : char
|
||||
{
|
||||
BS0 = 0b0,
|
||||
BS1 = 0b1,
|
||||
}
|
||||
|
||||
enum T_ffdly : const char
|
||||
constdef T_ffdly : char
|
||||
{
|
||||
FF0 = 0b0,
|
||||
FF1 = 0b1,
|
||||
}
|
||||
|
||||
enum T_vtdly : const char
|
||||
constdef T_vtdly : char
|
||||
{
|
||||
VT0 = 0b0,
|
||||
VT1 = 0b1,
|
||||
}
|
||||
|
||||
enum T_csize : const char
|
||||
constdef T_csize : char
|
||||
{
|
||||
CS5 = 0b00,
|
||||
CS6 = 0b01,
|
||||
@@ -215,31 +217,31 @@ enum T_csize : const char
|
||||
CS8 = 0b11,
|
||||
}
|
||||
|
||||
enum Speed : const CUInt
|
||||
constdef Speed : CUInt
|
||||
{
|
||||
B0 = 0o0000000,
|
||||
B50 = 0o0000001,
|
||||
B75 = 0o0000002,
|
||||
B110 = 0o0000003,
|
||||
B134 = 0o0000004,
|
||||
B150 = 0o0000005,
|
||||
B200 = 0o0000006,
|
||||
B300 = 0o0000007,
|
||||
B600 = 0o0000010,
|
||||
B1200 = 0o0000011,
|
||||
B1800 = 0o0000012,
|
||||
B2400 = 0o0000013,
|
||||
B4800 = 0o0000014,
|
||||
B9600 = 0o0000015,
|
||||
B19200 = 0o0000016,
|
||||
B38400 = 0o0000017,
|
||||
B57600 = 0o0010001,
|
||||
B115200 = 0o0010002,
|
||||
B230400 = 0o0010003,
|
||||
B460800 = 0o0010004,
|
||||
B500000 = 0o0010005,
|
||||
B576000 = 0o0010006,
|
||||
B921600 = 0o0010007,
|
||||
B0 = 0o0000000,
|
||||
B50 = 0o0000001,
|
||||
B75 = 0o0000002,
|
||||
B110 = 0o0000003,
|
||||
B134 = 0o0000004,
|
||||
B150 = 0o0000005,
|
||||
B200 = 0o0000006,
|
||||
B300 = 0o0000007,
|
||||
B600 = 0o0000010,
|
||||
B1200 = 0o0000011,
|
||||
B1800 = 0o0000012,
|
||||
B2400 = 0o0000013,
|
||||
B4800 = 0o0000014,
|
||||
B9600 = 0o0000015,
|
||||
B19200 = 0o0000016,
|
||||
B38400 = 0o0000017,
|
||||
B57600 = 0o0010001,
|
||||
B115200 = 0o0010002,
|
||||
B230400 = 0o0010003,
|
||||
B460800 = 0o0010004,
|
||||
B500000 = 0o0010005,
|
||||
B576000 = 0o0010006,
|
||||
B921600 = 0o0010007,
|
||||
B1000000 = 0o0010010,
|
||||
B1152000 = 0o0010011,
|
||||
B1500000 = 0o0010012,
|
||||
@@ -251,37 +253,37 @@ enum Speed : const CUInt
|
||||
MAX_BAUD = B4000000,
|
||||
}
|
||||
|
||||
enum Cc : const inline char
|
||||
constdef Cc : inline char
|
||||
{
|
||||
VINTR = 0,
|
||||
VQUIT = 1,
|
||||
VERASE = 2,
|
||||
VKILL = 3,
|
||||
VEOF = 4,
|
||||
VTIME = 5,
|
||||
VMIN = 6,
|
||||
VSWTC = 7,
|
||||
VSTART = 8,
|
||||
VSTOP = 9,
|
||||
VSUSP = 10,
|
||||
VEOL = 11,
|
||||
VINTR = 0,
|
||||
VQUIT = 1,
|
||||
VERASE = 2,
|
||||
VKILL = 3,
|
||||
VEOF = 4,
|
||||
VTIME = 5,
|
||||
VMIN = 6,
|
||||
VSWTC = 7,
|
||||
VSTART = 8,
|
||||
VSTOP = 9,
|
||||
VSUSP = 10,
|
||||
VEOL = 11,
|
||||
VREPRINT = 12,
|
||||
VDISCARD = 13,
|
||||
VWERASE = 14,
|
||||
VLNEXT = 15,
|
||||
VEOL2 = 16,
|
||||
VWERASE = 14,
|
||||
VLNEXT = 15,
|
||||
VEOL2 = 16,
|
||||
}
|
||||
|
||||
enum Tcactions : const CInt
|
||||
constdef Tcactions : CInt
|
||||
{
|
||||
TCOOFF = 0,
|
||||
TCOON = 1,
|
||||
TCIOFF = 2,
|
||||
TCION = 3,
|
||||
TCIFLUSH = 0,
|
||||
TCOFLUSH = 1,
|
||||
TCOOFF = 0,
|
||||
TCOON = 1,
|
||||
TCIOFF = 2,
|
||||
TCION = 3,
|
||||
TCIFLUSH = 0,
|
||||
TCOFLUSH = 1,
|
||||
TCIOFLUSH = 2,
|
||||
TCSANOW = 0,
|
||||
TCSANOW = 0,
|
||||
TCSADRAIN = 1,
|
||||
TCSAFLUSH = 2,
|
||||
}
|
||||
@@ -310,65 +312,65 @@ struct Termios
|
||||
Speed c_ospeed;
|
||||
}
|
||||
|
||||
const Tcactions TCOOFF @deprecated = 0;
|
||||
const Tcactions TCOON @deprecated = 1;
|
||||
const Tcactions TCIOFF @deprecated = 2;
|
||||
const Tcactions TCION @deprecated = 3;
|
||||
const Tcactions TCIFLUSH @deprecated = 0;
|
||||
const Tcactions TCOFLUSH @deprecated = 1;
|
||||
const Tcactions TCIOFLUSH @deprecated = 2;
|
||||
const Tcactions TCSANOW @deprecated = 0;
|
||||
const Tcactions TCSADRAIN @deprecated = 1;
|
||||
const Tcactions TCSAFLUSH @deprecated = 2;
|
||||
const Speed B0 @deprecated = 0o0000000;
|
||||
const Speed B50 @deprecated = 0o0000001;
|
||||
const Speed B75 @deprecated = 0o0000002;
|
||||
const Speed B110 @deprecated = 0o0000003;
|
||||
const Speed B134 @deprecated = 0o0000004;
|
||||
const Speed B150 @deprecated = 0o0000005;
|
||||
const Speed B200 @deprecated = 0o0000006;
|
||||
const Speed B300 @deprecated = 0o0000007;
|
||||
const Speed B600 @deprecated = 0o0000010;
|
||||
const Speed B1200 @deprecated = 0o0000011;
|
||||
const Speed B1800 @deprecated = 0o0000012;
|
||||
const Speed B2400 @deprecated = 0o0000013;
|
||||
const Speed B4800 @deprecated = 0o0000014;
|
||||
const Speed B9600 @deprecated = 0o0000015;
|
||||
const Speed B19200 @deprecated = 0o0000016;
|
||||
const Speed B38400 @deprecated = 0o0000017;
|
||||
const Speed B57600 @deprecated = 0o0010001;
|
||||
const Speed B115200 @deprecated = 0o0010002;
|
||||
const Speed B230400 @deprecated = 0o0010003;
|
||||
const Speed B460800 @deprecated = 0o0010004;
|
||||
const Speed B500000 @deprecated = 0o0010005;
|
||||
const Speed B576000 @deprecated = 0o0010006;
|
||||
const Speed B921600 @deprecated = 0o0010007;
|
||||
const Speed B1000000 @deprecated = 0o0010010;
|
||||
const Speed B1152000 @deprecated = 0o0010011;
|
||||
const Speed B1500000 @deprecated = 0o0010012;
|
||||
const Speed B2000000 @deprecated = 0o0010013;
|
||||
const Speed B2500000 @deprecated = 0o0010014;
|
||||
const Speed B3000000 @deprecated = 0o0010015;
|
||||
const Speed B3500000 @deprecated = 0o0010016;
|
||||
const Speed B4000000 @deprecated = 0o0010017;
|
||||
const Speed MAX_BAUD @deprecated = B4000000;
|
||||
const Cc VINTR @deprecated = 0;
|
||||
const Cc VQUIT @deprecated = 1;
|
||||
const Cc VERASE @deprecated = 2;
|
||||
const Cc VKILL @deprecated = 3;
|
||||
const Cc VEOF @deprecated = 4;
|
||||
const Cc VTIME @deprecated = 5;
|
||||
const Cc VMIN @deprecated = 6;
|
||||
const Cc VSWTC @deprecated = 7;
|
||||
const Cc VSTART @deprecated = 8;
|
||||
const Cc VSTOP @deprecated = 9;
|
||||
const Cc VSUSP @deprecated = 10;
|
||||
const Cc VEOL @deprecated = 11;
|
||||
const Cc VREPRINT @deprecated = 12;
|
||||
const Cc VDISCARD @deprecated = 13;
|
||||
const Cc VWERASE @deprecated = 14;
|
||||
const Cc VLNEXT @deprecated = 15;
|
||||
const Cc VEOL2 @deprecated = 16;
|
||||
const Tcactions TCOOFF @deprecated = (Tcactions)0;
|
||||
const Tcactions TCOON @deprecated = (Tcactions)1;
|
||||
const Tcactions TCIOFF @deprecated = (Tcactions)2;
|
||||
const Tcactions TCION @deprecated = (Tcactions)3;
|
||||
const Tcactions TCIFLUSH @deprecated = (Tcactions)0;
|
||||
const Tcactions TCOFLUSH @deprecated = (Tcactions)1;
|
||||
const Tcactions TCIOFLUSH @deprecated = (Tcactions)2;
|
||||
const Tcactions TCSANOW @deprecated = (Tcactions)0;
|
||||
const Tcactions TCSADRAIN @deprecated = (Tcactions)1;
|
||||
const Tcactions TCSAFLUSH @deprecated = (Tcactions)2;
|
||||
const Speed B0 @deprecated = (Speed)0o0000000;
|
||||
const Speed B50 @deprecated = (Speed)0o0000001;
|
||||
const Speed B75 @deprecated = (Speed)0o0000002;
|
||||
const Speed B110 @deprecated = (Speed)0o0000003;
|
||||
const Speed B134 @deprecated = (Speed)0o0000004;
|
||||
const Speed B150 @deprecated = (Speed)0o0000005;
|
||||
const Speed B200 @deprecated = (Speed)0o0000006;
|
||||
const Speed B300 @deprecated = (Speed)0o0000007;
|
||||
const Speed B600 @deprecated = (Speed)0o0000010;
|
||||
const Speed B1200 @deprecated = (Speed)0o0000011;
|
||||
const Speed B1800 @deprecated = (Speed)0o0000012;
|
||||
const Speed B2400 @deprecated = (Speed)0o0000013;
|
||||
const Speed B4800 @deprecated = (Speed)0o0000014;
|
||||
const Speed B9600 @deprecated = (Speed)0o0000015;
|
||||
const Speed B19200 @deprecated = (Speed)0o0000016;
|
||||
const Speed B38400 @deprecated = (Speed)0o0000017;
|
||||
const Speed B57600 @deprecated = (Speed)0o0010001;
|
||||
const Speed B115200 @deprecated = (Speed)0o0010002;
|
||||
const Speed B230400 @deprecated = (Speed)0o0010003;
|
||||
const Speed B460800 @deprecated = (Speed)0o0010004;
|
||||
const Speed B500000 @deprecated = (Speed)0o0010005;
|
||||
const Speed B576000 @deprecated = (Speed)0o0010006;
|
||||
const Speed B921600 @deprecated = (Speed)0o0010007;
|
||||
const Speed B1000000 @deprecated = (Speed)0o0010010;
|
||||
const Speed B1152000 @deprecated = (Speed)0o0010011;
|
||||
const Speed B1500000 @deprecated = (Speed)0o0010012;
|
||||
const Speed B2000000 @deprecated = (Speed)0o0010013;
|
||||
const Speed B2500000 @deprecated = (Speed)0o0010014;
|
||||
const Speed B3000000 @deprecated = (Speed)0o0010015;
|
||||
const Speed B3500000 @deprecated = (Speed)0o0010016;
|
||||
const Speed B4000000 @deprecated = (Speed)0o0010017;
|
||||
const Speed MAX_BAUD @deprecated = (Speed)0o0010017;
|
||||
const Cc VINTR @deprecated = (Cc)0;
|
||||
const Cc VQUIT @deprecated = (Cc)1;
|
||||
const Cc VERASE @deprecated = (Cc)2;
|
||||
const Cc VKILL @deprecated = (Cc)3;
|
||||
const Cc VEOF @deprecated = (Cc)4;
|
||||
const Cc VTIME @deprecated = (Cc)5;
|
||||
const Cc VMIN @deprecated = (Cc)6;
|
||||
const Cc VSWTC @deprecated = (Cc)7;
|
||||
const Cc VSTART @deprecated = (Cc)8;
|
||||
const Cc VSTOP @deprecated = (Cc)9;
|
||||
const Cc VSUSP @deprecated = (Cc)10;
|
||||
const Cc VEOL @deprecated = (Cc)11;
|
||||
const Cc VREPRINT @deprecated = (Cc)12;
|
||||
const Cc VDISCARD @deprecated = (Cc)13;
|
||||
const Cc VWERASE @deprecated = (Cc)14;
|
||||
const Cc VLNEXT @deprecated = (Cc)15;
|
||||
const Cc VEOL2 @deprecated = (Cc)16;
|
||||
const Tc_lflags ISIG @deprecated = {.isig};
|
||||
const Tc_lflags ICANON @deprecated = {.icanon};
|
||||
const Tc_lflags ECHO @deprecated = {.echo};
|
||||
|
||||
@@ -37,7 +37,7 @@ fn BigInt* BigInt.init(&self, int128 value)
|
||||
assert(value < 0 || tmp == 0 || !self.is_negative());
|
||||
assert(value >= 0 || tmp == -1 || self.is_negative());
|
||||
self.len = len;
|
||||
self.reduce_len();
|
||||
reduce_len(self);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -153,15 +153,11 @@ fn void BigInt.add_this(&self, BigInt other) @operator(+=)
|
||||
self.data[self.len++] = (uint)carry;
|
||||
}
|
||||
|
||||
self.reduce_len();
|
||||
reduce_len(self);
|
||||
|
||||
assert(sign != sign_arg || sign == self.is_negative(), "Overflow in addition");
|
||||
}
|
||||
|
||||
fn void BigInt.reduce_len(&self) @private
|
||||
{
|
||||
self.len = max(find_length(&self.data, self.len), 1);
|
||||
}
|
||||
|
||||
macro uint find_length(uint* data, uint length)
|
||||
{
|
||||
@@ -226,7 +222,7 @@ fn void BigInt.mult_this(&self, BigInt bi2) @operator(*=)
|
||||
|
||||
res.len = min(self.len + bi2.len, (uint)MAX_LEN);
|
||||
|
||||
res.reduce_len();
|
||||
reduce_len(&res);
|
||||
|
||||
// overflow check (result is -ve)
|
||||
assert(!res.is_negative(), "Multiplication overflow");
|
||||
@@ -265,7 +261,7 @@ fn void BigInt.negate(&self)
|
||||
assert(self.is_negative() != was_negative, "Overflow in negation");
|
||||
|
||||
self.len = MAX_LEN;
|
||||
self.reduce_len();
|
||||
reduce_len(self);
|
||||
}
|
||||
|
||||
macro bool BigInt.is_zero(&self) => self.len == 1 && self.data[0] == 0;
|
||||
@@ -301,7 +297,7 @@ fn BigInt* BigInt.sub_this(&self, BigInt other) @operator(-=)
|
||||
self.len = MAX_LEN;
|
||||
}
|
||||
|
||||
self.reduce_len();
|
||||
reduce_len(self);
|
||||
|
||||
// overflow check
|
||||
|
||||
@@ -311,7 +307,7 @@ fn BigInt* BigInt.sub_this(&self, BigInt other) @operator(-=)
|
||||
|
||||
fn int BigInt.bitcount(&self)
|
||||
{
|
||||
self.reduce_len();
|
||||
reduce_len(self);
|
||||
uint val = self.data[self.len - 1];
|
||||
uint mask = 0x80000000;
|
||||
int bits = 32;
|
||||
@@ -364,11 +360,11 @@ fn void BigInt.div_this(&self, BigInt other) @operator(/=)
|
||||
|
||||
if (other.len == 1)
|
||||
{
|
||||
self.single_byte_divide(&other, "ient, &remainder);
|
||||
single_byte_divide(self, &other, "ient, &remainder);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.multi_byte_divide(&other, "ient, &remainder);
|
||||
multi_byte_divide(self, &other, "ient, &remainder);
|
||||
}
|
||||
if (negate_answer)
|
||||
{
|
||||
@@ -407,11 +403,11 @@ fn void BigInt.mod_this(&self, BigInt bi2) @operator(%=)
|
||||
|
||||
if (bi2.len == 1)
|
||||
{
|
||||
self.single_byte_divide(&bi2, "ient, &remainder);
|
||||
single_byte_divide(self, &bi2, "ient, &remainder);
|
||||
}
|
||||
else
|
||||
{
|
||||
self.multi_byte_divide(&bi2, "ient, &remainder);
|
||||
multi_byte_divide(self, &bi2, "ient, &remainder);
|
||||
}
|
||||
if (negate_answer)
|
||||
{
|
||||
@@ -425,7 +421,7 @@ fn void BigInt.bit_negate_this(&self)
|
||||
foreach (&r : self.data) *r = ~*r;
|
||||
|
||||
self.len = MAX_LEN;
|
||||
self.reduce_len();
|
||||
reduce_len(self);
|
||||
}
|
||||
|
||||
fn BigInt BigInt.bit_negate(self) @operator(~)
|
||||
@@ -523,7 +519,7 @@ fn usz? BigInt.to_format(&self, Formatter* format) @dynamic
|
||||
usz len;
|
||||
if (negative)
|
||||
{
|
||||
format.out('-')!;
|
||||
format.print_char('-')!;
|
||||
len++;
|
||||
a.negate();
|
||||
}
|
||||
@@ -580,7 +576,7 @@ fn String BigInt.to_string_with_radix(&self, int radix, Allocator allocator)
|
||||
|
||||
while (!a.is_zero())
|
||||
{
|
||||
a.single_byte_divide(&big_radix, "ient, &remainder);
|
||||
single_byte_divide(&a, &big_radix, "ient, &remainder);
|
||||
|
||||
if (remainder.data[0] < 10)
|
||||
{
|
||||
@@ -740,7 +736,7 @@ fn BigInt barrett_reduction(BigInt x, BigInt n, BigInt constant)
|
||||
}
|
||||
|
||||
r2.len = k_plus_one;
|
||||
r2.reduce_len();
|
||||
reduce_len(&r2);
|
||||
|
||||
r1.sub_this(r2);
|
||||
if (r1.is_negative())
|
||||
@@ -816,7 +812,7 @@ fn void BigInt.bit_and_this(&self, BigInt bi2)
|
||||
}
|
||||
self.len = MAX_LEN;
|
||||
|
||||
self.reduce_len();
|
||||
reduce_len(self);
|
||||
}
|
||||
|
||||
fn BigInt BigInt.bit_or(self, BigInt bi2) @operator(|)
|
||||
@@ -834,7 +830,7 @@ fn void BigInt.bit_or_this(&self, BigInt bi2)
|
||||
}
|
||||
self.len = MAX_LEN;
|
||||
|
||||
self.reduce_len();
|
||||
reduce_len(self);
|
||||
}
|
||||
|
||||
fn BigInt BigInt.bit_xor(self, BigInt bi2) @operator(^)
|
||||
@@ -852,7 +848,7 @@ fn void BigInt.bit_xor_this(&self, BigInt bi2)
|
||||
}
|
||||
self.len = MAX_LEN;
|
||||
|
||||
self.reduce_len();
|
||||
reduce_len(self);
|
||||
}
|
||||
|
||||
fn void BigInt.shl_this(&self, int shift) @operator(<<=)
|
||||
@@ -928,13 +924,18 @@ fn void BigInt.randomize_bits(&self, Random random, int bits)
|
||||
|
||||
module std::math::bigint @private;
|
||||
|
||||
fn void reduce_len(BigInt* self) @private
|
||||
{
|
||||
self.len = max(find_length(&self.data, self.len), 1);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@param [&inout] quotient
|
||||
@param [&in] bi2
|
||||
@param [&inout] remainder
|
||||
*>
|
||||
fn void BigInt.single_byte_divide(&self, BigInt* bi2, BigInt* quotient, BigInt* remainder)
|
||||
fn void single_byte_divide(BigInt* self, BigInt* bi2, BigInt* quotient, BigInt* remainder) @private
|
||||
{
|
||||
uint[MAX_LEN] result;
|
||||
int result_pos = 0;
|
||||
@@ -977,8 +978,8 @@ fn void BigInt.single_byte_divide(&self, BigInt* bi2, BigInt* quotient, BigInt*
|
||||
}
|
||||
|
||||
quotient.data[j..] = 0;
|
||||
quotient.reduce_len();
|
||||
remainder.reduce_len();
|
||||
reduce_len(quotient);
|
||||
reduce_len(remainder);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -986,7 +987,7 @@ fn void BigInt.single_byte_divide(&self, BigInt* bi2, BigInt* quotient, BigInt*
|
||||
@param [&in] other
|
||||
@param [&inout] remainder
|
||||
*>
|
||||
fn void BigInt.multi_byte_divide(&self, BigInt* other, BigInt* quotient, BigInt* remainder)
|
||||
fn void multi_byte_divide(BigInt* self, BigInt* other, BigInt* quotient, BigInt* remainder)
|
||||
{
|
||||
uint[MAX_LEN] result;
|
||||
uint[MAX_LEN] r;
|
||||
@@ -1081,7 +1082,7 @@ fn void BigInt.multi_byte_divide(&self, BigInt* other, BigInt* quotient, BigInt*
|
||||
quotient.data[y] = 0;
|
||||
}
|
||||
|
||||
quotient.reduce_len();
|
||||
reduce_len(quotient);
|
||||
|
||||
remainder.len = shift_right(&r, remainder_len, shift);
|
||||
|
||||
|
||||
589
lib/std/math/distributions.c3
Normal file
589
lib/std/math/distributions.c3
Normal file
@@ -0,0 +1,589 @@
|
||||
// Copyright (c) 2026 Koni Marti. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license.
|
||||
<*
|
||||
This module provides a comprehensive set of continuous and discrete
|
||||
probability distributions with support for PDF, CDF, inverse CDF,
|
||||
random sampling, mean, and variance calculations.
|
||||
*>
|
||||
module std::math::distributions;
|
||||
import std::math::random;
|
||||
|
||||
// Distribution interface defining common statistical operations
|
||||
interface Distribution
|
||||
{
|
||||
<* Calculate the mean (expected value) of the distribution *>
|
||||
fn double mean();
|
||||
|
||||
<* Calculate the variance of the distribution *>
|
||||
fn double variance();
|
||||
}
|
||||
|
||||
interface ContinuousDistribution : Distribution
|
||||
{
|
||||
<* Probability density function (PDF) *>
|
||||
fn double pdf(double x);
|
||||
|
||||
<* Cumulative distribution function (CDF) *>
|
||||
fn double cdf(double x);
|
||||
|
||||
<* Inverse cumulative distribution function (quantile function) *>
|
||||
fn double quantile(double p);
|
||||
|
||||
<* Generate a random sample from the distribution *>
|
||||
fn double random(Random rand);
|
||||
}
|
||||
|
||||
interface DiscreteDistribution : Distribution
|
||||
{
|
||||
<* Probability mass function (PMF) *>
|
||||
fn double pmf(int k);
|
||||
|
||||
<* Cumulative distribution function (CDF) *>
|
||||
fn double cdf(int k);
|
||||
|
||||
<* Inverse cumulative distribution function *>
|
||||
fn int quantile(double p);
|
||||
|
||||
<* Generate a random sample from the distribution *>
|
||||
fn int random(Random rand);
|
||||
}
|
||||
|
||||
<*
|
||||
Uniform distribution over [a, b]
|
||||
*>
|
||||
struct UniformDist (ContinuousDistribution)
|
||||
{
|
||||
double a;
|
||||
double b;
|
||||
}
|
||||
|
||||
<*
|
||||
@require b > a : "Upper bound must be greater than lower bound."
|
||||
*>
|
||||
fn UniformDist uniform(double a, double b)
|
||||
{
|
||||
return (UniformDist){ a, b };
|
||||
}
|
||||
|
||||
fn double UniformDist.mean(&self) @dynamic
|
||||
{
|
||||
return (self.a + self.b) / 2.0;
|
||||
}
|
||||
|
||||
fn double UniformDist.variance(&self) @dynamic
|
||||
{
|
||||
double range = self.b - self.a;
|
||||
return range * range / 12.0;
|
||||
}
|
||||
|
||||
fn double UniformDist.pdf(&self, double x) @dynamic
|
||||
{
|
||||
if (x < self.a || x > self.b) return 0;
|
||||
return 1.0 / (self.b - self.a);
|
||||
}
|
||||
|
||||
fn double UniformDist.cdf(&self, double x) @dynamic
|
||||
{
|
||||
if (x < self.a) return 0.0;
|
||||
if (x > self.b) return 1.0;
|
||||
return (x - self.a) / (self.b - self.a);
|
||||
}
|
||||
|
||||
<*
|
||||
@require p >= 0.0 && p <= 1.0 : "Probability must be between 0 and 1."
|
||||
*>
|
||||
fn double UniformDist.quantile(&self, double p) @dynamic
|
||||
{
|
||||
return self.a + p * (self.b - self.a);
|
||||
}
|
||||
|
||||
fn double UniformDist.random(&self, Random rand) @dynamic
|
||||
{
|
||||
return self.a + random::next_double(rand) * (self.b - self.a);
|
||||
}
|
||||
|
||||
<*
|
||||
Normal (Gaussian) distribution
|
||||
*>
|
||||
struct NormalDist (ContinuousDistribution)
|
||||
{
|
||||
double mu;
|
||||
double sigma;
|
||||
}
|
||||
|
||||
<*
|
||||
@require sigma > 0.0 : "Standard deviation must be positive"
|
||||
*>
|
||||
fn NormalDist normal(double mu = 0.0, double sigma = 1.0)
|
||||
{
|
||||
return (NormalDist){ mu, sigma };
|
||||
}
|
||||
|
||||
fn double NormalDist.mean(&self) @dynamic
|
||||
{
|
||||
return self.mu;
|
||||
}
|
||||
|
||||
fn double NormalDist.variance(&self) @dynamic
|
||||
{
|
||||
return self.sigma * self.sigma;
|
||||
}
|
||||
|
||||
fn double NormalDist.pdf(&self, double x) @dynamic
|
||||
{
|
||||
double z = (x - self.mu) / self.sigma;
|
||||
return math::exp(-0.5 * z * z) / (self.sigma * math::sqrt(2.0 * math::PI));
|
||||
}
|
||||
|
||||
fn double NormalDist.cdf(&self, double x) @dynamic
|
||||
{
|
||||
double z = (x - self.mu) / self.sigma;
|
||||
return math::clamp(0.5 * (1.0 + math::erf(z / math::SQRT2)), 0.0, 1.0);
|
||||
}
|
||||
|
||||
<*
|
||||
@require p >= 0.0 && p <= 1.0 : "Probability must be between 0 and 1."
|
||||
*>
|
||||
fn double NormalDist.quantile(&self, double p) @dynamic
|
||||
{
|
||||
double z = inverse_erf(2.0 * p - 1.0) * math::SQRT2;
|
||||
return self.mu + self.sigma * z;
|
||||
}
|
||||
|
||||
fn double NormalDist.random(&self, Random rand) @dynamic
|
||||
{
|
||||
// Box-Muller transform.
|
||||
double u1 = random::next_double(rand);
|
||||
double u2 = random::next_double(rand);
|
||||
double z = math::sqrt(-2.0 * math::ln(u1)) * math::cos(2.0 * math::PI * u2);
|
||||
return self.mu + self.sigma * z;
|
||||
}
|
||||
|
||||
<*
|
||||
Exponential distribution
|
||||
*>
|
||||
struct ExponentialDist (ContinuousDistribution)
|
||||
{
|
||||
double lambda;
|
||||
}
|
||||
|
||||
<*
|
||||
@require lambda > 0.0 : "Rate parameter must be positive."
|
||||
*>
|
||||
fn ExponentialDist exponential(double lambda = 1.0)
|
||||
{
|
||||
return (ExponentialDist){ lambda };
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.lambda > 0.0 : "Rate parameter must be positive."
|
||||
*>
|
||||
fn double ExponentialDist.mean(&self) @dynamic
|
||||
{
|
||||
return 1.0 / self.lambda;
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.lambda > 0.0 : "Rate parameter must be positive."
|
||||
*>
|
||||
fn double ExponentialDist.variance(&self) @dynamic
|
||||
{
|
||||
return 1.0 / (self.lambda * self.lambda);
|
||||
}
|
||||
|
||||
fn double ExponentialDist.pdf(&self, double x) @dynamic
|
||||
{
|
||||
if (x < 0.0) return 0.0;
|
||||
return self.lambda * math::exp(-self.lambda * x);
|
||||
}
|
||||
|
||||
fn double ExponentialDist.cdf(&self, double x) @dynamic
|
||||
{
|
||||
if (x < 0.0) return 0.0;
|
||||
return math::clamp(1.0 - math::exp(-self.lambda * x), 0.0, 1.0);
|
||||
}
|
||||
|
||||
<*
|
||||
@require p >= 0.0 && p <= 1.0 : "Probability must be between 0 and 1."
|
||||
@require self.lambda > 0.0 : "Rate parameter must be positive."
|
||||
*>
|
||||
fn double ExponentialDist.quantile(&self, double p) @dynamic
|
||||
{
|
||||
return -math::ln(1.0 - p) / self.lambda;
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.lambda > 0.0 : "Rate parameter must be positive."
|
||||
*>
|
||||
fn double ExponentialDist.random(&self, Random rand) @dynamic
|
||||
{
|
||||
return -math::ln(1.0 - random::next_double(rand)) / self.lambda;
|
||||
}
|
||||
|
||||
<*
|
||||
Student's t-distribution
|
||||
*>
|
||||
struct TDist (ContinuousDistribution)
|
||||
{
|
||||
double df;
|
||||
}
|
||||
|
||||
<*
|
||||
@require df > 0.0 : "Degrees of freedom must be positive."
|
||||
*>
|
||||
fn TDist t_distribution(double df)
|
||||
{
|
||||
return (TDist){ df };
|
||||
}
|
||||
|
||||
fn double TDist.mean(&self) @dynamic
|
||||
{
|
||||
if (self.df <= 1.0) return double.nan;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
fn double TDist.variance(&self) @dynamic
|
||||
{
|
||||
if (self.df <= 1.0) return double.nan;
|
||||
if (self.df <= 2.0) return double.inf;
|
||||
return self.df / (self.df - 2.0);
|
||||
}
|
||||
|
||||
fn double TDist.pdf(&self, double x) @dynamic
|
||||
{
|
||||
double v = self.df;
|
||||
double coef = math::tgamma((v + 1.0) / 2.0) /
|
||||
(math::sqrt(v * math::PI) * math::tgamma(v / 2.0));
|
||||
return coef * math::pow(1.0 + x * x / v, -(v + 1.0) / 2.0);
|
||||
}
|
||||
|
||||
fn double TDist.cdf(&self, double x) @dynamic
|
||||
{
|
||||
double v = self.df;
|
||||
if (x == 0.0) return 0.5;
|
||||
|
||||
double t = v / (v + x * x);
|
||||
double a = v / 2.0;
|
||||
double b = 0.5;
|
||||
|
||||
// Using regularized incomplete beta function.
|
||||
double beta_cdf = incomplete_beta(t, a, b);
|
||||
|
||||
double p = x >= 0.0 ? 1.0 - 0.5 * beta_cdf : 0.5 * beta_cdf;
|
||||
return math::clamp(p, 0.0, 1.0);
|
||||
}
|
||||
|
||||
<*
|
||||
@require p >= 0.0 && p <= 1.0 : "Probability must be between 0 and 1."
|
||||
*>
|
||||
fn double TDist.quantile(&self, double p) @dynamic
|
||||
{
|
||||
if (p == 0.5) return 0.0;
|
||||
double x = (p < 0.5) ? -1.0 : 1.0;
|
||||
return newton_raphson(self, x, p) ?? double.nan;
|
||||
}
|
||||
|
||||
fn double TDist.random(&self, Random rand) @dynamic
|
||||
{
|
||||
// Generate using relationship with normal and chi-squared
|
||||
NormalDist std_normal = normal(0.0, 1.0);
|
||||
double z = std_normal.random(rand);
|
||||
double v = chi_squared_sample(self.df, rand);
|
||||
return z / math::sqrt(v / self.df);
|
||||
}
|
||||
|
||||
<*
|
||||
F-distribution
|
||||
*>
|
||||
struct FDist (ContinuousDistribution)
|
||||
{
|
||||
double d1;
|
||||
double d2;
|
||||
}
|
||||
|
||||
<*
|
||||
@require d1 > 0.0 && d2 > 0.0 : "Degrees of freedom must be positive."
|
||||
*>
|
||||
fn FDist f_distribution(double d1, double d2)
|
||||
{
|
||||
return (FDist){ d1, d2 };
|
||||
}
|
||||
|
||||
fn double FDist.mean(&self) @dynamic
|
||||
{
|
||||
if (self.d2 <= 2.0) return double.nan;
|
||||
return self.d2 / (self.d2 - 2.0);
|
||||
}
|
||||
|
||||
fn double FDist.variance(&self) @dynamic
|
||||
{
|
||||
if (self.d2 <= 4.0) return double.nan;
|
||||
double d1 = self.d1;
|
||||
double d2 = self.d2;
|
||||
return 2.0 * d2 * d2 * (d1 + d2 - 2.0) /
|
||||
(d1 * (d2 - 2.0) * (d2 - 2.0) * (d2 - 4.0));
|
||||
}
|
||||
|
||||
fn double FDist.pdf(&self, double x) @dynamic
|
||||
{
|
||||
if (x < 0.0) return 0.0;
|
||||
|
||||
double d1 = self.d1;
|
||||
double d2 = self.d2;
|
||||
|
||||
double num = math::pow(d1 * x, d1) * math::pow(d2, d2);
|
||||
double denom = math::pow(d1 * x + d2, d1 + d2);
|
||||
double beta_term = x * beta_function(d1 / 2.0, d2 / 2.0);
|
||||
|
||||
return math::sqrt(num / denom) / beta_term;
|
||||
}
|
||||
|
||||
|
||||
fn double FDist.cdf(&self, double x) @dynamic
|
||||
{
|
||||
if (x <= 0.0) return 0.0;
|
||||
|
||||
double d1 = self.d1;
|
||||
double d2 = self.d2;
|
||||
double t = d1 * x / (d1 * x + d2);
|
||||
|
||||
double p = incomplete_beta(t, d1 / 2.0, d2 / 2.0);
|
||||
return math::clamp(p, 0.0, 1.0);
|
||||
}
|
||||
|
||||
<*
|
||||
@require p >= 0.0 && p <= 1.0 : "Probability must be between 0 and 1."
|
||||
*>
|
||||
fn double FDist.quantile(&self, double p) @dynamic
|
||||
{
|
||||
return find_quantile(self, 0.0, 1000.0, p);
|
||||
}
|
||||
|
||||
fn double FDist.random(&self, Random rand) @dynamic
|
||||
{
|
||||
// Generate using ratio of chi-squared variables.
|
||||
double u1 = chi_squared_sample(self.d1, rand);
|
||||
double u2 = chi_squared_sample(self.d2, rand);
|
||||
return (u1 / self.d1) / (u2 / self.d2);
|
||||
}
|
||||
|
||||
<*
|
||||
Chi-squared distribution
|
||||
*>
|
||||
struct ChiSquaredDist (ContinuousDistribution)
|
||||
{
|
||||
double k;
|
||||
}
|
||||
|
||||
<*
|
||||
@require k > 0.0 : "Degrees of freedom must be positive"
|
||||
*>
|
||||
fn ChiSquaredDist chi_squared(double k)
|
||||
{
|
||||
return (ChiSquaredDist){ k };
|
||||
}
|
||||
|
||||
fn double ChiSquaredDist.mean(&self) @dynamic
|
||||
{
|
||||
return self.k;
|
||||
}
|
||||
|
||||
fn double ChiSquaredDist.variance(&self) @dynamic
|
||||
{
|
||||
return 2.0 * self.k;
|
||||
}
|
||||
|
||||
fn double ChiSquaredDist.pdf(&self, double x) @dynamic
|
||||
{
|
||||
if (x < 0.0) return 0.0;
|
||||
if (x == 0.0 && self.k < 2.0) return double.inf;
|
||||
if (x == 0.0) return 0.0;
|
||||
|
||||
double k = self.k;
|
||||
return math::pow(x, k / 2.0 - 1.0) * math::exp(-x / 2.0) /
|
||||
(math::pow(2.0, k / 2.0) * math::tgamma(k / 2.0));
|
||||
}
|
||||
|
||||
fn double ChiSquaredDist.cdf(&self, double x) @dynamic
|
||||
{
|
||||
if (x <= 0.0) return 0.0;
|
||||
double p = lower_incomplete_gamma(self.k / 2.0, x / 2.0);
|
||||
return math::clamp(p, 0.0, 1.0);
|
||||
}
|
||||
|
||||
<*
|
||||
@require p >= 0.0 && p <= 1.0 : "Probability must be between 0 and 1."
|
||||
*>
|
||||
fn double ChiSquaredDist.quantile(&self, double p) @dynamic
|
||||
{
|
||||
double low = 0.0;
|
||||
double high = self.k + 10.0 * math::sqrt(2.0 * self.k);
|
||||
return find_quantile(self, low, high, p);
|
||||
}
|
||||
|
||||
fn double ChiSquaredDist.random(&self, Random rand) @dynamic
|
||||
{
|
||||
return chi_squared_sample(self.k, rand);
|
||||
}
|
||||
|
||||
<*
|
||||
Binomial distribution
|
||||
*>
|
||||
struct BinomialDist (DiscreteDistribution)
|
||||
{
|
||||
int n;
|
||||
double p;
|
||||
}
|
||||
|
||||
<*
|
||||
@require n >= 0 : "Number of trials must be non-negative."
|
||||
@require p >= 0.0 && p <= 1.0 : "Probability must be between 0 and 1."
|
||||
*>
|
||||
fn BinomialDist binomial(int n, double p)
|
||||
{
|
||||
return (BinomialDist){ n, p };
|
||||
}
|
||||
|
||||
fn double BinomialDist.mean(&self) @dynamic
|
||||
{
|
||||
return (double)self.n * self.p;
|
||||
}
|
||||
|
||||
fn double BinomialDist.variance(&self) @dynamic
|
||||
{
|
||||
return (double)self.n * self.p * (1.0 - self.p);
|
||||
}
|
||||
|
||||
fn double BinomialDist.pmf(&self, int k) @dynamic
|
||||
{
|
||||
if (k < 0 || k > self.n) return 0.0;
|
||||
return binomial_coefficient(self.n, k) *
|
||||
math::pow(self.p, (double)k) *
|
||||
math::pow(1.0 - self.p, (double)(self.n - k));
|
||||
}
|
||||
|
||||
fn double BinomialDist.cdf(&self, int k) @dynamic
|
||||
{
|
||||
if (k < 0) return 0.0;
|
||||
if (k >= self.n) return 1.0;
|
||||
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i <= k; i++)
|
||||
{
|
||||
sum += self.pmf(i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
<*
|
||||
@require p >= 0.0 && p <= 1.0 : "Probability must be between 0 and 1."
|
||||
*>
|
||||
fn int BinomialDist.quantile(&self, double p) @dynamic
|
||||
{
|
||||
double cumulative = 0.0;
|
||||
for (int k = 0; k <= self.n; k++)
|
||||
{
|
||||
cumulative += self.pmf(k);
|
||||
if (cumulative >= p) return k;
|
||||
}
|
||||
return self.n;
|
||||
}
|
||||
|
||||
fn int BinomialDist.random(&self, Random rand) @dynamic
|
||||
{
|
||||
// Generate using Bernoulli trials.
|
||||
int successes = 0;
|
||||
for (int i = 0; i < self.n; i++)
|
||||
{
|
||||
if (random::next_double(rand) < self.p)
|
||||
{
|
||||
successes++;
|
||||
}
|
||||
}
|
||||
return successes;
|
||||
}
|
||||
|
||||
<*
|
||||
Poisson distribution
|
||||
*>
|
||||
struct PoissonDist (DiscreteDistribution)
|
||||
{
|
||||
double lambda;
|
||||
}
|
||||
|
||||
<*
|
||||
@require lambda > 0.0 : "Rate parameter must be positive."
|
||||
*>
|
||||
fn PoissonDist poisson(double lambda)
|
||||
{
|
||||
return (PoissonDist){ lambda };
|
||||
}
|
||||
|
||||
fn double PoissonDist.mean(&self) @dynamic
|
||||
{
|
||||
return self.lambda;
|
||||
}
|
||||
|
||||
fn double PoissonDist.variance(&self) @dynamic
|
||||
{
|
||||
return self.lambda;
|
||||
}
|
||||
|
||||
fn double PoissonDist.pmf(&self, int k) @dynamic
|
||||
{
|
||||
if (k < 0) return 0.0;
|
||||
return math::exp(-self.lambda + (double)k * math::ln(self.lambda) - ln_factorial(k));
|
||||
}
|
||||
|
||||
fn double PoissonDist.cdf(&self, int k) @dynamic
|
||||
{
|
||||
if (k < 0) return 0.0;
|
||||
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i <= k; i++)
|
||||
{
|
||||
sum += self.pmf(i);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
<*
|
||||
@require p >= 0.0 && p <= 1.0 : "Probability must be between 0 and 1."
|
||||
*>
|
||||
fn int PoissonDist.quantile(&self, double p) @dynamic
|
||||
{
|
||||
double cumulative = 0.0;
|
||||
int k = 0;
|
||||
while (cumulative < p)
|
||||
{
|
||||
cumulative += self.pmf(k);
|
||||
if (cumulative >= p) return k;
|
||||
k++;
|
||||
if (k > 1_000_000) break; // Safety limit
|
||||
}
|
||||
return k;
|
||||
}
|
||||
|
||||
fn int PoissonDist.random(&self, Random rand) @dynamic
|
||||
{
|
||||
// Knuth's algorithm for small lambda.
|
||||
if (self.lambda < 30.0)
|
||||
{
|
||||
double l = math::exp(-self.lambda);
|
||||
int k = 0;
|
||||
double p = 1.0;
|
||||
do
|
||||
{
|
||||
k++;
|
||||
p *= random::next_double(rand);
|
||||
} while (p > l);
|
||||
return (k - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use normal approximation for large lambda
|
||||
NormalDist approx = normal(self.lambda, math::sqrt(self.lambda));
|
||||
return (int)math::max(0.0, math::round(approx.random(rand)));
|
||||
}
|
||||
}
|
||||
|
||||
407
lib/std/math/distributions_private.c3
Normal file
407
lib/std/math/distributions_private.c3
Normal file
@@ -0,0 +1,407 @@
|
||||
// Copyright (c) 2026 Koni Marti. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license.
|
||||
module std::math::distributions @private;
|
||||
import std::math::random;
|
||||
|
||||
struct ConvergenceControl
|
||||
{
|
||||
usz max_iter;
|
||||
double epsilon;
|
||||
}
|
||||
|
||||
const DEFAULT_CONV = (ConvergenceControl){ 600, 1e-12 };
|
||||
const RELAXED_CONV = (ConvergenceControl){ 600, 1e-6 };
|
||||
|
||||
faultdef NOT_CONVERGED;
|
||||
|
||||
<*
|
||||
Compute binomial coefficient C(n, k)
|
||||
*>
|
||||
fn double binomial_coefficient(int n, int k)
|
||||
{
|
||||
if (k < 0 || k > n) return 0.0;
|
||||
if (k == 0 || k == n) return 1.0;
|
||||
|
||||
// Use symmetry.
|
||||
if (k > n - k) k = n - k;
|
||||
|
||||
double result = 1.0;
|
||||
for (int i = 0; i < k; i++)
|
||||
{
|
||||
result *= (double)(n - i);
|
||||
result /= (double)(i + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
<*
|
||||
Natural logarithm of factorial.
|
||||
*>
|
||||
fn double ln_factorial(int n)
|
||||
{
|
||||
if (n < 0) return double.nan;
|
||||
if (n <= 1) return 0.0;
|
||||
return math::lgamma((double)(n + 1));
|
||||
}
|
||||
|
||||
<*
|
||||
Beta function B(a, b)
|
||||
@require a > 0
|
||||
@require b > 0
|
||||
*>
|
||||
fn double beta_function(double a, double b)
|
||||
{
|
||||
return math::exp(math::lgamma(a) + math::lgamma(b) - math::lgamma(a + b));
|
||||
}
|
||||
|
||||
<*
|
||||
Regularized incomplete beta function I_x(a, b)
|
||||
Based on https://github.com/codeplea/incbeta/blob/master/incbeta.c
|
||||
*>
|
||||
fn double incomplete_beta(double x, double a, double b, ConvergenceControl conv = DEFAULT_CONV)
|
||||
{
|
||||
if (x < 0.0 || x > 1.0) return double.nan;
|
||||
if (x == 0.0) return 0.0;
|
||||
if (x == 1.0) return 1.0;
|
||||
|
||||
if (x > (a + 1.0)/(a + b + 2.0)) return 1.0 - incomplete_beta(1.0 - x, b,a);
|
||||
|
||||
const double TINY = 1e-30;
|
||||
|
||||
// Find the first part before the continued fraction.
|
||||
double lbeta_ab = math::lgamma(a) + math::lgamma(b) - math::lgamma(a + b);
|
||||
double front = math::exp(math::ln(x) * a + math::ln(1.0 - x) * b - lbeta_ab) / a;
|
||||
|
||||
// Use Lentz's algorithm to evaluate the continued fraction.
|
||||
double f = 1.0;
|
||||
double c = 1.0;
|
||||
double d = 0.0;
|
||||
|
||||
usz m;
|
||||
for (usz i = 0; i <= conv.max_iter; ++i)
|
||||
{
|
||||
m = i/2;
|
||||
|
||||
double numerator;
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
numerator = 1.0;
|
||||
}
|
||||
else if (i % 2 == 0)
|
||||
{
|
||||
numerator = (m * (b - m) * x) / ((a + 2.0 * m - 1.0) * (a + 2.0 * m));
|
||||
}
|
||||
else
|
||||
{
|
||||
numerator = -((a + m) * (a + b + m) * x) / ((a + 2.0 * m) * (a + 2.0 * m + 1));
|
||||
}
|
||||
|
||||
d = 1.0 + numerator * d;
|
||||
if (math::abs(d) < TINY) d = TINY;
|
||||
d = 1.0 / d;
|
||||
|
||||
c = 1.0 + numerator / c;
|
||||
if (math::abs(c) < TINY) c = TINY;
|
||||
|
||||
double cd = c*d;
|
||||
f *= cd;
|
||||
|
||||
if (math::abs(1.0 - cd) < conv.epsilon) return front * (f - 1.0);
|
||||
}
|
||||
|
||||
return double.nan; // Not convered.
|
||||
}
|
||||
|
||||
<*
|
||||
Calculates the p-th quantile for a continuous distribution using bisection.
|
||||
@return? NOT_CONVERGED
|
||||
*>
|
||||
fn double? bisection_search(ContinuousDistribution dist, double low, double high, double p,
|
||||
ConvergenceControl conv = DEFAULT_CONV)
|
||||
{
|
||||
// Expand upper bound if needed.
|
||||
while (dist.cdf(high) < p) high *= 2.0;
|
||||
|
||||
// Bisection search.
|
||||
for (usz i = 0; i < conv.max_iter; i++)
|
||||
{
|
||||
double mid = (low + high) * 0.5;
|
||||
if (high - low < conv.epsilon) return mid;
|
||||
|
||||
if (dist.cdf(mid) < p)
|
||||
{
|
||||
low = mid;
|
||||
}
|
||||
else
|
||||
{
|
||||
high = mid;
|
||||
}
|
||||
}
|
||||
return NOT_CONVERGED~;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculates the p-th quantile for a continuous distribution using Newton-Raphson.
|
||||
@return? NOT_CONVERGED
|
||||
*>
|
||||
fn double? newton_raphson(ContinuousDistribution dist, double x, double p,
|
||||
ConvergenceControl conv = DEFAULT_CONV)
|
||||
{
|
||||
double delta, pdf;
|
||||
for (usz i = 0; i < conv.max_iter; i++)
|
||||
{
|
||||
pdf = dist.pdf(x);
|
||||
if (pdf < 1e-300) break;
|
||||
|
||||
delta = (dist.cdf(x) - p) / pdf;
|
||||
x -= delta;
|
||||
|
||||
if (math::abs(delta) < conv.epsilon) return x;
|
||||
}
|
||||
return NOT_CONVERGED~;
|
||||
}
|
||||
|
||||
<*
|
||||
Calculates the p-th quantile for a continuous distribution.
|
||||
@require p >= 0.0 && p <= 1.0
|
||||
@require low < high
|
||||
*>
|
||||
fn double find_quantile(ContinuousDistribution dist, double low, double high, double p)
|
||||
{
|
||||
double mid = bisection_search(dist, low, high, p, RELAXED_CONV) ?? (low + high) * 0.5;
|
||||
return newton_raphson(dist, mid, p, DEFAULT_CONV) ?? mid;
|
||||
}
|
||||
|
||||
<*
|
||||
Generate a chi-squared random sample.
|
||||
@param k : "Degrees of freedom"
|
||||
@require k > 0.0
|
||||
*>
|
||||
fn double chi_squared_sample(double k, Random rand)
|
||||
{
|
||||
// Sum of k squared standard normals.
|
||||
NormalDist std_normal = normal(0.0, 1.0);
|
||||
double sum = 0.0;
|
||||
|
||||
int k_int = (int)k;
|
||||
for (int i = 0; i < k_int; i++)
|
||||
{
|
||||
double z = std_normal.random(rand);
|
||||
sum += z * z;
|
||||
}
|
||||
|
||||
// Handle fractional degrees of freedom.
|
||||
double frac = k - (double)k_int;
|
||||
if (frac > 0.0)
|
||||
{
|
||||
double z = std_normal.random(rand);
|
||||
sum += frac * z * z;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
<*
|
||||
Inverse of the error function (math::erf).
|
||||
Based on Golang's math.Erfinv.
|
||||
*>
|
||||
fn double inverse_erf(double x)
|
||||
{
|
||||
if (x < -1 || x > 1)
|
||||
{
|
||||
return double.nan;
|
||||
}
|
||||
else if (x == 1.0)
|
||||
{
|
||||
return double.inf;
|
||||
}
|
||||
else if (x == -1.0)
|
||||
{
|
||||
return -double.inf;
|
||||
}
|
||||
|
||||
const double LN2 = 6.931471805599453094172321214581e-1;
|
||||
|
||||
const double A0 = 1.1975323115670912564578e0;
|
||||
const double A1 = 4.7072688112383978012285e1;
|
||||
const double A2 = 6.9706266534389598238465e2;
|
||||
const double A3 = 4.8548868893843886794648e3;
|
||||
const double A4 = 1.6235862515167575384252e4;
|
||||
const double A5 = 2.3782041382114385731252e4;
|
||||
const double A6 = 1.1819493347062294404278e4;
|
||||
const double A7 = 8.8709406962545514830200e2;
|
||||
|
||||
const double B0 = 1.0000000000000000000e0;
|
||||
const double B1 = 4.2313330701600911252e1;
|
||||
const double B2 = 6.8718700749205790830e2;
|
||||
const double B3 = 5.3941960214247511077e3;
|
||||
const double B4 = 2.1213794301586595867e4;
|
||||
const double B5 = 3.9307895800092710610e4;
|
||||
const double B6 = 2.8729085735721942674e4;
|
||||
const double B7 = 5.2264952788528545610e3;
|
||||
|
||||
const double C0 = 1.42343711074968357734e0;
|
||||
const double C1 = 4.63033784615654529590e0;
|
||||
const double C2 = 5.76949722146069140550e0;
|
||||
const double C3 = 3.64784832476320460504e0;
|
||||
const double C4 = 1.27045825245236838258e0;
|
||||
const double C5 = 2.41780725177450611770e-1;
|
||||
const double C6 = 2.27238449892691845833e-2;
|
||||
const double C7 = 7.74545014278341407640e-4;
|
||||
|
||||
const double D0 = 1.4142135623730950488016887e0;
|
||||
const double D1 = 2.9036514445419946173133295e0;
|
||||
const double D2 = 2.3707661626024532365971225e0;
|
||||
const double D3 = 9.7547832001787427186894837e-1;
|
||||
const double D4 = 2.0945065210512749128288442e-1;
|
||||
const double D5 = 2.1494160384252876777097297e-2;
|
||||
const double D6 = 7.7441459065157709165577218e-4;
|
||||
const double D7 = 1.4859850019840355905497876e-9;
|
||||
|
||||
const double E0 = 6.65790464350110377720e0;
|
||||
const double E1 = 5.46378491116411436990e0;
|
||||
const double E2 = 1.78482653991729133580e0;
|
||||
const double E3 = 2.96560571828504891230e-1;
|
||||
const double E4 = 2.65321895265761230930e-2;
|
||||
const double E5 = 1.24266094738807843860e-3;
|
||||
const double E6 = 2.71155556874348757815e-5;
|
||||
const double E7 = 2.01033439929228813265e-7;
|
||||
|
||||
const double F0 = 1.414213562373095048801689e0;
|
||||
const double F1 = 8.482908416595164588112026e-1;
|
||||
const double F2 = 1.936480946950659106176712e-1;
|
||||
const double F3 = 2.103693768272068968719679e-2;
|
||||
const double F4 = 1.112800997078859844711555e-3;
|
||||
const double F5 = 2.611088405080593625138020e-5;
|
||||
const double F6 = 2.010321207683943062279931e-7;
|
||||
const double F7 = 2.891024605872965461538222e-15;
|
||||
|
||||
double sign = 1.0;
|
||||
if (x < 0)
|
||||
{
|
||||
x = -x;
|
||||
sign = -1.0;
|
||||
}
|
||||
|
||||
double ans;
|
||||
if (x <= 0.85)
|
||||
{
|
||||
double r = 0.180625 - 0.25 * x *x;
|
||||
double z1 = ((((((A7 * r + A6) * r + A5) * r + A4) * r + A3) * r + A2) * r + A1) * r + A0;
|
||||
double z2 = ((((((B7 * r + B6) * r + B5) * r + B4) * r + B3) * r + B2) * r + B1) * r + B0;
|
||||
ans = (x * z1) / z2;
|
||||
}
|
||||
else
|
||||
{
|
||||
double z1, z2;
|
||||
double r = math::sqrt(LN2 - math::ln(1.0 - x));
|
||||
if (r <= 5.0)
|
||||
{
|
||||
r -= 1.6;
|
||||
z1 = ((((((C7 * r + C6) * r + C5) * r + C4) * r + C3) * r + C2) * r + C1) * r + C0;
|
||||
z2 = ((((((D7 * r + D6) * r + D5) * r + D4) * r + D3) * r + D2) * r + D1) * r + D0;
|
||||
}
|
||||
else
|
||||
{
|
||||
r -= 5.0;
|
||||
z1 = ((((((E7 * r + E6) * r + E5) * r + E4) * r + E3) * r + E2) * r + E1) * r + E0;
|
||||
z2 = ((((((F7 * r + F6) * r + F5) * r + F4) * r + F3) * r + F2) * r + F1) * r + F0;
|
||||
}
|
||||
ans = z1 / z2;
|
||||
}
|
||||
return sign * ans;
|
||||
}
|
||||
|
||||
<*
|
||||
Regularized Lower incomplete gamma function.
|
||||
Returns nan when not converged.
|
||||
@param s : "Shape parameter"
|
||||
@param x : "Upper limit of integration"
|
||||
@require s > 0.0 : "s must be positive."
|
||||
@require x >= 0.0 : "x must be non-negative."
|
||||
*>
|
||||
fn double lower_incomplete_gamma(double s, double x)
|
||||
{
|
||||
if (x == 0.0) return 0.0;
|
||||
if (x == double.inf) return 1.0;
|
||||
|
||||
// Use series expansion for x < s+1
|
||||
if (x < s + 1.0)
|
||||
{
|
||||
return incomplete_gamma_series_expansion(s, x) ?? double.nan;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1.0 - incomplete_gamma_continued_fraction(s, x) ?? double.nan;
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Lower incomplete gamma series expansion.
|
||||
@return? NOT_CONVERGED
|
||||
*>
|
||||
fn double? incomplete_gamma_series_expansion(double s, double x, ConvergenceControl conv = DEFAULT_CONV)
|
||||
{
|
||||
double lnpre = s * math::ln(x) - x - math::lgamma(s + 1.0);
|
||||
if (lnpre < -708.0) return 0.0; // result underflows to zero
|
||||
|
||||
double term = 1.0;
|
||||
double sum = 1.0;
|
||||
double ap = s;
|
||||
|
||||
for (int n = 1; n <= conv.max_iter; n++)
|
||||
{
|
||||
ap += 1.0;
|
||||
term *= x / ap;
|
||||
sum += term;
|
||||
if (math::abs(term) < math::abs(sum) * conv.epsilon)
|
||||
{
|
||||
return math::exp(lnpre) * sum;
|
||||
}
|
||||
}
|
||||
|
||||
// Non-convergence.
|
||||
return NOT_CONVERGED~;
|
||||
}
|
||||
|
||||
<*
|
||||
Modified Lentz continued fraction.
|
||||
@return? NOT_CONVERGED
|
||||
*>
|
||||
fn double? incomplete_gamma_continued_fraction(double s, double x, ConvergenceControl conv = DEFAULT_CONV)
|
||||
{
|
||||
double lnpre = s * math::ln(x) - x - math::lgamma(s);
|
||||
|
||||
// Lentz initialisation: fpmin guards against division by zero.
|
||||
double fpmin = 1e-300;
|
||||
|
||||
double b = x + 1.0 - s;
|
||||
double c = 1.0 / fpmin;
|
||||
double d = 1.0 / b;
|
||||
double h = d;
|
||||
|
||||
for (int i = 1; i <= conv.max_iter; i++)
|
||||
{
|
||||
double an = (double)i * (s - (double)i);
|
||||
b += 2.0;
|
||||
|
||||
d = an * d + b;
|
||||
if (math::abs(d) < fpmin) d = fpmin;
|
||||
|
||||
c = b + an / c;
|
||||
if (math::abs(c) < fpmin) c = fpmin;
|
||||
|
||||
d = 1.0 / d;
|
||||
double delta = d * c;
|
||||
h *= delta;
|
||||
|
||||
if (math::abs(delta - 1.0) < conv.epsilon)
|
||||
{
|
||||
return math::exp(lnpre) * h;
|
||||
}
|
||||
}
|
||||
|
||||
// Non-convergence.
|
||||
return NOT_CONVERGED~;
|
||||
}
|
||||
@@ -89,9 +89,12 @@ macro abs(x) => $$abs(x);
|
||||
*>
|
||||
macro bool is_approx(x, y, eps)
|
||||
{
|
||||
if (x == y) return true;
|
||||
if (is_nan(x) || is_nan(y)) return false;
|
||||
return abs(x - y) <= eps;
|
||||
return fn bool($typeof(x) a, $typeof(y) b, $typeof(eps) eps)
|
||||
{
|
||||
if (a == b) return true;
|
||||
if (is_nan(a) || is_nan(b)) return false;
|
||||
return abs(a - b) <= eps;
|
||||
}(x, y, eps);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -100,9 +103,12 @@ macro bool is_approx(x, y, eps)
|
||||
*>
|
||||
macro bool is_approx_rel(x, y, eps)
|
||||
{
|
||||
if (x == y) return true;
|
||||
if (is_nan(x) || is_nan(y)) return false;
|
||||
return abs(x - y) <= eps * max(abs(x), abs(y));
|
||||
return fn bool($typeof(x) a, $typeof(y) b, $typeof(eps) eps)
|
||||
{
|
||||
if (a == b) return true;
|
||||
if (is_nan(a) || is_nan(b)) return false;
|
||||
return abs(a - b) <= eps * max(abs(a), abs(b));
|
||||
}(x, y, eps);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -118,6 +124,42 @@ macro sign(x)
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
*>
|
||||
macro erf(x)
|
||||
{
|
||||
$if $typeof(x) == float:
|
||||
return _erff(x);
|
||||
$else
|
||||
return _erf(x);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
*>
|
||||
macro tgamma(x)
|
||||
{
|
||||
$if $typeof(x) == float:
|
||||
return _tgammaf(x);
|
||||
$else
|
||||
return _tgamma(x);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
*>
|
||||
macro lgamma(x)
|
||||
{
|
||||
$if $typeof(x) == float:
|
||||
return _lgammaf(x);
|
||||
$else
|
||||
return _lgamma(x);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
|
||||
@require values::@is_int(y) || values::@is_float(y) : "Expected an integer or floating point value"
|
||||
@@ -474,7 +516,7 @@ macro round(x) => $$round(x);
|
||||
macro round_to_decimals(x, int decimal_places)
|
||||
{
|
||||
var div = $$pow_int(($typeof(x))10, decimal_places);
|
||||
return round(div * x) / div;
|
||||
return $$round(div * x) / div;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -997,11 +1039,29 @@ macro bool is_power_of_2(x)
|
||||
return x != 0 && (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
<*
|
||||
Returns the next power of two that is greater than or equal to x.
|
||||
|
||||
@require types::is_int($typeof(x)) : "Input must be an integer type"
|
||||
@require x >= 0 : "Input must be non-negative"
|
||||
*>
|
||||
macro next_power_of_2(x)
|
||||
{
|
||||
$typeof(x) y = 1;
|
||||
while (y < x) y += y;
|
||||
return y;
|
||||
if (x <= 1) return 1;
|
||||
if (x == 2) return 2;
|
||||
|
||||
$typeof(x) v = x - 1;
|
||||
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
|
||||
$if ($sizeof(x) >= 2): v |= v >> 8; $endif;
|
||||
$if ($sizeof(x) >= 4): v |= v >> 16; $endif;
|
||||
$if ($sizeof(x) >= 8): v |= v >> 32; $endif;
|
||||
$if ($sizeof(x) >= 16): v |= v >> 64; $endif;
|
||||
|
||||
return v + 1;
|
||||
}
|
||||
|
||||
macro equals_vec(v1, v2) @private
|
||||
@@ -1064,6 +1124,14 @@ extern fn float _acoshf(float x) @MathLibc("acoshf");
|
||||
extern fn float _asinhf(float x) @MathLibc("asinhf");
|
||||
extern fn float _atanhf(float x) @MathLibc("atanhf");
|
||||
|
||||
extern fn double _erf(double x) @MathLibc("erf");
|
||||
extern fn double _lgamma(double x) @MathLibc("lgamma");
|
||||
extern fn double _tgamma(double x) @MathLibc("tgamma");
|
||||
|
||||
extern fn float _erff(float x) @MathLibc("erf");
|
||||
extern fn float _lgammaf(float x) @MathLibc("lgammaf");
|
||||
extern fn float _tgammaf(float x) @MathLibc("tgammaf");
|
||||
|
||||
|
||||
fn double _frexp(double x, int* e)
|
||||
{
|
||||
|
||||
77
lib/std/math/math_nolibc/erf.c3
Normal file
77
lib/std/math/math_nolibc/erf.c3
Normal file
@@ -0,0 +1,77 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
<*
|
||||
Error function for float.
|
||||
*>
|
||||
fn float _erff(float x)
|
||||
{
|
||||
// Handle special cases.
|
||||
if (x == 0.0) return 0.0;
|
||||
if (x == float.inf) return 1.0;
|
||||
if (x == -float.inf) return -1.0;
|
||||
|
||||
// Use symmetry: erf(-x) = -erf(x)
|
||||
float sign = 1.0;
|
||||
if (x < 0.0)
|
||||
{
|
||||
x = -x;
|
||||
sign = -1.0;
|
||||
}
|
||||
|
||||
const float P1 = 0.4067420160f;
|
||||
const float P2 = 0.0072279182f;
|
||||
const float A1 = 0.3168798904f;
|
||||
const float A2 = -0.1383293141f;
|
||||
const float A3 = 1.0868083034f;
|
||||
const float A4 = -1.1169415512f;
|
||||
const float A5 = 1.2064490307f;
|
||||
const float A6 = -0.3931277152f;
|
||||
const float A7 = 0.0382613542f;
|
||||
|
||||
float t = 1.0f / (1.0f + P1 * x + P2 * x * x);
|
||||
float t2 = t * t;
|
||||
float sum = A1 * t + A2 * t2 + A3 * t2 * t + A4 * t2 * t2;
|
||||
sum += A5 * t2 * t2 * t + A6 * t2 * t2 * t2 + A7 * t2 * t2 * t2 * t;
|
||||
|
||||
// For intermediate x, use continued fraction.
|
||||
return sign * (1.0f - sum * math::exp(-x * x));
|
||||
}
|
||||
|
||||
<*
|
||||
Error function for double.
|
||||
*>
|
||||
fn double _erf(double x)
|
||||
{
|
||||
// Handle special cases.
|
||||
if (x == 0.0) return 0.0;
|
||||
if (x == double.inf) return 1.0;
|
||||
if (x == -double.inf) return -1.0;
|
||||
|
||||
// Use symmetry: erf(-x) = -erf(x)
|
||||
double sign = 1.0;
|
||||
if (x < 0.0)
|
||||
{
|
||||
x = -x;
|
||||
sign = -1.0;
|
||||
}
|
||||
|
||||
const double P1 = 0.406742016006509;
|
||||
const double P2 = 0.0072279182302319;
|
||||
const double A1 = 0.316879890481381;
|
||||
const double A2 = -0.138329314150635;
|
||||
const double A3 = 1.08680830347054;
|
||||
const double A4 = -1.11694155120396;
|
||||
const double A5 = 1.20644903073232;
|
||||
const double A6 = -0.393127715207728;
|
||||
const double A7 = 0.0382613542530727;
|
||||
|
||||
double t = 1.0 / (1.0 + P1 * x + P2 * x * x);
|
||||
double t2 = t * t;
|
||||
double sum = A1 * t + A2 * t2 + A3 * t2 * t + A4 * t2 * t2;
|
||||
sum += A5 * t2 * t2 * t + A6 * t2 * t2 * t2 + A7 * t2 * t2 * t2 * t;
|
||||
|
||||
// For intermediate x, use continued fraction.
|
||||
return sign * (1.0 - sum * math::exp(-x * x));
|
||||
}
|
||||
|
||||
|
||||
70
lib/std/math/math_nolibc/gamma.c3
Normal file
70
lib/std/math/math_nolibc/gamma.c3
Normal file
@@ -0,0 +1,70 @@
|
||||
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
|
||||
|
||||
// Constants
|
||||
const double SQRT_PI = 1.7724538509055160272981674833411451;
|
||||
const double LN_SQRT_2PI = 0.9189385332046727417803297364056176;
|
||||
|
||||
<*
|
||||
Natural logarithm of the gamma function using the Lanczos approximation.
|
||||
@require x > 0.0 : "x must be positive."
|
||||
*>
|
||||
fn double lgamma(double x)
|
||||
{
|
||||
// Lanczos approximation coefficients (g = 7, n = 9)
|
||||
const double[*] LANCZOS_COEF = {
|
||||
0.99999999999980993,
|
||||
676.5203681218851,
|
||||
-1259.1392167224028,
|
||||
771.32342877765313,
|
||||
-176.61502916214059,
|
||||
12.507343278686905,
|
||||
-0.13857109526572012,
|
||||
9.9843695780195716e-6,
|
||||
1.5056327351493116e-7
|
||||
};
|
||||
const double LANCZOS_G = 7.0;
|
||||
|
||||
// For small x, use the reflection formula.
|
||||
if (x < 0.5)
|
||||
{
|
||||
return math::ln(math::PI) - math::ln(math::sin(math::PI * x)) - lgamma(1.0 - x);
|
||||
}
|
||||
|
||||
// Shift to use approximation around x >= 1.5
|
||||
x -= 1.0;
|
||||
|
||||
double base = x + LANCZOS_G + 0.5;
|
||||
double sum = LANCZOS_COEF[0];
|
||||
|
||||
for (int i = 1; i < 9; i++)
|
||||
{
|
||||
sum += LANCZOS_COEF[i] / (x + (double)i);
|
||||
}
|
||||
|
||||
return LN_SQRT_2PI + math::ln(sum) + (x + 0.5) * math::ln(base) - base;
|
||||
}
|
||||
|
||||
<*
|
||||
Gamma function.
|
||||
Valid for x > 0 and some negative non-integer values.
|
||||
*>
|
||||
fn double tgamma(double x)
|
||||
{
|
||||
// Handle special cases.
|
||||
if (x == 0.0) return double.inf;
|
||||
|
||||
// Check for negative integers (poles).
|
||||
if (x < 0.0 && x == math::floor(x))
|
||||
{
|
||||
return double.nan;
|
||||
}
|
||||
|
||||
// For positive values, use exp(lgamma(x)).
|
||||
if (x > 0.0)
|
||||
{
|
||||
return math::exp(lgamma(x));
|
||||
}
|
||||
|
||||
// For negative non-integer values, use the reflection formula.
|
||||
return math::PI / (math::sin(math::PI * x) * tgamma(1.0 - x));
|
||||
}
|
||||
42
lib/std/math/random/math.xorshiro.c3
Normal file
42
lib/std/math/random/math.xorshiro.c3
Normal file
@@ -0,0 +1,42 @@
|
||||
module std::math::random;
|
||||
|
||||
struct Xorshiro128PPRandom (Random)
|
||||
{
|
||||
uint[4] state;
|
||||
}
|
||||
|
||||
<*
|
||||
@require seed.len > 0
|
||||
*>
|
||||
fn void Xorshiro128PPRandom.set_seed(&self, char[] seed) @dynamic
|
||||
{
|
||||
self.state = random::make_seed(uint[4], seed);
|
||||
}
|
||||
|
||||
// Xorshiro128++ implementation
|
||||
fn uint Xorshiro128PPRandom.next_int(&self) @dynamic
|
||||
{
|
||||
uint result = (self.state[0] + self.state[3]).rotl(7) + self.state[0];
|
||||
|
||||
uint t = self.state[1] << 9U;
|
||||
|
||||
self.state[2] ^= self.state[0];
|
||||
self.state[3] ^= self.state[1];
|
||||
self.state[1] ^= self.state[2];
|
||||
self.state[0] ^= self.state[3];
|
||||
|
||||
self.state[2] ^= t;
|
||||
|
||||
self.state[3] = self.state[3].rotl(11);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
<*
|
||||
@require bytes.len > 0
|
||||
*>
|
||||
fn void Xorshiro128PPRandom.next_bytes(&self, char[] bytes) @dynamic => @random_value_to_bytes(self.next_int, bytes);
|
||||
fn uint128 Xorshiro128PPRandom.next_int128(&self) @dynamic => @long_to_int128(self.next_long());
|
||||
fn ulong Xorshiro128PPRandom.next_long(&self) @dynamic => @int_to_long(self.next_int());
|
||||
fn ushort Xorshiro128PPRandom.next_short(&self) @dynamic => (ushort)self.next_int();
|
||||
fn char Xorshiro128PPRandom.next_byte(&self) @dynamic => (char)self.next_int();
|
||||
@@ -3,9 +3,9 @@ import std::io;
|
||||
|
||||
enum IpProtocol : char (AIFamily ai_family)
|
||||
{
|
||||
UNSPECIFIED = os::AF_UNSPEC,
|
||||
IPV4 = os::AF_INET,
|
||||
IPV6 = os::AF_INET6,
|
||||
UNSPECIFIED { os::AF_UNSPEC },
|
||||
IPV4 { os::AF_INET },
|
||||
IPV6 { os::AF_INET6 },
|
||||
}
|
||||
|
||||
struct InetAddress (Printable)
|
||||
|
||||
@@ -34,22 +34,22 @@ struct AddrInfo
|
||||
|
||||
const PLATFORM_O_NONBLOCK @if(!$defined(PLATFORM_O_NONBLOCK)) = 0;
|
||||
|
||||
const AISockType SOCK_STREAM = 1; // Stream
|
||||
const AISockType SOCK_DGRAM = 2; // Datagram
|
||||
const AISockType SOCK_RAW = 3; // Raw
|
||||
const AISockType SOCK_RDM = 4; // Reliably delivered
|
||||
const AISockType SOCK_SEQPACKET = 5; // Sequential packet
|
||||
const AISockType SOCK_STREAM = (AISockType)1; // Stream
|
||||
const AISockType SOCK_DGRAM = (AISockType)2; // Datagram
|
||||
const AISockType SOCK_RAW = (AISockType)3; // Raw
|
||||
const AISockType SOCK_RDM = (AISockType)4; // Reliably delivered
|
||||
const AISockType SOCK_SEQPACKET = (AISockType)5; // Sequential packet
|
||||
|
||||
const AIFlags AI_PASSIVE = 0x1;
|
||||
const AIFlags AI_CANONNAME = 0x2;
|
||||
const AIFlags AI_NUMERICHOST = 0x4;
|
||||
const AIFlags AI_PASSIVE = (AIFlags)0x1;
|
||||
const AIFlags AI_CANONNAME = (AIFlags)0x2;
|
||||
const AIFlags AI_NUMERICHOST = (AIFlags)0x4;
|
||||
|
||||
const AIFamily AF_UNSPEC = 0;
|
||||
const AIFamily AF_UNIX = 1;
|
||||
const AIFamily AF_INET = 2;
|
||||
const AIFamily AF_INET6 = PLATFORM_AF_INET6;
|
||||
const AIFamily AF_IPX = PLATFORM_AF_IPX;
|
||||
const AIFamily AF_APPLETALK = PLATFORM_AF_APPLETALK;
|
||||
const AIFamily AF_UNSPEC = (AIFamily)0;
|
||||
const AIFamily AF_UNIX = (AIFamily)1;
|
||||
const AIFamily AF_INET = (AIFamily)2;
|
||||
const AIFamily AF_INET6 = (AIFamily)PLATFORM_AF_INET6;
|
||||
const AIFamily AF_IPX = (AIFamily)PLATFORM_AF_IPX;
|
||||
const AIFamily AF_APPLETALK = (AIFamily)PLATFORM_AF_APPLETALK;
|
||||
|
||||
const O_NONBLOCK = PLATFORM_O_NONBLOCK;
|
||||
|
||||
|
||||
@@ -1,50 +1,50 @@
|
||||
module std::net::os @if(env::DARWIN);
|
||||
import libc;
|
||||
|
||||
const AIFlags AI_NUMERICSERV = 0x1000;
|
||||
const AIFlags AI_ALL = 0x100;
|
||||
const AIFlags AI_V4MAPPED_CFG = 0x200;
|
||||
const AIFlags AI_ADDRCONFIG = 0x400;
|
||||
const AIFlags AI_V4MAPPED = 0x800;
|
||||
const AIFlags AI_UNUSABLE = 0x10000000;
|
||||
const AIFlags AI_DEFAULT = AI_V4MAPPED_CFG | AI_ADDRCONFIG;
|
||||
const AIFlags AI_NUMERICSERV = (AIFlags)0x1000;
|
||||
const AIFlags AI_ALL = (AIFlags)0x100;
|
||||
const AIFlags AI_V4MAPPED_CFG = (AIFlags)0x200;
|
||||
const AIFlags AI_ADDRCONFIG = (AIFlags)0x400;
|
||||
const AIFlags AI_V4MAPPED = (AIFlags)0x800;
|
||||
const AIFlags AI_UNUSABLE = (AIFlags)0x10000000;
|
||||
const AIFlags AI_DEFAULT = AI_V4MAPPED_CFG | AI_ADDRCONFIG;
|
||||
|
||||
const AIFamily PLATFORM_AF_IMPLINK = 3;
|
||||
const AIFamily PLATFORM_AF_PUP = 4;
|
||||
const AIFamily PLATFORM_AF_CHAOS = 5;
|
||||
const AIFamily PLATFORM_AF_NS = 6;
|
||||
const AIFamily PLATFORM_AF_ISO = 7;
|
||||
const AIFamily PLATFORM_AF_ECMA = 8;
|
||||
const AIFamily PLATFORM_AF_DATAKIT = 9;
|
||||
const AIFamily PLATFORM_AF_CCITT = 10;
|
||||
const AIFamily PLATFORM_AF_SNA = 11;
|
||||
const AIFamily PLATFORM_AF_DECNET = 12;
|
||||
const AIFamily PLATFORM_AF_DLI = 13;
|
||||
const AIFamily PLATFORM_AF_LAT = 14;
|
||||
const AIFamily PLATFORM_AF_HYLINK = 15;
|
||||
const AIFamily PLATFORM_AF_APPLETALK = 16;
|
||||
const AIFamily PLATFORM_AF_ROUTE = 17;
|
||||
const AIFamily PLATFORM_AF_LINK = 18;
|
||||
const AIFamily PLATFORM_PSEUDO_AF_XTP = 19;
|
||||
const AIFamily PLATFORM_AF_COIP = 20;
|
||||
const AIFamily PLATFORM_AF_CNT = 21;
|
||||
const AIFamily PLATFORM_PSEUDO_AF_RTIP = 22;
|
||||
const AIFamily PLATFORM_AF_IPX = 23;
|
||||
const AIFamily PLATFORM_AF_SIP = 24;
|
||||
const AIFamily PLATFORM_PSEUDO_AF_PIP = 25;
|
||||
const AIFamily PLATFORM_AF_NDRV = 27;
|
||||
const AIFamily PLATFORM_AF_ISDN = 28;
|
||||
const AIFamily PLATFORM_PSEUDO_AF_KEY = 29;
|
||||
const AIFamily PLATFORM_AF_INET6 = 30;
|
||||
const AIFamily PLATFORM_AF_NATM = 31;
|
||||
const AIFamily PLATFORM_AF_SYSTEM = 32;
|
||||
const AIFamily PLATFORM_AF_NETBIOS = 33;
|
||||
const AIFamily PLATFORM_AF_PPP = 34;
|
||||
const AIFamily PLATFORM_PSEUDO_AF_HDRCMPLT = 35;
|
||||
const AIFamily PLATFORM_AF_IEEE80211 = 37;
|
||||
const AIFamily PLATFORM_AF_UTUN = 38;
|
||||
const AIFamily PLATFORM_AF_VSOCK = 40;
|
||||
const AIFamily PLATFORM_AF_MAX = 41;
|
||||
const AIFamily PLATFORM_AF_IMPLINK = (AIFamily)3;
|
||||
const AIFamily PLATFORM_AF_PUP = (AIFamily)4;
|
||||
const AIFamily PLATFORM_AF_CHAOS = (AIFamily)5;
|
||||
const AIFamily PLATFORM_AF_NS = (AIFamily)6;
|
||||
const AIFamily PLATFORM_AF_ISO = (AIFamily)7;
|
||||
const AIFamily PLATFORM_AF_ECMA = (AIFamily)8;
|
||||
const AIFamily PLATFORM_AF_DATAKIT = (AIFamily)9;
|
||||
const AIFamily PLATFORM_AF_CCITT = (AIFamily)10;
|
||||
const AIFamily PLATFORM_AF_SNA = (AIFamily)11;
|
||||
const AIFamily PLATFORM_AF_DECNET = (AIFamily)12;
|
||||
const AIFamily PLATFORM_AF_DLI = (AIFamily)13;
|
||||
const AIFamily PLATFORM_AF_LAT = (AIFamily)14;
|
||||
const AIFamily PLATFORM_AF_HYLINK = (AIFamily)15;
|
||||
const AIFamily PLATFORM_AF_APPLETALK = (AIFamily)16;
|
||||
const AIFamily PLATFORM_AF_ROUTE = (AIFamily)17;
|
||||
const AIFamily PLATFORM_AF_LINK = (AIFamily)18;
|
||||
const AIFamily PLATFORM_PSEUDO_AF_XTP = (AIFamily)19;
|
||||
const AIFamily PLATFORM_AF_COIP = (AIFamily)20;
|
||||
const AIFamily PLATFORM_AF_CNT = (AIFamily)21;
|
||||
const AIFamily PLATFORM_PSEUDO_AF_RTIP = (AIFamily)22;
|
||||
const AIFamily PLATFORM_AF_IPX = (AIFamily)23;
|
||||
const AIFamily PLATFORM_AF_SIP = (AIFamily)24;
|
||||
const AIFamily PLATFORM_PSEUDO_AF_PIP = (AIFamily)25;
|
||||
const AIFamily PLATFORM_AF_NDRV = (AIFamily)27;
|
||||
const AIFamily PLATFORM_AF_ISDN = (AIFamily)28;
|
||||
const AIFamily PLATFORM_PSEUDO_AF_KEY = (AIFamily)29;
|
||||
const AIFamily PLATFORM_AF_INET6 = (AIFamily)30;
|
||||
const AIFamily PLATFORM_AF_NATM = (AIFamily)31;
|
||||
const AIFamily PLATFORM_AF_SYSTEM = (AIFamily)32;
|
||||
const AIFamily PLATFORM_AF_NETBIOS = (AIFamily)33;
|
||||
const AIFamily PLATFORM_AF_PPP = (AIFamily)34;
|
||||
const AIFamily PLATFORM_PSEUDO_AF_HDRCMPLT = (AIFamily)35;
|
||||
const AIFamily PLATFORM_AF_IEEE80211 = (AIFamily)37;
|
||||
const AIFamily PLATFORM_AF_UTUN = (AIFamily)38;
|
||||
const AIFamily PLATFORM_AF_VSOCK = (AIFamily)40;
|
||||
const AIFamily PLATFORM_AF_MAX = (AIFamily)41;
|
||||
|
||||
const int PLATFORM_O_NONBLOCK = 0x04;
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ macro void @loop_over_ai(AddrInfo* ai; @body(NativeSocket fd, AddrInfo* ai))
|
||||
|
||||
const Duration POLL_FOREVER = (Duration)-1;
|
||||
|
||||
enum PollSubscribe : const ushort
|
||||
constdef PollSubscribe
|
||||
{
|
||||
ANY_READ = os::POLLIN,
|
||||
PRIO_READ = os::POLLPRI,
|
||||
@@ -44,7 +44,7 @@ const PollSubscribe SUBSCRIBE_ANY_WRITE = (PollSubscribe)os::POLLOUT;
|
||||
const PollSubscribe SUBSCRIBE_OOB_WRITE = (PollSubscribe)os::POLLWRBAND;
|
||||
const PollSubscribe SUBSCRIBE_WRITE = (PollSubscribe)os::POLLWRNORM;
|
||||
|
||||
enum PollEvent : const ushort
|
||||
constdef PollEvent : ushort
|
||||
{
|
||||
READ_PRIO = os::POLLPRI,
|
||||
READ_OOB = os::POLLRDBAND,
|
||||
@@ -109,12 +109,12 @@ macro Socket new_socket(fd, ai)
|
||||
|
||||
enum SocketOption : char (CInt value)
|
||||
{
|
||||
REUSEADDR = os::SO_REUSEADDR,
|
||||
REUSEPORT @if(!env::WIN32) = os::SO_REUSEPORT,
|
||||
KEEPALIVE = os::SO_KEEPALIVE,
|
||||
BROADCAST = os::SO_BROADCAST,
|
||||
OOBINLINE = os::SO_OOBINLINE,
|
||||
DONTROUTE = os::SO_DONTROUTE,
|
||||
REUSEADDR { os::SO_REUSEADDR },
|
||||
REUSEPORT @if(!env::WIN32) { os::SO_REUSEPORT },
|
||||
KEEPALIVE { os::SO_KEEPALIVE },
|
||||
BROADCAST { os::SO_BROADCAST },
|
||||
OOBINLINE { os::SO_OOBINLINE },
|
||||
DONTROUTE { os::SO_DONTROUTE },
|
||||
}
|
||||
|
||||
fn bool? Socket.get_broadcast(&self) => self.get_option(BROADCAST);
|
||||
@@ -193,9 +193,9 @@ fn usz? Socket.peek(&self, char[] bytes) @dynamic
|
||||
|
||||
enum SocketShutdownHow : (CInt native_value)
|
||||
{
|
||||
RECEIVE = env::WIN32 ??? libc::SD_RECEIVE : libc::SHUT_RD,
|
||||
SEND = env::WIN32 ??? libc::SD_SEND : libc::SHUT_WR,
|
||||
BOTH = env::WIN32 ??? libc::SD_BOTH : libc::SHUT_RDWR,
|
||||
RECEIVE { env::WIN32 ??? libc::SD_RECEIVE : libc::SHUT_RD },
|
||||
SEND { env::WIN32 ??? libc::SD_SEND : libc::SHUT_WR },
|
||||
BOTH { env::WIN32 ??? libc::SD_BOTH : libc::SHUT_RDWR },
|
||||
}
|
||||
|
||||
fn void? Socket.shutdown(&self, SocketShutdownHow how)
|
||||
|
||||
@@ -47,7 +47,7 @@ fn Socket? connect_with_timeout_from_addrinfo(AddrInfo* addrinfo, SocketOption[]
|
||||
$if env::WIN32:
|
||||
os::start_wsa()!;
|
||||
$endif
|
||||
Clock c = 0;
|
||||
Clock c;
|
||||
@loop_over_ai(addrinfo; NativeSocket sockfd, AddrInfo* ai)
|
||||
{
|
||||
apply_sockoptions(sockfd, options)!;
|
||||
@@ -75,7 +75,7 @@ fn Socket? connect_with_timeout_from_addrinfo(AddrInfo* addrinfo, SocketOption[]
|
||||
{
|
||||
c = clock::now();
|
||||
}
|
||||
Poll poll_request = { sockfd, SUBSCRIBE_ANY_WRITE, 0 };
|
||||
Poll poll_request = { sockfd, SUBSCRIBE_ANY_WRITE, (PollEvent)0 };
|
||||
if (!poll((&poll_request)[:1], timeout_left)!)
|
||||
{
|
||||
return CONNECTION_TIMED_OUT~;
|
||||
|
||||
@@ -7,12 +7,12 @@ import std::encoding::hex;
|
||||
|
||||
enum UrlEncodingMode : char (String allowed)
|
||||
{
|
||||
UNRESERVED = "-_.~", // section 2.3
|
||||
PATH = "$&+,/:;=@", // section 3.3
|
||||
HOST = "!$&'()*+,;=:[]", // section 3.2.2 (also include ':', '[', ']' for ipv6 hosts)
|
||||
USERPASS = ";:&=+$,", // section 3.2.1
|
||||
QUERY = "", // section 3.4
|
||||
FRAGMENT = "$&+,/:;=?@!()*", // section 4.1
|
||||
UNRESERVED { "-_.~" }, // section 2.3
|
||||
PATH { "$&+,/:;=@" }, // section 3.3
|
||||
HOST { "!$&'()*+,;=:[]" }, // section 3.2.2 (also include ':', '[', ']' for ipv6 hosts)
|
||||
USERPASS { ";:&=+$," }, // section 3.2.1
|
||||
QUERY { "" }, // section 3.4
|
||||
FRAGMENT { "$&+,/:;=?@!()*" }, // section 4.1
|
||||
}
|
||||
|
||||
faultdef INVALID_HEX;
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
module std::os::android @if(env::ANDROID);
|
||||
|
||||
|
||||
enum LogPriority : (CInt val)
|
||||
enum AndroidLogPriority : (CInt val)
|
||||
{
|
||||
ANDROID_LOG_UNKNOWN = 0,
|
||||
ANDROID_LOG_DEFAULT = 1,
|
||||
ANDROID_LOG_VERBOSE = 2,
|
||||
ANDROID_LOG_DEBUG = 3,
|
||||
ANDROID_LOG_INFO = 4,
|
||||
ANDROID_LOG_WARN = 5,
|
||||
ANDROID_LOG_ERROR = 6,
|
||||
ANDROID_LOG_FATAL = 7,
|
||||
ANDROID_LOG_SILENT = 8
|
||||
ANDROID_LOG_UNKNOWN { 0 },
|
||||
ANDROID_LOG_DEFAULT { 1 },
|
||||
ANDROID_LOG_VERBOSE { 2 },
|
||||
ANDROID_LOG_DEBUG { 3 },
|
||||
ANDROID_LOG_INFO { 4 },
|
||||
ANDROID_LOG_WARN { 5 },
|
||||
ANDROID_LOG_ERROR { 6 },
|
||||
ANDROID_LOG_FATAL { 7 },
|
||||
ANDROID_LOG_SILENT { 8 }
|
||||
}
|
||||
|
||||
enum LogId : (CInt val)
|
||||
enum AndroidLogId : (CInt val)
|
||||
{
|
||||
LOG_ID_MIN = 0,
|
||||
LOG_ID_MAIN = 0,
|
||||
LOG_ID_RADIO = 1,
|
||||
LOG_ID_EVENTS = 2,
|
||||
LOG_ID_SYSTEM = 3,
|
||||
LOG_ID_CRASH = 4,
|
||||
LOG_ID_STATS = 5,
|
||||
LOG_ID_SECURITY = 6,
|
||||
LOG_ID_KERNEL = 7,
|
||||
LOG_ID_MAX = 7,
|
||||
LOG_ID_DEFAULT = 0x7FFFFFFF
|
||||
LOG_ID_MIN { 0 },
|
||||
LOG_ID_MAIN { 0 },
|
||||
LOG_ID_RADIO { 1 },
|
||||
LOG_ID_EVENTS { 2 },
|
||||
LOG_ID_SYSTEM { 3 },
|
||||
LOG_ID_CRASH { 4 },
|
||||
LOG_ID_STATS { 5 },
|
||||
LOG_ID_SECURITY { 6 },
|
||||
LOG_ID_KERNEL { 7 },
|
||||
LOG_ID_MAX { 7 },
|
||||
LOG_ID_DEFAULT { 0x7FFFFFFF }
|
||||
}
|
||||
|
||||
struct LogMessage @packed
|
||||
struct AndroidLogMessage @packed
|
||||
{
|
||||
usz struct_size;
|
||||
CInt buffer_id;
|
||||
@@ -40,11 +40,11 @@ struct LogMessage @packed
|
||||
ZString message;
|
||||
}
|
||||
|
||||
extern fn CInt log_write(LogPriority prio, ZString tag, ZString text) @cname("__android_log_write");
|
||||
extern fn CInt log_print(LogPriority prio, ZString tag, ZString fmt, ...) @cname("__android_log_print");
|
||||
extern fn CInt log_write(AndroidLogPriority prio, ZString tag, ZString text) @cname("__android_log_write");
|
||||
extern fn CInt log_print(AndroidLogPriority prio, ZString tag, ZString fmt, ...) @cname("__android_log_print");
|
||||
extern fn void log_assert(ZString cond, ZString tag, ZString fmt, ...) @cname("__android_log_assert");
|
||||
|
||||
fn bool log_id_is_valid(LogId id)
|
||||
fn bool log_id_is_valid(AndroidLogId id)
|
||||
{
|
||||
return id >= LOG_ID_MIN && id < LOG_ID_MAX;
|
||||
}
|
||||
@@ -52,13 +52,13 @@ fn bool log_id_is_valid(LogId id)
|
||||
extern fn CInt log_buf_write(CInt bufID, CInt prio, ZString tag, ZString text) @cname("__android_log_buf_write");
|
||||
extern fn CInt log_buf_print(CInt bufID, CInt prio, ZString tag, ZString fmt, ...) @cname("__android_log_buf_print");
|
||||
|
||||
alias LoggerFunction = fn void(LogMessage* log_message);
|
||||
alias LoggerFunction = fn void(AndroidLogMessage* log_message);
|
||||
alias AborterFunction = fn void(ZString abort_message);
|
||||
|
||||
extern fn void log_write_log_message(LogMessage log_message) @cname("__android_log_write_log_message");
|
||||
extern fn void log_write_log_message(AndroidLogMessage log_message) @cname("__android_log_write_log_message");
|
||||
extern fn void log_set_logger(LoggerFunction logger) @cname("__android_log_set_logger");
|
||||
extern fn void log_logd_logger(LogMessage log_message) @cname("__android_log_logd_logger");
|
||||
extern fn void log_stderr_logger(LogMessage log_message) @cname("__android_log_stderr_logger");
|
||||
extern fn void log_logd_logger(AndroidLogMessage log_message) @cname("__android_log_logd_logger");
|
||||
extern fn void log_stderr_logger(AndroidLogMessage log_message) @cname("__android_log_stderr_logger");
|
||||
extern fn void log_set_aborter(AborterFunction aborter) @cname("__android_log_set_aborter");
|
||||
extern fn void log_call_aborter(ZString abort_message) @cname("__android_log_call_aborter");
|
||||
extern fn void log_default_aborter(ZString abort_message) @cname("__android_log_default_aborter");
|
||||
|
||||
@@ -15,7 +15,7 @@ fn uint num_cpu()
|
||||
return count;
|
||||
}
|
||||
|
||||
module std::os @if(env::LINUX);
|
||||
module std::os @if(env::LINUX || env::ANDROID);
|
||||
import std::os::posix;
|
||||
|
||||
fn uint num_cpu()
|
||||
|
||||
@@ -18,23 +18,23 @@ const uint EPOLLWAKEUP = EpollEvents.EPOLLWAKEUP;
|
||||
const uint EPOLLONESHOT = EpollEvents.EPOLLONESHOT;
|
||||
const uint EPOLLET = EpollEvents.EPOLLET;
|
||||
|
||||
enum EpollEvents: const inline uint
|
||||
constdef EpollEvents : inline uint
|
||||
{
|
||||
EPOLLIN = 0x001,
|
||||
EPOLLPRI = 0x002,
|
||||
EPOLLOUT = 0x004,
|
||||
EPOLLRDNORM = 0x040,
|
||||
EPOLLRDBAND = 0x080,
|
||||
EPOLLWRNORM = 0x100,
|
||||
EPOLLWRBAND = 0x200,
|
||||
EPOLLMSG = 0x400,
|
||||
EPOLLERR = 0x008,
|
||||
EPOLLHUP = 0x010,
|
||||
EPOLLRDHUP = 0x2000,
|
||||
EPOLLIN = 0x001,
|
||||
EPOLLPRI = 0x002,
|
||||
EPOLLOUT = 0x004,
|
||||
EPOLLRDNORM = 0x040,
|
||||
EPOLLRDBAND = 0x080,
|
||||
EPOLLWRNORM = 0x100,
|
||||
EPOLLWRBAND = 0x200,
|
||||
EPOLLMSG = 0x400,
|
||||
EPOLLERR = 0x008,
|
||||
EPOLLHUP = 0x010,
|
||||
EPOLLRDHUP = 0x2000,
|
||||
EPOLLEXCLUSIVE = 1u << 28,
|
||||
EPOLLWAKEUP = 1u << 29,
|
||||
EPOLLONESHOT = 1u << 30,
|
||||
EPOLLET = 1u << 31
|
||||
EPOLLWAKEUP = 1u << 29,
|
||||
EPOLLONESHOT = 1u << 30,
|
||||
EPOLLET = 1u << 31
|
||||
}
|
||||
|
||||
/* Valid opcodes ( "op" parameter ) to issue to epoll_ctl(). */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user