mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 19:21:37 -07:00
Compare commits
454 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d6125f05d4 | |||
| ad22eda87c | |||
| 8d31ba492d | |||
| 8f611b3399 | |||
| 618f858930 | |||
| 185277a2b6 | |||
| 8e5c3141f6 | |||
| 87aaa9c3f0 | |||
| 095605b7db | |||
| 6836b501af | |||
| d949d5a046 | |||
| 064516bf0b | |||
| eaf88bcc4d | |||
| 76d3bab955 | |||
| 02f9668185 | |||
| abda75feee | |||
| a779cb2798 | |||
| 5e42675b42 | |||
| 11c565e199 | |||
| 4689a6b341 | |||
| 30a5808460 | |||
| 8ba1c9a6cd | |||
| 65b78b1aa9 | |||
| c369719362 | |||
| 40a12cca53 | |||
| d8940f5fd7 | |||
| 74dee82dbf | |||
| f1cf724bd4 | |||
| fc809d3fad | |||
| 42d135aade | |||
| ab12ca69af | |||
| 17748f18b9 | |||
| 3803bdff5f | |||
| 0a94a67190 | |||
| 8dcb14fbc2 | |||
| f62f40e508 | |||
| 980aa37bee | |||
| 1e9cc058a0 | |||
| 824efc82b9 | |||
| 1cfe226686 | |||
| ea0ce7bd2c | |||
| 342adb627d | |||
| a70635d2f6 | |||
| 5cb16c4cd1 | |||
| 538004ba5f | |||
| 8663c64e47 | |||
| bb3ffdbdfb | |||
| fcf7cf973b | |||
| fb2e1c32bd | |||
| ae0b8fce44 | |||
| d2e55f9ffa | |||
| d5e2b387fa | |||
| 922b511a24 | |||
| 9de9111082 | |||
| 5a7fe2e3d9 | |||
| 1e8c12c2a3 | |||
| b92c70e55c | |||
| 352494cbb4 | |||
| 797c41950a | |||
| e036a321a0 | |||
| 18471f358e | |||
| 55d09251d8 | |||
| d66fd71d21 | |||
| f10fc2a494 | |||
| 055d8c5370 | |||
| 955472bd21 | |||
| a2d5f660ed | |||
| 8a4b9ddaa3 | |||
| 54a2b6f00e | |||
| abe728dbbb | |||
| 574ceb37a9 | |||
| dbdc4471c2 | |||
| af95381bf8 | |||
| 41f2539484 | |||
| 8f57bb0799 | |||
| 84a4188c72 | |||
| 31f1e0aeae | |||
| 4b8cbb5a3b | |||
| 3e7aabe6d8 | |||
| d4df325e0a | |||
| 9f8a63cb43 | |||
| 6f745677b4 | |||
| 86649e6b44 | |||
| 3ca887a60a | |||
| 312a1e8a94 | |||
| 9a311ab9e7 | |||
| 7d92eaaeb2 | |||
| 582f423b67 | |||
| c2524464f9 | |||
| 55e472cdb6 | |||
| e6ad773a88 | |||
| 82eae32bca | |||
| b0e21bd616 | |||
| 7b88bed098 | |||
| 28abf5d33b | |||
| fb8ad338d0 | |||
| ee60be0137 | |||
| 9efd4c5097 | |||
| f30e6f50bd | |||
| 8ec2ca9dcd | |||
| f19bb0f4d4 | |||
| 76bb82a726 | |||
| 8e62f9c83c | |||
| 696f758435 | |||
| 6421152104 | |||
| 1e989f5c10 | |||
| 3eb42ecb55 | |||
| 286cb60c45 | |||
| 28e9a4f79c | |||
| e79fb68291 | |||
| 16eaa17ed9 | |||
| 9491c13a5c | |||
| 8d8adac1b4 | |||
| ba6c63e366 | |||
| 963b1a12d7 | |||
| 89890d7900 | |||
| 661fcad895 | |||
| cef16feb0b | |||
| 84b851f578 | |||
| 785c27daa7 | |||
| 795a5910cf | |||
| 4c21f9495d | |||
| f119fd1ee1 | |||
| 46ab822316 | |||
| 104aea9f42 | |||
| a2f2041aa6 | |||
| 599d18f26f | |||
| 61084d832d | |||
| b957996577 | |||
| f41150f8e9 | |||
| 21adad4e09 | |||
| 47f9e8f850 | |||
| ba3f2a6a0c | |||
| b9888f8f68 | |||
| 32cda5d56a | |||
| a4d9847f45 | |||
| 8aa6d70dec | |||
| ea6b222430 | |||
| 91b4bf3daa | |||
| 8c943eb054 | |||
| 446703ba75 | |||
| 266b5d7d85 | |||
| d90adb7a8e | |||
| 2573da12fc | |||
| bbc9c6a93c | |||
| 673134185a | |||
| 3bf00e6125 | |||
| 8fd4fe25d6 | |||
| ea1dc5373d | |||
| b8d6dd4eb7 | |||
| 4d80f8598d | |||
| bc4591fc1e | |||
| 6465fb8ec7 | |||
| 46204ed7f0 | |||
| 0f91ffe28f | |||
| 4b46d691f8 | |||
| 599d0cf6ac | |||
| d7dba495fd | |||
| c05f6b4a31 | |||
| a0a578c72a | |||
| 55fc2c00c0 | |||
| 6944e2fc04 | |||
| c8b353b6d8 | |||
| 6a8a31824d | |||
| 275b8d2e8a | |||
| 8cfdd9805d | |||
| 7d304f4e8b | |||
| b65e5d5e03 | |||
| b110153b51 | |||
| b261937233 | |||
| 4455ba5b65 | |||
| e8aa767c8d | |||
| bb7bd94b0a | |||
| 4a886a1bc5 | |||
| c21c754b6f | |||
| e948fcd2f1 | |||
| 0c46d06e63 | |||
| 44b6e7c45d | |||
| 284a2631fd | |||
| 3f156bcb4b | |||
| d35a9e65b6 | |||
| c440296ae8 | |||
| 79f115d6a7 | |||
| 39eccdf6b9 | |||
| 71cfa0c9fe | |||
| e82f8214e8 | |||
| b8f8d4c3a1 | |||
| 9e2eb717fe | |||
| f0c9f82e1b | |||
| cc1d3a7b19 | |||
| 159daba759 | |||
| d7e85725e1 | |||
| 247f4f3293 | |||
| 8758afdf4e | |||
| 362f07d7c5 | |||
| fe74b479c6 | |||
| 44ee0f2cdc | |||
| 50035f257e | |||
| 92abddddc5 | |||
| 4d580ed693 | |||
| 9c54ed5792 | |||
| 5108ebf015 | |||
| 86dbcb1b20 | |||
| 3ac934dd15 | |||
| 26ce40c188 | |||
| b34e4a9fd1 | |||
| 28561ef5f5 | |||
| 10b798456c | |||
| a580cdbe7b | |||
| e82e4398b6 | |||
| e0225c3579 | |||
| 8d044fd442 | |||
| 6ef96d3300 | |||
| 2e633f57a0 | |||
| 50369cf19c | |||
| 385385364b | |||
| 9ccdc40f65 | |||
| df3512b112 | |||
| ce08e832f7 | |||
| 24c89b3eee | |||
| 85e5be03d1 | |||
| b1cfeb6c95 | |||
| fffb83282b | |||
| 1cf6b6679d | |||
| 7886798156 | |||
| 0ad599675e | |||
| 5d03bc61b8 | |||
| 5e31c04a01 | |||
| 7b7081d607 | |||
| 5ae564cc8c | |||
| 0507b9ebb7 | |||
| f7b1290fe9 | |||
| b01c2e1017 | |||
| 63b54ce7c6 | |||
| b8a35c658c | |||
| 465b6139d5 | |||
| b37d344eb2 | |||
| d4ee1a9e19 | |||
| be12f12c3c | |||
| 2e5f57d8a1 | |||
| d5c3f99655 | |||
| 083e9e2053 | |||
| c6c5af527b | |||
| 5420cc083d | |||
| c81f7b31c6 | |||
| fdd0c726bc | |||
| da9cabc334 | |||
| d962cfdc6b | |||
| dfb8143149 | |||
| d2fcbf0e1d | |||
| e08f39ec28 | |||
| d353f97f91 | |||
| 8144e82c6c | |||
| d0f7cf74e9 | |||
| e1c2528d87 | |||
| eac61fb536 | |||
| 20f7e61363 | |||
| 8bb6651dda | |||
| 8ff80dec58 | |||
| f31b09212a | |||
| 073bd3f6c9 | |||
| 9a4d942b0b | |||
| ff6fdc7812 | |||
| c85c5ec38c | |||
| d70b5475eb | |||
| 42138d1ad5 | |||
| 47f97b8f89 | |||
| 902be0d09c | |||
| 502ad0c10b | |||
| 27f5aadd5a | |||
| da7a9a3584 | |||
| 03862d1f48 | |||
| 15ce8b0454 | |||
| 4f51d74fc2 | |||
| b6a1ec0229 | |||
| 60685369b9 | |||
| 0f2a9e6143 | |||
| 278de3a92f | |||
| abdf54800e | |||
| 579b317be8 | |||
| 08360e2337 | |||
| f11f84964d | |||
| bb9c1d04db | |||
| f3c4d97250 | |||
| afb6ebd21e | |||
| 6fa5eb9e1f | |||
| 94570a24c1 | |||
| 17001bf38c | |||
| b83e67f45f | |||
| a5eea97edb | |||
| 866d5302fe | |||
| 5d70289b69 | |||
| d76ba7895b | |||
| dfe1dedeb1 | |||
| d027a5f1a4 | |||
| 746e880eb5 | |||
| e4286d0ff9 | |||
| 3a556eb304 | |||
| b44a56118e | |||
| 0d044eabac | |||
| 3cf26af600 | |||
| 9a39ce6b75 | |||
| 3a5245dcce | |||
| ca0f36be42 | |||
| 59b3c472ca | |||
| 357f66fcee | |||
| 050f128554 | |||
| 1a3784c4df | |||
| 518ecaf9c9 | |||
| 77e2e1e1d0 | |||
| 448f834b28 | |||
| 9212e3176a | |||
| 3160a6a12c | |||
| ef7b72d14c | |||
| 7d534769d6 | |||
| 0a81fcc2af | |||
| 2451014b6e | |||
| 348d25c43a | |||
| 9854dbe889 | |||
| 038337fd07 | |||
| cf0bf1a7cb | |||
| 364e6c9573 | |||
| 52d38ae42b | |||
| 2dbdff07c5 | |||
| 2ad8f99790 | |||
| 87a1833862 | |||
| 0a0ba95e85 | |||
| 433d742183 | |||
| b845db1618 | |||
| ced7700cdb | |||
| 406d2ab6ba | |||
| 327116b84b | |||
| fd56b48825 | |||
| 1ef9b094b6 | |||
| 0659a11a1a | |||
| 956b59d48c | |||
| 97b537f800 | |||
| ae907bf724 | |||
| db1ef078ff | |||
| c2603297ee | |||
| a02bcd3bfd | |||
| 0e972296a4 | |||
| 5ec82623ab | |||
| 47ff50a92d | |||
| a1558b3398 | |||
| 5f617c56e1 | |||
| 7ef30355cb | |||
| 06f1eaa153 | |||
| 58e023e0cf | |||
| 222bab501c | |||
| 9c6ab05981 | |||
| bee637aef1 | |||
| d5844dfd2a | |||
| 3751322521 | |||
| 71ac145f49 | |||
| e50ef33c2a | |||
| 3061dd2497 | |||
| 088f4b5039 | |||
| 002184cd49 | |||
| 02da4d61ae | |||
| 102c29575b | |||
| 24fce21d90 | |||
| afe185ee22 | |||
| ffffb04d85 | |||
| 17390cd317 | |||
| 7ac80544a1 | |||
| 96b60d8779 | |||
| 24f2d97c0e | |||
| f6d98d2a16 | |||
| ab5460e2e2 | |||
| cd1658e56c | |||
| e7e1866e50 | |||
| 51b198aa56 | |||
| 94fd59e6f0 | |||
| c07ab5f9ad | |||
| 06e0da97b7 | |||
| 04535b2913 | |||
| 6667b78c12 | |||
| 7e0c78eae7 | |||
| 6d1eb473cf | |||
| 898245431f | |||
| 43942a6199 | |||
| 53c7e65c57 | |||
| a38586420c | |||
| 1aa9c49172 | |||
| 5a88fef483 | |||
| 43b55223af | |||
| 1d628a5e3d | |||
| af3784ebb6 | |||
| ff933ca37f | |||
| bbf79fc1d4 | |||
| 2af8e956a6 | |||
| 9ed826f6fb | |||
| 07f9e551c1 | |||
| df4404e093 | |||
| 583fd89fcf | |||
| 2691c394e0 | |||
| 4662bad59c | |||
| 7086b49ae6 | |||
| 72aa0e6e38 | |||
| cb2e6ea31d | |||
| 74c683e908 | |||
| 6383714bff | |||
| a25e796b00 | |||
| 833784e196 | |||
| 66a70b9d27 | |||
| cd125c0f41 | |||
| 87bfd31664 | |||
| 5fba548aa0 | |||
| aafbf5bac7 | |||
| 951e940470 | |||
| 3b5b845ea6 | |||
| 3f9ad6ba09 | |||
| f10f7ebbf1 | |||
| 40ed7e48d0 | |||
| ab53900c95 | |||
| 43589a56b7 | |||
| de1838c0cb | |||
| 9b8563dfc0 | |||
| b18e8898d8 | |||
| ca112c0b6d | |||
| 6eb64f2bdc | |||
| 25c3b6dc95 | |||
| 3464784e5e | |||
| f1dc7c0b27 | |||
| c708f649ec | |||
| f7b8b3a340 | |||
| 2cbb3443d3 | |||
| 11bd518f36 | |||
| a721802337 | |||
| 5a8c7b4f90 | |||
| f17fc05ff2 | |||
| a12db382e0 | |||
| 22daa50374 | |||
| a09300fb0e | |||
| bab4e5531a | |||
| 1de928df78 | |||
| aded272b33 | |||
| b0721f1e0c | |||
| 7e90ece84a | |||
| 1f563f2810 | |||
| c46317c00b | |||
| cb4b7efd3e | |||
| 411beaa3bf | |||
| 1cb3a31f32 | |||
| c99afd04ad | |||
| 3157467e4b | |||
| 2001384ae6 | |||
| b09e53d7fe | |||
| d046c9c072 | |||
| 2e8da35851 | |||
| 1d3458cadb | |||
| 9139ca4673 | |||
| e0e6bba865 |
@@ -7,6 +7,8 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
# PLEASE POST THIS IN THE DISCUSSION TAB UNDER "PROPOSALS" OR "IDEAS/REQUESTS"
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
|
||||
@@ -10,12 +10,12 @@ jobs:
|
||||
run: sudo apt-get install llvm-11 clang-11 llvm
|
||||
- name: build odin
|
||||
run: make release
|
||||
- name: Odin run -llvm-api
|
||||
run: ./odin run examples/demo/demo.odin -llvm-api
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
build_macOS:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
@@ -23,28 +23,21 @@ jobs:
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm@11
|
||||
echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH
|
||||
echo "/usr/local/opt/llvm@11/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||
- name: build odin
|
||||
run: make release
|
||||
- name: Odin run
|
||||
run: |
|
||||
./odin run examples/demo/demo.odin
|
||||
./odin run examples/demo/demo.odin -llvm-api
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download and unpack LLVM bins
|
||||
shell: powershell
|
||||
run: |
|
||||
cd bin
|
||||
$ProgressPreference = "SilentlyContinue";
|
||||
Invoke-WebRequest -Uri https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip -OutFile llvm-binaries.zip
|
||||
7z x llvm-binaries.zip > $null
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -60,5 +53,7 @@ jobs:
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check examples/demo/demo.odin -vet
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
|
||||
|
||||
|
||||
@@ -10,15 +10,6 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install cURL
|
||||
run: choco install curl
|
||||
- name: Download and unpack LLVM bins
|
||||
shell: cmd
|
||||
run: |
|
||||
cd bin
|
||||
curl -sL https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip --output llvm-binaries.zip
|
||||
7z x llvm-binaries.zip > nul
|
||||
rm -f llvm-binaries.zip
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -52,8 +43,6 @@ jobs:
|
||||
run: sudo apt-get install llvm-11 clang-11 llvm
|
||||
- name: build odin
|
||||
run: make nightly
|
||||
- name: Odin run -llvm-api
|
||||
run: ./odin run examples/demo/demo.odin -llvm-api
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Copy artifacts
|
||||
@@ -74,14 +63,12 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm
|
||||
echo "/usr/local/opt/llvm/bin" >> $GITHUB_PATH
|
||||
brew install llvm@11
|
||||
echo "/usr/local/opt/llvm@11/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||
- name: build odin
|
||||
run: make nightly
|
||||
- name: Odin run -llvm-api
|
||||
run: ./odin run examples/demo/demo.odin -llvm-api
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Copy artifacts
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2016-2020 Ginger Bill. All rights reserved.
|
||||
Copyright (c) 2016-2021 Ginger Bill. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -2,18 +2,37 @@ GIT_SHA=$(shell git rev-parse --short HEAD)
|
||||
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined
|
||||
LDFLAGS=-pthread -ldl -lm -lstdc++
|
||||
CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
|
||||
CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
|
||||
CC=clang
|
||||
|
||||
OS=$(shell uname)
|
||||
|
||||
ifeq ($(OS), Darwin)
|
||||
LDFLAGS:=$(LDFLAGS) -liconv
|
||||
CFLAGS:=$(CFLAGS) $(shell llvm-config --cxxflags --ldflags) -DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM
|
||||
LDFLAGS:=$(LDFLAGS) -lLLVM-C
|
||||
LLVM_CONFIG=llvm-config
|
||||
ifneq ($(shell llvm-config --version | grep '^11\.'),)
|
||||
LLVM_CONFIG=llvm-config
|
||||
else
|
||||
$(error "Requirement: llvm-config must be version 11")
|
||||
endif
|
||||
|
||||
LDFLAGS:=$(LDFLAGS) -liconv
|
||||
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
||||
LDFLAGS:=$(LDFLAGS) -lLLVM-C
|
||||
endif
|
||||
ifeq ($(OS), Linux)
|
||||
CFLAGS:=$(CFLAGS) $(shell llvm-config-11 --cxxflags --ldflags) -DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM
|
||||
LDFLAGS:=$(LDFLAGS) $(shell llvm-config-11 --libs core native --system-libs)
|
||||
LLVM_CONFIG=llvm-config-11
|
||||
ifneq ($(shell which llvm-config-11 2>/dev/null),)
|
||||
LLVM_CONFIG=llvm-config-11
|
||||
else
|
||||
ifneq ($(shell llvm-config --version | grep '^11\.'),)
|
||||
LLVM_CONFIG=llvm-config
|
||||
else
|
||||
$(error "Requirement: llvm-config must be version 11")
|
||||
endif
|
||||
endif
|
||||
|
||||
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
|
||||
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
|
||||
endif
|
||||
|
||||
all: debug demo
|
||||
|
||||
@@ -100,7 +100,6 @@ In addition, the following platform-specific steps are necessary:
|
||||
|
||||
- Windows
|
||||
* Have Visual Studio installed (MSVC 2010 or later, for the linker)
|
||||
* Have a copy of `opt.exe` and `llc.exe` in `Odin/bin`. Pre-built Windows binaries can be found [here](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) and *must* be explicitly copied
|
||||
* Open a valid command prompt:
|
||||
* **Basic:** run the `x64 Native Tools Command Prompt for VS2017` shortcut bundled with VS 2017, or
|
||||
* **Advanced:** run `vcvarsall.bat x64` from a blank `cmd` session
|
||||
@@ -128,7 +127,6 @@ Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#g
|
||||
- Windows
|
||||
* x86-64/amd64
|
||||
* MSVC 2010 installed (C++11 support)
|
||||
* [LLVM binaries](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) for `opt.exe`, `llc.exe`, and `lld-link.exe`
|
||||
* Requires MSVC's link.exe as the linker
|
||||
* run `vcvarsall.bat` to setup the path
|
||||
|
||||
|
||||
+3
-4
@@ -2,13 +2,12 @@
|
||||
|
||||
## Setup
|
||||
|
||||
Odin only supports x86-64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
|
||||
Odin currently supports x86-64 and ARM64 at the moment (64-bit), relies on LLVM for code generation and an external linker.
|
||||
|
||||
In addition, the following platform-specific steps are necessary:
|
||||
|
||||
- Windows
|
||||
* Have Visual Studio installed (MSVC 2010 or later, for the linker)
|
||||
* Have a copy of `opt.exe` and `llc.exe` in `Odin/bin`. Pre-built Windows binaries can be found [here](https://github.com/odin-lang/Odin/releases/tag/llvm-windows) and *must* be explicitly copied
|
||||
* Open a valid command prompt:
|
||||
* **Basic:** run the `x64 Native Tools Command Prompt for VS2017` shortcut bundled with VS 2017, or
|
||||
* **Advanced:** run `vcvarsall.bat x64` from a blank `cmd` session
|
||||
@@ -19,12 +18,12 @@ In addition, the following platform-specific steps are necessary:
|
||||
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
|
||||
|
||||
- GNU/Linux
|
||||
* Have LLVM installed (opt/llc)
|
||||
* Have Clang installed (version X.X or later, for linking)
|
||||
* Make sure the LLVM binaries and the linker are added to your `$PATH` environmental variable
|
||||
|
||||
Then build the compiler by calling `build.bat` (Windows) or `make` (Linux/MacOS). This will automatically run the demo program if successful.
|
||||
|
||||
**Notes for Linux:**: The compiler currently relies on the `core` and `shared` library collection being relative to the compiler executable. Installing the compiler in the usual sense (to `/usr/local/bin` or similar) is therefore not as straight forward as you need to make sure the mentioned libraries are available. As a result, it is recommended to simply explicitly invoke the compiler with `/path/to/odin` in your preferred build system, or add `/path/to/odin` to `$PATH`.
|
||||
**Notes for \*Nix Systems:**: The compiler currently relies on the `core` and `shared` library collection being relative to the compiler executable, by default. Installing the compiler in the usual sense (to `/usr/local/bin` or similar) is therefore not as straight forward as you need to make sure the mentioned libraries are available. As a result, it is recommended to either simply explicitly invoke the compiler with `/path/to/odin` in your preferred build system, or `set ODIN_ROOT=/path/to/odin_root`.
|
||||
|
||||
|
||||
Please read the [Getting Started Guide](https://github.com/odin-lang/Odin/wiki#getting-started-with-odin) for more information.
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
release_mode=$1
|
||||
|
||||
warnings_to_disable="-std=c++11 -Wno-switch"
|
||||
|
||||
libraries="-pthread -ldl -lm -lstdc++ -lz -lcurses -lxml2"
|
||||
other_args="-DLLVM_BACKEND_SUPPORT"
|
||||
compiler="clang"
|
||||
|
||||
if [ -z "$release_mode" ]; then release_mode="0"; fi
|
||||
|
||||
if [ "$release_mode" -eq "0" ]; then
|
||||
other_args="${other_args} -g"
|
||||
fi
|
||||
if [ "$release_mode" -eq "1" ]; then
|
||||
other_args="${other_args} -O3 -march=native"
|
||||
fi
|
||||
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
|
||||
# Set compiler to clang on MacOS
|
||||
# MacOS provides a symlink to clang called gcc, but it's nice to be explicit here.
|
||||
compiler="clang"
|
||||
|
||||
llvm_config_flags="--cxxflags --ldflags"
|
||||
# llvm_config_flags="${llvm_config_flags} --link-static"
|
||||
llvm_config="llvm-config ${llvm_config_flags}"
|
||||
|
||||
other_args="${other_args} -liconv"
|
||||
other_args="${other_args} `${llvm_config}` -lLLVM-C"
|
||||
elif [[ "$(uname)" == "FreeBSD" ]]; then
|
||||
compiler="clang"
|
||||
fi
|
||||
|
||||
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin
|
||||
# && ./odin run examples/demo/demo.odin -llvm-api
|
||||
@@ -1,5 +1,14 @@
|
||||
@echo off
|
||||
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
||||
set CURR_DATE_TIME=%%j
|
||||
)
|
||||
|
||||
set curr_year=%CURR_DATE_TIME:~0,4%
|
||||
set curr_month=%CURR_DATE_TIME:~4,2%
|
||||
|
||||
:: Make sure this is a decent name and not generic
|
||||
set exe_name=odin.exe
|
||||
|
||||
@@ -19,8 +28,10 @@ if "%2" == "1" (
|
||||
set nightly=0
|
||||
)
|
||||
|
||||
set odin_version_raw="dev-%curr_year%-%curr_month%"
|
||||
|
||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
||||
set compiler_defines= -DLLVM_BACKEND_SUPPORT -DUSE_NEW_LLVM_ABI_SYSTEM
|
||||
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
|
||||
|
||||
for /f %%i in ('git rev-parse --short HEAD') do set GIT_SHA=%%i
|
||||
if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
release_mode=$1
|
||||
|
||||
warnings_to_disable="-std=c++11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined"
|
||||
libraries="-pthread -ldl -lm -lstdc++"
|
||||
other_args=""
|
||||
compiler="clang"
|
||||
|
||||
if [ -z "$release_mode" ]; then release_mode="0"; fi
|
||||
|
||||
if [ "$release_mode" -eq "0" ]; then
|
||||
other_args="${other_args} -g"
|
||||
fi
|
||||
if [ "$release_mode" -eq "1" ]; then
|
||||
other_args="${other_args} -O3 -march=native"
|
||||
fi
|
||||
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
|
||||
# Set compiler to clang on MacOS
|
||||
# MacOS provides a symlink to clang called gcc, but it's nice to be explicit here.
|
||||
compiler="clang"
|
||||
|
||||
other_args="${other_args} -liconv"
|
||||
elif [[ "$(uname)" == "FreeBSD" ]]; then
|
||||
compiler="clang"
|
||||
fi
|
||||
|
||||
${compiler} src/main.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin && ./odin run examples/demo/demo.odin
|
||||
@@ -0,0 +1,83 @@
|
||||
package bufio
|
||||
|
||||
import "core:io"
|
||||
|
||||
// Loadahead_Reader provides io lookahead.
|
||||
// This is useful for tokenizers/parsers.
|
||||
// Loadahead_Reader is similar to bufio.Reader, but unlike bufio.Reader, Loadahead_Reader's buffer size
|
||||
// will EXACTLY match the specified size, whereas bufio.Reader's buffer size may differ from the specified size.
|
||||
// This makes sure that the buffer will not be accidentally read beyond the expected size.
|
||||
Loadahead_Reader :: struct {
|
||||
r: io.Reader,
|
||||
buf: []byte,
|
||||
n: int,
|
||||
}
|
||||
|
||||
lookahead_reader_init :: proc(lr: ^Loadahead_Reader, r: io.Reader, buf: []byte) -> ^Loadahead_Reader {
|
||||
lr.r = r;
|
||||
lr.buf = buf;
|
||||
lr.n = 0;
|
||||
return lr;
|
||||
}
|
||||
|
||||
lookahead_reader_buffer :: proc(lr: ^Loadahead_Reader) -> []byte {
|
||||
return lr.buf[:lr.n];
|
||||
}
|
||||
|
||||
|
||||
// lookahead_reader_peek returns a slice of the Lookahead_Reader which holds n bytes
|
||||
// If the Lookahead_Reader cannot hold enough bytes, it will read from the underlying reader to populate the rest.
|
||||
// NOTE: The returned buffer is not a copy of the underlying buffer
|
||||
lookahead_reader_peek :: proc(lr: ^Loadahead_Reader, n: int) -> ([]byte, io.Error) {
|
||||
switch {
|
||||
case n < 0:
|
||||
return nil, .Negative_Read;
|
||||
case n > len(lr.buf):
|
||||
return nil, .Buffer_Full;
|
||||
}
|
||||
|
||||
n := n;
|
||||
err: io.Error;
|
||||
read_count: int;
|
||||
|
||||
if lr.n < n {
|
||||
read_count, err = io.read_at_least(lr.r, lr.buf[lr.n:], n-lr.n);
|
||||
if err == .Unexpected_EOF {
|
||||
err = .EOF;
|
||||
}
|
||||
}
|
||||
|
||||
lr.n += read_count;
|
||||
|
||||
if n > lr.n {
|
||||
n = lr.n;
|
||||
}
|
||||
return lr.buf[:n], err;
|
||||
}
|
||||
|
||||
// lookahead_reader_peek_all returns a slice of the Lookahead_Reader populating the full buffer
|
||||
// If the Lookahead_Reader cannot hold enough bytes, it will read from the underlying reader to populate the rest.
|
||||
// NOTE: The returned buffer is not a copy of the underlying buffer
|
||||
lookahead_reader_peek_all :: proc(lr: ^Loadahead_Reader) -> ([]byte, io.Error) {
|
||||
return lookahead_reader_peek(lr, len(lr.buf));
|
||||
}
|
||||
|
||||
|
||||
// lookahead_reader_consume drops the first n populated bytes from the Lookahead_Reader.
|
||||
lookahead_reader_consume :: proc(lr: ^Loadahead_Reader, n: int) -> io.Error {
|
||||
switch {
|
||||
case n == 0:
|
||||
return nil;
|
||||
case n < 0:
|
||||
return .Negative_Read;
|
||||
case lr.n < n:
|
||||
return .Short_Buffer;
|
||||
}
|
||||
copy(lr.buf, lr.buf[n:lr.n]);
|
||||
lr.n -= n;
|
||||
return nil;
|
||||
}
|
||||
|
||||
lookahead_reader_consume_all :: proc(lr: ^Loadahead_Reader) -> io.Error {
|
||||
return lookahead_reader_consume(lr, lr.n);
|
||||
}
|
||||
@@ -17,6 +17,8 @@ Reader :: struct {
|
||||
|
||||
last_byte: int, // last byte read, invalid is -1
|
||||
last_rune_size: int, // size of last rune read, invalid is -1
|
||||
|
||||
max_consecutive_empty_reads: int,
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +27,7 @@ DEFAULT_BUF_SIZE :: 4096;
|
||||
@(private)
|
||||
MIN_READ_BUFFER_SIZE :: 16;
|
||||
@(private)
|
||||
MAX_CONSECUTIVE_EMPTY_READS :: 128;
|
||||
DEFAULT_MAX_CONSECUTIVE_EMPTY_READS :: 128;
|
||||
|
||||
reader_init :: proc(b: ^Reader, rd: io.Reader, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
|
||||
size := size;
|
||||
@@ -71,8 +73,12 @@ _reader_read_new_chunk :: proc(b: ^Reader) -> io.Error {
|
||||
return .Buffer_Full;
|
||||
}
|
||||
|
||||
if b.max_consecutive_empty_reads <= 0 {
|
||||
b.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS;
|
||||
}
|
||||
|
||||
// read new data, and try a limited number of times
|
||||
for i := MAX_CONSECUTIVE_EMPTY_READS; i > 0; i -= 1 {
|
||||
for i := b.max_consecutive_empty_reads; i > 0; i -= 1 {
|
||||
n, err := io.read(b.rd, b.buf[b.w:]);
|
||||
if n < 0 {
|
||||
return .Negative_Read;
|
||||
|
||||
@@ -0,0 +1,340 @@
|
||||
package bufio
|
||||
|
||||
import "core:bytes"
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:unicode/utf8"
|
||||
import "intrinsics"
|
||||
|
||||
// Extra errors returns by scanning procedures
|
||||
Scanner_Extra_Error :: enum i32 {
|
||||
Negative_Advance,
|
||||
Advanced_Too_Far,
|
||||
Bad_Read_Count,
|
||||
Too_Long,
|
||||
Too_Short,
|
||||
}
|
||||
|
||||
Scanner_Error :: union {
|
||||
io.Error,
|
||||
Scanner_Extra_Error,
|
||||
}
|
||||
|
||||
// Split_Proc is the signature of the split procedure used to tokenize the input.
|
||||
Split_Proc :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool);
|
||||
|
||||
Scanner :: struct {
|
||||
r: io.Reader,
|
||||
split: Split_Proc,
|
||||
|
||||
buf: [dynamic]byte,
|
||||
max_token_size: int,
|
||||
start: int,
|
||||
end: int,
|
||||
token: []byte,
|
||||
|
||||
_err: Scanner_Error,
|
||||
max_consecutive_empty_reads: int,
|
||||
successive_empty_token_count: int,
|
||||
scan_called: bool,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
DEFAULT_MAX_SCAN_TOKEN_SIZE :: 1<<16;
|
||||
|
||||
@(private)
|
||||
_INIT_BUF_SIZE :: 4096;
|
||||
|
||||
scanner_init :: proc(s: ^Scanner, r: io.Reader, buf_allocator := context.allocator) -> ^Scanner {
|
||||
s.r = r;
|
||||
s.split = scan_lines;
|
||||
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE;
|
||||
s.buf.allocator = buf_allocator;
|
||||
return s;
|
||||
}
|
||||
scanner_init_with_buffer :: proc(s: ^Scanner, r: io.Reader, buf: []byte) -> ^Scanner {
|
||||
s.r = r;
|
||||
s.split = scan_lines;
|
||||
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE;
|
||||
s.buf = mem.buffer_from_slice(buf);
|
||||
resize(&s.buf, cap(s.buf));
|
||||
return s;
|
||||
}
|
||||
scanner_destroy :: proc(s: ^Scanner) {
|
||||
delete(s.buf);
|
||||
}
|
||||
|
||||
|
||||
// Returns the first non-EOF error that was encounted by the scanner
|
||||
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
|
||||
switch s._err {
|
||||
case .EOF, .None:
|
||||
return nil;
|
||||
}
|
||||
return s._err;
|
||||
}
|
||||
|
||||
// Returns the most recent token created by scanner_scan.
|
||||
// The underlying array may point to data that may be overwritten
|
||||
// by another call to scanner_scan.
|
||||
// Treat the returned value as if it is immutable.
|
||||
scanner_bytes :: proc(s: ^Scanner) -> []byte {
|
||||
return s.token;
|
||||
}
|
||||
|
||||
// Returns the most recent token created by scanner_scan.
|
||||
// The underlying array may point to data that may be overwritten
|
||||
// by another call to scanner_scan.
|
||||
// Treat the returned value as if it is immutable.
|
||||
scanner_text :: proc(s: ^Scanner) -> string {
|
||||
return string(s.token);
|
||||
}
|
||||
|
||||
// scanner_scan advances the scanner
|
||||
scanner_scan :: proc(s: ^Scanner) -> bool {
|
||||
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
|
||||
err := err;
|
||||
if err == .None {
|
||||
err = nil;
|
||||
}
|
||||
switch s._err {
|
||||
case nil, .EOF:
|
||||
s._err = err;
|
||||
}
|
||||
}
|
||||
|
||||
if s.done {
|
||||
return false;
|
||||
}
|
||||
s.scan_called = true;
|
||||
|
||||
for {
|
||||
// Check if a token is possible with what is available
|
||||
// Allow the split procedure to recover if it fails
|
||||
if s.start < s.end || s._err != nil {
|
||||
advance, token, err, final_token := s.split(s.buf[s.start:s.end], s._err != nil);
|
||||
if final_token {
|
||||
s.token = token;
|
||||
s.done = true;
|
||||
return true;
|
||||
}
|
||||
if err != nil {
|
||||
set_err(s, err);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do advance
|
||||
if advance < 0 {
|
||||
set_err(s, .Negative_Advance);
|
||||
return false;
|
||||
}
|
||||
if advance > s.end-s.start {
|
||||
set_err(s, .Advanced_Too_Far);
|
||||
return false;
|
||||
}
|
||||
s.start += advance;
|
||||
|
||||
s.token = token;
|
||||
if s.token != nil {
|
||||
if s._err == nil || advance > 0 {
|
||||
s.successive_empty_token_count = 0;
|
||||
} else {
|
||||
s.successive_empty_token_count += 1;
|
||||
|
||||
if s.max_consecutive_empty_reads <= 0 {
|
||||
s.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS;
|
||||
}
|
||||
if s.successive_empty_token_count > s.max_consecutive_empty_reads {
|
||||
set_err(s, .No_Progress);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If an error is hit, no token can be created
|
||||
if s._err != nil {
|
||||
s.start = 0;
|
||||
s.end = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// More data must be required to be read
|
||||
if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
|
||||
copy(s.buf[:], s.buf[s.start:s.end]);
|
||||
s.end -= s.start;
|
||||
s.start = 0;
|
||||
}
|
||||
|
||||
could_be_too_short := false;
|
||||
|
||||
// Resize the buffer if full
|
||||
if s.end == len(s.buf) {
|
||||
if s.max_token_size <= 0 {
|
||||
s.max_token_size = DEFAULT_MAX_SCAN_TOKEN_SIZE;
|
||||
}
|
||||
if len(s.buf) >= s.max_token_size {
|
||||
set_err(s, .Too_Long);
|
||||
return false;
|
||||
}
|
||||
// overflow check
|
||||
new_size := _INIT_BUF_SIZE;
|
||||
if len(s.buf) > 0 {
|
||||
overflowed: bool;
|
||||
if new_size, overflowed = intrinsics.overflow_mul(len(s.buf), 2); overflowed {
|
||||
set_err(s, .Too_Long);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
old_size := len(s.buf);
|
||||
new_size = min(new_size, s.max_token_size);
|
||||
resize(&s.buf, new_size);
|
||||
s.end -= s.start;
|
||||
s.start = 0;
|
||||
|
||||
could_be_too_short = old_size >= len(s.buf);
|
||||
|
||||
}
|
||||
|
||||
// Read data into the buffer
|
||||
loop := 0;
|
||||
for {
|
||||
n, err := io.read(s.r, s.buf[s.end:len(s.buf)]);
|
||||
if n < 0 || len(s.buf)-s.end < n {
|
||||
set_err(s, .Bad_Read_Count);
|
||||
break;
|
||||
}
|
||||
s.end += n;
|
||||
if err != nil {
|
||||
set_err(s, err);
|
||||
break;
|
||||
}
|
||||
if n > 0 {
|
||||
s.successive_empty_token_count = 0;
|
||||
break;
|
||||
}
|
||||
loop += 1;
|
||||
|
||||
if s.max_consecutive_empty_reads <= 0 {
|
||||
s.max_consecutive_empty_reads = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS;
|
||||
}
|
||||
if loop > s.max_consecutive_empty_reads {
|
||||
if could_be_too_short {
|
||||
set_err(s, .Too_Short);
|
||||
} else {
|
||||
set_err(s, .No_Progress);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan_bytes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
if at_eof && len(data) == 0 {
|
||||
return;
|
||||
}
|
||||
return 1, data[0:1], nil, false;
|
||||
}
|
||||
|
||||
scan_runes :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
if at_eof && len(data) == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
if data[0] < utf8.RUNE_SELF {
|
||||
advance = 1;
|
||||
token = data[0:1];
|
||||
return;
|
||||
}
|
||||
|
||||
_, width := utf8.decode_rune(data);
|
||||
if width > 1 {
|
||||
advance = width;
|
||||
token = data[0:width];
|
||||
return;
|
||||
}
|
||||
|
||||
if !at_eof && !utf8.full_rune(data) {
|
||||
return;
|
||||
}
|
||||
|
||||
@thread_local ERROR_RUNE := []byte{0xef, 0xbf, 0xbd};
|
||||
|
||||
advance = 1;
|
||||
token = ERROR_RUNE;
|
||||
return;
|
||||
}
|
||||
|
||||
scan_words :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
is_space :: proc "contextless" (r: rune) -> bool {
|
||||
switch r {
|
||||
// lower ones
|
||||
case ' ', '\t', '\n', '\v', '\f', '\r':
|
||||
return true;
|
||||
case '\u0085', '\u00a0':
|
||||
return true;
|
||||
// higher ones
|
||||
case '\u2000' ..= '\u200a':
|
||||
return true;
|
||||
case '\u1680', '\u2028', '\u2029', '\u202f', '\u205f', '\u3000':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// skip spaces at the beginning
|
||||
start := 0;
|
||||
for width := 0; start < len(data); start += width {
|
||||
r: rune;
|
||||
r, width = utf8.decode_rune(data[start:]);
|
||||
if !is_space(r) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for width, i := 0, start; i < len(data); i += width {
|
||||
r: rune;
|
||||
r, width = utf8.decode_rune(data[i:]);
|
||||
if is_space(r) {
|
||||
advance = i+width;
|
||||
token = data[start:i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if at_eof && len(data) > start {
|
||||
advance = len(data);
|
||||
token = data[start:];
|
||||
return;
|
||||
}
|
||||
|
||||
advance = start;
|
||||
return;
|
||||
}
|
||||
|
||||
scan_lines :: proc(data: []byte, at_eof: bool) -> (advance: int, token: []byte, err: Scanner_Error, final_token: bool) {
|
||||
trim_carriage_return :: proc "contextless" (data: []byte) -> []byte {
|
||||
if len(data) > 0 && data[len(data)-1] == '\r' {
|
||||
return data[0:len(data)-1];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
if at_eof && len(data) == 0 {
|
||||
return;
|
||||
}
|
||||
if i := bytes.index_byte(data, '\n'); i >= 0 {
|
||||
advance = i+1;
|
||||
token = trim_carriage_return(data[0:i]);
|
||||
return;
|
||||
}
|
||||
|
||||
if at_eof {
|
||||
advance = len(data);
|
||||
token = trim_carriage_return(data);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -15,6 +15,8 @@ Writer :: struct {
|
||||
|
||||
err: io.Error,
|
||||
|
||||
max_consecutive_empty_writes: int,
|
||||
|
||||
}
|
||||
|
||||
writer_init :: proc(b: ^Writer, wr: io.Writer, size: int = DEFAULT_BUF_SIZE, allocator := context.allocator) {
|
||||
@@ -185,16 +187,20 @@ writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
return n, ferr;
|
||||
}
|
||||
}
|
||||
if b.max_consecutive_empty_writes <= 0 {
|
||||
b.max_consecutive_empty_writes = DEFAULT_MAX_CONSECUTIVE_EMPTY_READS;
|
||||
}
|
||||
|
||||
m: int;
|
||||
nr := 0;
|
||||
for nr < MAX_CONSECUTIVE_EMPTY_READS {
|
||||
for nr < b.max_consecutive_empty_writes {
|
||||
m, err = io.read(r, b.buf[b.n:]);
|
||||
if m != 0 || err != nil {
|
||||
break;
|
||||
}
|
||||
nr += 1;
|
||||
}
|
||||
if nr == MAX_CONSECUTIVE_EMPTY_READS {
|
||||
if nr == b.max_consecutive_empty_writes {
|
||||
return n, .No_Progress;
|
||||
}
|
||||
b.n += m;
|
||||
|
||||
@@ -91,7 +91,7 @@ equal_fold :: proc(u, v: []byte) -> bool {
|
||||
|
||||
if tr < utf8.RUNE_SELF {
|
||||
switch sr {
|
||||
case 'A'..'Z':
|
||||
case 'A'..='Z':
|
||||
if tr == (sr+'a')-'A' {
|
||||
continue loop;
|
||||
}
|
||||
@@ -526,6 +526,14 @@ replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator) ->
|
||||
return;
|
||||
}
|
||||
|
||||
remove :: proc(s, key: []byte, n: int, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
|
||||
return replace(s, key, {}, n, allocator);
|
||||
}
|
||||
|
||||
remove_all :: proc(s, key: []byte, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
|
||||
return remove(s, key, -1, allocator);
|
||||
}
|
||||
|
||||
@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1};
|
||||
|
||||
|
||||
|
||||
@@ -139,18 +139,18 @@ append_token :: proc(a, b: ^Token) -> ^Token {
|
||||
|
||||
is_hex_digit :: proc(x: byte) -> bool {
|
||||
switch x {
|
||||
case '0'..'9', 'a'..'f', 'A'..'F':
|
||||
case '0'..='9', 'a'..='f', 'A'..='F':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
from_hex :: proc(x: byte) -> i32 {
|
||||
switch x {
|
||||
case '0'..'9':
|
||||
case '0'..='9':
|
||||
return i32(x) - '0';
|
||||
case 'a'..'f':
|
||||
case 'a'..='f':
|
||||
return i32(x) - 'a' + 10;
|
||||
case 'A'..'F':
|
||||
case 'A'..='F':
|
||||
return i32(x) - 'A' + 10;
|
||||
}
|
||||
return 16;
|
||||
|
||||
@@ -5,9 +5,9 @@ import "core:unicode/utf8"
|
||||
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
|
||||
hex_to_int :: proc(c: byte) -> int {
|
||||
switch c {
|
||||
case '0'..'9': return int(c-'0');
|
||||
case 'a'..'f': return int(c-'a')+10;
|
||||
case 'A'..'F': return int(c-'A')+10;
|
||||
case '0'..='9': return int(c-'0');
|
||||
case 'a'..='f': return int(c-'a')+10;
|
||||
case 'A'..='F': return int(c-'A')+10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -45,7 +45,7 @@ unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool
|
||||
case '"': r = '"';
|
||||
case '\'': r = '\'';
|
||||
|
||||
case '0'..'7':
|
||||
case '0'..='7':
|
||||
v := int(c-'0');
|
||||
if len(s) < 2 {
|
||||
return;
|
||||
|
||||
@@ -224,11 +224,11 @@ scan_string :: proc(t: ^Tokenizer) -> string {
|
||||
|
||||
digit_val :: proc(r: rune) -> int {
|
||||
switch r {
|
||||
case '0'..'9':
|
||||
case '0'..='9':
|
||||
return int(r-'0');
|
||||
case 'A'..'F':
|
||||
case 'A'..='F':
|
||||
return int(r-'A' + 10);
|
||||
case 'a'..'f':
|
||||
case 'a'..='f':
|
||||
return int(r-'a' + 10);
|
||||
}
|
||||
return 16;
|
||||
@@ -245,7 +245,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool {
|
||||
advance_rune(t);
|
||||
return true;
|
||||
|
||||
case '0'..'7':
|
||||
case '0'..='7':
|
||||
for digit_val(t.ch) < 8 {
|
||||
advance_rune(t);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,478 @@
|
||||
package compress
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation, optimization.
|
||||
*/
|
||||
|
||||
import "core:io"
|
||||
import "core:image"
|
||||
import "core:bytes"
|
||||
|
||||
/*
|
||||
These settings bound how much compression algorithms will allocate for their output buffer.
|
||||
If streaming their output, these are unnecessary and will be ignored.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
When a decompression routine doesn't stream its output, but writes to a buffer,
|
||||
we pre-allocate an output buffer to speed up decompression. The default is 1 MiB.
|
||||
*/
|
||||
COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 20));
|
||||
|
||||
/*
|
||||
This bounds the maximum a buffer will resize to as needed, or the maximum we'll
|
||||
pre-allocate if you inform the decompression routine you know the payload size.
|
||||
|
||||
For reference, the largest payload size of a GZIP file is 4 GiB.
|
||||
|
||||
*/
|
||||
when size_of(uintptr) == 8 {
|
||||
/*
|
||||
For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
||||
which is GZIP and PKZIP's max payload size.
|
||||
*/
|
||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32));
|
||||
} else {
|
||||
/*
|
||||
For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
||||
*/
|
||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29));
|
||||
}
|
||||
|
||||
|
||||
Error :: union {
|
||||
General_Error,
|
||||
Deflate_Error,
|
||||
ZLIB_Error,
|
||||
GZIP_Error,
|
||||
ZIP_Error,
|
||||
/*
|
||||
This is here because png.load will return a this type of error union,
|
||||
as it may involve an I/O error, a Deflate error, etc.
|
||||
*/
|
||||
image.Error,
|
||||
}
|
||||
|
||||
General_Error :: enum {
|
||||
File_Not_Found,
|
||||
Cannot_Open_File,
|
||||
File_Too_Short,
|
||||
Stream_Too_Short,
|
||||
Output_Too_Short,
|
||||
Unknown_Compression_Method,
|
||||
Checksum_Failed,
|
||||
Incompatible_Options,
|
||||
Unimplemented,
|
||||
|
||||
|
||||
/*
|
||||
Memory errors
|
||||
*/
|
||||
Allocation_Failed,
|
||||
Resize_Failed,
|
||||
}
|
||||
|
||||
GZIP_Error :: enum {
|
||||
Invalid_GZIP_Signature,
|
||||
Reserved_Flag_Set,
|
||||
Invalid_Extra_Data,
|
||||
Original_Name_Too_Long,
|
||||
Comment_Too_Long,
|
||||
Payload_Length_Invalid,
|
||||
Payload_CRC_Invalid,
|
||||
|
||||
/*
|
||||
GZIP's payload can be a maximum of max(u32le), or 4 GiB.
|
||||
If you tell it you expect it to contain more, that's obviously an error.
|
||||
*/
|
||||
Payload_Size_Exceeds_Max_Payload,
|
||||
/*
|
||||
For buffered instead of streamed output, the payload size can't exceed
|
||||
the max set by the `COMPRESS_OUTPUT_ALLOCATE_MAX` switch in compress/common.odin.
|
||||
|
||||
You can tweak this setting using `-define:COMPRESS_OUTPUT_ALLOCATE_MAX=size_in_bytes`
|
||||
*/
|
||||
Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX,
|
||||
|
||||
}
|
||||
|
||||
ZIP_Error :: enum {
|
||||
Invalid_ZIP_File_Signature,
|
||||
Unexpected_Signature,
|
||||
Insert_Next_Disk,
|
||||
Expected_End_of_Central_Directory_Record,
|
||||
}
|
||||
|
||||
ZLIB_Error :: enum {
|
||||
Unsupported_Window_Size,
|
||||
FDICT_Unsupported,
|
||||
Unsupported_Compression_Level,
|
||||
Code_Buffer_Malformed,
|
||||
}
|
||||
|
||||
Deflate_Error :: enum {
|
||||
Huffman_Bad_Sizes,
|
||||
Huffman_Bad_Code_Lengths,
|
||||
Inflate_Error,
|
||||
Bad_Distance,
|
||||
Bad_Huffman_Code,
|
||||
Len_Nlen_Mismatch,
|
||||
BType_3,
|
||||
}
|
||||
|
||||
|
||||
// General I/O context for ZLIB, LZW, etc.
|
||||
Context_Memory_Input :: struct #packed {
|
||||
input_data: []u8,
|
||||
output: ^bytes.Buffer,
|
||||
bytes_written: i64,
|
||||
|
||||
code_buffer: u64,
|
||||
num_bits: u64,
|
||||
|
||||
/*
|
||||
If we know the data size, we can optimize the reads and writes.
|
||||
*/
|
||||
size_packed: i64,
|
||||
size_unpacked: i64,
|
||||
}
|
||||
#assert(size_of(Context_Memory_Input) == 64);
|
||||
|
||||
Context_Stream_Input :: struct #packed {
|
||||
input_data: []u8,
|
||||
input: io.Stream,
|
||||
output: ^bytes.Buffer,
|
||||
bytes_written: i64,
|
||||
|
||||
code_buffer: u64,
|
||||
num_bits: u64,
|
||||
|
||||
/*
|
||||
If we know the data size, we can optimize the reads and writes.
|
||||
*/
|
||||
size_packed: i64,
|
||||
size_unpacked: i64,
|
||||
|
||||
/*
|
||||
Flags:
|
||||
`input_fully_in_memory`
|
||||
true = This tells us we read input from `input_data` exclusively. [] = EOF.
|
||||
false = Try to refill `input_data` from the `input` stream.
|
||||
*/
|
||||
input_fully_in_memory: b8,
|
||||
|
||||
padding: [1]u8,
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: The stream versions should really only check if a certain method is available once, perhaps even during setup.
|
||||
|
||||
Bit and byte readers may be merged so that reading bytes will grab them from the bit buffer first.
|
||||
This simplifies end-of-stream handling where bits may be left in the bit buffer.
|
||||
*/
|
||||
|
||||
// TODO: Make these return compress.Error errors.
|
||||
|
||||
input_size_from_memory :: proc(z: ^Context_Memory_Input) -> (res: i64, err: Error) {
|
||||
return i64(len(z.input_data)), nil;
|
||||
}
|
||||
|
||||
input_size_from_stream :: proc(z: ^Context_Stream_Input) -> (res: i64, err: Error) {
|
||||
return io.size(z.input), nil;
|
||||
}
|
||||
|
||||
input_size :: proc{input_size_from_memory, input_size_from_stream};
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||
#no_bounds_check {
|
||||
if len(z.input_data) >= size {
|
||||
res = z.input_data[:size];
|
||||
z.input_data = z.input_data[size:];
|
||||
return res, .None;
|
||||
}
|
||||
}
|
||||
|
||||
if len(z.input_data) == 0 {
|
||||
return []u8{}, .EOF;
|
||||
} else {
|
||||
return []u8{}, .Short_Buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
|
||||
b := make([]u8, size, context.temp_allocator);
|
||||
_, e := z.input->impl_read(b[:]);
|
||||
if e == .None {
|
||||
return b, .None;
|
||||
}
|
||||
|
||||
return []u8{}, e;
|
||||
}
|
||||
|
||||
read_slice :: proc{read_slice_from_memory, read_slice_from_stream};
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_data :: #force_inline proc(z: ^$C, $T: typeid) -> (res: T, err: io.Error) {
|
||||
b, e := read_slice(z, size_of(T));
|
||||
if e == .None {
|
||||
return (^T)(&b[0])^, .None;
|
||||
}
|
||||
|
||||
return T{}, e;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_u8_from_memory :: #force_inline proc(z: ^Context_Memory_Input) -> (res: u8, err: io.Error) {
|
||||
#no_bounds_check {
|
||||
if len(z.input_data) >= 1 {
|
||||
res = z.input_data[0];
|
||||
z.input_data = z.input_data[1:];
|
||||
return res, .None;
|
||||
}
|
||||
}
|
||||
return 0, .EOF;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_u8_from_stream :: #force_inline proc(z: ^Context_Stream_Input) -> (res: u8, err: io.Error) {
|
||||
b, e := read_slice_from_stream(z, 1);
|
||||
if e == .None {
|
||||
return b[0], .None;
|
||||
}
|
||||
|
||||
return 0, e;
|
||||
}
|
||||
|
||||
read_u8 :: proc{read_u8_from_memory, read_u8_from_stream};
|
||||
|
||||
/*
|
||||
You would typically only use this at the end of Inflate, to drain bits from the code buffer
|
||||
preferentially.
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
read_u8_prefer_code_buffer_lsb :: #force_inline proc(z: ^$C) -> (res: u8, err: io.Error) {
|
||||
if z.num_bits >= 8 {
|
||||
res = u8(read_bits_no_refill_lsb(z, 8));
|
||||
} else {
|
||||
size, _ := input_size(z);
|
||||
if size > 0 {
|
||||
res, err = read_u8(z);
|
||||
} else {
|
||||
err = .EOF;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T);
|
||||
|
||||
#no_bounds_check {
|
||||
if len(z.input_data) >= size {
|
||||
buf := z.input_data[:size];
|
||||
return (^T)(&buf[0])^, .None;
|
||||
}
|
||||
}
|
||||
|
||||
if len(z.input_data) == 0 {
|
||||
return T{}, .EOF;
|
||||
} else {
|
||||
return T{}, .Short_Buffer;
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T);
|
||||
|
||||
// Get current position to read from.
|
||||
curr, e1 := z.input->impl_seek(0, .Current);
|
||||
if e1 != .None {
|
||||
return T{}, e1;
|
||||
}
|
||||
r, e2 := io.to_reader_at(z.input);
|
||||
if !e2 {
|
||||
return T{}, .Empty;
|
||||
}
|
||||
when size <= 128 {
|
||||
b: [size]u8;
|
||||
} else {
|
||||
b := make([]u8, size, context.temp_allocator);
|
||||
}
|
||||
_, e3 := io.read_at(r, b[:], curr);
|
||||
if e3 != .None {
|
||||
return T{}, .Empty;
|
||||
}
|
||||
|
||||
res = (^T)(&b[0])^;
|
||||
return res, .None;
|
||||
}
|
||||
|
||||
peek_data :: proc{peek_data_from_memory, peek_data_from_stream};
|
||||
|
||||
|
||||
|
||||
// Sliding window read back
|
||||
@(optimization_mode="speed")
|
||||
peek_back_byte :: #force_inline proc(z: ^$C, offset: i64) -> (res: u8, err: io.Error) {
|
||||
// Look back into the sliding window.
|
||||
return z.output.buf[z.bytes_written - offset], .None;
|
||||
}
|
||||
|
||||
// Generalized bit reader LSB
|
||||
@(optimization_mode="speed")
|
||||
refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width := i8(48)) {
|
||||
refill := u64(width);
|
||||
b := u64(0);
|
||||
|
||||
if z.num_bits > refill {
|
||||
return;
|
||||
}
|
||||
|
||||
for {
|
||||
if len(z.input_data) != 0 {
|
||||
b = u64(z.input_data[0]);
|
||||
z.input_data = z.input_data[1:];
|
||||
} else {
|
||||
b = 0;
|
||||
}
|
||||
|
||||
z.code_buffer |= b << u8(z.num_bits);
|
||||
z.num_bits += 8;
|
||||
if z.num_bits > refill {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generalized bit reader LSB
|
||||
@(optimization_mode="speed")
|
||||
refill_lsb_from_stream :: proc(z: ^Context_Stream_Input, width := i8(24)) {
|
||||
refill := u64(width);
|
||||
|
||||
for {
|
||||
if z.num_bits > refill {
|
||||
break;
|
||||
}
|
||||
if z.code_buffer == 0 && z.num_bits > 63 {
|
||||
z.num_bits = 0;
|
||||
}
|
||||
if z.code_buffer >= 1 << uint(z.num_bits) {
|
||||
// Code buffer is malformed.
|
||||
z.num_bits = max(u64);
|
||||
return;
|
||||
}
|
||||
b, err := read_u8(z);
|
||||
if err != .None {
|
||||
// This is fine at the end of the file.
|
||||
return;
|
||||
}
|
||||
z.code_buffer |= (u64(b) << u8(z.num_bits));
|
||||
z.num_bits += 8;
|
||||
}
|
||||
}
|
||||
|
||||
refill_lsb :: proc{refill_lsb_from_memory, refill_lsb_from_stream};
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
consume_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) {
|
||||
z.code_buffer >>= width;
|
||||
z.num_bits -= u64(width);
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
consume_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) {
|
||||
z.code_buffer >>= width;
|
||||
z.num_bits -= u64(width);
|
||||
}
|
||||
|
||||
consume_bits_lsb :: proc{consume_bits_lsb_from_memory, consume_bits_lsb_from_stream};
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
if z.num_bits < u64(width) {
|
||||
refill_lsb(z);
|
||||
}
|
||||
return u32(z.code_buffer & ~(~u64(0) << width));
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
if z.num_bits < u64(width) {
|
||||
refill_lsb(z);
|
||||
}
|
||||
return u32(z.code_buffer & ~(~u64(0) << width));
|
||||
}
|
||||
|
||||
peek_bits_lsb :: proc{peek_bits_lsb_from_memory, peek_bits_lsb_from_stream};
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
assert(z.num_bits >= u64(width));
|
||||
return u32(z.code_buffer & ~(~u64(0) << width));
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
assert(z.num_bits >= u64(width));
|
||||
return u32(z.code_buffer & ~(~u64(0) << width));
|
||||
}
|
||||
|
||||
peek_bits_no_refill_lsb :: proc{peek_bits_no_refill_lsb_from_memory, peek_bits_no_refill_lsb_from_stream};
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_bits_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
k := #force_inline peek_bits_lsb(z, width);
|
||||
#force_inline consume_bits_lsb(z, width);
|
||||
return k;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_bits_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
k := peek_bits_lsb(z, width);
|
||||
consume_bits_lsb(z, width);
|
||||
return k;
|
||||
}
|
||||
|
||||
read_bits_lsb :: proc{read_bits_lsb_from_memory, read_bits_lsb_from_stream};
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_bits_no_refill_lsb_from_memory :: #force_inline proc(z: ^Context_Memory_Input, width: u8) -> u32 {
|
||||
k := #force_inline peek_bits_no_refill_lsb(z, width);
|
||||
#force_inline consume_bits_lsb(z, width);
|
||||
return k;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
read_bits_no_refill_lsb_from_stream :: #force_inline proc(z: ^Context_Stream_Input, width: u8) -> u32 {
|
||||
k := peek_bits_no_refill_lsb(z, width);
|
||||
consume_bits_lsb(z, width);
|
||||
return k;
|
||||
}
|
||||
|
||||
read_bits_no_refill_lsb :: proc{read_bits_no_refill_lsb_from_memory, read_bits_no_refill_lsb_from_stream};
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
discard_to_next_byte_lsb_from_memory :: proc(z: ^Context_Memory_Input) {
|
||||
discard := u8(z.num_bits & 7);
|
||||
#force_inline consume_bits_lsb(z, discard);
|
||||
}
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
|
||||
discard := u8(z.num_bits & 7);
|
||||
consume_bits_lsb(z, discard);
|
||||
}
|
||||
|
||||
discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream};
|
||||
@@ -0,0 +1,89 @@
|
||||
//+ignore
|
||||
package gzip
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
|
||||
A small GZIP implementation as an example.
|
||||
*/
|
||||
|
||||
import "core:bytes"
|
||||
import "core:os"
|
||||
import "core:compress"
|
||||
import "core:fmt"
|
||||
|
||||
// Small GZIP file with fextra, fname and fcomment present.
|
||||
@private
|
||||
TEST: []u8 = {
|
||||
0x1f, 0x8b, 0x08, 0x1c, 0xcb, 0x3b, 0x3a, 0x5a,
|
||||
0x02, 0x03, 0x07, 0x00, 0x61, 0x62, 0x03, 0x00,
|
||||
0x63, 0x64, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x6e,
|
||||
0x61, 0x6d, 0x65, 0x00, 0x54, 0x68, 0x69, 0x73,
|
||||
0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x63, 0x6f,
|
||||
0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x00, 0x2b, 0x48,
|
||||
0xac, 0xcc, 0xc9, 0x4f, 0x4c, 0x01, 0x00, 0x15,
|
||||
0x6a, 0x2c, 0x42, 0x07, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
main :: proc() {
|
||||
// Set up output buffer.
|
||||
buf := bytes.Buffer{};
|
||||
|
||||
stdout :: proc(s: string) {
|
||||
os.write_string(os.stdout, s);
|
||||
}
|
||||
stderr :: proc(s: string) {
|
||||
os.write_string(os.stderr, s);
|
||||
}
|
||||
|
||||
args := os.args;
|
||||
|
||||
if len(args) < 2 {
|
||||
stderr("No input file specified.\n");
|
||||
err := load(slice=TEST, buf=&buf, known_gzip_size=len(TEST));
|
||||
if err == nil {
|
||||
stdout("Displaying test vector: ");
|
||||
stdout(bytes.buffer_to_string(&buf));
|
||||
stdout("\n");
|
||||
} else {
|
||||
fmt.printf("gzip.load returned %v\n", err);
|
||||
}
|
||||
bytes.buffer_destroy(&buf);
|
||||
os.exit(0);
|
||||
}
|
||||
|
||||
// The rest are all files.
|
||||
args = args[1:];
|
||||
err: Error;
|
||||
|
||||
for file in args {
|
||||
if file == "-" {
|
||||
// Read from stdin
|
||||
s := os.stream_from_handle(os.stdin);
|
||||
ctx := &compress.Context_Stream_Input{
|
||||
input = s,
|
||||
};
|
||||
err = load(ctx, &buf);
|
||||
} else {
|
||||
err = load(file, &buf);
|
||||
}
|
||||
if err != nil {
|
||||
if err != E_General.File_Not_Found {
|
||||
stderr("File not found: ");
|
||||
stderr(file);
|
||||
stderr("\n");
|
||||
os.exit(1);
|
||||
}
|
||||
stderr("GZIP returned an error.\n");
|
||||
bytes.buffer_destroy(&buf);
|
||||
os.exit(2);
|
||||
}
|
||||
stdout(bytes.buffer_to_string(&buf));
|
||||
}
|
||||
bytes.buffer_destroy(&buf);
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
package gzip
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
|
||||
This package implements support for the GZIP file format v4.3,
|
||||
as specified in RFC 1952.
|
||||
|
||||
It is implemented in such a way that it lends itself naturally
|
||||
to be the input to a complementary TAR implementation.
|
||||
*/
|
||||
|
||||
import "core:compress/zlib"
|
||||
import "core:compress"
|
||||
import "core:os"
|
||||
import "core:io"
|
||||
import "core:bytes"
|
||||
import "core:hash"
|
||||
|
||||
Magic :: enum u16le {
|
||||
GZIP = 0x8b << 8 | 0x1f,
|
||||
}
|
||||
|
||||
Header :: struct #packed {
|
||||
magic: Magic,
|
||||
compression_method: Compression,
|
||||
flags: Header_Flags,
|
||||
modification_time: u32le,
|
||||
xfl: Compression_Flags,
|
||||
os: OS,
|
||||
}
|
||||
#assert(size_of(Header) == 10);
|
||||
|
||||
Header_Flag :: enum u8 {
|
||||
// Order is important
|
||||
text = 0,
|
||||
header_crc = 1,
|
||||
extra = 2,
|
||||
name = 3,
|
||||
comment = 4,
|
||||
reserved_1 = 5,
|
||||
reserved_2 = 6,
|
||||
reserved_3 = 7,
|
||||
}
|
||||
Header_Flags :: distinct bit_set[Header_Flag; u8];
|
||||
|
||||
OS :: enum u8 {
|
||||
FAT = 0,
|
||||
Amiga = 1,
|
||||
VMS = 2,
|
||||
Unix = 3,
|
||||
VM_CMS = 4,
|
||||
Atari_TOS = 5,
|
||||
HPFS = 6,
|
||||
Macintosh = 7,
|
||||
Z_System = 8,
|
||||
CP_M = 9,
|
||||
TOPS_20 = 10,
|
||||
NTFS = 11,
|
||||
QDOS = 12,
|
||||
Acorn_RISCOS = 13,
|
||||
_Unknown = 14,
|
||||
Unknown = 255,
|
||||
}
|
||||
OS_Name :: #partial [OS]string{
|
||||
.FAT = "FAT",
|
||||
.Amiga = "Amiga",
|
||||
.VMS = "VMS/OpenVMS",
|
||||
.Unix = "Unix",
|
||||
.VM_CMS = "VM/CMS",
|
||||
.Atari_TOS = "Atari TOS",
|
||||
.HPFS = "HPFS",
|
||||
.Macintosh = "Macintosh",
|
||||
.Z_System = "Z-System",
|
||||
.CP_M = "CP/M",
|
||||
.TOPS_20 = "TOPS-20",
|
||||
.NTFS = "NTFS",
|
||||
.QDOS = "QDOS",
|
||||
.Acorn_RISCOS = "Acorn RISCOS",
|
||||
.Unknown = "Unknown",
|
||||
};
|
||||
|
||||
Compression :: enum u8 {
|
||||
DEFLATE = 8,
|
||||
}
|
||||
|
||||
Compression_Flags :: enum u8 {
|
||||
Maximum_Compression = 2,
|
||||
Fastest_Compression = 4,
|
||||
}
|
||||
|
||||
Error :: compress.Error;
|
||||
E_General :: compress.General_Error;
|
||||
E_GZIP :: compress.GZIP_Error;
|
||||
E_ZLIB :: compress.ZLIB_Error;
|
||||
E_Deflate :: compress.Deflate_Error;
|
||||
|
||||
GZIP_MAX_PAYLOAD_SIZE :: int(max(u32le));
|
||||
|
||||
load :: proc{load_from_slice, load_from_file, load_from_context};
|
||||
|
||||
load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
data, ok := os.read_entire_file(filename, allocator);
|
||||
defer delete(data);
|
||||
|
||||
err = E_General.File_Not_Found;
|
||||
if ok {
|
||||
err = load_from_slice(data, buf, len(data), expected_output_size, allocator);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
load_from_slice :: proc(slice: []u8, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
buf := buf;
|
||||
|
||||
z := &compress.Context_Memory_Input{
|
||||
input_data = slice,
|
||||
output = buf,
|
||||
};
|
||||
return load_from_context(z, buf, known_gzip_size, expected_output_size, allocator);
|
||||
}
|
||||
|
||||
load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
buf := buf;
|
||||
expected_output_size := expected_output_size;
|
||||
|
||||
input_data_consumed := 0;
|
||||
|
||||
z.output = buf;
|
||||
|
||||
if expected_output_size > GZIP_MAX_PAYLOAD_SIZE {
|
||||
return E_GZIP.Payload_Size_Exceeds_Max_Payload;
|
||||
}
|
||||
|
||||
if expected_output_size > compress.COMPRESS_OUTPUT_ALLOCATE_MAX {
|
||||
return E_GZIP.Output_Exceeds_COMPRESS_OUTPUT_ALLOCATE_MAX;
|
||||
}
|
||||
|
||||
b: []u8;
|
||||
|
||||
header, e := compress.read_data(z, Header);
|
||||
if e != .None {
|
||||
return E_General.File_Too_Short;
|
||||
}
|
||||
input_data_consumed += size_of(Header);
|
||||
|
||||
if header.magic != .GZIP {
|
||||
return E_GZIP.Invalid_GZIP_Signature;
|
||||
}
|
||||
if header.compression_method != .DEFLATE {
|
||||
return E_General.Unknown_Compression_Method;
|
||||
}
|
||||
|
||||
if header.os >= ._Unknown {
|
||||
header.os = .Unknown;
|
||||
}
|
||||
|
||||
if .reserved_1 in header.flags || .reserved_2 in header.flags || .reserved_3 in header.flags {
|
||||
return E_GZIP.Reserved_Flag_Set;
|
||||
}
|
||||
|
||||
// printf("signature: %v\n", header.magic);
|
||||
// printf("compression: %v\n", header.compression_method);
|
||||
// printf("flags: %v\n", header.flags);
|
||||
// printf("modification time: %v\n", time.unix(i64(header.modification_time), 0));
|
||||
// printf("xfl: %v (%v)\n", header.xfl, int(header.xfl));
|
||||
// printf("os: %v\n", OS_Name[header.os]);
|
||||
|
||||
if .extra in header.flags {
|
||||
xlen, e_extra := compress.read_data(z, u16le);
|
||||
input_data_consumed += 2;
|
||||
|
||||
if e_extra != .None {
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
// printf("Extra data present (%v bytes)\n", xlen);
|
||||
if xlen < 4 {
|
||||
// Minimum length is 2 for ID + 2 for a field length, if set to zero.
|
||||
return E_GZIP.Invalid_Extra_Data;
|
||||
}
|
||||
|
||||
field_id: [2]u8;
|
||||
field_length: u16le;
|
||||
field_error: io.Error;
|
||||
|
||||
for xlen >= 4 {
|
||||
// println("Parsing Extra field(s).");
|
||||
field_id, field_error = compress.read_data(z, [2]u8);
|
||||
if field_error != .None {
|
||||
// printf("Parsing Extra returned: %v\n", field_error);
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
xlen -= 2;
|
||||
input_data_consumed += 2;
|
||||
|
||||
field_length, field_error = compress.read_data(z, u16le);
|
||||
if field_error != .None {
|
||||
// printf("Parsing Extra returned: %v\n", field_error);
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
xlen -= 2;
|
||||
input_data_consumed += 2;
|
||||
|
||||
if xlen <= 0 {
|
||||
// We're not going to try and recover by scanning for a ZLIB header.
|
||||
// Who knows what else is wrong with this file.
|
||||
return E_GZIP.Invalid_Extra_Data;
|
||||
}
|
||||
|
||||
// printf(" Field \"%v\" of length %v found: ", string(field_id[:]), field_length);
|
||||
if field_length > 0 {
|
||||
b, field_error = compress.read_slice(z, int(field_length));
|
||||
if field_error != .None {
|
||||
// printf("Parsing Extra returned: %v\n", field_error);
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
xlen -= field_length;
|
||||
input_data_consumed += int(field_length);
|
||||
|
||||
// printf("%v\n", string(field_data));
|
||||
}
|
||||
|
||||
if xlen != 0 {
|
||||
return E_GZIP.Invalid_Extra_Data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if .name in header.flags {
|
||||
// Should be enough.
|
||||
name: [1024]u8;
|
||||
i := 0;
|
||||
name_error: io.Error;
|
||||
|
||||
for i < len(name) {
|
||||
b, name_error = compress.read_slice(z, 1);
|
||||
if name_error != .None {
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
input_data_consumed += 1;
|
||||
if b[0] == 0 {
|
||||
break;
|
||||
}
|
||||
name[i] = b[0];
|
||||
i += 1;
|
||||
if i >= len(name) {
|
||||
return E_GZIP.Original_Name_Too_Long;
|
||||
}
|
||||
}
|
||||
// printf("Original filename: %v\n", string(name[:i]));
|
||||
}
|
||||
|
||||
if .comment in header.flags {
|
||||
// Should be enough.
|
||||
comment: [1024]u8;
|
||||
i := 0;
|
||||
comment_error: io.Error;
|
||||
|
||||
for i < len(comment) {
|
||||
b, comment_error = compress.read_slice(z, 1);
|
||||
if comment_error != .None {
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
input_data_consumed += 1;
|
||||
if b[0] == 0 {
|
||||
break;
|
||||
}
|
||||
comment[i] = b[0];
|
||||
i += 1;
|
||||
if i >= len(comment) {
|
||||
return E_GZIP.Comment_Too_Long;
|
||||
}
|
||||
}
|
||||
// printf("Comment: %v\n", string(comment[:i]));
|
||||
}
|
||||
|
||||
if .header_crc in header.flags {
|
||||
crc_error: io.Error;
|
||||
_, crc_error = compress.read_slice(z, 2);
|
||||
input_data_consumed += 2;
|
||||
if crc_error != .None {
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
/*
|
||||
We don't actually check the CRC16 (lower 2 bytes of CRC32 of header data until the CRC field).
|
||||
If we find a gzip file in the wild that sets this field, we can add proper support for it.
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
We should have arrived at the ZLIB payload.
|
||||
*/
|
||||
payload_u32le: u32le;
|
||||
|
||||
// fmt.printf("known_gzip_size: %v | expected_output_size: %v\n", known_gzip_size, expected_output_size);
|
||||
|
||||
if expected_output_size > -1 {
|
||||
/*
|
||||
We already checked that it's not larger than the output buffer max,
|
||||
or GZIP length field's max.
|
||||
|
||||
We'll just pass it on to `zlib.inflate_raw`;
|
||||
*/
|
||||
} else {
|
||||
/*
|
||||
If we know the size of the GZIP file *and* it is fully in memory,
|
||||
then we can peek at the unpacked size at the end.
|
||||
|
||||
We'll still want to ensure there's capacity left in the output buffer when we write, of course.
|
||||
|
||||
*/
|
||||
if known_gzip_size > -1 {
|
||||
offset := i64(known_gzip_size - input_data_consumed - 4);
|
||||
size, _ := compress.input_size(z);
|
||||
if size >= offset + 4 {
|
||||
length_bytes := z.input_data[offset:][:4];
|
||||
payload_u32le = (^u32le)(&length_bytes[0])^;
|
||||
expected_output_size = int(payload_u32le);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
TODO(Jeroen): When reading a GZIP from a stream, check if impl_seek is present.
|
||||
If so, we can seek to the end, grab the size from the footer, and seek back to payload start.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
// fmt.printf("GZIP: Expected Payload Size: %v\n", expected_output_size);
|
||||
|
||||
zlib_error := zlib.inflate_raw(z=z, expected_output_size=expected_output_size);
|
||||
if zlib_error != nil {
|
||||
return zlib_error;
|
||||
}
|
||||
/*
|
||||
Read CRC32 using the ctx bit reader because zlib may leave bytes in there.
|
||||
*/
|
||||
compress.discard_to_next_byte_lsb(z);
|
||||
|
||||
footer_error: io.Error;
|
||||
|
||||
payload_crc_b: [4]u8;
|
||||
for _, i in payload_crc_b {
|
||||
payload_crc_b[i], footer_error = compress.read_u8_prefer_code_buffer_lsb(z);
|
||||
}
|
||||
payload_crc := transmute(u32le)payload_crc_b;
|
||||
|
||||
payload := bytes.buffer_to_bytes(buf);
|
||||
crc32 := u32le(hash.crc32(payload));
|
||||
if crc32 != payload_crc {
|
||||
return E_GZIP.Payload_CRC_Invalid;
|
||||
}
|
||||
|
||||
payload_len_b: [4]u8;
|
||||
for _, i in payload_len_b {
|
||||
payload_len_b[i], footer_error = compress.read_u8_prefer_code_buffer_lsb(z);
|
||||
}
|
||||
payload_len := transmute(u32le)payload_len_b;
|
||||
|
||||
if len(payload) != int(payload_len) {
|
||||
return E_GZIP.Payload_Length_Invalid;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//+ignore
|
||||
package zlib
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
|
||||
An example of how to use `zlib.inflate`.
|
||||
*/
|
||||
|
||||
import "core:bytes"
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
|
||||
ODIN_DEMO := []u8{
|
||||
120, 156, 101, 144, 77, 110, 131, 48, 16, 133, 215, 204, 41, 158, 44,
|
||||
69, 73, 32, 148, 182, 75, 35, 14, 208, 125, 47, 96, 185, 195, 143,
|
||||
130, 13, 50, 38, 81, 84, 101, 213, 75, 116, 215, 43, 246, 8, 53,
|
||||
82, 126, 8, 181, 188, 152, 153, 111, 222, 147, 159, 123, 165, 247, 170,
|
||||
98, 24, 213, 88, 162, 198, 244, 157, 243, 16, 186, 115, 44, 75, 227,
|
||||
5, 77, 115, 72, 137, 222, 117, 122, 179, 197, 39, 69, 161, 170, 156,
|
||||
50, 144, 5, 68, 130, 4, 49, 126, 127, 190, 191, 144, 34, 19, 57,
|
||||
69, 74, 235, 209, 140, 173, 242, 157, 155, 54, 158, 115, 162, 168, 12,
|
||||
181, 239, 246, 108, 17, 188, 174, 242, 224, 20, 13, 199, 198, 235, 250,
|
||||
194, 166, 129, 86, 3, 99, 157, 172, 37, 230, 62, 73, 129, 151, 252,
|
||||
70, 211, 5, 77, 31, 104, 188, 160, 113, 129, 215, 59, 205, 22, 52,
|
||||
123, 160, 83, 142, 255, 242, 89, 123, 93, 149, 200, 50, 188, 85, 54,
|
||||
252, 18, 248, 192, 238, 228, 235, 198, 86, 224, 118, 224, 176, 113, 166,
|
||||
112, 67, 106, 227, 159, 122, 215, 88, 95, 110, 196, 123, 205, 183, 224,
|
||||
98, 53, 8, 104, 213, 234, 201, 147, 7, 248, 192, 14, 170, 29, 25,
|
||||
171, 15, 18, 59, 138, 112, 63, 23, 205, 110, 254, 136, 109, 78, 231,
|
||||
63, 234, 138, 133, 204,
|
||||
};
|
||||
OUTPUT_SIZE :: 438;
|
||||
|
||||
buf: bytes.Buffer;
|
||||
|
||||
// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
|
||||
err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE);
|
||||
defer bytes.buffer_destroy(&buf);
|
||||
|
||||
if err != nil {
|
||||
fmt.printf("\nError: %v\n", err);
|
||||
}
|
||||
s := bytes.buffer_to_string(&buf);
|
||||
fmt.printf("Input: %v bytes, output (%v bytes):\n%v\n", len(ODIN_DEMO), len(s), s);
|
||||
assert(len(s) == OUTPUT_SIZE);
|
||||
}
|
||||
@@ -0,0 +1,715 @@
|
||||
package zlib
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation, optimization.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
*/
|
||||
|
||||
import "core:compress"
|
||||
|
||||
import "core:mem"
|
||||
import "core:io"
|
||||
import "core:hash"
|
||||
import "core:bytes"
|
||||
|
||||
/*
|
||||
zlib.inflate decompresses a ZLIB stream passed in as a []u8 or io.Stream.
|
||||
Returns: Error.
|
||||
*/
|
||||
|
||||
/*
|
||||
Do we do Adler32 as we write bytes to output?
|
||||
It used to be faster to do it inline, now it's faster to do it at the end of `inflate`.
|
||||
|
||||
We'll see what's faster after more optimization, and might end up removing
|
||||
`Context.rolling_hash` if not inlining it is still faster.
|
||||
|
||||
*/
|
||||
|
||||
Compression_Method :: enum u8 {
|
||||
DEFLATE = 8,
|
||||
Reserved = 15,
|
||||
}
|
||||
|
||||
Compression_Level :: enum u8 {
|
||||
Fastest = 0,
|
||||
Fast = 1,
|
||||
Default = 2,
|
||||
Maximum = 3,
|
||||
}
|
||||
|
||||
Options :: struct {
|
||||
window_size: u16,
|
||||
level: u8,
|
||||
}
|
||||
|
||||
Error :: compress.Error;
|
||||
E_General :: compress.General_Error;
|
||||
E_ZLIB :: compress.ZLIB_Error;
|
||||
E_Deflate :: compress.Deflate_Error;
|
||||
|
||||
DEFLATE_MAX_CHUNK_SIZE :: 65535;
|
||||
DEFLATE_MAX_LITERAL_SIZE :: 65535;
|
||||
DEFLATE_MAX_DISTANCE :: 32768;
|
||||
DEFLATE_MAX_LENGTH :: 258;
|
||||
|
||||
HUFFMAN_MAX_BITS :: 16;
|
||||
HUFFMAN_FAST_BITS :: 9;
|
||||
HUFFMAN_FAST_MASK :: ((1 << HUFFMAN_FAST_BITS) - 1);
|
||||
|
||||
Z_LENGTH_BASE := [31]u16{
|
||||
3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,
|
||||
67,83,99,115,131,163,195,227,258,0,0,
|
||||
};
|
||||
|
||||
Z_LENGTH_EXTRA := [31]u8{
|
||||
0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,
|
||||
};
|
||||
|
||||
Z_DIST_BASE := [32]u16{
|
||||
1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,
|
||||
257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0,
|
||||
};
|
||||
|
||||
Z_DIST_EXTRA := [32]u8{
|
||||
0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0,
|
||||
};
|
||||
|
||||
Z_LENGTH_DEZIGZAG := []u8{
|
||||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
|
||||
};
|
||||
|
||||
Z_FIXED_LENGTH := [288]u8{
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
|
||||
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||
9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,
|
||||
};
|
||||
|
||||
Z_FIXED_DIST := [32]u8{
|
||||
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
|
||||
};
|
||||
|
||||
/*
|
||||
Accelerate all cases in default tables.
|
||||
*/
|
||||
ZFAST_BITS :: 9;
|
||||
ZFAST_MASK :: ((1 << ZFAST_BITS) - 1);
|
||||
|
||||
/*
|
||||
ZLIB-style Huffman encoding.
|
||||
JPEG packs from left, ZLIB from right. We can't share code.
|
||||
*/
|
||||
Huffman_Table :: struct {
|
||||
fast: [1 << ZFAST_BITS]u16,
|
||||
firstcode: [16]u16,
|
||||
maxcode: [17]int,
|
||||
firstsymbol: [16]u16,
|
||||
size: [288]u8,
|
||||
value: [288]u16,
|
||||
};
|
||||
|
||||
// Implementation starts here
|
||||
@(optimization_mode="speed")
|
||||
z_bit_reverse :: #force_inline proc(n: u16, bits: u8) -> (r: u16) {
|
||||
assert(bits <= 16);
|
||||
// NOTE: Can optimize with llvm.bitreverse.i64 or some bit twiddling
|
||||
// by reversing all of the bits and masking out the unneeded ones.
|
||||
r = n;
|
||||
r = ((r & 0xAAAA) >> 1) | ((r & 0x5555) << 1);
|
||||
r = ((r & 0xCCCC) >> 2) | ((r & 0x3333) << 2);
|
||||
r = ((r & 0xF0F0) >> 4) | ((r & 0x0F0F) << 4);
|
||||
r = ((r & 0xFF00) >> 8) | ((r & 0x00FF) << 8);
|
||||
|
||||
r >>= (16 - bits);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
grow_buffer :: proc(buf: ^[dynamic]u8) -> (err: compress.Error) {
|
||||
/*
|
||||
That we get here at all means that we didn't pass an expected output size,
|
||||
or that it was too little.
|
||||
*/
|
||||
|
||||
/*
|
||||
Double until we reach the maximum allowed.
|
||||
*/
|
||||
new_size := min(len(buf) << 1, compress.COMPRESS_OUTPUT_ALLOCATE_MAX);
|
||||
resize(buf, new_size);
|
||||
if len(buf) != new_size {
|
||||
/*
|
||||
Resize failed.
|
||||
*/
|
||||
return .Resize_Failed;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Make these return compress.Error.
|
||||
*/
|
||||
|
||||
@(optimization_mode="speed")
|
||||
write_byte :: #force_inline proc(z: ^$C, c: u8) -> (err: io.Error) #no_bounds_check {
|
||||
/*
|
||||
Resize if needed.
|
||||
*/
|
||||
if int(z.bytes_written) + 1 >= len(z.output.buf) {
|
||||
e := grow_buffer(&z.output.buf);
|
||||
if e != nil {
|
||||
return .Short_Write;
|
||||
}
|
||||
}
|
||||
|
||||
#no_bounds_check {
|
||||
z.output.buf[z.bytes_written] = c;
|
||||
}
|
||||
z.bytes_written += 1;
|
||||
return .None;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
repl_byte :: proc(z: ^$C, count: u16, c: u8) -> (err: io.Error) #no_bounds_check {
|
||||
/*
|
||||
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
||||
without having to worry about wrapping, so no need for a temp allocation to give to
|
||||
the output stream, just give it _that_ slice.
|
||||
*/
|
||||
|
||||
/*
|
||||
Resize if needed.
|
||||
*/
|
||||
if int(z.bytes_written) + int(count) >= len(z.output.buf) {
|
||||
e := grow_buffer(&z.output.buf);
|
||||
if e != nil {
|
||||
return .Short_Write;
|
||||
}
|
||||
}
|
||||
|
||||
#no_bounds_check {
|
||||
for _ in 0..<count {
|
||||
z.output.buf[z.bytes_written] = c;
|
||||
z.bytes_written += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return .None;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
repl_bytes :: proc(z: ^$C, count: u16, distance: u16) -> (err: io.Error) {
|
||||
/*
|
||||
TODO(Jeroen): Once we have a magic ring buffer, we can just peek/write into it
|
||||
without having to worry about wrapping, so no need for a temp allocation to give to
|
||||
the output stream, just give it _that_ slice.
|
||||
*/
|
||||
|
||||
offset := i64(distance);
|
||||
|
||||
if int(z.bytes_written) + int(count) >= len(z.output.buf) {
|
||||
e := grow_buffer(&z.output.buf);
|
||||
if e != nil {
|
||||
return .Short_Write;
|
||||
}
|
||||
}
|
||||
|
||||
#no_bounds_check {
|
||||
for _ in 0..<count {
|
||||
c := z.output.buf[z.bytes_written - offset];
|
||||
z.output.buf[z.bytes_written] = c;
|
||||
z.bytes_written += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return .None;
|
||||
}
|
||||
|
||||
|
||||
allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_Table, err: Error) {
|
||||
return new(Huffman_Table, allocator), nil;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
|
||||
sizes: [HUFFMAN_MAX_BITS+1]int;
|
||||
next_code: [HUFFMAN_MAX_BITS]int;
|
||||
|
||||
k := int(0);
|
||||
|
||||
mem.zero_slice(sizes[:]);
|
||||
mem.zero_slice(z.fast[:]);
|
||||
|
||||
for v in code_lengths {
|
||||
sizes[v] += 1;
|
||||
}
|
||||
sizes[0] = 0;
|
||||
|
||||
for i in 1..<(HUFFMAN_MAX_BITS+1) {
|
||||
if sizes[i] > (1 << uint(i)) {
|
||||
return E_Deflate.Huffman_Bad_Sizes;
|
||||
}
|
||||
}
|
||||
code := int(0);
|
||||
|
||||
for i in 1..<HUFFMAN_MAX_BITS {
|
||||
next_code[i] = code;
|
||||
z.firstcode[i] = u16(code);
|
||||
z.firstsymbol[i] = u16(k);
|
||||
code = code + sizes[i];
|
||||
if sizes[i] != 0 {
|
||||
if code - 1 >= (1 << u16(i)) {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths;
|
||||
}
|
||||
}
|
||||
z.maxcode[i] = code << (HUFFMAN_MAX_BITS - uint(i));
|
||||
code <<= 1;
|
||||
k += int(sizes[i]);
|
||||
}
|
||||
|
||||
z.maxcode[HUFFMAN_MAX_BITS] = 0x10000; // Sentinel
|
||||
c: int;
|
||||
|
||||
for v, ci in code_lengths {
|
||||
if v != 0 {
|
||||
c = next_code[v] - int(z.firstcode[v]) + int(z.firstsymbol[v]);
|
||||
fastv := u16((u16(v) << 9) | u16(ci));
|
||||
z.size[c] = u8(v);
|
||||
z.value[c] = u16(ci);
|
||||
if v <= ZFAST_BITS {
|
||||
j := z_bit_reverse(u16(next_code[v]), v);
|
||||
for j < (1 << ZFAST_BITS) {
|
||||
z.fast[j] = fastv;
|
||||
j += (1 << v);
|
||||
}
|
||||
}
|
||||
next_code[v] += 1;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||
code := u16(compress.peek_bits_lsb(z,16));
|
||||
|
||||
k := int(z_bit_reverse(code, 16));
|
||||
s: u8;
|
||||
|
||||
#no_bounds_check for s = HUFFMAN_FAST_BITS+1; ; {
|
||||
if k < t.maxcode[s] {
|
||||
break;
|
||||
}
|
||||
s += 1;
|
||||
}
|
||||
if s >= 16 {
|
||||
return 0, E_Deflate.Bad_Huffman_Code;
|
||||
}
|
||||
// code size is s, so:
|
||||
b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s]);
|
||||
if b >= size_of(t.size) {
|
||||
return 0, E_Deflate.Bad_Huffman_Code;
|
||||
}
|
||||
if t.size[b] != s {
|
||||
return 0, E_Deflate.Bad_Huffman_Code;
|
||||
}
|
||||
|
||||
compress.consume_bits_lsb(z, s);
|
||||
|
||||
r = t.value[b];
|
||||
return r, nil;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
|
||||
if z.num_bits < 16 {
|
||||
if z.num_bits > 63 {
|
||||
return 0, E_ZLIB.Code_Buffer_Malformed;
|
||||
}
|
||||
compress.refill_lsb(z);
|
||||
if z.num_bits > 63 {
|
||||
return 0, E_General.Stream_Too_Short;
|
||||
}
|
||||
}
|
||||
#no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK];
|
||||
if b != 0 {
|
||||
s := u8(b >> ZFAST_BITS);
|
||||
compress.consume_bits_lsb(z, s);
|
||||
return b & 511, nil;
|
||||
}
|
||||
return decode_huffman_slowpath(z, t);
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err: Error) #no_bounds_check {
|
||||
#no_bounds_check for {
|
||||
value, e := decode_huffman(z, z_repeat);
|
||||
if e != nil {
|
||||
return err;
|
||||
}
|
||||
if value < 256 {
|
||||
e := write_byte(z, u8(value));
|
||||
if e != .None {
|
||||
return E_General.Output_Too_Short;
|
||||
}
|
||||
} else {
|
||||
if value == 256 {
|
||||
// End of block
|
||||
return nil;
|
||||
}
|
||||
|
||||
value -= 257;
|
||||
length := Z_LENGTH_BASE[value];
|
||||
if Z_LENGTH_EXTRA[value] > 0 {
|
||||
length += u16(compress.read_bits_lsb(z, Z_LENGTH_EXTRA[value]));
|
||||
}
|
||||
|
||||
value, e = decode_huffman(z, z_offset);
|
||||
if e != nil {
|
||||
return E_Deflate.Bad_Huffman_Code;
|
||||
}
|
||||
|
||||
distance := Z_DIST_BASE[value];
|
||||
if Z_DIST_EXTRA[value] > 0 {
|
||||
distance += u16(compress.read_bits_lsb(z, Z_DIST_EXTRA[value]));
|
||||
}
|
||||
|
||||
if z.bytes_written < i64(distance) {
|
||||
// Distance is longer than we've decoded so far.
|
||||
return E_Deflate.Bad_Distance;
|
||||
}
|
||||
|
||||
/*
|
||||
These might be sped up with a repl_byte call that copies
|
||||
from the already written output more directly, and that
|
||||
update the Adler checksum once after.
|
||||
|
||||
That way we'd suffer less Stream vtable overhead.
|
||||
*/
|
||||
if distance == 1 {
|
||||
/*
|
||||
Replicate the last outputted byte, length times.
|
||||
*/
|
||||
if length > 0 {
|
||||
c := z.output.buf[z.bytes_written - i64(distance)];
|
||||
e := repl_byte(z, length, c);
|
||||
if e != .None {
|
||||
return E_General.Output_Too_Short;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if length > 0 {
|
||||
e := repl_bytes(z, length, distance);
|
||||
if e != .None {
|
||||
return E_General.Output_Too_Short;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := false, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
||||
/*
|
||||
ctx.output must be a bytes.Buffer for now. We'll add a separate implementation that writes to a stream.
|
||||
|
||||
raw determines whether the ZLIB header is processed, or we're inflating a raw
|
||||
DEFLATE stream.
|
||||
*/
|
||||
|
||||
if !raw {
|
||||
size, size_err := compress.input_size(ctx);
|
||||
if size < 6 || size_err != nil {
|
||||
return E_General.Stream_Too_Short;
|
||||
}
|
||||
|
||||
cmf, _ := compress.read_u8(ctx);
|
||||
|
||||
method := Compression_Method(cmf & 0xf);
|
||||
if method != .DEFLATE {
|
||||
return E_General.Unknown_Compression_Method;
|
||||
}
|
||||
|
||||
cinfo := (cmf >> 4) & 0xf;
|
||||
if cinfo > 7 {
|
||||
return E_ZLIB.Unsupported_Window_Size;
|
||||
}
|
||||
flg, _ := compress.read_u8(ctx);
|
||||
|
||||
fcheck := flg & 0x1f;
|
||||
fcheck_computed := (cmf << 8 | flg) & 0x1f;
|
||||
if fcheck != fcheck_computed {
|
||||
return E_General.Checksum_Failed;
|
||||
}
|
||||
|
||||
fdict := (flg >> 5) & 1;
|
||||
/*
|
||||
We don't handle built-in dictionaries for now.
|
||||
They're application specific and PNG doesn't use them.
|
||||
*/
|
||||
if fdict != 0 {
|
||||
return E_ZLIB.FDICT_Unsupported;
|
||||
}
|
||||
|
||||
// flevel := Compression_Level((flg >> 6) & 3);
|
||||
/*
|
||||
Inflate can consume bits belonging to the Adler checksum.
|
||||
We pass the entire stream to Inflate and will unget bytes if we need to
|
||||
at the end to compare checksums.
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
// Parse ZLIB stream without header.
|
||||
err = inflate_raw(z=ctx, expected_output_size=expected_output_size);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
if !raw {
|
||||
compress.discard_to_next_byte_lsb(ctx);
|
||||
|
||||
adler_b: [4]u8;
|
||||
for _, i in adler_b {
|
||||
adler_b[i], _ = compress.read_u8_prefer_code_buffer_lsb(ctx);
|
||||
}
|
||||
adler := transmute(u32be)adler_b;
|
||||
|
||||
output_hash := hash.adler32(ctx.output.buf[:]);
|
||||
|
||||
if output_hash != u32(adler) {
|
||||
return E_General.Checksum_Failed;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// TODO: Check alignment of reserve/resize.
|
||||
|
||||
@(optimization_mode="speed")
|
||||
inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.allocator) -> (err: Error) #no_bounds_check {
|
||||
expected_output_size := expected_output_size;
|
||||
|
||||
/*
|
||||
Always set up a minimum allocation size.
|
||||
*/
|
||||
expected_output_size = max(max(expected_output_size, compress.COMPRESS_OUTPUT_ALLOCATE_MIN), 512);
|
||||
|
||||
// fmt.printf("\nZLIB: Expected Payload Size: %v\n\n", expected_output_size);
|
||||
|
||||
if expected_output_size > 0 && expected_output_size <= compress.COMPRESS_OUTPUT_ALLOCATE_MAX {
|
||||
/*
|
||||
Try to pre-allocate the output buffer.
|
||||
*/
|
||||
reserve(&z.output.buf, expected_output_size);
|
||||
resize (&z.output.buf, expected_output_size);
|
||||
};
|
||||
|
||||
if len(z.output.buf) != expected_output_size {
|
||||
return .Resize_Failed;
|
||||
}
|
||||
|
||||
z.num_bits = 0;
|
||||
z.code_buffer = 0;
|
||||
|
||||
z_repeat: ^Huffman_Table;
|
||||
z_offset: ^Huffman_Table;
|
||||
codelength_ht: ^Huffman_Table;
|
||||
|
||||
z_repeat, err = allocate_huffman_table(allocator=context.allocator);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
z_offset, err = allocate_huffman_table(allocator=context.allocator);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
codelength_ht, err = allocate_huffman_table(allocator=context.allocator);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
defer free(z_repeat);
|
||||
defer free(z_offset);
|
||||
defer free(codelength_ht);
|
||||
|
||||
final := u32(0);
|
||||
type := u32(0);
|
||||
|
||||
for {
|
||||
final = compress.read_bits_lsb(z, 1);
|
||||
type = compress.read_bits_lsb(z, 2);
|
||||
|
||||
// fmt.printf("Final: %v | Type: %v\n", final, type);
|
||||
|
||||
switch type {
|
||||
case 0:
|
||||
// Uncompressed block
|
||||
|
||||
// Discard bits until next byte boundary
|
||||
compress.discard_to_next_byte_lsb(z);
|
||||
|
||||
uncompressed_len := i16(compress.read_bits_lsb(z, 16));
|
||||
length_check := i16(compress.read_bits_lsb(z, 16));
|
||||
|
||||
// fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check);
|
||||
|
||||
|
||||
if ~uncompressed_len != length_check {
|
||||
return E_Deflate.Len_Nlen_Mismatch;
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Maybe speed this up with a stream-to-stream copy (read_from)
|
||||
and a single Adler32 update after.
|
||||
*/
|
||||
#no_bounds_check for uncompressed_len > 0 {
|
||||
compress.refill_lsb(z);
|
||||
lit := compress.read_bits_lsb(z, 8);
|
||||
write_byte(z, u8(lit));
|
||||
uncompressed_len -= 1;
|
||||
}
|
||||
case 3:
|
||||
return E_Deflate.BType_3;
|
||||
case:
|
||||
// log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type);
|
||||
if type == 1 {
|
||||
// Use fixed code lengths.
|
||||
err = build_huffman(z_repeat, Z_FIXED_LENGTH[:]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
err = build_huffman(z_offset, Z_FIXED_DIST[:]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
} else {
|
||||
lencodes: [286+32+137]u8;
|
||||
codelength_sizes: [19]u8;
|
||||
|
||||
//i: u32;
|
||||
n: u32;
|
||||
|
||||
compress.refill_lsb(z, 14);
|
||||
hlit := compress.read_bits_no_refill_lsb(z, 5) + 257;
|
||||
hdist := compress.read_bits_no_refill_lsb(z, 5) + 1;
|
||||
hclen := compress.read_bits_no_refill_lsb(z, 4) + 4;
|
||||
ntot := hlit + hdist;
|
||||
|
||||
#no_bounds_check for i in 0..<hclen {
|
||||
s := compress.read_bits_lsb(z, 3);
|
||||
codelength_sizes[Z_LENGTH_DEZIGZAG[i]] = u8(s);
|
||||
}
|
||||
err = build_huffman(codelength_ht, codelength_sizes[:]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
n = 0;
|
||||
c: u16;
|
||||
|
||||
for n < ntot {
|
||||
c, err = decode_huffman(z, codelength_ht);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
if c < 0 || c >= 19 {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths;
|
||||
}
|
||||
if c < 16 {
|
||||
lencodes[n] = u8(c);
|
||||
n += 1;
|
||||
} else {
|
||||
fill := u8(0);
|
||||
compress.refill_lsb(z, 7);
|
||||
switch c {
|
||||
case 16:
|
||||
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3);
|
||||
if n == 0 {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths;
|
||||
}
|
||||
fill = lencodes[n - 1];
|
||||
case 17:
|
||||
c = u16(compress.read_bits_no_refill_lsb(z, 3) + 3);
|
||||
case 18:
|
||||
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11);
|
||||
case:
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths;
|
||||
}
|
||||
|
||||
if ntot - n < u32(c) {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths;
|
||||
}
|
||||
|
||||
nc := n + u32(c);
|
||||
#no_bounds_check for ; n < nc; n += 1 {
|
||||
lencodes[n] = fill;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if n != ntot {
|
||||
return E_Deflate.Huffman_Bad_Code_Lengths;
|
||||
}
|
||||
|
||||
err = build_huffman(z_repeat, lencodes[:hlit]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = build_huffman(z_offset, lencodes[hlit:ntot]);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
err = parse_huffman_block(z, z_repeat, z_offset);
|
||||
// log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type);
|
||||
if err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if final == 1 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if int(z.bytes_written) != len(z.output.buf) {
|
||||
resize(&z.output.buf, int(z.bytes_written));
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, expected_output_size := -1) -> (err: Error) {
|
||||
ctx := compress.Context_Memory_Input{};
|
||||
|
||||
ctx.input_data = input;
|
||||
ctx.output = buf;
|
||||
|
||||
err = inflate_from_context(ctx=&ctx, raw=raw, expected_output_size=expected_output_size);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, expected_output_size := -1) -> (err: Error) {
|
||||
ctx := compress.Context_Memory_Input{};
|
||||
|
||||
ctx.input_data = input;
|
||||
ctx.output = buf;
|
||||
|
||||
return inflate_raw(z=&ctx, expected_output_size=expected_output_size);
|
||||
}
|
||||
|
||||
inflate :: proc{inflate_from_context, inflate_from_byte_array};
|
||||
@@ -3,7 +3,7 @@ package container
|
||||
import "core:mem"
|
||||
import "core:runtime"
|
||||
|
||||
Array :: struct(T: typeid) {
|
||||
Array :: struct($T: typeid) {
|
||||
data: ^T,
|
||||
len: int,
|
||||
cap: int,
|
||||
|
||||
@@ -4,12 +4,12 @@ import "intrinsics"
|
||||
_ :: intrinsics;
|
||||
|
||||
|
||||
Map :: struct(Key, Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
||||
Map :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
||||
hash: Array(int),
|
||||
entries: Array(Map_Entry(Key, Value)),
|
||||
}
|
||||
|
||||
Map_Entry :: struct(Key, Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
||||
Map_Entry :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
|
||||
hash: uintptr,
|
||||
next: int,
|
||||
key: Key,
|
||||
@@ -141,7 +141,7 @@ map_clear :: proc(m: ^$M/Map($Key, $Value)) {
|
||||
|
||||
|
||||
|
||||
multi_map_find_first :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Map_Entry(Value) {
|
||||
multi_map_find_first :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Map_Entry(Key, Value) {
|
||||
i := _map_find_or_fail(m, key);
|
||||
if i < 0 {
|
||||
return nil;
|
||||
@@ -149,7 +149,7 @@ multi_map_find_first :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Map_Entry(Va
|
||||
return array_get_ptr(m.entries, i);
|
||||
}
|
||||
|
||||
multi_map_find_next :: proc(m: $M/Map($Key, $Value), e: ^Map_Entry(Value)) -> ^Map_Entry(Value) {
|
||||
multi_map_find_next :: proc(m: $M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> ^Map_Entry(Key, Value) {
|
||||
i := e.next;
|
||||
for i >= 0 {
|
||||
it := array_get_ptr(m.entries, i);
|
||||
@@ -220,7 +220,7 @@ multi_map_insert :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
|
||||
}
|
||||
}
|
||||
|
||||
multi_map_remove :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Value)) {
|
||||
multi_map_remove :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) {
|
||||
fr := _map_find_entry(m, e);
|
||||
if fr.entry_index >= 0 {
|
||||
_map_erase(m, fr);
|
||||
@@ -306,7 +306,7 @@ _map_find_key :: proc(m: $M/Map($Key, $Value), key: Key) -> Map_Find_Result wher
|
||||
return fr;
|
||||
}
|
||||
|
||||
_map_find_entry :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Value)) -> Map_Find_Result {
|
||||
_map_find_entry :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> Map_Find_Result {
|
||||
fr: Map_Find_Result;
|
||||
fr.hash_index = -1;
|
||||
fr.entry_prev = -1;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package container
|
||||
|
||||
Priority_Queue :: struct(T: typeid) {
|
||||
Priority_Queue :: struct($T: typeid) {
|
||||
data: Array(T),
|
||||
len: int,
|
||||
priority: proc(item: T) -> int,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package container
|
||||
|
||||
Queue :: struct(T: typeid) {
|
||||
Queue :: struct($T: typeid) {
|
||||
data: Array(T),
|
||||
len: int,
|
||||
offset: int,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package container
|
||||
|
||||
|
||||
Ring :: struct(T: typeid) {
|
||||
Ring :: struct($T: typeid) {
|
||||
next, prev: ^Ring(T),
|
||||
value: T,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package container
|
||||
|
||||
Small_Array :: struct(N: int, T: typeid) where N >= 0 {
|
||||
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||
data: [N]T,
|
||||
len: int,
|
||||
}
|
||||
|
||||
@@ -201,9 +201,9 @@ next_token :: proc(p: ^Parser) -> Token {
|
||||
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
|
||||
hex_to_int :: proc(c: byte) -> int {
|
||||
switch c {
|
||||
case '0'..'9': return int(c-'0');
|
||||
case 'a'..'f': return int(c-'a')+10;
|
||||
case 'A'..'F': return int(c-'A')+10;
|
||||
case '0'..='9': return int(c-'0');
|
||||
case 'a'..='f': return int(c-'a')+10;
|
||||
case 'A'..='F': return int(c-'A')+10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -241,7 +241,7 @@ unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool
|
||||
case '"': r = '"';
|
||||
case '\'': r = '\'';
|
||||
|
||||
case '0'..'7':
|
||||
case '0'..='7':
|
||||
v := int(c-'0');
|
||||
if len(s) < 2 {
|
||||
return;
|
||||
|
||||
@@ -232,7 +232,7 @@ get_pos :: proc(t: ^Tokenizer) -> Pos {
|
||||
|
||||
is_letter :: proc(r: rune) -> bool {
|
||||
switch r {
|
||||
case 'a'..'z', 'A'..'Z', '_':
|
||||
case 'a'..='z', 'A'..='Z', '_':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -240,7 +240,7 @@ is_letter :: proc(r: rune) -> bool {
|
||||
|
||||
is_digit :: proc(r: rune) -> bool {
|
||||
switch r {
|
||||
case '0'..'9':
|
||||
case '0'..='9':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -273,9 +273,9 @@ scan_identifier :: proc(t: ^Tokenizer) -> string {
|
||||
|
||||
digit_value :: proc(r: rune) -> int {
|
||||
switch r {
|
||||
case '0'..'9': return int(r - '0');
|
||||
case 'a'..'f': return int(r - 'a' + 10);
|
||||
case 'A'..'F': return int(r - 'A' + 10);
|
||||
case '0'..='9': return int(r - '0');
|
||||
case 'a'..='f': return int(r - 'a' + 10);
|
||||
case 'A'..='F': return int(r - 'A' + 10);
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
|
||||
@@ -290,9 +290,9 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
|
||||
for c in s[2:4] {
|
||||
x: rune;
|
||||
switch c {
|
||||
case '0'..'9': x = c - '0';
|
||||
case 'a'..'f': x = c - 'a' + 10;
|
||||
case 'A'..'F': x = c - 'A' + 10;
|
||||
case '0'..='9': x = c - '0';
|
||||
case 'a'..='f': x = c - 'a' + 10;
|
||||
case 'A'..='F': x = c - 'A' + 10;
|
||||
case: return -1;
|
||||
}
|
||||
r = r*16 + x;
|
||||
@@ -308,9 +308,9 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
|
||||
for c in s[2:6] {
|
||||
x: rune;
|
||||
switch c {
|
||||
case '0'..'9': x = c - '0';
|
||||
case 'a'..'f': x = c - 'a' + 10;
|
||||
case 'A'..'F': x = c - 'A' + 10;
|
||||
case '0'..='9': x = c - '0';
|
||||
case 'a'..='f': x = c - 'a' + 10;
|
||||
case 'A'..='F': x = c - 'A' + 10;
|
||||
case: return -1;
|
||||
}
|
||||
r = r*16 + x;
|
||||
|
||||
@@ -82,7 +82,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
for t.offset < len(t.data) {
|
||||
next_rune(t);
|
||||
switch t.r {
|
||||
case '0'..'9', 'a'..'f', 'A'..'F':
|
||||
case '0'..='9', 'a'..='f', 'A'..='F':
|
||||
// Okay
|
||||
case:
|
||||
return;
|
||||
@@ -100,7 +100,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
for i := 0; i < 4; i += 1 {
|
||||
r := next_rune(t);
|
||||
switch r {
|
||||
case '0'..'9', 'a'..'f', 'A'..'F':
|
||||
case '0'..='9', 'a'..='f', 'A'..='F':
|
||||
// Okay
|
||||
case:
|
||||
return false;
|
||||
@@ -149,7 +149,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
skip_alphanum :: proc(t: ^Tokenizer) {
|
||||
for t.offset < len(t.data) {
|
||||
switch next_rune(t) {
|
||||
case 'A'..'Z', 'a'..'z', '0'..'9', '_':
|
||||
case 'A'..='Z', 'a'..='z', '0'..='9', '_':
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -173,7 +173,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
token.kind = .EOF;
|
||||
err = .EOF;
|
||||
|
||||
case 'A'..'Z', 'a'..'z', '_':
|
||||
case 'A'..='Z', 'a'..='z', '_':
|
||||
token.kind = .Ident;
|
||||
|
||||
skip_alphanum(t);
|
||||
@@ -200,7 +200,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
|
||||
case '-':
|
||||
switch t.r {
|
||||
case '0'..'9':
|
||||
case '0'..='9':
|
||||
// Okay
|
||||
case:
|
||||
// Illegal use of +/-
|
||||
@@ -219,7 +219,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
}
|
||||
fallthrough;
|
||||
|
||||
case '0'..'9':
|
||||
case '0'..='9':
|
||||
token.kind = t.parse_integers ? .Integer : .Float;
|
||||
if t.spec == .JSON5 { // Hexadecimal Numbers
|
||||
if curr_rune == '0' && (t.r == 'x' || t.r == 'X') {
|
||||
@@ -361,7 +361,7 @@ is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
switch s[0] {
|
||||
case '0':
|
||||
s = s[1:];
|
||||
case '1'..'9':
|
||||
case '1'..='9':
|
||||
s = s[1:];
|
||||
for len(s) > 0 && '0' <= s[0] && s[0] <= '9' {
|
||||
s = s[1:];
|
||||
@@ -453,7 +453,7 @@ is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
|
||||
for j := 0; j < 4; j += 1 {
|
||||
c2 := hex[j];
|
||||
switch c2 {
|
||||
case '0'..'9', 'a'..'z', 'A'..'Z':
|
||||
case '0'..='9', 'a'..='z', 'A'..='Z':
|
||||
// Okay
|
||||
case:
|
||||
return false;
|
||||
|
||||
+3
-14
@@ -641,9 +641,9 @@ fmt_write_padding :: proc(fi: ^Info, width: int) {
|
||||
return;
|
||||
}
|
||||
|
||||
pad_byte: byte = '0';
|
||||
if fi.space {
|
||||
pad_byte = ' ';
|
||||
pad_byte: byte = ' ';
|
||||
if !fi.space {
|
||||
pad_byte = '0';
|
||||
}
|
||||
|
||||
for i := 0; i < width; i += 1 {
|
||||
@@ -1908,17 +1908,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
handle_relative_pointer :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
|
||||
if ptr^ == 0 {
|
||||
return nil;
|
||||
}
|
||||
when intrinsics.type_is_unsigned(T) {
|
||||
return rawptr(uintptr(ptr) + uintptr(ptr^));
|
||||
} else {
|
||||
return rawptr(uintptr(ptr) + uintptr(i64(ptr^)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
|
||||
|
||||
+3
-74
@@ -1,86 +1,15 @@
|
||||
package hash
|
||||
|
||||
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
result := ~u32(seed);
|
||||
for b in data {
|
||||
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
crc64 :: proc(data: []byte, seed := u32(0)) -> u64 #no_bounds_check {
|
||||
result := ~u64(seed);
|
||||
for b in data {
|
||||
#no_bounds_check for b in data {
|
||||
result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
|
||||
@private _crc32_table := [256]u32{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
||||
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
|
||||
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
|
||||
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
||||
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
|
||||
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
|
||||
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
||||
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
|
||||
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
|
||||
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
||||
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
|
||||
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
|
||||
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
||||
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
|
||||
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
|
||||
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
||||
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
||||
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
||||
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
|
||||
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
||||
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
|
||||
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
|
||||
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
||||
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
||||
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
|
||||
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
|
||||
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
||||
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
|
||||
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
|
||||
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
|
||||
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
|
||||
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
};
|
||||
@private _crc64_table := [256]u64{
|
||||
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
|
||||
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
|
||||
|
||||
@@ -0,0 +1,401 @@
|
||||
package hash
|
||||
|
||||
import "intrinsics"
|
||||
|
||||
@(optimization_mode="speed")
|
||||
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
crc := ~seed;
|
||||
buffer := raw_data(data);
|
||||
length := len(data);
|
||||
|
||||
for length != 0 && uintptr(buffer) & 7 != 0 {
|
||||
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8);
|
||||
buffer = intrinsics.ptr_offset(buffer, 1);
|
||||
length -= 1;
|
||||
}
|
||||
|
||||
for length >= 8 {
|
||||
buf := (^[8]byte)(buffer);
|
||||
word := u32((^u32le)(buffer)^);
|
||||
crc ~= word;
|
||||
|
||||
crc = crc32_table[7][crc & 0xff] ~
|
||||
crc32_table[6][(crc >> 8) & 0xff] ~
|
||||
crc32_table[5][(crc >> 16) & 0xff] ~
|
||||
crc32_table[4][(crc >> 24) & 0xff] ~
|
||||
crc32_table[3][buf[4]] ~
|
||||
crc32_table[2][buf[5]] ~
|
||||
crc32_table[1][buf[6]] ~
|
||||
crc32_table[0][buf[7]];
|
||||
|
||||
buffer = intrinsics.ptr_offset(buffer, 8);
|
||||
length -= 8;
|
||||
}
|
||||
|
||||
|
||||
for length != 0 {
|
||||
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8);
|
||||
buffer = intrinsics.ptr_offset(buffer, 1);
|
||||
length -= 1;
|
||||
}
|
||||
|
||||
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
@(private)
|
||||
crc32_table := [8][256]u32{
|
||||
{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
||||
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
||||
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
||||
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
||||
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
||||
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
||||
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
||||
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
||||
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
||||
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
||||
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
},
|
||||
{
|
||||
0x00000000, 0x191b3141, 0x32366282, 0x2b2d53c3, 0x646cc504, 0x7d77f445, 0x565aa786, 0x4f4196c7,
|
||||
0xc8d98a08, 0xd1c2bb49, 0xfaefe88a, 0xe3f4d9cb, 0xacb54f0c, 0xb5ae7e4d, 0x9e832d8e, 0x87981ccf,
|
||||
0x4ac21251, 0x53d92310, 0x78f470d3, 0x61ef4192, 0x2eaed755, 0x37b5e614, 0x1c98b5d7, 0x05838496,
|
||||
0x821b9859, 0x9b00a918, 0xb02dfadb, 0xa936cb9a, 0xe6775d5d, 0xff6c6c1c, 0xd4413fdf, 0xcd5a0e9e,
|
||||
0x958424a2, 0x8c9f15e3, 0xa7b24620, 0xbea97761, 0xf1e8e1a6, 0xe8f3d0e7, 0xc3de8324, 0xdac5b265,
|
||||
0x5d5daeaa, 0x44469feb, 0x6f6bcc28, 0x7670fd69, 0x39316bae, 0x202a5aef, 0x0b07092c, 0x121c386d,
|
||||
0xdf4636f3, 0xc65d07b2, 0xed705471, 0xf46b6530, 0xbb2af3f7, 0xa231c2b6, 0x891c9175, 0x9007a034,
|
||||
0x179fbcfb, 0x0e848dba, 0x25a9de79, 0x3cb2ef38, 0x73f379ff, 0x6ae848be, 0x41c51b7d, 0x58de2a3c,
|
||||
0xf0794f05, 0xe9627e44, 0xc24f2d87, 0xdb541cc6, 0x94158a01, 0x8d0ebb40, 0xa623e883, 0xbf38d9c2,
|
||||
0x38a0c50d, 0x21bbf44c, 0x0a96a78f, 0x138d96ce, 0x5ccc0009, 0x45d73148, 0x6efa628b, 0x77e153ca,
|
||||
0xbabb5d54, 0xa3a06c15, 0x888d3fd6, 0x91960e97, 0xded79850, 0xc7cca911, 0xece1fad2, 0xf5facb93,
|
||||
0x7262d75c, 0x6b79e61d, 0x4054b5de, 0x594f849f, 0x160e1258, 0x0f152319, 0x243870da, 0x3d23419b,
|
||||
0x65fd6ba7, 0x7ce65ae6, 0x57cb0925, 0x4ed03864, 0x0191aea3, 0x188a9fe2, 0x33a7cc21, 0x2abcfd60,
|
||||
0xad24e1af, 0xb43fd0ee, 0x9f12832d, 0x8609b26c, 0xc94824ab, 0xd05315ea, 0xfb7e4629, 0xe2657768,
|
||||
0x2f3f79f6, 0x362448b7, 0x1d091b74, 0x04122a35, 0x4b53bcf2, 0x52488db3, 0x7965de70, 0x607eef31,
|
||||
0xe7e6f3fe, 0xfefdc2bf, 0xd5d0917c, 0xcccba03d, 0x838a36fa, 0x9a9107bb, 0xb1bc5478, 0xa8a76539,
|
||||
0x3b83984b, 0x2298a90a, 0x09b5fac9, 0x10aecb88, 0x5fef5d4f, 0x46f46c0e, 0x6dd93fcd, 0x74c20e8c,
|
||||
0xf35a1243, 0xea412302, 0xc16c70c1, 0xd8774180, 0x9736d747, 0x8e2de606, 0xa500b5c5, 0xbc1b8484,
|
||||
0x71418a1a, 0x685abb5b, 0x4377e898, 0x5a6cd9d9, 0x152d4f1e, 0x0c367e5f, 0x271b2d9c, 0x3e001cdd,
|
||||
0xb9980012, 0xa0833153, 0x8bae6290, 0x92b553d1, 0xddf4c516, 0xc4eff457, 0xefc2a794, 0xf6d996d5,
|
||||
0xae07bce9, 0xb71c8da8, 0x9c31de6b, 0x852aef2a, 0xca6b79ed, 0xd37048ac, 0xf85d1b6f, 0xe1462a2e,
|
||||
0x66de36e1, 0x7fc507a0, 0x54e85463, 0x4df36522, 0x02b2f3e5, 0x1ba9c2a4, 0x30849167, 0x299fa026,
|
||||
0xe4c5aeb8, 0xfdde9ff9, 0xd6f3cc3a, 0xcfe8fd7b, 0x80a96bbc, 0x99b25afd, 0xb29f093e, 0xab84387f,
|
||||
0x2c1c24b0, 0x350715f1, 0x1e2a4632, 0x07317773, 0x4870e1b4, 0x516bd0f5, 0x7a468336, 0x635db277,
|
||||
0xcbfad74e, 0xd2e1e60f, 0xf9ccb5cc, 0xe0d7848d, 0xaf96124a, 0xb68d230b, 0x9da070c8, 0x84bb4189,
|
||||
0x03235d46, 0x1a386c07, 0x31153fc4, 0x280e0e85, 0x674f9842, 0x7e54a903, 0x5579fac0, 0x4c62cb81,
|
||||
0x8138c51f, 0x9823f45e, 0xb30ea79d, 0xaa1596dc, 0xe554001b, 0xfc4f315a, 0xd7626299, 0xce7953d8,
|
||||
0x49e14f17, 0x50fa7e56, 0x7bd72d95, 0x62cc1cd4, 0x2d8d8a13, 0x3496bb52, 0x1fbbe891, 0x06a0d9d0,
|
||||
0x5e7ef3ec, 0x4765c2ad, 0x6c48916e, 0x7553a02f, 0x3a1236e8, 0x230907a9, 0x0824546a, 0x113f652b,
|
||||
0x96a779e4, 0x8fbc48a5, 0xa4911b66, 0xbd8a2a27, 0xf2cbbce0, 0xebd08da1, 0xc0fdde62, 0xd9e6ef23,
|
||||
0x14bce1bd, 0x0da7d0fc, 0x268a833f, 0x3f91b27e, 0x70d024b9, 0x69cb15f8, 0x42e6463b, 0x5bfd777a,
|
||||
0xdc656bb5, 0xc57e5af4, 0xee530937, 0xf7483876, 0xb809aeb1, 0xa1129ff0, 0x8a3fcc33, 0x9324fd72,
|
||||
},
|
||||
{
|
||||
0x00000000, 0x01c26a37, 0x0384d46e, 0x0246be59, 0x0709a8dc, 0x06cbc2eb, 0x048d7cb2, 0x054f1685,
|
||||
0x0e1351b8, 0x0fd13b8f, 0x0d9785d6, 0x0c55efe1, 0x091af964, 0x08d89353, 0x0a9e2d0a, 0x0b5c473d,
|
||||
0x1c26a370, 0x1de4c947, 0x1fa2771e, 0x1e601d29, 0x1b2f0bac, 0x1aed619b, 0x18abdfc2, 0x1969b5f5,
|
||||
0x1235f2c8, 0x13f798ff, 0x11b126a6, 0x10734c91, 0x153c5a14, 0x14fe3023, 0x16b88e7a, 0x177ae44d,
|
||||
0x384d46e0, 0x398f2cd7, 0x3bc9928e, 0x3a0bf8b9, 0x3f44ee3c, 0x3e86840b, 0x3cc03a52, 0x3d025065,
|
||||
0x365e1758, 0x379c7d6f, 0x35dac336, 0x3418a901, 0x3157bf84, 0x3095d5b3, 0x32d36bea, 0x331101dd,
|
||||
0x246be590, 0x25a98fa7, 0x27ef31fe, 0x262d5bc9, 0x23624d4c, 0x22a0277b, 0x20e69922, 0x2124f315,
|
||||
0x2a78b428, 0x2bbade1f, 0x29fc6046, 0x283e0a71, 0x2d711cf4, 0x2cb376c3, 0x2ef5c89a, 0x2f37a2ad,
|
||||
0x709a8dc0, 0x7158e7f7, 0x731e59ae, 0x72dc3399, 0x7793251c, 0x76514f2b, 0x7417f172, 0x75d59b45,
|
||||
0x7e89dc78, 0x7f4bb64f, 0x7d0d0816, 0x7ccf6221, 0x798074a4, 0x78421e93, 0x7a04a0ca, 0x7bc6cafd,
|
||||
0x6cbc2eb0, 0x6d7e4487, 0x6f38fade, 0x6efa90e9, 0x6bb5866c, 0x6a77ec5b, 0x68315202, 0x69f33835,
|
||||
0x62af7f08, 0x636d153f, 0x612bab66, 0x60e9c151, 0x65a6d7d4, 0x6464bde3, 0x662203ba, 0x67e0698d,
|
||||
0x48d7cb20, 0x4915a117, 0x4b531f4e, 0x4a917579, 0x4fde63fc, 0x4e1c09cb, 0x4c5ab792, 0x4d98dda5,
|
||||
0x46c49a98, 0x4706f0af, 0x45404ef6, 0x448224c1, 0x41cd3244, 0x400f5873, 0x4249e62a, 0x438b8c1d,
|
||||
0x54f16850, 0x55330267, 0x5775bc3e, 0x56b7d609, 0x53f8c08c, 0x523aaabb, 0x507c14e2, 0x51be7ed5,
|
||||
0x5ae239e8, 0x5b2053df, 0x5966ed86, 0x58a487b1, 0x5deb9134, 0x5c29fb03, 0x5e6f455a, 0x5fad2f6d,
|
||||
0xe1351b80, 0xe0f771b7, 0xe2b1cfee, 0xe373a5d9, 0xe63cb35c, 0xe7fed96b, 0xe5b86732, 0xe47a0d05,
|
||||
0xef264a38, 0xeee4200f, 0xeca29e56, 0xed60f461, 0xe82fe2e4, 0xe9ed88d3, 0xebab368a, 0xea695cbd,
|
||||
0xfd13b8f0, 0xfcd1d2c7, 0xfe976c9e, 0xff5506a9, 0xfa1a102c, 0xfbd87a1b, 0xf99ec442, 0xf85cae75,
|
||||
0xf300e948, 0xf2c2837f, 0xf0843d26, 0xf1465711, 0xf4094194, 0xf5cb2ba3, 0xf78d95fa, 0xf64fffcd,
|
||||
0xd9785d60, 0xd8ba3757, 0xdafc890e, 0xdb3ee339, 0xde71f5bc, 0xdfb39f8b, 0xddf521d2, 0xdc374be5,
|
||||
0xd76b0cd8, 0xd6a966ef, 0xd4efd8b6, 0xd52db281, 0xd062a404, 0xd1a0ce33, 0xd3e6706a, 0xd2241a5d,
|
||||
0xc55efe10, 0xc49c9427, 0xc6da2a7e, 0xc7184049, 0xc25756cc, 0xc3953cfb, 0xc1d382a2, 0xc011e895,
|
||||
0xcb4dafa8, 0xca8fc59f, 0xc8c97bc6, 0xc90b11f1, 0xcc440774, 0xcd866d43, 0xcfc0d31a, 0xce02b92d,
|
||||
0x91af9640, 0x906dfc77, 0x922b422e, 0x93e92819, 0x96a63e9c, 0x976454ab, 0x9522eaf2, 0x94e080c5,
|
||||
0x9fbcc7f8, 0x9e7eadcf, 0x9c381396, 0x9dfa79a1, 0x98b56f24, 0x99770513, 0x9b31bb4a, 0x9af3d17d,
|
||||
0x8d893530, 0x8c4b5f07, 0x8e0de15e, 0x8fcf8b69, 0x8a809dec, 0x8b42f7db, 0x89044982, 0x88c623b5,
|
||||
0x839a6488, 0x82580ebf, 0x801eb0e6, 0x81dcdad1, 0x8493cc54, 0x8551a663, 0x8717183a, 0x86d5720d,
|
||||
0xa9e2d0a0, 0xa820ba97, 0xaa6604ce, 0xaba46ef9, 0xaeeb787c, 0xaf29124b, 0xad6fac12, 0xacadc625,
|
||||
0xa7f18118, 0xa633eb2f, 0xa4755576, 0xa5b73f41, 0xa0f829c4, 0xa13a43f3, 0xa37cfdaa, 0xa2be979d,
|
||||
0xb5c473d0, 0xb40619e7, 0xb640a7be, 0xb782cd89, 0xb2cddb0c, 0xb30fb13b, 0xb1490f62, 0xb08b6555,
|
||||
0xbbd72268, 0xba15485f, 0xb853f606, 0xb9919c31, 0xbcde8ab4, 0xbd1ce083, 0xbf5a5eda, 0xbe9834ed,
|
||||
},
|
||||
{
|
||||
0x00000000, 0xb8bc6765, 0xaa09c88b, 0x12b5afee, 0x8f629757, 0x37def032, 0x256b5fdc, 0x9dd738b9,
|
||||
0xc5b428ef, 0x7d084f8a, 0x6fbde064, 0xd7018701, 0x4ad6bfb8, 0xf26ad8dd, 0xe0df7733, 0x58631056,
|
||||
0x5019579f, 0xe8a530fa, 0xfa109f14, 0x42acf871, 0xdf7bc0c8, 0x67c7a7ad, 0x75720843, 0xcdce6f26,
|
||||
0x95ad7f70, 0x2d111815, 0x3fa4b7fb, 0x8718d09e, 0x1acfe827, 0xa2738f42, 0xb0c620ac, 0x087a47c9,
|
||||
0xa032af3e, 0x188ec85b, 0x0a3b67b5, 0xb28700d0, 0x2f503869, 0x97ec5f0c, 0x8559f0e2, 0x3de59787,
|
||||
0x658687d1, 0xdd3ae0b4, 0xcf8f4f5a, 0x7733283f, 0xeae41086, 0x525877e3, 0x40edd80d, 0xf851bf68,
|
||||
0xf02bf8a1, 0x48979fc4, 0x5a22302a, 0xe29e574f, 0x7f496ff6, 0xc7f50893, 0xd540a77d, 0x6dfcc018,
|
||||
0x359fd04e, 0x8d23b72b, 0x9f9618c5, 0x272a7fa0, 0xbafd4719, 0x0241207c, 0x10f48f92, 0xa848e8f7,
|
||||
0x9b14583d, 0x23a83f58, 0x311d90b6, 0x89a1f7d3, 0x1476cf6a, 0xaccaa80f, 0xbe7f07e1, 0x06c36084,
|
||||
0x5ea070d2, 0xe61c17b7, 0xf4a9b859, 0x4c15df3c, 0xd1c2e785, 0x697e80e0, 0x7bcb2f0e, 0xc377486b,
|
||||
0xcb0d0fa2, 0x73b168c7, 0x6104c729, 0xd9b8a04c, 0x446f98f5, 0xfcd3ff90, 0xee66507e, 0x56da371b,
|
||||
0x0eb9274d, 0xb6054028, 0xa4b0efc6, 0x1c0c88a3, 0x81dbb01a, 0x3967d77f, 0x2bd27891, 0x936e1ff4,
|
||||
0x3b26f703, 0x839a9066, 0x912f3f88, 0x299358ed, 0xb4446054, 0x0cf80731, 0x1e4da8df, 0xa6f1cfba,
|
||||
0xfe92dfec, 0x462eb889, 0x549b1767, 0xec277002, 0x71f048bb, 0xc94c2fde, 0xdbf98030, 0x6345e755,
|
||||
0x6b3fa09c, 0xd383c7f9, 0xc1366817, 0x798a0f72, 0xe45d37cb, 0x5ce150ae, 0x4e54ff40, 0xf6e89825,
|
||||
0xae8b8873, 0x1637ef16, 0x048240f8, 0xbc3e279d, 0x21e91f24, 0x99557841, 0x8be0d7af, 0x335cb0ca,
|
||||
0xed59b63b, 0x55e5d15e, 0x47507eb0, 0xffec19d5, 0x623b216c, 0xda874609, 0xc832e9e7, 0x708e8e82,
|
||||
0x28ed9ed4, 0x9051f9b1, 0x82e4565f, 0x3a58313a, 0xa78f0983, 0x1f336ee6, 0x0d86c108, 0xb53aa66d,
|
||||
0xbd40e1a4, 0x05fc86c1, 0x1749292f, 0xaff54e4a, 0x322276f3, 0x8a9e1196, 0x982bbe78, 0x2097d91d,
|
||||
0x78f4c94b, 0xc048ae2e, 0xd2fd01c0, 0x6a4166a5, 0xf7965e1c, 0x4f2a3979, 0x5d9f9697, 0xe523f1f2,
|
||||
0x4d6b1905, 0xf5d77e60, 0xe762d18e, 0x5fdeb6eb, 0xc2098e52, 0x7ab5e937, 0x680046d9, 0xd0bc21bc,
|
||||
0x88df31ea, 0x3063568f, 0x22d6f961, 0x9a6a9e04, 0x07bda6bd, 0xbf01c1d8, 0xadb46e36, 0x15080953,
|
||||
0x1d724e9a, 0xa5ce29ff, 0xb77b8611, 0x0fc7e174, 0x9210d9cd, 0x2aacbea8, 0x38191146, 0x80a57623,
|
||||
0xd8c66675, 0x607a0110, 0x72cfaefe, 0xca73c99b, 0x57a4f122, 0xef189647, 0xfdad39a9, 0x45115ecc,
|
||||
0x764dee06, 0xcef18963, 0xdc44268d, 0x64f841e8, 0xf92f7951, 0x41931e34, 0x5326b1da, 0xeb9ad6bf,
|
||||
0xb3f9c6e9, 0x0b45a18c, 0x19f00e62, 0xa14c6907, 0x3c9b51be, 0x842736db, 0x96929935, 0x2e2efe50,
|
||||
0x2654b999, 0x9ee8defc, 0x8c5d7112, 0x34e11677, 0xa9362ece, 0x118a49ab, 0x033fe645, 0xbb838120,
|
||||
0xe3e09176, 0x5b5cf613, 0x49e959fd, 0xf1553e98, 0x6c820621, 0xd43e6144, 0xc68bceaa, 0x7e37a9cf,
|
||||
0xd67f4138, 0x6ec3265d, 0x7c7689b3, 0xc4caeed6, 0x591dd66f, 0xe1a1b10a, 0xf3141ee4, 0x4ba87981,
|
||||
0x13cb69d7, 0xab770eb2, 0xb9c2a15c, 0x017ec639, 0x9ca9fe80, 0x241599e5, 0x36a0360b, 0x8e1c516e,
|
||||
0x866616a7, 0x3eda71c2, 0x2c6fde2c, 0x94d3b949, 0x090481f0, 0xb1b8e695, 0xa30d497b, 0x1bb12e1e,
|
||||
0x43d23e48, 0xfb6e592d, 0xe9dbf6c3, 0x516791a6, 0xccb0a91f, 0x740cce7a, 0x66b96194, 0xde0506f1,
|
||||
},
|
||||
{
|
||||
0x00000000, 0x3d6029b0, 0x7ac05360, 0x47a07ad0, 0xf580a6c0, 0xc8e08f70, 0x8f40f5a0, 0xb220dc10,
|
||||
0x30704bc1, 0x0d106271, 0x4ab018a1, 0x77d03111, 0xc5f0ed01, 0xf890c4b1, 0xbf30be61, 0x825097d1,
|
||||
0x60e09782, 0x5d80be32, 0x1a20c4e2, 0x2740ed52, 0x95603142, 0xa80018f2, 0xefa06222, 0xd2c04b92,
|
||||
0x5090dc43, 0x6df0f5f3, 0x2a508f23, 0x1730a693, 0xa5107a83, 0x98705333, 0xdfd029e3, 0xe2b00053,
|
||||
0xc1c12f04, 0xfca106b4, 0xbb017c64, 0x866155d4, 0x344189c4, 0x0921a074, 0x4e81daa4, 0x73e1f314,
|
||||
0xf1b164c5, 0xccd14d75, 0x8b7137a5, 0xb6111e15, 0x0431c205, 0x3951ebb5, 0x7ef19165, 0x4391b8d5,
|
||||
0xa121b886, 0x9c419136, 0xdbe1ebe6, 0xe681c256, 0x54a11e46, 0x69c137f6, 0x2e614d26, 0x13016496,
|
||||
0x9151f347, 0xac31daf7, 0xeb91a027, 0xd6f18997, 0x64d15587, 0x59b17c37, 0x1e1106e7, 0x23712f57,
|
||||
0x58f35849, 0x659371f9, 0x22330b29, 0x1f532299, 0xad73fe89, 0x9013d739, 0xd7b3ade9, 0xead38459,
|
||||
0x68831388, 0x55e33a38, 0x124340e8, 0x2f236958, 0x9d03b548, 0xa0639cf8, 0xe7c3e628, 0xdaa3cf98,
|
||||
0x3813cfcb, 0x0573e67b, 0x42d39cab, 0x7fb3b51b, 0xcd93690b, 0xf0f340bb, 0xb7533a6b, 0x8a3313db,
|
||||
0x0863840a, 0x3503adba, 0x72a3d76a, 0x4fc3feda, 0xfde322ca, 0xc0830b7a, 0x872371aa, 0xba43581a,
|
||||
0x9932774d, 0xa4525efd, 0xe3f2242d, 0xde920d9d, 0x6cb2d18d, 0x51d2f83d, 0x167282ed, 0x2b12ab5d,
|
||||
0xa9423c8c, 0x9422153c, 0xd3826fec, 0xeee2465c, 0x5cc29a4c, 0x61a2b3fc, 0x2602c92c, 0x1b62e09c,
|
||||
0xf9d2e0cf, 0xc4b2c97f, 0x8312b3af, 0xbe729a1f, 0x0c52460f, 0x31326fbf, 0x7692156f, 0x4bf23cdf,
|
||||
0xc9a2ab0e, 0xf4c282be, 0xb362f86e, 0x8e02d1de, 0x3c220dce, 0x0142247e, 0x46e25eae, 0x7b82771e,
|
||||
0xb1e6b092, 0x8c869922, 0xcb26e3f2, 0xf646ca42, 0x44661652, 0x79063fe2, 0x3ea64532, 0x03c66c82,
|
||||
0x8196fb53, 0xbcf6d2e3, 0xfb56a833, 0xc6368183, 0x74165d93, 0x49767423, 0x0ed60ef3, 0x33b62743,
|
||||
0xd1062710, 0xec660ea0, 0xabc67470, 0x96a65dc0, 0x248681d0, 0x19e6a860, 0x5e46d2b0, 0x6326fb00,
|
||||
0xe1766cd1, 0xdc164561, 0x9bb63fb1, 0xa6d61601, 0x14f6ca11, 0x2996e3a1, 0x6e369971, 0x5356b0c1,
|
||||
0x70279f96, 0x4d47b626, 0x0ae7ccf6, 0x3787e546, 0x85a73956, 0xb8c710e6, 0xff676a36, 0xc2074386,
|
||||
0x4057d457, 0x7d37fde7, 0x3a978737, 0x07f7ae87, 0xb5d77297, 0x88b75b27, 0xcf1721f7, 0xf2770847,
|
||||
0x10c70814, 0x2da721a4, 0x6a075b74, 0x576772c4, 0xe547aed4, 0xd8278764, 0x9f87fdb4, 0xa2e7d404,
|
||||
0x20b743d5, 0x1dd76a65, 0x5a7710b5, 0x67173905, 0xd537e515, 0xe857cca5, 0xaff7b675, 0x92979fc5,
|
||||
0xe915e8db, 0xd475c16b, 0x93d5bbbb, 0xaeb5920b, 0x1c954e1b, 0x21f567ab, 0x66551d7b, 0x5b3534cb,
|
||||
0xd965a31a, 0xe4058aaa, 0xa3a5f07a, 0x9ec5d9ca, 0x2ce505da, 0x11852c6a, 0x562556ba, 0x6b457f0a,
|
||||
0x89f57f59, 0xb49556e9, 0xf3352c39, 0xce550589, 0x7c75d999, 0x4115f029, 0x06b58af9, 0x3bd5a349,
|
||||
0xb9853498, 0x84e51d28, 0xc34567f8, 0xfe254e48, 0x4c059258, 0x7165bbe8, 0x36c5c138, 0x0ba5e888,
|
||||
0x28d4c7df, 0x15b4ee6f, 0x521494bf, 0x6f74bd0f, 0xdd54611f, 0xe03448af, 0xa794327f, 0x9af41bcf,
|
||||
0x18a48c1e, 0x25c4a5ae, 0x6264df7e, 0x5f04f6ce, 0xed242ade, 0xd044036e, 0x97e479be, 0xaa84500e,
|
||||
0x4834505d, 0x755479ed, 0x32f4033d, 0x0f942a8d, 0xbdb4f69d, 0x80d4df2d, 0xc774a5fd, 0xfa148c4d,
|
||||
0x78441b9c, 0x4524322c, 0x028448fc, 0x3fe4614c, 0x8dc4bd5c, 0xb0a494ec, 0xf704ee3c, 0xca64c78c,
|
||||
},
|
||||
{
|
||||
0x00000000, 0xcb5cd3a5, 0x4dc8a10b, 0x869472ae, 0x9b914216, 0x50cd91b3, 0xd659e31d, 0x1d0530b8,
|
||||
0xec53826d, 0x270f51c8, 0xa19b2366, 0x6ac7f0c3, 0x77c2c07b, 0xbc9e13de, 0x3a0a6170, 0xf156b2d5,
|
||||
0x03d6029b, 0xc88ad13e, 0x4e1ea390, 0x85427035, 0x9847408d, 0x531b9328, 0xd58fe186, 0x1ed33223,
|
||||
0xef8580f6, 0x24d95353, 0xa24d21fd, 0x6911f258, 0x7414c2e0, 0xbf481145, 0x39dc63eb, 0xf280b04e,
|
||||
0x07ac0536, 0xccf0d693, 0x4a64a43d, 0x81387798, 0x9c3d4720, 0x57619485, 0xd1f5e62b, 0x1aa9358e,
|
||||
0xebff875b, 0x20a354fe, 0xa6372650, 0x6d6bf5f5, 0x706ec54d, 0xbb3216e8, 0x3da66446, 0xf6fab7e3,
|
||||
0x047a07ad, 0xcf26d408, 0x49b2a6a6, 0x82ee7503, 0x9feb45bb, 0x54b7961e, 0xd223e4b0, 0x197f3715,
|
||||
0xe82985c0, 0x23755665, 0xa5e124cb, 0x6ebdf76e, 0x73b8c7d6, 0xb8e41473, 0x3e7066dd, 0xf52cb578,
|
||||
0x0f580a6c, 0xc404d9c9, 0x4290ab67, 0x89cc78c2, 0x94c9487a, 0x5f959bdf, 0xd901e971, 0x125d3ad4,
|
||||
0xe30b8801, 0x28575ba4, 0xaec3290a, 0x659ffaaf, 0x789aca17, 0xb3c619b2, 0x35526b1c, 0xfe0eb8b9,
|
||||
0x0c8e08f7, 0xc7d2db52, 0x4146a9fc, 0x8a1a7a59, 0x971f4ae1, 0x5c439944, 0xdad7ebea, 0x118b384f,
|
||||
0xe0dd8a9a, 0x2b81593f, 0xad152b91, 0x6649f834, 0x7b4cc88c, 0xb0101b29, 0x36846987, 0xfdd8ba22,
|
||||
0x08f40f5a, 0xc3a8dcff, 0x453cae51, 0x8e607df4, 0x93654d4c, 0x58399ee9, 0xdeadec47, 0x15f13fe2,
|
||||
0xe4a78d37, 0x2ffb5e92, 0xa96f2c3c, 0x6233ff99, 0x7f36cf21, 0xb46a1c84, 0x32fe6e2a, 0xf9a2bd8f,
|
||||
0x0b220dc1, 0xc07ede64, 0x46eaacca, 0x8db67f6f, 0x90b34fd7, 0x5bef9c72, 0xdd7beedc, 0x16273d79,
|
||||
0xe7718fac, 0x2c2d5c09, 0xaab92ea7, 0x61e5fd02, 0x7ce0cdba, 0xb7bc1e1f, 0x31286cb1, 0xfa74bf14,
|
||||
0x1eb014d8, 0xd5ecc77d, 0x5378b5d3, 0x98246676, 0x852156ce, 0x4e7d856b, 0xc8e9f7c5, 0x03b52460,
|
||||
0xf2e396b5, 0x39bf4510, 0xbf2b37be, 0x7477e41b, 0x6972d4a3, 0xa22e0706, 0x24ba75a8, 0xefe6a60d,
|
||||
0x1d661643, 0xd63ac5e6, 0x50aeb748, 0x9bf264ed, 0x86f75455, 0x4dab87f0, 0xcb3ff55e, 0x006326fb,
|
||||
0xf135942e, 0x3a69478b, 0xbcfd3525, 0x77a1e680, 0x6aa4d638, 0xa1f8059d, 0x276c7733, 0xec30a496,
|
||||
0x191c11ee, 0xd240c24b, 0x54d4b0e5, 0x9f886340, 0x828d53f8, 0x49d1805d, 0xcf45f2f3, 0x04192156,
|
||||
0xf54f9383, 0x3e134026, 0xb8873288, 0x73dbe12d, 0x6eded195, 0xa5820230, 0x2316709e, 0xe84aa33b,
|
||||
0x1aca1375, 0xd196c0d0, 0x5702b27e, 0x9c5e61db, 0x815b5163, 0x4a0782c6, 0xcc93f068, 0x07cf23cd,
|
||||
0xf6999118, 0x3dc542bd, 0xbb513013, 0x700de3b6, 0x6d08d30e, 0xa65400ab, 0x20c07205, 0xeb9ca1a0,
|
||||
0x11e81eb4, 0xdab4cd11, 0x5c20bfbf, 0x977c6c1a, 0x8a795ca2, 0x41258f07, 0xc7b1fda9, 0x0ced2e0c,
|
||||
0xfdbb9cd9, 0x36e74f7c, 0xb0733dd2, 0x7b2fee77, 0x662adecf, 0xad760d6a, 0x2be27fc4, 0xe0beac61,
|
||||
0x123e1c2f, 0xd962cf8a, 0x5ff6bd24, 0x94aa6e81, 0x89af5e39, 0x42f38d9c, 0xc467ff32, 0x0f3b2c97,
|
||||
0xfe6d9e42, 0x35314de7, 0xb3a53f49, 0x78f9ecec, 0x65fcdc54, 0xaea00ff1, 0x28347d5f, 0xe368aefa,
|
||||
0x16441b82, 0xdd18c827, 0x5b8cba89, 0x90d0692c, 0x8dd55994, 0x46898a31, 0xc01df89f, 0x0b412b3a,
|
||||
0xfa1799ef, 0x314b4a4a, 0xb7df38e4, 0x7c83eb41, 0x6186dbf9, 0xaada085c, 0x2c4e7af2, 0xe712a957,
|
||||
0x15921919, 0xdececabc, 0x585ab812, 0x93066bb7, 0x8e035b0f, 0x455f88aa, 0xc3cbfa04, 0x089729a1,
|
||||
0xf9c19b74, 0x329d48d1, 0xb4093a7f, 0x7f55e9da, 0x6250d962, 0xa90c0ac7, 0x2f987869, 0xe4c4abcc,
|
||||
},
|
||||
{
|
||||
0x00000000, 0xa6770bb4, 0x979f1129, 0x31e81a9d, 0xf44f2413, 0x52382fa7, 0x63d0353a, 0xc5a73e8e,
|
||||
0x33ef4e67, 0x959845d3, 0xa4705f4e, 0x020754fa, 0xc7a06a74, 0x61d761c0, 0x503f7b5d, 0xf64870e9,
|
||||
0x67de9cce, 0xc1a9977a, 0xf0418de7, 0x56368653, 0x9391b8dd, 0x35e6b369, 0x040ea9f4, 0xa279a240,
|
||||
0x5431d2a9, 0xf246d91d, 0xc3aec380, 0x65d9c834, 0xa07ef6ba, 0x0609fd0e, 0x37e1e793, 0x9196ec27,
|
||||
0xcfbd399c, 0x69ca3228, 0x582228b5, 0xfe552301, 0x3bf21d8f, 0x9d85163b, 0xac6d0ca6, 0x0a1a0712,
|
||||
0xfc5277fb, 0x5a257c4f, 0x6bcd66d2, 0xcdba6d66, 0x081d53e8, 0xae6a585c, 0x9f8242c1, 0x39f54975,
|
||||
0xa863a552, 0x0e14aee6, 0x3ffcb47b, 0x998bbfcf, 0x5c2c8141, 0xfa5b8af5, 0xcbb39068, 0x6dc49bdc,
|
||||
0x9b8ceb35, 0x3dfbe081, 0x0c13fa1c, 0xaa64f1a8, 0x6fc3cf26, 0xc9b4c492, 0xf85cde0f, 0x5e2bd5bb,
|
||||
0x440b7579, 0xe27c7ecd, 0xd3946450, 0x75e36fe4, 0xb044516a, 0x16335ade, 0x27db4043, 0x81ac4bf7,
|
||||
0x77e43b1e, 0xd19330aa, 0xe07b2a37, 0x460c2183, 0x83ab1f0d, 0x25dc14b9, 0x14340e24, 0xb2430590,
|
||||
0x23d5e9b7, 0x85a2e203, 0xb44af89e, 0x123df32a, 0xd79acda4, 0x71edc610, 0x4005dc8d, 0xe672d739,
|
||||
0x103aa7d0, 0xb64dac64, 0x87a5b6f9, 0x21d2bd4d, 0xe47583c3, 0x42028877, 0x73ea92ea, 0xd59d995e,
|
||||
0x8bb64ce5, 0x2dc14751, 0x1c295dcc, 0xba5e5678, 0x7ff968f6, 0xd98e6342, 0xe86679df, 0x4e11726b,
|
||||
0xb8590282, 0x1e2e0936, 0x2fc613ab, 0x89b1181f, 0x4c162691, 0xea612d25, 0xdb8937b8, 0x7dfe3c0c,
|
||||
0xec68d02b, 0x4a1fdb9f, 0x7bf7c102, 0xdd80cab6, 0x1827f438, 0xbe50ff8c, 0x8fb8e511, 0x29cfeea5,
|
||||
0xdf879e4c, 0x79f095f8, 0x48188f65, 0xee6f84d1, 0x2bc8ba5f, 0x8dbfb1eb, 0xbc57ab76, 0x1a20a0c2,
|
||||
0x8816eaf2, 0x2e61e146, 0x1f89fbdb, 0xb9fef06f, 0x7c59cee1, 0xda2ec555, 0xebc6dfc8, 0x4db1d47c,
|
||||
0xbbf9a495, 0x1d8eaf21, 0x2c66b5bc, 0x8a11be08, 0x4fb68086, 0xe9c18b32, 0xd82991af, 0x7e5e9a1b,
|
||||
0xefc8763c, 0x49bf7d88, 0x78576715, 0xde206ca1, 0x1b87522f, 0xbdf0599b, 0x8c184306, 0x2a6f48b2,
|
||||
0xdc27385b, 0x7a5033ef, 0x4bb82972, 0xedcf22c6, 0x28681c48, 0x8e1f17fc, 0xbff70d61, 0x198006d5,
|
||||
0x47abd36e, 0xe1dcd8da, 0xd034c247, 0x7643c9f3, 0xb3e4f77d, 0x1593fcc9, 0x247be654, 0x820cede0,
|
||||
0x74449d09, 0xd23396bd, 0xe3db8c20, 0x45ac8794, 0x800bb91a, 0x267cb2ae, 0x1794a833, 0xb1e3a387,
|
||||
0x20754fa0, 0x86024414, 0xb7ea5e89, 0x119d553d, 0xd43a6bb3, 0x724d6007, 0x43a57a9a, 0xe5d2712e,
|
||||
0x139a01c7, 0xb5ed0a73, 0x840510ee, 0x22721b5a, 0xe7d525d4, 0x41a22e60, 0x704a34fd, 0xd63d3f49,
|
||||
0xcc1d9f8b, 0x6a6a943f, 0x5b828ea2, 0xfdf58516, 0x3852bb98, 0x9e25b02c, 0xafcdaab1, 0x09baa105,
|
||||
0xfff2d1ec, 0x5985da58, 0x686dc0c5, 0xce1acb71, 0x0bbdf5ff, 0xadcafe4b, 0x9c22e4d6, 0x3a55ef62,
|
||||
0xabc30345, 0x0db408f1, 0x3c5c126c, 0x9a2b19d8, 0x5f8c2756, 0xf9fb2ce2, 0xc813367f, 0x6e643dcb,
|
||||
0x982c4d22, 0x3e5b4696, 0x0fb35c0b, 0xa9c457bf, 0x6c636931, 0xca146285, 0xfbfc7818, 0x5d8b73ac,
|
||||
0x03a0a617, 0xa5d7ada3, 0x943fb73e, 0x3248bc8a, 0xf7ef8204, 0x519889b0, 0x6070932d, 0xc6079899,
|
||||
0x304fe870, 0x9638e3c4, 0xa7d0f959, 0x01a7f2ed, 0xc400cc63, 0x6277c7d7, 0x539fdd4a, 0xf5e8d6fe,
|
||||
0x647e3ad9, 0xc209316d, 0xf3e12bf0, 0x55962044, 0x90311eca, 0x3646157e, 0x07ae0fe3, 0xa1d90457,
|
||||
0x579174be, 0xf1e67f0a, 0xc00e6597, 0x66796e23, 0xa3de50ad, 0x05a95b19, 0x34414184, 0x92364a30,
|
||||
},
|
||||
{
|
||||
0x00000000, 0xccaa009e, 0x4225077d, 0x8e8f07e3, 0x844a0efa, 0x48e00e64, 0xc66f0987, 0x0ac50919,
|
||||
0xd3e51bb5, 0x1f4f1b2b, 0x91c01cc8, 0x5d6a1c56, 0x57af154f, 0x9b0515d1, 0x158a1232, 0xd92012ac,
|
||||
0x7cbb312b, 0xb01131b5, 0x3e9e3656, 0xf23436c8, 0xf8f13fd1, 0x345b3f4f, 0xbad438ac, 0x767e3832,
|
||||
0xaf5e2a9e, 0x63f42a00, 0xed7b2de3, 0x21d12d7d, 0x2b142464, 0xe7be24fa, 0x69312319, 0xa59b2387,
|
||||
0xf9766256, 0x35dc62c8, 0xbb53652b, 0x77f965b5, 0x7d3c6cac, 0xb1966c32, 0x3f196bd1, 0xf3b36b4f,
|
||||
0x2a9379e3, 0xe639797d, 0x68b67e9e, 0xa41c7e00, 0xaed97719, 0x62737787, 0xecfc7064, 0x205670fa,
|
||||
0x85cd537d, 0x496753e3, 0xc7e85400, 0x0b42549e, 0x01875d87, 0xcd2d5d19, 0x43a25afa, 0x8f085a64,
|
||||
0x562848c8, 0x9a824856, 0x140d4fb5, 0xd8a74f2b, 0xd2624632, 0x1ec846ac, 0x9047414f, 0x5ced41d1,
|
||||
0x299dc2ed, 0xe537c273, 0x6bb8c590, 0xa712c50e, 0xadd7cc17, 0x617dcc89, 0xeff2cb6a, 0x2358cbf4,
|
||||
0xfa78d958, 0x36d2d9c6, 0xb85dde25, 0x74f7debb, 0x7e32d7a2, 0xb298d73c, 0x3c17d0df, 0xf0bdd041,
|
||||
0x5526f3c6, 0x998cf358, 0x1703f4bb, 0xdba9f425, 0xd16cfd3c, 0x1dc6fda2, 0x9349fa41, 0x5fe3fadf,
|
||||
0x86c3e873, 0x4a69e8ed, 0xc4e6ef0e, 0x084cef90, 0x0289e689, 0xce23e617, 0x40ace1f4, 0x8c06e16a,
|
||||
0xd0eba0bb, 0x1c41a025, 0x92cea7c6, 0x5e64a758, 0x54a1ae41, 0x980baedf, 0x1684a93c, 0xda2ea9a2,
|
||||
0x030ebb0e, 0xcfa4bb90, 0x412bbc73, 0x8d81bced, 0x8744b5f4, 0x4beeb56a, 0xc561b289, 0x09cbb217,
|
||||
0xac509190, 0x60fa910e, 0xee7596ed, 0x22df9673, 0x281a9f6a, 0xe4b09ff4, 0x6a3f9817, 0xa6959889,
|
||||
0x7fb58a25, 0xb31f8abb, 0x3d908d58, 0xf13a8dc6, 0xfbff84df, 0x37558441, 0xb9da83a2, 0x7570833c,
|
||||
0x533b85da, 0x9f918544, 0x111e82a7, 0xddb48239, 0xd7718b20, 0x1bdb8bbe, 0x95548c5d, 0x59fe8cc3,
|
||||
0x80de9e6f, 0x4c749ef1, 0xc2fb9912, 0x0e51998c, 0x04949095, 0xc83e900b, 0x46b197e8, 0x8a1b9776,
|
||||
0x2f80b4f1, 0xe32ab46f, 0x6da5b38c, 0xa10fb312, 0xabcaba0b, 0x6760ba95, 0xe9efbd76, 0x2545bde8,
|
||||
0xfc65af44, 0x30cfafda, 0xbe40a839, 0x72eaa8a7, 0x782fa1be, 0xb485a120, 0x3a0aa6c3, 0xf6a0a65d,
|
||||
0xaa4de78c, 0x66e7e712, 0xe868e0f1, 0x24c2e06f, 0x2e07e976, 0xe2ade9e8, 0x6c22ee0b, 0xa088ee95,
|
||||
0x79a8fc39, 0xb502fca7, 0x3b8dfb44, 0xf727fbda, 0xfde2f2c3, 0x3148f25d, 0xbfc7f5be, 0x736df520,
|
||||
0xd6f6d6a7, 0x1a5cd639, 0x94d3d1da, 0x5879d144, 0x52bcd85d, 0x9e16d8c3, 0x1099df20, 0xdc33dfbe,
|
||||
0x0513cd12, 0xc9b9cd8c, 0x4736ca6f, 0x8b9ccaf1, 0x8159c3e8, 0x4df3c376, 0xc37cc495, 0x0fd6c40b,
|
||||
0x7aa64737, 0xb60c47a9, 0x3883404a, 0xf42940d4, 0xfeec49cd, 0x32464953, 0xbcc94eb0, 0x70634e2e,
|
||||
0xa9435c82, 0x65e95c1c, 0xeb665bff, 0x27cc5b61, 0x2d095278, 0xe1a352e6, 0x6f2c5505, 0xa386559b,
|
||||
0x061d761c, 0xcab77682, 0x44387161, 0x889271ff, 0x825778e6, 0x4efd7878, 0xc0727f9b, 0x0cd87f05,
|
||||
0xd5f86da9, 0x19526d37, 0x97dd6ad4, 0x5b776a4a, 0x51b26353, 0x9d1863cd, 0x1397642e, 0xdf3d64b0,
|
||||
0x83d02561, 0x4f7a25ff, 0xc1f5221c, 0x0d5f2282, 0x079a2b9b, 0xcb302b05, 0x45bf2ce6, 0x89152c78,
|
||||
0x50353ed4, 0x9c9f3e4a, 0x121039a9, 0xdeba3937, 0xd47f302e, 0x18d530b0, 0x965a3753, 0x5af037cd,
|
||||
0xff6b144a, 0x33c114d4, 0xbd4e1337, 0x71e413a9, 0x7b211ab0, 0xb78b1a2e, 0x39041dcd, 0xf5ae1d53,
|
||||
0x2c8e0fff, 0xe0240f61, 0x6eab0882, 0xa201081c, 0xa8c40105, 0x646e019b, 0xeae10678, 0x264b06e6,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
@(optimization_mode="speed")
|
||||
crc32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
result := ~u32(seed);
|
||||
#no_bounds_check for b in data {
|
||||
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
|
||||
|
||||
@private _crc32_table := [256]u32{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
||||
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
|
||||
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
|
||||
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
|
||||
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
|
||||
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
|
||||
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
|
||||
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
|
||||
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
|
||||
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
|
||||
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
|
||||
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
|
||||
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
|
||||
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
|
||||
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
|
||||
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
|
||||
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
|
||||
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
|
||||
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
|
||||
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
|
||||
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
|
||||
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
|
||||
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
|
||||
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
|
||||
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
|
||||
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
|
||||
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
|
||||
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
|
||||
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
|
||||
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
|
||||
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
|
||||
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
|
||||
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
|
||||
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
|
||||
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
|
||||
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
|
||||
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
|
||||
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
|
||||
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
|
||||
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
|
||||
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
};
|
||||
*/
|
||||
+49
-7
@@ -1,17 +1,52 @@
|
||||
package hash
|
||||
|
||||
import "core:mem"
|
||||
import "intrinsics"
|
||||
|
||||
@(optimization_mode="speed")
|
||||
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||
|
||||
adler32 :: proc(data: []byte, seed := u32(1)) -> u32 {
|
||||
ADLER_CONST :: 65521;
|
||||
a, b: u32 = seed & 0xFFFF, seed >> 16;
|
||||
for x in data {
|
||||
a = (a + u32(x)) % ADLER_CONST;
|
||||
b = (b + a) % ADLER_CONST;
|
||||
|
||||
buffer := raw_data(data);
|
||||
a, b: u64 = u64(seed) & 0xFFFF, u64(seed) >> 16;
|
||||
buf := data[:];
|
||||
|
||||
for len(buf) != 0 && uintptr(buffer) & 7 != 0 {
|
||||
a = (a + u64(buf[0]));
|
||||
b = (b + a);
|
||||
buffer = intrinsics.ptr_offset(buffer, 1);
|
||||
buf = buf[1:];
|
||||
}
|
||||
return (b << 16) | a;
|
||||
|
||||
for len(buf) > 7 {
|
||||
count := min(len(buf), 5552);
|
||||
for count > 7 {
|
||||
a += u64(buf[0]); b += a;
|
||||
a += u64(buf[1]); b += a;
|
||||
a += u64(buf[2]); b += a;
|
||||
a += u64(buf[3]); b += a;
|
||||
a += u64(buf[4]); b += a;
|
||||
a += u64(buf[5]); b += a;
|
||||
a += u64(buf[6]); b += a;
|
||||
a += u64(buf[7]); b += a;
|
||||
|
||||
buf = buf[8:];
|
||||
count -= 8;
|
||||
}
|
||||
a %= ADLER_CONST;
|
||||
b %= ADLER_CONST;
|
||||
}
|
||||
|
||||
for len(buf) != 0 {
|
||||
a = (a + u64(buf[0])) % ADLER_CONST;
|
||||
b = (b + a) % ADLER_CONST;
|
||||
buf = buf[1:];
|
||||
}
|
||||
return (u32(b) << 16) | u32(a);
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
djb2 :: proc(data: []byte) -> u32 {
|
||||
hash: u32 = 5381;
|
||||
for b in data {
|
||||
@@ -20,6 +55,7 @@ djb2 :: proc(data: []byte) -> u32 {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv32 :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
@@ -28,6 +64,7 @@ fnv32 :: proc(data: []byte) -> u32 {
|
||||
return h;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv64 :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
@@ -36,6 +73,7 @@ fnv64 :: proc(data: []byte) -> u64 {
|
||||
return h;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv32a :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
@@ -44,6 +82,7 @@ fnv32a :: proc(data: []byte) -> u32 {
|
||||
return h;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv64a :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
@@ -52,6 +91,7 @@ fnv64a :: proc(data: []byte) -> u64 {
|
||||
return h;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
jenkins :: proc(data: []byte) -> u32 {
|
||||
hash: u32 = 0;
|
||||
for b in data {
|
||||
@@ -65,6 +105,7 @@ jenkins :: proc(data: []byte) -> u32 {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
murmur32 :: proc(data: []byte) -> u32 {
|
||||
c1_32: u32 : 0xcc9e2d51;
|
||||
c2_32: u32 : 0x1b873593;
|
||||
@@ -114,6 +155,7 @@ murmur32 :: proc(data: []byte) -> u32 {
|
||||
return h1;
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
murmur64 :: proc(data: []byte) -> u64 {
|
||||
SEED :: 0x9747b28c;
|
||||
|
||||
@@ -219,7 +261,7 @@ murmur64 :: proc(data: []byte) -> u64 {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@(optimization_mode="speed")
|
||||
sdbm :: proc(data: []byte) -> u32 {
|
||||
hash: u32 = 0;
|
||||
for b in data {
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
package image
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation, optimization.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
*/
|
||||
|
||||
import "core:bytes"
|
||||
import "core:mem"
|
||||
|
||||
Image :: struct {
|
||||
width: int,
|
||||
height: int,
|
||||
channels: int,
|
||||
depth: int,
|
||||
pixels: bytes.Buffer,
|
||||
/*
|
||||
Some image loaders/writers can return/take an optional background color.
|
||||
For convenience, we return them as u16 so we don't need to switch on the type
|
||||
in our viewer, and can just test against nil.
|
||||
*/
|
||||
background: Maybe([3]u16),
|
||||
|
||||
metadata_ptr: rawptr,
|
||||
metadata_type: typeid,
|
||||
}
|
||||
|
||||
/*
|
||||
IMPORTANT: `.do_not_expand_*` options currently skip handling of the `alpha_*` options,
|
||||
therefore Gray+Alpha will be returned as such even if you add `.alpha_drop_if_present`,
|
||||
and `.alpha_add_if_missing` and keyed transparency will likewise be ignored.
|
||||
|
||||
The same goes for indexed images. This will be remedied in a near future update.
|
||||
*/
|
||||
|
||||
/*
|
||||
Image_Option:
|
||||
`.info`
|
||||
This option behaves as `.return_ihdr` and `.do_not_decompress_image` and can be used
|
||||
to gather an image's dimensions and color information.
|
||||
|
||||
`.return_header`
|
||||
Fill out img.sidecar.header with the image's format-specific header struct.
|
||||
If we only care about the image specs, we can set `.return_header` +
|
||||
`.do_not_decompress_image`, or `.info`, which works as if both of these were set.
|
||||
|
||||
`.return_metadata`
|
||||
Returns all chunks not needed to decode the data.
|
||||
It also returns the header as if `.return_header` was set.
|
||||
|
||||
`.do_not_decompress_image`
|
||||
Skip decompressing IDAT chunk, defiltering and the rest.
|
||||
|
||||
`.do_not_expand_grayscale`
|
||||
Do not turn grayscale (+ Alpha) images into RGB(A).
|
||||
Returns just the 1 or 2 channels present, although 1, 2 and 4 bit are still scaled to 8-bit.
|
||||
|
||||
`.do_not_expand_indexed`
|
||||
Do not turn indexed (+ Alpha) images into RGB(A).
|
||||
Returns just the 1 or 2 (with `tRNS`) channels present.
|
||||
Make sure to use `return_metadata` to also return the palette chunk so you can recolor it yourself.
|
||||
|
||||
`.do_not_expand_channels`
|
||||
Applies both `.do_not_expand_grayscale` and `.do_not_expand_indexed`.
|
||||
|
||||
`.alpha_add_if_missing`
|
||||
If the image has no alpha channel, it'll add one set to max(type).
|
||||
Turns RGB into RGBA and Gray into Gray+Alpha
|
||||
|
||||
`.alpha_drop_if_present`
|
||||
If the image has an alpha channel, drop it.
|
||||
You may want to use `.alpha_premultiply` in this case.
|
||||
|
||||
NOTE: For PNG, this also skips handling of the tRNS chunk, if present,
|
||||
unless you select `alpha_premultiply`.
|
||||
In this case it'll premultiply the specified pixels in question only,
|
||||
as the others are implicitly fully opaque.
|
||||
|
||||
`.alpha_premultiply`
|
||||
If the image has an alpha channel, returns image data as follows:
|
||||
RGB *= A, Gray = Gray *= A
|
||||
|
||||
`.blend_background`
|
||||
If a bKGD chunk is present in a PNG, we normally just set `img.background`
|
||||
with its value and leave it up to the application to decide how to display the image,
|
||||
as per the PNG specification.
|
||||
|
||||
With `.blend_background` selected, we blend the image against the background
|
||||
color. As this negates the use for an alpha channel, we'll drop it _unless_
|
||||
you also specify `.alpha_add_if_missing`.
|
||||
|
||||
Options that don't apply to an image format will be ignored by their loader.
|
||||
*/
|
||||
|
||||
Option :: enum {
|
||||
info = 0,
|
||||
do_not_decompress_image,
|
||||
return_header,
|
||||
return_metadata,
|
||||
alpha_add_if_missing,
|
||||
alpha_drop_if_present,
|
||||
alpha_premultiply,
|
||||
blend_background,
|
||||
// Unimplemented
|
||||
do_not_expand_grayscale,
|
||||
do_not_expand_indexed,
|
||||
do_not_expand_channels,
|
||||
}
|
||||
Options :: distinct bit_set[Option];
|
||||
|
||||
Error :: enum {
|
||||
Invalid_PNG_Signature,
|
||||
IHDR_Not_First_Chunk,
|
||||
IHDR_Corrupt,
|
||||
IDAT_Missing,
|
||||
IDAT_Must_Be_Contiguous,
|
||||
IDAT_Corrupt,
|
||||
PNG_Does_Not_Adhere_to_Spec,
|
||||
PLTE_Encountered_Unexpectedly,
|
||||
PLTE_Invalid_Length,
|
||||
TRNS_Encountered_Unexpectedly,
|
||||
BKGD_Invalid_Length,
|
||||
Invalid_Image_Dimensions,
|
||||
Unknown_Color_Type,
|
||||
Invalid_Color_Bit_Depth_Combo,
|
||||
Unknown_Filter_Method,
|
||||
Unknown_Interlace_Method,
|
||||
Requested_Channel_Not_Present,
|
||||
Post_Processing_Error,
|
||||
}
|
||||
|
||||
/*
|
||||
Functions to help with image buffer calculations
|
||||
*/
|
||||
|
||||
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
|
||||
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
For when you have an RGB(A) image, but want a particular channel.
|
||||
*/
|
||||
|
||||
Channel :: enum u8 {
|
||||
R = 1,
|
||||
G = 2,
|
||||
B = 3,
|
||||
A = 4,
|
||||
}
|
||||
|
||||
return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {
|
||||
ok = false;
|
||||
t: bytes.Buffer;
|
||||
|
||||
idx := int(channel);
|
||||
|
||||
if img.channels == 2 && idx == 4 {
|
||||
// Alpha requested, which in a two channel image is index 2: G.
|
||||
idx = 2;
|
||||
}
|
||||
|
||||
if idx > img.channels {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
switch img.depth {
|
||||
case 8:
|
||||
buffer_size := compute_buffer_size(img.width, img.height, 1, 8);
|
||||
t = bytes.Buffer{};
|
||||
resize(&t.buf, buffer_size);
|
||||
|
||||
i := bytes.buffer_to_bytes(&img.pixels);
|
||||
o := bytes.buffer_to_bytes(&t);
|
||||
|
||||
for len(i) > 0 {
|
||||
o[0] = i[idx];
|
||||
i = i[img.channels:];
|
||||
o = o[1:];
|
||||
}
|
||||
case 16:
|
||||
buffer_size := compute_buffer_size(img.width, img.height, 2, 8);
|
||||
t = bytes.Buffer{};
|
||||
resize(&t.buf, buffer_size);
|
||||
|
||||
i := mem.slice_data_cast([]u16, img.pixels.buf[:]);
|
||||
o := mem.slice_data_cast([]u16, t.buf[:]);
|
||||
|
||||
for len(i) > 0 {
|
||||
o[0] = i[idx];
|
||||
i = i[img.channels:];
|
||||
o = o[1:];
|
||||
}
|
||||
case 1, 2, 4:
|
||||
// We shouldn't see this case, as the loader already turns these into 8-bit.
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
res = new(Image);
|
||||
res.width = img.width;
|
||||
res.height = img.height;
|
||||
res.channels = 1;
|
||||
res.depth = img.depth;
|
||||
res.pixels = t;
|
||||
res.background = img.background;
|
||||
res.metadata_ptr = img.metadata_ptr;
|
||||
res.metadata_type = img.metadata_type;
|
||||
|
||||
return res, true;
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
//+ignore
|
||||
package png
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
|
||||
An example of how to use `load`.
|
||||
*/
|
||||
|
||||
import "core:compress"
|
||||
import "core:image"
|
||||
// import "core:image/png"
|
||||
import "core:bytes"
|
||||
import "core:fmt"
|
||||
|
||||
// For PPM writer
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
|
||||
main :: proc() {
|
||||
track := mem.Tracking_Allocator{};
|
||||
mem.tracking_allocator_init(&track, context.allocator);
|
||||
|
||||
context.allocator = mem.tracking_allocator(&track);
|
||||
|
||||
demo();
|
||||
|
||||
if len(track.allocation_map) > 0 {
|
||||
fmt.println("Leaks:");
|
||||
for _, v in track.allocation_map {
|
||||
fmt.printf("\t%v\n\n", v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
demo :: proc() {
|
||||
file: string;
|
||||
|
||||
options := image.Options{}; // {.return_metadata};
|
||||
err: compress.Error;
|
||||
img: ^image.Image;
|
||||
|
||||
file = "../../../misc/logo-slim.png";
|
||||
|
||||
img, err = load(file, options);
|
||||
defer destroy(img);
|
||||
|
||||
if err != nil {
|
||||
fmt.printf("Trying to read PNG file %v returned %v\n", file, err);
|
||||
} else {
|
||||
v: ^Info;
|
||||
|
||||
fmt.printf("Image: %vx%vx%v, %v-bit.\n", img.width, img.height, img.channels, img.depth);
|
||||
if img.metadata_ptr != nil && img.metadata_type == Info {
|
||||
v = (^Info)(img.metadata_ptr);
|
||||
|
||||
// Handle ancillary chunks as you wish.
|
||||
// We provide helper functions for a few types.
|
||||
for c in v.chunks {
|
||||
#partial switch c.header.type {
|
||||
case .tIME:
|
||||
t, _ := core_time(c);
|
||||
fmt.printf("[tIME]: %v\n", t);
|
||||
case .gAMA:
|
||||
fmt.printf("[gAMA]: %v\n", gamma(c));
|
||||
case .pHYs:
|
||||
phys := phys(c);
|
||||
if phys.unit == .Meter {
|
||||
xm := f32(img.width) / f32(phys.ppu_x);
|
||||
ym := f32(img.height) / f32(phys.ppu_y);
|
||||
dpi_x, dpi_y := phys_to_dpi(phys);
|
||||
fmt.printf("[pHYs] Image resolution is %v x %v pixels per meter.\n", phys.ppu_x, phys.ppu_y);
|
||||
fmt.printf("[pHYs] Image resolution is %v x %v DPI.\n", dpi_x, dpi_y);
|
||||
fmt.printf("[pHYs] Image dimensions are %v x %v meters.\n", xm, ym);
|
||||
} else {
|
||||
fmt.printf("[pHYs] x: %v, y: %v pixels per unknown unit.\n", phys.ppu_x, phys.ppu_y);
|
||||
}
|
||||
case .iTXt, .zTXt, .tEXt:
|
||||
res, ok_text := text(c);
|
||||
if ok_text {
|
||||
if c.header.type == .iTXt {
|
||||
fmt.printf("[iTXt] %v (%v:%v): %v\n", res.keyword, res.language, res.keyword_localized, res.text);
|
||||
} else {
|
||||
fmt.printf("[tEXt/zTXt] %v: %v\n", res.keyword, res.text);
|
||||
}
|
||||
}
|
||||
defer text_destroy(res);
|
||||
case .bKGD:
|
||||
fmt.printf("[bKGD] %v\n", img.background);
|
||||
case .eXIf:
|
||||
res, ok_exif := exif(c);
|
||||
if ok_exif {
|
||||
/*
|
||||
Other than checking the signature and byte order, we don't handle Exif data.
|
||||
If you wish to interpret it, pass it to an Exif parser.
|
||||
*/
|
||||
fmt.printf("[eXIf] %v\n", res);
|
||||
}
|
||||
case .PLTE:
|
||||
plte, plte_ok := plte(c);
|
||||
if plte_ok {
|
||||
fmt.printf("[PLTE] %v\n", plte);
|
||||
} else {
|
||||
fmt.printf("[PLTE] Error\n");
|
||||
}
|
||||
case .hIST:
|
||||
res, ok_hist := hist(c);
|
||||
if ok_hist {
|
||||
fmt.printf("[hIST] %v\n", res);
|
||||
}
|
||||
case .cHRM:
|
||||
res, ok_chrm := chrm(c);
|
||||
if ok_chrm {
|
||||
fmt.printf("[cHRM] %v\n", res);
|
||||
}
|
||||
case .sPLT:
|
||||
res, ok_splt := splt(c);
|
||||
if ok_splt {
|
||||
fmt.printf("[sPLT] %v\n", res);
|
||||
}
|
||||
splt_destroy(res);
|
||||
case .sBIT:
|
||||
if res, ok_sbit := sbit(c); ok_sbit {
|
||||
fmt.printf("[sBIT] %v\n", res);
|
||||
}
|
||||
case .iCCP:
|
||||
res, ok_iccp := iccp(c);
|
||||
if ok_iccp {
|
||||
fmt.printf("[iCCP] %v\n", res);
|
||||
}
|
||||
iccp_destroy(res);
|
||||
case .sRGB:
|
||||
if res, ok_srgb := srgb(c); ok_srgb {
|
||||
fmt.printf("[sRGB] Rendering intent: %v\n", res);
|
||||
}
|
||||
case:
|
||||
type := c.header.type;
|
||||
name := chunk_type_to_name(&type);
|
||||
fmt.printf("[%v]: %v\n", name, c.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && .do_not_decompress_image not_in options && .info not_in options {
|
||||
if ok := write_image_as_ppm("out.ppm", img); ok {
|
||||
fmt.println("Saved decoded image.");
|
||||
} else {
|
||||
fmt.println("Error saving out.ppm.");
|
||||
fmt.println(img);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Crappy PPM writer used during testing. Don't use in production.
|
||||
write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: bool) {
|
||||
|
||||
_bg :: proc(bg: Maybe([3]u16), x, y: int, high := true) -> (res: [3]u16) {
|
||||
if v, ok := bg.?; ok {
|
||||
res = v;
|
||||
} else {
|
||||
if high {
|
||||
l := u16(30 * 256 + 30);
|
||||
|
||||
if (x & 4 == 0) ~ (y & 4 == 0) {
|
||||
res = [3]u16{l, 0, l};
|
||||
} else {
|
||||
res = [3]u16{l >> 1, 0, l >> 1};
|
||||
}
|
||||
} else {
|
||||
if (x & 4 == 0) ~ (y & 4 == 0) {
|
||||
res = [3]u16{30, 30, 30};
|
||||
} else {
|
||||
res = [3]u16{15, 15, 15};
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// profiler.timed_proc();
|
||||
using image;
|
||||
using os;
|
||||
|
||||
flags: int = O_WRONLY|O_CREATE|O_TRUNC;
|
||||
|
||||
img := image;
|
||||
|
||||
// PBM 16-bit images are big endian
|
||||
when ODIN_ENDIAN == "little" {
|
||||
if img.depth == 16 {
|
||||
// The pixel components are in Big Endian. Let's byteswap back.
|
||||
input := mem.slice_data_cast([]u16, img.pixels.buf[:]);
|
||||
output := mem.slice_data_cast([]u16be, img.pixels.buf[:]);
|
||||
#no_bounds_check for v, i in input {
|
||||
output[i] = u16be(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pix := bytes.buffer_to_bytes(&img.pixels);
|
||||
|
||||
if len(pix) == 0 || len(pix) < image.width * image.height * int(image.channels) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mode: int = 0;
|
||||
when ODIN_OS == "linux" || ODIN_OS == "darwin" {
|
||||
// NOTE(justasd): 644 (owner read, write; group read; others read)
|
||||
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
}
|
||||
|
||||
fd, err := open(filename, flags, mode);
|
||||
if err != 0 {
|
||||
return false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
write_string(fd,
|
||||
fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << uint(depth) - 1)),
|
||||
);
|
||||
|
||||
if channels == 3 {
|
||||
// We don't handle transparency here...
|
||||
write_ptr(fd, raw_data(pix), len(pix));
|
||||
} else {
|
||||
bpp := depth == 16 ? 2 : 1;
|
||||
bytes_needed := width * height * 3 * bpp;
|
||||
|
||||
op := bytes.Buffer{};
|
||||
bytes.buffer_init_allocator(&op, bytes_needed, bytes_needed);
|
||||
defer bytes.buffer_destroy(&op);
|
||||
|
||||
if channels == 1 {
|
||||
if depth == 16 {
|
||||
assert(len(pix) == width * height * 2);
|
||||
p16 := mem.slice_data_cast([]u16, pix);
|
||||
o16 := mem.slice_data_cast([]u16, op.buf[:]);
|
||||
#no_bounds_check for len(p16) != 0 {
|
||||
r := u16(p16[0]);
|
||||
o16[0] = r;
|
||||
o16[1] = r;
|
||||
o16[2] = r;
|
||||
p16 = p16[1:];
|
||||
o16 = o16[3:];
|
||||
}
|
||||
} else {
|
||||
o := 0;
|
||||
for i := 0; i < len(pix); i += 1 {
|
||||
r := pix[i];
|
||||
op.buf[o ] = r;
|
||||
op.buf[o+1] = r;
|
||||
op.buf[o+2] = r;
|
||||
o += 3;
|
||||
}
|
||||
}
|
||||
write_ptr(fd, raw_data(op.buf), len(op.buf));
|
||||
} else if channels == 2 {
|
||||
if depth == 16 {
|
||||
p16 := mem.slice_data_cast([]u16, pix);
|
||||
o16 := mem.slice_data_cast([]u16, op.buf[:]);
|
||||
|
||||
bgcol := img.background;
|
||||
|
||||
#no_bounds_check for len(p16) != 0 {
|
||||
r := f64(u16(p16[0]));
|
||||
bg: f64;
|
||||
if bgcol != nil {
|
||||
v := bgcol.([3]u16)[0];
|
||||
bg = f64(v);
|
||||
}
|
||||
a := f64(u16(p16[1])) / 65535.0;
|
||||
l := (a * r) + (1 - a) * bg;
|
||||
|
||||
o16[0] = u16(l);
|
||||
o16[1] = u16(l);
|
||||
o16[2] = u16(l);
|
||||
|
||||
p16 = p16[2:];
|
||||
o16 = o16[3:];
|
||||
}
|
||||
} else {
|
||||
o := 0;
|
||||
for i := 0; i < len(pix); i += 2 {
|
||||
r := pix[i]; a := pix[i+1]; a1 := f32(a) / 255.0;
|
||||
c := u8(f32(r) * a1);
|
||||
op.buf[o ] = c;
|
||||
op.buf[o+1] = c;
|
||||
op.buf[o+2] = c;
|
||||
o += 3;
|
||||
}
|
||||
}
|
||||
write_ptr(fd, raw_data(op.buf), len(op.buf));
|
||||
} else if channels == 4 {
|
||||
if depth == 16 {
|
||||
p16 := mem.slice_data_cast([]u16be, pix);
|
||||
o16 := mem.slice_data_cast([]u16be, op.buf[:]);
|
||||
|
||||
#no_bounds_check for len(p16) != 0 {
|
||||
|
||||
bg := _bg(img.background, 0, 0);
|
||||
r := f32(p16[0]);
|
||||
g := f32(p16[1]);
|
||||
b := f32(p16[2]);
|
||||
a := f32(p16[3]) / 65535.0;
|
||||
|
||||
lr := (a * r) + (1 - a) * f32(bg[0]);
|
||||
lg := (a * g) + (1 - a) * f32(bg[1]);
|
||||
lb := (a * b) + (1 - a) * f32(bg[2]);
|
||||
|
||||
o16[0] = u16be(lr);
|
||||
o16[1] = u16be(lg);
|
||||
o16[2] = u16be(lb);
|
||||
|
||||
p16 = p16[4:];
|
||||
o16 = o16[3:];
|
||||
}
|
||||
} else {
|
||||
o := 0;
|
||||
|
||||
for i := 0; i < len(pix); i += 4 {
|
||||
|
||||
x := (i / 4) % width;
|
||||
y := i / width / 4;
|
||||
|
||||
_b := _bg(img.background, x, y, false);
|
||||
bgcol := [3]u8{u8(_b[0]), u8(_b[1]), u8(_b[2])};
|
||||
|
||||
r := f32(pix[i]);
|
||||
g := f32(pix[i+1]);
|
||||
b := f32(pix[i+2]);
|
||||
a := f32(pix[i+3]) / 255.0;
|
||||
|
||||
lr := u8(f32(r) * a + (1 - a) * f32(bgcol[0]));
|
||||
lg := u8(f32(g) * a + (1 - a) * f32(bgcol[1]));
|
||||
lb := u8(f32(b) * a + (1 - a) * f32(bgcol[2]));
|
||||
op.buf[o ] = lr;
|
||||
op.buf[o+1] = lg;
|
||||
op.buf[o+2] = lb;
|
||||
o += 3;
|
||||
}
|
||||
}
|
||||
write_ptr(fd, raw_data(op.buf), len(op.buf));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,525 @@
|
||||
package png
|
||||
|
||||
/*
|
||||
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-2 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Ginger Bill: Cosmetic changes.
|
||||
|
||||
These are a few useful utility functions to work with PNG images.
|
||||
*/
|
||||
|
||||
import "core:image"
|
||||
import "core:compress/zlib"
|
||||
import coretime "core:time"
|
||||
import "core:strings"
|
||||
import "core:bytes"
|
||||
import "core:mem"
|
||||
|
||||
/*
|
||||
Cleanup of image-specific data.
|
||||
There are other helpers for cleanup of PNG-specific data.
|
||||
Those are named *_destroy, where * is the name of the helper.
|
||||
*/
|
||||
|
||||
destroy :: proc(img: ^Image) {
|
||||
if img == nil {
|
||||
/*
|
||||
Nothing to do.
|
||||
Load must've returned with an error.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
bytes.buffer_destroy(&img.pixels);
|
||||
// Clean up Info.
|
||||
free(img.metadata_ptr);
|
||||
|
||||
/*
|
||||
We don't need to do anything for the individual chunks.
|
||||
They're allocated on the temp allocator, as is info.chunks
|
||||
|
||||
See read_chunk.
|
||||
*/
|
||||
free(img);
|
||||
}
|
||||
|
||||
/*
|
||||
Chunk helpers
|
||||
*/
|
||||
|
||||
gamma :: proc(c: Chunk) -> f32 {
|
||||
assert(c.header.type == .gAMA);
|
||||
res := (^gAMA)(raw_data(c.data))^;
|
||||
when true {
|
||||
// Returns the wrong result on old backend
|
||||
// Fixed for -llvm-api
|
||||
return f32(res.gamma_100k) / 100_000.0;
|
||||
} else {
|
||||
return f32(u32(res.gamma_100k)) / 100_000.0;
|
||||
}
|
||||
}
|
||||
|
||||
INCHES_PER_METER :: 1000.0 / 25.4;
|
||||
|
||||
phys :: proc(c: Chunk) -> pHYs {
|
||||
assert(c.header.type == .pHYs);
|
||||
res := (^pHYs)(raw_data(c.data))^;
|
||||
return res;
|
||||
}
|
||||
|
||||
phys_to_dpi :: proc(p: pHYs) -> (x_dpi, y_dpi: f32) {
|
||||
return f32(p.ppu_x) / INCHES_PER_METER, f32(p.ppu_y) / INCHES_PER_METER;
|
||||
}
|
||||
|
||||
time :: proc(c: Chunk) -> tIME {
|
||||
assert(c.header.type == .tIME);
|
||||
res := (^tIME)(raw_data(c.data))^;
|
||||
return res;
|
||||
}
|
||||
|
||||
core_time :: proc(c: Chunk) -> (t: coretime.Time, ok: bool) {
|
||||
png_time := time(c);
|
||||
using png_time;
|
||||
return coretime.datetime_to_time(
|
||||
int(year), int(month), int(day),
|
||||
int(hour), int(minute), int(second),
|
||||
);
|
||||
}
|
||||
|
||||
text :: proc(c: Chunk) -> (res: Text, ok: bool) {
|
||||
#partial switch c.header.type {
|
||||
case .tEXt:
|
||||
ok = true;
|
||||
|
||||
fields := bytes.split(s=c.data, sep=[]u8{0}, allocator=context.temp_allocator);
|
||||
if len(fields) == 2 {
|
||||
res.keyword = strings.clone(string(fields[0]));
|
||||
res.text = strings.clone(string(fields[1]));
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
return;
|
||||
case .zTXt:
|
||||
ok = true;
|
||||
|
||||
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator);
|
||||
if len(fields) != 3 || len(fields[1]) != 0 {
|
||||
// Compression method must be 0=Deflate, which thanks to the split above turns
|
||||
// into an empty slice
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
// Set up ZLIB context and decompress text payload.
|
||||
buf: bytes.Buffer;
|
||||
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf);
|
||||
defer bytes.buffer_destroy(&buf);
|
||||
if zlib_error != nil {
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
res.keyword = strings.clone(string(fields[0]));
|
||||
res.text = strings.clone(bytes.buffer_to_string(&buf));
|
||||
return;
|
||||
case .iTXt:
|
||||
ok = true;
|
||||
|
||||
s := string(c.data);
|
||||
null := strings.index_byte(s, 0);
|
||||
if null == -1 {
|
||||
ok = false; return;
|
||||
}
|
||||
if len(c.data) < null + 4 {
|
||||
// At a minimum, including the \0 following the keyword, we require 5 more bytes.
|
||||
ok = false; return;
|
||||
}
|
||||
res.keyword = strings.clone(string(c.data[:null]));
|
||||
rest := c.data[null+1:];
|
||||
|
||||
compression_flag := rest[:1][0];
|
||||
if compression_flag > 1 {
|
||||
ok = false; return;
|
||||
}
|
||||
compression_method := rest[1:2][0];
|
||||
if compression_flag == 1 && compression_method > 0 {
|
||||
// Only Deflate is supported
|
||||
ok = false; return;
|
||||
}
|
||||
rest = rest[2:];
|
||||
|
||||
// We now expect an optional language keyword and translated keyword, both followed by a \0
|
||||
null = strings.index_byte(string(rest), 0);
|
||||
if null == -1 {
|
||||
ok = false; return;
|
||||
}
|
||||
res.language = strings.clone(string(rest[:null]));
|
||||
rest = rest[null+1:];
|
||||
|
||||
null = strings.index_byte(string(rest), 0);
|
||||
if null == -1 {
|
||||
ok = false; return;
|
||||
}
|
||||
res.keyword_localized = strings.clone(string(rest[:null]));
|
||||
rest = rest[null+1:];
|
||||
if compression_flag == 0 {
|
||||
res.text = strings.clone(string(rest));
|
||||
} else {
|
||||
// Set up ZLIB context and decompress text payload.
|
||||
buf: bytes.Buffer;
|
||||
zlib_error := zlib.inflate_from_byte_array(rest, &buf);
|
||||
defer bytes.buffer_destroy(&buf);
|
||||
if zlib_error != nil {
|
||||
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
res.text = strings.clone(bytes.buffer_to_string(&buf));
|
||||
}
|
||||
return;
|
||||
case:
|
||||
// PNG text helper called with an unrecognized chunk type.
|
||||
ok = false; return;
|
||||
}
|
||||
}
|
||||
|
||||
text_destroy :: proc(text: Text) {
|
||||
delete(text.keyword);
|
||||
delete(text.keyword_localized);
|
||||
delete(text.language);
|
||||
delete(text.text);
|
||||
}
|
||||
|
||||
iccp :: proc(c: Chunk) -> (res: iCCP, ok: bool) {
|
||||
ok = true;
|
||||
|
||||
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator);
|
||||
|
||||
if len(fields[0]) < 1 || len(fields[0]) > 79 {
|
||||
// Invalid profile name
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
if len(fields[1]) != 0 {
|
||||
// Compression method should be a zero, which the split turned into an empty slice.
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
// Set up ZLIB context and decompress iCCP payload
|
||||
buf: bytes.Buffer;
|
||||
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf);
|
||||
if zlib_error != nil {
|
||||
bytes.buffer_destroy(&buf);
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
res.name = strings.clone(string(fields[0]));
|
||||
res.profile = bytes.buffer_to_bytes(&buf);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
iccp_destroy :: proc(i: iCCP) {
|
||||
delete(i.name);
|
||||
|
||||
delete(i.profile);
|
||||
|
||||
}
|
||||
|
||||
srgb :: proc(c: Chunk) -> (res: sRGB, ok: bool) {
|
||||
ok = true;
|
||||
|
||||
if c.header.type != .sRGB || len(c.data) != 1 {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
res.intent = sRGB_Rendering_Intent(c.data[0]);
|
||||
if res.intent > max(sRGB_Rendering_Intent) {
|
||||
ok = false; return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
plte :: proc(c: Chunk) -> (res: PLTE, ok: bool) {
|
||||
if c.header.type != .PLTE {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
i := 0; j := 0; ok = true;
|
||||
for j < int(c.header.length) {
|
||||
res.entries[i] = {c.data[j], c.data[j+1], c.data[j+2]};
|
||||
i += 1; j += 3;
|
||||
}
|
||||
res.used = u16(i);
|
||||
return;
|
||||
}
|
||||
|
||||
splt :: proc(c: Chunk) -> (res: sPLT, ok: bool) {
|
||||
if c.header.type != .sPLT {
|
||||
return {}, false;
|
||||
}
|
||||
ok = true;
|
||||
|
||||
fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator);
|
||||
if len(fields) != 2 {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
res.depth = fields[1][0];
|
||||
if res.depth != 8 && res.depth != 16 {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
data := fields[1][1:];
|
||||
count: int;
|
||||
|
||||
if res.depth == 8 {
|
||||
if len(data) % 6 != 0 {
|
||||
return {}, false;
|
||||
}
|
||||
count = len(data) / 6;
|
||||
if count > 256 {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
res.entries = mem.slice_data_cast([][4]u8, data);
|
||||
} else { // res.depth == 16
|
||||
if len(data) % 10 != 0 {
|
||||
return {}, false;
|
||||
}
|
||||
count = len(data) / 10;
|
||||
if count > 256 {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
res.entries = mem.slice_data_cast([][4]u16, data);
|
||||
}
|
||||
|
||||
res.name = strings.clone(string(fields[0]));
|
||||
res.used = u16(count);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
splt_destroy :: proc(s: sPLT) {
|
||||
delete(s.name);
|
||||
}
|
||||
|
||||
sbit :: proc(c: Chunk) -> (res: [4]u8, ok: bool) {
|
||||
/*
|
||||
Returns [4]u8 with the significant bits in each channel.
|
||||
A channel will contain zero if not applicable to the PNG color type.
|
||||
*/
|
||||
|
||||
if len(c.data) < 1 || len(c.data) > 4 {
|
||||
ok = false; return;
|
||||
}
|
||||
ok = true;
|
||||
|
||||
for i := 0; i < len(c.data); i += 1 {
|
||||
res[i] = c.data[i];
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
hist :: proc(c: Chunk) -> (res: hIST, ok: bool) {
|
||||
if c.header.type != .hIST {
|
||||
return {}, false;
|
||||
}
|
||||
if c.header.length & 1 == 1 || c.header.length > 512 {
|
||||
// The entries are u16be, so the length must be even.
|
||||
// At most 256 entries must be present
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
ok = true;
|
||||
data := mem.slice_data_cast([]u16be, c.data);
|
||||
i := 0;
|
||||
for len(data) > 0 {
|
||||
// HIST entries are u16be, we unpack them to machine format
|
||||
res.entries[i] = u16(data[0]);
|
||||
i += 1; data = data[1:];
|
||||
}
|
||||
res.used = u16(i);
|
||||
return;
|
||||
}
|
||||
|
||||
chrm :: proc(c: Chunk) -> (res: cHRM, ok: bool) {
|
||||
ok = true;
|
||||
if c.header.length != size_of(cHRM_Raw) {
|
||||
return {}, false;
|
||||
}
|
||||
chrm := (^cHRM_Raw)(raw_data(c.data))^;
|
||||
|
||||
res.w.x = f32(chrm.w.x) / 100_000.0;
|
||||
res.w.y = f32(chrm.w.y) / 100_000.0;
|
||||
res.r.x = f32(chrm.r.x) / 100_000.0;
|
||||
res.r.y = f32(chrm.r.y) / 100_000.0;
|
||||
res.g.x = f32(chrm.g.x) / 100_000.0;
|
||||
res.g.y = f32(chrm.g.y) / 100_000.0;
|
||||
res.b.x = f32(chrm.b.x) / 100_000.0;
|
||||
res.b.y = f32(chrm.b.y) / 100_000.0;
|
||||
return;
|
||||
}
|
||||
|
||||
exif :: proc(c: Chunk) -> (res: Exif, ok: bool) {
|
||||
|
||||
ok = true;
|
||||
|
||||
if len(c.data) < 4 {
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
if c.data[0] == 'M' && c.data[1] == 'M' {
|
||||
res.byte_order = .big_endian;
|
||||
if c.data[2] != 0 || c.data[3] != 42 {
|
||||
ok = false; return;
|
||||
}
|
||||
} else if c.data[0] == 'I' && c.data[1] == 'I' {
|
||||
res.byte_order = .little_endian;
|
||||
if c.data[2] != 42 || c.data[3] != 0 {
|
||||
ok = false; return;
|
||||
}
|
||||
} else {
|
||||
ok = false; return;
|
||||
}
|
||||
|
||||
res.data = c.data;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
General helper functions
|
||||
*/
|
||||
|
||||
compute_buffer_size :: image.compute_buffer_size;
|
||||
|
||||
/*
|
||||
PNG save helpers
|
||||
*/
|
||||
|
||||
when false {
|
||||
|
||||
make_chunk :: proc(c: any, t: Chunk_Type) -> (res: Chunk) {
|
||||
|
||||
data: []u8;
|
||||
if v, ok := c.([]u8); ok {
|
||||
data = v;
|
||||
} else {
|
||||
data = mem.any_to_bytes(c);
|
||||
}
|
||||
|
||||
res.header.length = u32be(len(data));
|
||||
res.header.type = t;
|
||||
res.data = data;
|
||||
|
||||
// CRC the type
|
||||
crc := hash.crc32(mem.any_to_bytes(res.header.type));
|
||||
// Extend the CRC with the data
|
||||
res.crc = u32be(hash.crc32(data, crc));
|
||||
return;
|
||||
}
|
||||
|
||||
write_chunk :: proc(fd: os.Handle, chunk: Chunk) {
|
||||
c := chunk;
|
||||
// Write length + type
|
||||
os.write_ptr(fd, &c.header, 8);
|
||||
// Write data
|
||||
os.write_ptr(fd, mem.raw_data(c.data), int(c.header.length));
|
||||
// Write CRC32
|
||||
os.write_ptr(fd, &c.crc, 4);
|
||||
}
|
||||
|
||||
write_image_as_png :: proc(filename: string, image: Image) -> (err: Error) {
|
||||
profiler.timed_proc();
|
||||
using image;
|
||||
using os;
|
||||
flags: int = O_WRONLY|O_CREATE|O_TRUNC;
|
||||
|
||||
if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) {
|
||||
return E_PNG.Invalid_Image_Dimensions;
|
||||
}
|
||||
|
||||
mode: int = 0;
|
||||
when ODIN_OS == "linux" || ODIN_OS == "darwin" {
|
||||
// NOTE(justasd): 644 (owner read, write; group read; others read)
|
||||
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
}
|
||||
|
||||
fd, fderr := open(filename, flags, mode);
|
||||
if fderr != 0 {
|
||||
return E_General.Cannot_Open_File;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
magic := Signature;
|
||||
|
||||
write_ptr(fd, &magic, 8);
|
||||
|
||||
ihdr := IHDR{
|
||||
width = u32be(width),
|
||||
height = u32be(height),
|
||||
bit_depth = depth,
|
||||
compression_method = 0,
|
||||
filter_method = 0,
|
||||
interlace_method = .None,
|
||||
};
|
||||
|
||||
switch channels {
|
||||
case 1: ihdr.color_type = Color_Type{};
|
||||
case 2: ihdr.color_type = Color_Type{.Alpha};
|
||||
case 3: ihdr.color_type = Color_Type{.Color};
|
||||
case 4: ihdr.color_type = Color_Type{.Color, .Alpha};
|
||||
case:// Unhandled
|
||||
return E_PNG.Unknown_Color_Type;
|
||||
}
|
||||
h := make_chunk(ihdr, .IHDR);
|
||||
write_chunk(fd, h);
|
||||
|
||||
bytes_needed := width * height * int(channels) + height;
|
||||
filter_bytes := mem.make_dynamic_array_len_cap([dynamic]u8, bytes_needed, bytes_needed, context.allocator);
|
||||
defer delete(filter_bytes);
|
||||
|
||||
i := 0; j := 0;
|
||||
// Add a filter byte 0 per pixel row
|
||||
for y := 0; y < height; y += 1 {
|
||||
filter_bytes[j] = 0; j += 1;
|
||||
for x := 0; x < width; x += 1 {
|
||||
for z := 0; z < channels; z += 1 {
|
||||
filter_bytes[j+z] = image.pixels[i+z];
|
||||
}
|
||||
i += channels; j += channels;
|
||||
}
|
||||
}
|
||||
assert(j == bytes_needed);
|
||||
|
||||
a: []u8 = filter_bytes[:];
|
||||
|
||||
out_buf: ^[dynamic]u8;
|
||||
defer free(out_buf);
|
||||
|
||||
ctx := zlib.ZLIB_Context{
|
||||
in_buf = &a,
|
||||
out_buf = out_buf,
|
||||
};
|
||||
err = zlib.write_zlib_stream_from_memory(&ctx);
|
||||
|
||||
b: []u8;
|
||||
if err == nil {
|
||||
b = ctx.out_buf[:];
|
||||
} else {
|
||||
return err;
|
||||
}
|
||||
|
||||
idat := make_chunk(b, .IDAT);
|
||||
|
||||
write_chunk(fd, idat);
|
||||
|
||||
iend := make_chunk([]u8{}, .IEND);
|
||||
write_chunk(fd, iend);
|
||||
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,7 +12,40 @@ volatile_store :: proc(dst: ^$T, val: T) -> T ---
|
||||
|
||||
// Trapping
|
||||
debug_trap :: proc() ---
|
||||
trap :: proc() -> ! ---
|
||||
trap :: proc() -> ! ---
|
||||
|
||||
// Instructions
|
||||
|
||||
alloca :: proc(size, align: int) -> ^u8 ---
|
||||
cpu_relax :: proc() ---
|
||||
read_cycle_counter :: proc() -> i64 ---
|
||||
|
||||
count_ones :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_trailing_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
||||
|
||||
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
|
||||
sqrt :: proc(x: $T) -> T where type_is_float(T) ---
|
||||
|
||||
mem_copy :: proc(dst, src: rawptr, len: int) ---
|
||||
mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) ---
|
||||
mem_zero :: proc(ptr: rawptr, len: int) ---
|
||||
|
||||
|
||||
fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
||||
fixed_point_div :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
||||
fixed_point_mul_sat :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
||||
fixed_point_div_sat :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
|
||||
|
||||
// Compiler Hints
|
||||
expect :: proc(val, expected_val: T) -> T ---
|
||||
|
||||
|
||||
// Atomics
|
||||
atomic_fence :: proc() ---
|
||||
@@ -67,36 +100,25 @@ atomic_xchg_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
|
||||
atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
|
||||
atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
|
||||
// Instructions
|
||||
|
||||
alloca :: proc(size, align: int) -> ^u8 ---
|
||||
cpu_relax :: proc() ---
|
||||
read_cycle_counter :: proc() -> i64 ---
|
||||
|
||||
|
||||
// Compiler Hints
|
||||
expect :: proc(val, expected_val: T) -> T ---
|
||||
atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
|
||||
atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
|
||||
// Constant type tests
|
||||
|
||||
@@ -114,6 +136,7 @@ type_is_string :: proc($T: typeid) -> bool ---
|
||||
type_is_typeid :: proc($T: typeid) -> bool ---
|
||||
type_is_any :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_endian_platform :: proc($T: typeid) -> bool ---
|
||||
type_is_endian_little :: proc($T: typeid) -> bool ---
|
||||
type_is_endian_big :: proc($T: typeid) -> bool ---
|
||||
type_is_unsigned :: proc($T: typeid) -> bool ---
|
||||
@@ -144,6 +167,7 @@ type_is_simd_vector :: proc($T: typeid) -> bool ---
|
||||
type_has_nil :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
||||
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
|
||||
|
||||
type_has_field :: proc($T: typeid, $name: string) -> bool ---
|
||||
|
||||
@@ -159,5 +183,5 @@ type_polymorphic_record_parameter_value :: proc($T: typeid, index: int) -> $V --
|
||||
|
||||
type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
|
||||
|
||||
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) ---
|
||||
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) ---
|
||||
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
|
||||
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
|
||||
|
||||
+13
-214
@@ -1,6 +1,6 @@
|
||||
package math_bits
|
||||
|
||||
import "core:runtime"
|
||||
import "intrinsics"
|
||||
|
||||
U8_MIN :: 0;
|
||||
U16_MIN :: 0;
|
||||
@@ -22,105 +22,19 @@ I16_MAX :: 1 << 15 - 1;
|
||||
I32_MAX :: 1 << 31 - 1;
|
||||
I64_MAX :: 1 << 63 - 1;
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.ctpop.i8") count_ones8 :: proc(i: u8) -> u8 ---
|
||||
@(link_name="llvm.ctpop.i16") count_ones16 :: proc(i: u16) -> u16 ---
|
||||
@(link_name="llvm.ctpop.i32") count_ones32 :: proc(i: u32) -> u32 ---
|
||||
@(link_name="llvm.ctpop.i64") count_ones64 :: proc(i: u64) -> u64 ---
|
||||
|
||||
@(link_name="llvm.cttz.i8") trailing_zeros8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
|
||||
@(link_name="llvm.cttz.i16") trailing_zeros16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
|
||||
@(link_name="llvm.cttz.i32") trailing_zeros32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
|
||||
@(link_name="llvm.cttz.i64") trailing_zeros64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
|
||||
count_ones :: intrinsics.count_ones;
|
||||
count_zeros :: intrinsics.count_zeros;
|
||||
trailing_zeros :: intrinsics.count_trailing_zeros;
|
||||
leading_zeros :: intrinsics.count_leading_zeros;
|
||||
count_trailing_zeros :: intrinsics.count_trailing_zeros;
|
||||
count_leading_zeros :: intrinsics.count_leading_zeros;
|
||||
reverse_bits :: intrinsics.reverse_bits;
|
||||
byte_swap :: intrinsics.byte_swap;
|
||||
|
||||
@(link_name="llvm.bitreverse.i8") reverse_bits8 :: proc(i: u8) -> u8 ---
|
||||
@(link_name="llvm.bitreverse.i16") reverse_bits16 :: proc(i: u16) -> u16 ---
|
||||
@(link_name="llvm.bitreverse.i32") reverse_bits32 :: proc(i: u32) -> u32 ---
|
||||
@(link_name="llvm.bitreverse.i64") reverse_bits64 :: proc(i: u64) -> u64 ---
|
||||
}
|
||||
|
||||
|
||||
trailing_zeros_uint :: proc(i: uint) -> uint {
|
||||
when size_of(uint) == size_of(u64) {
|
||||
return uint(trailing_zeros64(u64(i)));
|
||||
} else {
|
||||
return uint(trailing_zeros32(u32(i)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
leading_zeros_u8 :: proc(i: u8) -> int {
|
||||
return 8*size_of(i) - len_u8(i);
|
||||
}
|
||||
leading_zeros_u16 :: proc(i: u16) -> int {
|
||||
return 8*size_of(i) - len_u16(i);
|
||||
}
|
||||
leading_zeros_u32 :: proc(i: u32) -> int {
|
||||
return 8*size_of(i) - len_u32(i);
|
||||
}
|
||||
leading_zeros_u64 :: proc(i: u64) -> int {
|
||||
return 8*size_of(i) - len_u64(i);
|
||||
}
|
||||
|
||||
|
||||
byte_swap_u16 :: proc(x: u16) -> u16 {
|
||||
return runtime.bswap_16(x);
|
||||
}
|
||||
byte_swap_u32 :: proc(x: u32) -> u32 {
|
||||
return runtime.bswap_32(x);
|
||||
}
|
||||
byte_swap_u64 :: proc(x: u64) -> u64 {
|
||||
return runtime.bswap_64(x);
|
||||
}
|
||||
byte_swap_i16 :: proc(x: i16) -> i16 {
|
||||
return i16(runtime.bswap_16(u16(x)));
|
||||
}
|
||||
byte_swap_i32 :: proc(x: i32) -> i32 {
|
||||
return i32(runtime.bswap_32(u32(x)));
|
||||
}
|
||||
byte_swap_i64 :: proc(x: i64) -> i64 {
|
||||
return i64(runtime.bswap_64(u64(x)));
|
||||
}
|
||||
byte_swap_u128 :: proc(x: u128) -> u128 {
|
||||
return runtime.bswap_128(x);
|
||||
}
|
||||
byte_swap_i128 :: proc(x: i128) -> i128 {
|
||||
return i128(runtime.bswap_128(u128(x)));
|
||||
}
|
||||
|
||||
byte_swap_uint :: proc(i: uint) -> uint {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
return uint(byte_swap_u32(u32(i)));
|
||||
} else {
|
||||
return uint(byte_swap_u64(u64(i)));
|
||||
}
|
||||
}
|
||||
byte_swap_int :: proc(i: int) -> int {
|
||||
when size_of(int) == size_of(i32) {
|
||||
return int(byte_swap_i32(i32(i)));
|
||||
} else {
|
||||
return int(byte_swap_i64(i64(i)));
|
||||
}
|
||||
}
|
||||
|
||||
byte_swap :: proc{
|
||||
byte_swap_u16,
|
||||
byte_swap_u32,
|
||||
byte_swap_u64,
|
||||
byte_swap_u128,
|
||||
byte_swap_i16,
|
||||
byte_swap_i32,
|
||||
byte_swap_i64,
|
||||
byte_swap_i128,
|
||||
byte_swap_uint,
|
||||
byte_swap_int,
|
||||
};
|
||||
|
||||
count_zeros8 :: proc(i: u8) -> u8 { return 8 - count_ones8(i); }
|
||||
count_zeros16 :: proc(i: u16) -> u16 { return 16 - count_ones16(i); }
|
||||
count_zeros32 :: proc(i: u32) -> u32 { return 32 - count_ones32(i); }
|
||||
count_zeros64 :: proc(i: u64) -> u64 { return 64 - count_ones64(i); }
|
||||
overflowing_add :: intrinsics.overflow_add;
|
||||
overflowing_sub :: intrinsics.overflow_sub;
|
||||
overflowing_mul :: intrinsics.overflow_mul;
|
||||
|
||||
|
||||
rotate_left8 :: proc(x: u8, k: int) -> u8 {
|
||||
@@ -176,121 +90,6 @@ to_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; }
|
||||
to_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.uadd.with.overflow.i8") overflowing_add_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i8") overflowing_add_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
|
||||
@(link_name="llvm.uadd.with.overflow.i16") overflowing_add_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i16") overflowing_add_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
|
||||
@(link_name="llvm.uadd.with.overflow.i32") overflowing_add_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i32") overflowing_add_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
|
||||
@(link_name="llvm.uadd.with.overflow.i64") overflowing_add_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
|
||||
@(link_name="llvm.sadd.with.overflow.i64") overflowing_add_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
|
||||
}
|
||||
|
||||
overflowing_add_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_add_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_add_u64(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_add_int :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_add_i32(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_add_i64(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_add :: proc{
|
||||
overflowing_add_u8, overflowing_add_i8,
|
||||
overflowing_add_u16, overflowing_add_i16,
|
||||
overflowing_add_u32, overflowing_add_i32,
|
||||
overflowing_add_u64, overflowing_add_i64,
|
||||
overflowing_add_uint, overflowing_add_int,
|
||||
};
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.usub.with.overflow.i8") overflowing_sub_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i8") overflowing_sub_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
|
||||
@(link_name="llvm.usub.with.overflow.i16") overflowing_sub_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i16") overflowing_sub_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
|
||||
@(link_name="llvm.usub.with.overflow.i32") overflowing_sub_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i32") overflowing_sub_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
|
||||
@(link_name="llvm.usub.with.overflow.i64") overflowing_sub_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
|
||||
@(link_name="llvm.ssub.with.overflow.i64") overflowing_sub_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
|
||||
}
|
||||
overflowing_sub_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_sub_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_sub_u64(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_sub_int :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_sub_i32(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_sub_i64(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_sub :: proc{
|
||||
overflowing_sub_u8, overflowing_sub_i8,
|
||||
overflowing_sub_u16, overflowing_sub_i16,
|
||||
overflowing_sub_u32, overflowing_sub_i32,
|
||||
overflowing_sub_u64, overflowing_sub_i64,
|
||||
overflowing_sub_uint, overflowing_sub_int,
|
||||
};
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.umul.with.overflow.i8") overflowing_mul_u8 :: proc(lhs, rhs: u8) -> (u8, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i8") overflowing_mul_i8 :: proc(lhs, rhs: i8) -> (i8, bool) ---
|
||||
@(link_name="llvm.umul.with.overflow.i16") overflowing_mul_u16 :: proc(lhs, rhs: u16) -> (u16, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i16") overflowing_mul_i16 :: proc(lhs, rhs: i16) -> (i16, bool) ---
|
||||
@(link_name="llvm.umul.with.overflow.i32") overflowing_mul_u32 :: proc(lhs, rhs: u32) -> (u32, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i32") overflowing_mul_i32 :: proc(lhs, rhs: i32) -> (i32, bool) ---
|
||||
@(link_name="llvm.umul.with.overflow.i64") overflowing_mul_u64 :: proc(lhs, rhs: u64) -> (u64, bool) ---
|
||||
@(link_name="llvm.smul.with.overflow.i64") overflowing_mul_i64 :: proc(lhs, rhs: i64) -> (i64, bool) ---
|
||||
}
|
||||
overflowing_mul_uint :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_mul_u32(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_mul_u64(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_mul_int :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_mul_i32(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_mul_i64(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_mul :: proc{
|
||||
overflowing_mul_u8, overflowing_mul_i8,
|
||||
overflowing_mul_u16, overflowing_mul_i16,
|
||||
overflowing_mul_u32, overflowing_mul_i32,
|
||||
overflowing_mul_u64, overflowing_mul_i64,
|
||||
overflowing_mul_uint, overflowing_mul_int,
|
||||
};
|
||||
|
||||
|
||||
len_u8 :: proc(x: u8) -> int {
|
||||
return int(len_u8_table[x]);
|
||||
@@ -448,7 +247,7 @@ div_u64 :: proc(hi, lo, y: u64) -> (quo, rem: u64) {
|
||||
panic("overflow error");
|
||||
}
|
||||
|
||||
s := uint(leading_zeros_u64(y));
|
||||
s := uint(count_leading_zeros(y));
|
||||
y <<= s;
|
||||
|
||||
yn1 := y >> 32;
|
||||
|
||||
@@ -6,7 +6,7 @@ import "core:strconv"
|
||||
import "intrinsics"
|
||||
_ :: intrinsics;
|
||||
|
||||
Fixed :: struct($Backing: typeid, Fraction_Width: uint)
|
||||
Fixed :: struct($Backing: typeid, $Fraction_Width: uint)
|
||||
where
|
||||
intrinsics.type_is_integer(Backing),
|
||||
0 <= Fraction_Width,
|
||||
|
||||
@@ -170,8 +170,7 @@ trace :: proc(m: $T/[$N][N]$E) -> (tr: E) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
transpose :: proc(a: $T/[$N][$M]$E) -> (m: T) {
|
||||
transpose :: proc(a: $T/[$N][$M]$E) -> (m: (T when N == M else [M][N]E)) {
|
||||
for j in 0..<M {
|
||||
for i in 0..<N {
|
||||
m[j][i] = a[i][j];
|
||||
|
||||
@@ -1284,13 +1284,13 @@ matrix3_from_quaternion :: proc{
|
||||
|
||||
|
||||
matrix3_inverse_f16 :: proc(m: Matrix3f16) -> Matrix3f16 {
|
||||
return transpose(matrix3_inverse_transpose(m));
|
||||
return auto_cast transpose(matrix3_inverse_transpose(m));
|
||||
}
|
||||
matrix3_inverse_f32 :: proc(m: Matrix3f32) -> Matrix3f32 {
|
||||
return transpose(matrix3_inverse_transpose(m));
|
||||
return auto_cast transpose(matrix3_inverse_transpose(m));
|
||||
}
|
||||
matrix3_inverse_f64 :: proc(m: Matrix3f64) -> Matrix3f64 {
|
||||
return transpose(matrix3_inverse_transpose(m));
|
||||
return auto_cast transpose(matrix3_inverse_transpose(m));
|
||||
}
|
||||
matrix3_inverse :: proc{
|
||||
matrix3_inverse_f16,
|
||||
@@ -1655,13 +1655,13 @@ matrix4_from_trs :: proc{
|
||||
|
||||
|
||||
matrix4_inverse_f16 :: proc(m: Matrix4f16) -> Matrix4f16 {
|
||||
return transpose(matrix4_inverse_transpose(m));
|
||||
return auto_cast transpose(matrix4_inverse_transpose(m));
|
||||
}
|
||||
matrix4_inverse_f32 :: proc(m: Matrix4f32) -> Matrix4f32 {
|
||||
return transpose(matrix4_inverse_transpose(m));
|
||||
return auto_cast transpose(matrix4_inverse_transpose(m));
|
||||
}
|
||||
matrix4_inverse_f64 :: proc(m: Matrix4f64) -> Matrix4f64 {
|
||||
return transpose(matrix4_inverse_transpose(m));
|
||||
return auto_cast transpose(matrix4_inverse_transpose(m));
|
||||
}
|
||||
matrix4_inverse :: proc{
|
||||
matrix4_inverse_f16,
|
||||
|
||||
+540
-165
@@ -96,45 +96,186 @@ foreign _ {
|
||||
ldexp_f64 :: proc(val: f64, exp: i32) -> f64 ---;
|
||||
}
|
||||
|
||||
sqrt :: proc{sqrt_f16, sqrt_f32, sqrt_f64};
|
||||
sin :: proc{sin_f16, sin_f32, sin_f64};
|
||||
cos :: proc{cos_f16, cos_f32, cos_f64};
|
||||
pow :: proc{pow_f16, pow_f32, pow_f64};
|
||||
fmuladd :: proc{fmuladd_f16, fmuladd_f32, fmuladd_f64};
|
||||
ln :: proc{ln_f16, ln_f32, ln_f64};
|
||||
exp :: proc{exp_f16, exp_f32, exp_f64};
|
||||
sqrt_f16le :: proc(x: f16le) -> f16le { return #force_inline f16le(sqrt_f16(f16(x))); }
|
||||
sqrt_f16be :: proc(x: f16be) -> f16be { return #force_inline f16be(sqrt_f16(f16(x))); }
|
||||
sqrt_f32le :: proc(x: f32le) -> f32le { return #force_inline f32le(sqrt_f32(f32(x))); }
|
||||
sqrt_f32be :: proc(x: f32be) -> f32be { return #force_inline f32be(sqrt_f32(f32(x))); }
|
||||
sqrt_f64le :: proc(x: f64le) -> f64le { return #force_inline f64le(sqrt_f64(f64(x))); }
|
||||
sqrt_f64be :: proc(x: f64be) -> f64be { return #force_inline f64be(sqrt_f64(f64(x))); }
|
||||
sqrt :: proc{
|
||||
sqrt_f16, sqrt_f16le, sqrt_f16be,
|
||||
sqrt_f32, sqrt_f32le, sqrt_f32be,
|
||||
sqrt_f64, sqrt_f64le, sqrt_f64be,
|
||||
};
|
||||
|
||||
ldexp :: proc{ldexp_f16, ldexp_f32, ldexp_f64};
|
||||
sin_f16le :: proc(θ: f16le) -> f16le { return #force_inline f16le(sin_f16(f16(θ))); }
|
||||
sin_f16be :: proc(θ: f16be) -> f16be { return #force_inline f16be(sin_f16(f16(θ))); }
|
||||
sin_f32le :: proc(θ: f32le) -> f32le { return #force_inline f32le(sin_f32(f32(θ))); }
|
||||
sin_f32be :: proc(θ: f32be) -> f32be { return #force_inline f32be(sin_f32(f32(θ))); }
|
||||
sin_f64le :: proc(θ: f64le) -> f64le { return #force_inline f64le(sin_f64(f64(θ))); }
|
||||
sin_f64be :: proc(θ: f64be) -> f64be { return #force_inline f64be(sin_f64(f64(θ))); }
|
||||
sin :: proc{
|
||||
sin_f16, sin_f16le, sin_f16be,
|
||||
sin_f32, sin_f32le, sin_f32be,
|
||||
sin_f64, sin_f64le, sin_f64be,
|
||||
};
|
||||
|
||||
log_f16 :: proc(x, base: f16) -> f16 { return ln(x) / ln(base); }
|
||||
log_f32 :: proc(x, base: f32) -> f32 { return ln(x) / ln(base); }
|
||||
log_f64 :: proc(x, base: f64) -> f64 { return ln(x) / ln(base); }
|
||||
log :: proc{log_f16, log_f32, log_f64};
|
||||
cos_f16le :: proc(θ: f16le) -> f16le { return #force_inline f16le(cos_f16(f16(θ))); }
|
||||
cos_f16be :: proc(θ: f16be) -> f16be { return #force_inline f16be(cos_f16(f16(θ))); }
|
||||
cos_f32le :: proc(θ: f32le) -> f32le { return #force_inline f32le(cos_f32(f32(θ))); }
|
||||
cos_f32be :: proc(θ: f32be) -> f32be { return #force_inline f32be(cos_f32(f32(θ))); }
|
||||
cos_f64le :: proc(θ: f64le) -> f64le { return #force_inline f64le(cos_f64(f64(θ))); }
|
||||
cos_f64be :: proc(θ: f64be) -> f64be { return #force_inline f64be(cos_f64(f64(θ))); }
|
||||
cos :: proc{
|
||||
cos_f16, cos_f16le, cos_f16be,
|
||||
cos_f32, cos_f32le, cos_f32be,
|
||||
cos_f64, cos_f64le, cos_f64be,
|
||||
};
|
||||
|
||||
log2_f16 :: proc(x: f16) -> f16 { return ln(x)/LN2; }
|
||||
log2_f32 :: proc(x: f32) -> f32 { return ln(x)/LN2; }
|
||||
log2_f64 :: proc(x: f64) -> f64 { return ln(x)/LN2; }
|
||||
log2 :: proc{log2_f16, log2_f32, log2_f64};
|
||||
pow_f16le :: proc(x, power: f16le) -> f16le { return #force_inline f16le(pow_f16(f16(x), f16(power))); }
|
||||
pow_f16be :: proc(x, power: f16be) -> f16be { return #force_inline f16be(pow_f16(f16(x), f16(power))); }
|
||||
pow_f32le :: proc(x, power: f32le) -> f32le { return #force_inline f32le(pow_f32(f32(x), f32(power))); }
|
||||
pow_f32be :: proc(x, power: f32be) -> f32be { return #force_inline f32be(pow_f32(f32(x), f32(power))); }
|
||||
pow_f64le :: proc(x, power: f64le) -> f64le { return #force_inline f64le(pow_f64(f64(x), f64(power))); }
|
||||
pow_f64be :: proc(x, power: f64be) -> f64be { return #force_inline f64be(pow_f64(f64(x), f64(power))); }
|
||||
pow :: proc{
|
||||
pow_f16, pow_f16le, pow_f16be,
|
||||
pow_f32, pow_f32le, pow_f32be,
|
||||
pow_f64, pow_f64le, pow_f64be,
|
||||
};
|
||||
|
||||
log10_f16 :: proc(x: f16) -> f16 { return ln(x)/LN10; }
|
||||
log10_f32 :: proc(x: f32) -> f32 { return ln(x)/LN10; }
|
||||
log10_f64 :: proc(x: f64) -> f64 { return ln(x)/LN10; }
|
||||
log10 :: proc{log10_f16, log10_f32, log10_f64};
|
||||
fmuladd_f16le :: proc(a, b, c: f16le) -> f16le { return #force_inline f16le(fmuladd_f16(f16(a), f16(b), f16(c))); }
|
||||
fmuladd_f16be :: proc(a, b, c: f16be) -> f16be { return #force_inline f16be(fmuladd_f16(f16(a), f16(b), f16(c))); }
|
||||
fmuladd_f32le :: proc(a, b, c: f32le) -> f32le { return #force_inline f32le(fmuladd_f32(f32(a), f32(b), f32(c))); }
|
||||
fmuladd_f32be :: proc(a, b, c: f32be) -> f32be { return #force_inline f32be(fmuladd_f32(f32(a), f32(b), f32(c))); }
|
||||
fmuladd_f64le :: proc(a, b, c: f64le) -> f64le { return #force_inline f64le(fmuladd_f64(f64(a), f64(b), f64(c))); }
|
||||
fmuladd_f64be :: proc(a, b, c: f64be) -> f64be { return #force_inline f64be(fmuladd_f64(f64(a), f64(b), f64(c))); }
|
||||
fmuladd :: proc{
|
||||
fmuladd_f16, fmuladd_f16le, fmuladd_f16be,
|
||||
fmuladd_f32, fmuladd_f32le, fmuladd_f32be,
|
||||
fmuladd_f64, fmuladd_f64le, fmuladd_f64be,
|
||||
};
|
||||
|
||||
ln_f16le :: proc(x: f16le) -> f16le { return #force_inline f16le(ln_f16(f16(x))); }
|
||||
ln_f16be :: proc(x: f16be) -> f16be { return #force_inline f16be(ln_f16(f16(x))); }
|
||||
ln_f32le :: proc(x: f32le) -> f32le { return #force_inline f32le(ln_f32(f32(x))); }
|
||||
ln_f32be :: proc(x: f32be) -> f32be { return #force_inline f32be(ln_f32(f32(x))); }
|
||||
ln_f64le :: proc(x: f64le) -> f64le { return #force_inline f64le(ln_f64(f64(x))); }
|
||||
ln_f64be :: proc(x: f64be) -> f64be { return #force_inline f64be(ln_f64(f64(x))); }
|
||||
ln :: proc{
|
||||
ln_f16, ln_f16le, ln_f16be,
|
||||
ln_f32, ln_f32le, ln_f32be,
|
||||
ln_f64, ln_f64le, ln_f64be,
|
||||
};
|
||||
|
||||
exp_f16le :: proc(x: f16le) -> f16le { return #force_inline f16le(exp_f16(f16(x))); }
|
||||
exp_f16be :: proc(x: f16be) -> f16be { return #force_inline f16be(exp_f16(f16(x))); }
|
||||
exp_f32le :: proc(x: f32le) -> f32le { return #force_inline f32le(exp_f32(f32(x))); }
|
||||
exp_f32be :: proc(x: f32be) -> f32be { return #force_inline f32be(exp_f32(f32(x))); }
|
||||
exp_f64le :: proc(x: f64le) -> f64le { return #force_inline f64le(exp_f64(f64(x))); }
|
||||
exp_f64be :: proc(x: f64be) -> f64be { return #force_inline f64be(exp_f64(f64(x))); }
|
||||
exp :: proc{
|
||||
exp_f16, exp_f16le, exp_f16be,
|
||||
exp_f32, exp_f32le, exp_f32be,
|
||||
exp_f64, exp_f64le, exp_f64be,
|
||||
};
|
||||
|
||||
ldexp_f16le :: proc(val: f16le, exp: i32) -> f16le { return #force_inline f16le(ldexp_f16(f16(val), exp)); }
|
||||
ldexp_f16be :: proc(val: f16be, exp: i32) -> f16be { return #force_inline f16be(ldexp_f16(f16(val), exp)); }
|
||||
ldexp_f32le :: proc(val: f32le, exp: i32) -> f32le { return #force_inline f32le(ldexp_f32(f32(val), exp)); }
|
||||
ldexp_f32be :: proc(val: f32be, exp: i32) -> f32be { return #force_inline f32be(ldexp_f32(f32(val), exp)); }
|
||||
ldexp_f64le :: proc(val: f64le, exp: i32) -> f64le { return #force_inline f64le(ldexp_f64(f64(val), exp)); }
|
||||
ldexp_f64be :: proc(val: f64be, exp: i32) -> f64be { return #force_inline f64be(ldexp_f64(f64(val), exp)); }
|
||||
ldexp :: proc{
|
||||
ldexp_f16, ldexp_f16le, ldexp_f16be,
|
||||
ldexp_f32, ldexp_f32le, ldexp_f32be,
|
||||
ldexp_f64, ldexp_f64le, ldexp_f64be,
|
||||
};
|
||||
|
||||
|
||||
tan_f16 :: proc(θ: f16) -> f16 { return sin(θ)/cos(θ); }
|
||||
tan_f32 :: proc(θ: f32) -> f32 { return sin(θ)/cos(θ); }
|
||||
tan_f64 :: proc(θ: f64) -> f64 { return sin(θ)/cos(θ); }
|
||||
tan :: proc{tan_f16, tan_f32, tan_f64};
|
||||
log_f16 :: proc(x, base: f16) -> f16 { return ln(x) / ln(base); }
|
||||
log_f16le :: proc(x, base: f16le) -> f16le { return f16le(log_f16(f16(x), f16(base))); }
|
||||
log_f16be :: proc(x, base: f16be) -> f16be { return f16be(log_f16(f16(x), f16(base))); }
|
||||
|
||||
log_f32 :: proc(x, base: f32) -> f32 { return ln(x) / ln(base); }
|
||||
log_f32le :: proc(x, base: f32le) -> f32le { return f32le(log_f32(f32(x), f32(base))); }
|
||||
log_f32be :: proc(x, base: f32be) -> f32be { return f32be(log_f32(f32(x), f32(base))); }
|
||||
|
||||
log_f64 :: proc(x, base: f64) -> f64 { return ln(x) / ln(base); }
|
||||
log_f64le :: proc(x, base: f64le) -> f64le { return f64le(log_f64(f64(x), f64(base))); }
|
||||
log_f64be :: proc(x, base: f64be) -> f64be { return f64be(log_f64(f64(x), f64(base))); }
|
||||
log :: proc{
|
||||
log_f16, log_f16le, log_f16be,
|
||||
log_f32, log_f32le, log_f32be,
|
||||
log_f64, log_f64le, log_f64be,
|
||||
};
|
||||
|
||||
log2_f16 :: proc(x: f16) -> f16 { return ln(x)/LN2; }
|
||||
log2_f16le :: proc(x: f16le) -> f16le { return f16le(log2_f16(f16(x))); }
|
||||
log2_f16be :: proc(x: f16be) -> f16be { return f16be(log2_f16(f16(x))); }
|
||||
|
||||
log2_f32 :: proc(x: f32) -> f32 { return ln(x)/LN2; }
|
||||
log2_f32le :: proc(x: f32le) -> f32le { return f32le(log2_f32(f32(x))); }
|
||||
log2_f32be :: proc(x: f32be) -> f32be { return f32be(log2_f32(f32(x))); }
|
||||
|
||||
log2_f64 :: proc(x: f64) -> f64 { return ln(x)/LN2; }
|
||||
log2_f64le :: proc(x: f64le) -> f64le { return f64le(log2_f64(f64(x))); }
|
||||
log2_f64be :: proc(x: f64be) -> f64be { return f64be(log2_f64(f64(x))); }
|
||||
log2 :: proc{
|
||||
log2_f16, log2_f16le, log2_f16be,
|
||||
log2_f32, log2_f32le, log2_f32be,
|
||||
log2_f64, log2_f64le, log2_f64be,
|
||||
};
|
||||
|
||||
log10_f16 :: proc(x: f16) -> f16 { return ln(x)/LN10; }
|
||||
log10_f16le :: proc(x: f16le) -> f16le { return f16le(log10_f16(f16(x))); }
|
||||
log10_f16be :: proc(x: f16be) -> f16be { return f16be(log10_f16(f16(x))); }
|
||||
|
||||
log10_f32 :: proc(x: f32) -> f32 { return ln(x)/LN10; }
|
||||
log10_f32le :: proc(x: f32le) -> f32le { return f32le(log10_f32(f32(x))); }
|
||||
log10_f32be :: proc(x: f32be) -> f32be { return f32be(log10_f32(f32(x))); }
|
||||
|
||||
log10_f64 :: proc(x: f64) -> f64 { return ln(x)/LN10; }
|
||||
log10_f64le :: proc(x: f64le) -> f64le { return f64le(log10_f64(f64(x))); }
|
||||
log10_f64be :: proc(x: f64be) -> f64be { return f64be(log10_f64(f64(x))); }
|
||||
log10 :: proc{
|
||||
log10_f16, log10_f16le, log10_f16be,
|
||||
log10_f32, log10_f32le, log10_f32be,
|
||||
log10_f64, log10_f64le, log10_f64be,
|
||||
};
|
||||
|
||||
tan_f16 :: proc(θ: f16) -> f16 { return sin(θ)/cos(θ); }
|
||||
tan_f16le :: proc(θ: f16le) -> f16le { return f16le(tan_f16(f16(θ))); }
|
||||
tan_f16be :: proc(θ: f16be) -> f16be { return f16be(tan_f16(f16(θ))); }
|
||||
|
||||
tan_f32 :: proc(θ: f32) -> f32 { return sin(θ)/cos(θ); }
|
||||
tan_f32le :: proc(θ: f32le) -> f32le { return f32le(tan_f32(f32(θ))); }
|
||||
tan_f32be :: proc(θ: f32be) -> f32be { return f32be(tan_f32(f32(θ))); }
|
||||
|
||||
tan_f64 :: proc(θ: f64) -> f64 { return sin(θ)/cos(θ); }
|
||||
tan_f64le :: proc(θ: f64le) -> f64le { return f64le(tan_f64(f64(θ))); }
|
||||
tan_f64be :: proc(θ: f64be) -> f64be { return f64be(tan_f64(f64(θ))); }
|
||||
tan :: proc{
|
||||
tan_f16, tan_f16le, tan_f16be,
|
||||
tan_f32, tan_f32le, tan_f32be,
|
||||
tan_f64, tan_f64le, tan_f64be,
|
||||
};
|
||||
|
||||
lerp :: proc(a, b: $T, t: $E) -> (x: T) { return a*(1-t) + b*t; }
|
||||
saturate :: proc(a: $T) -> (x: T) { return clamp(a, 0, 1); };
|
||||
|
||||
unlerp_f16 :: proc(a, b, x: f16) -> (t: f16) { return (x-a)/(b-a); }
|
||||
unlerp_f32 :: proc(a, b, x: f32) -> (t: f32) { return (x-a)/(b-a); }
|
||||
unlerp_f64 :: proc(a, b, x: f64) -> (t: f64) { return (x-a)/(b-a); }
|
||||
unlerp :: proc{unlerp_f16, unlerp_f32, unlerp_f64};
|
||||
unlerp :: proc(a, b, x: $T) -> (t: T) where intrinsics.type_is_float(T), !intrinsics.type_is_array(T) {
|
||||
return (x-a)/(b-a);
|
||||
}
|
||||
|
||||
remap :: proc(old_value, old_min, old_max, new_min, new_max: $T) -> (x: T) where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
|
||||
old_range := old_max - old_min;
|
||||
new_range := new_max - new_min;
|
||||
if old_range == 0 {
|
||||
return new_range / 2;
|
||||
}
|
||||
return ((old_value - old_min) / old_range) * new_range + new_min;
|
||||
}
|
||||
|
||||
wrap :: proc(x, y: $T) -> T where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
|
||||
tmp := mod(x, y);
|
||||
@@ -170,57 +311,105 @@ gain :: proc(t, g: $T) -> T where intrinsics.type_is_numeric(T) {
|
||||
}
|
||||
|
||||
|
||||
sign_f16 :: proc(x: f16) -> f16 { return f16(int(0 < x) - int(x < 0)); }
|
||||
sign_f32 :: proc(x: f32) -> f32 { return f32(int(0 < x) - int(x < 0)); }
|
||||
sign_f64 :: proc(x: f64) -> f64 { return f64(int(0 < x) - int(x < 0)); }
|
||||
sign :: proc{sign_f16, sign_f32, sign_f64};
|
||||
sign_f16 :: proc(x: f16) -> f16 { return f16(int(0 < x) - int(x < 0)); }
|
||||
sign_f16le :: proc(x: f16le) -> f16le { return f16le(int(0 < x) - int(x < 0)); }
|
||||
sign_f16be :: proc(x: f16be) -> f16be { return f16be(int(0 < x) - int(x < 0)); }
|
||||
sign_f32 :: proc(x: f32) -> f32 { return f32(int(0 < x) - int(x < 0)); }
|
||||
sign_f32le :: proc(x: f32le) -> f32le { return f32le(int(0 < x) - int(x < 0)); }
|
||||
sign_f32be :: proc(x: f32be) -> f32be { return f32be(int(0 < x) - int(x < 0)); }
|
||||
sign_f64 :: proc(x: f64) -> f64 { return f64(int(0 < x) - int(x < 0)); }
|
||||
sign_f64le :: proc(x: f64le) -> f64le { return f64le(int(0 < x) - int(x < 0)); }
|
||||
sign_f64be :: proc(x: f64be) -> f64be { return f64be(int(0 < x) - int(x < 0)); }
|
||||
sign :: proc{
|
||||
sign_f16, sign_f16le, sign_f16be,
|
||||
sign_f32, sign_f32le, sign_f32be,
|
||||
sign_f64, sign_f64le, sign_f64be,
|
||||
};
|
||||
|
||||
|
||||
sign_bit_f16 :: proc(x: f16) -> bool {
|
||||
sign_bit_f16 :: proc(x: f16) -> bool {
|
||||
return (transmute(u16)x) & (1<<15) != 0;
|
||||
}
|
||||
sign_bit_f32 :: proc(x: f32) -> bool {
|
||||
sign_bit_f16le :: proc(x: f16le) -> bool { return #force_inline sign_bit_f16(f16(x)); }
|
||||
sign_bit_f16be :: proc(x: f16be) -> bool { return #force_inline sign_bit_f16(f16(x)); }
|
||||
sign_bit_f32 :: proc(x: f32) -> bool {
|
||||
return (transmute(u32)x) & (1<<31) != 0;
|
||||
}
|
||||
sign_bit_f64 :: proc(x: f64) -> bool {
|
||||
sign_bit_f32le :: proc(x: f32le) -> bool { return #force_inline sign_bit_f32(f32(x)); }
|
||||
sign_bit_f32be :: proc(x: f32be) -> bool { return #force_inline sign_bit_f32(f32(x)); }
|
||||
sign_bit_f64 :: proc(x: f64) -> bool {
|
||||
return (transmute(u64)x) & (1<<63) != 0;
|
||||
}
|
||||
sign_bit :: proc{sign_bit_f16, sign_bit_f32, sign_bit_f64};
|
||||
sign_bit_f64le :: proc(x: f64le) -> bool { return #force_inline sign_bit_f64(f64(x)); }
|
||||
sign_bit_f64be :: proc(x: f64be) -> bool { return #force_inline sign_bit_f64(f64(x)); }
|
||||
sign_bit :: proc{
|
||||
sign_bit_f16, sign_bit_f16le, sign_bit_f16be,
|
||||
sign_bit_f32, sign_bit_f32le, sign_bit_f32be,
|
||||
sign_bit_f64, sign_bit_f64le, sign_bit_f64be,
|
||||
};
|
||||
|
||||
copy_sign_f16 :: proc(x, y: f16) -> f16 {
|
||||
copy_sign_f16 :: proc(x, y: f16) -> f16 {
|
||||
ix := transmute(u16)x;
|
||||
iy := transmute(u16)y;
|
||||
ix &= 0x7fff;
|
||||
ix |= iy & 0x8000;
|
||||
return transmute(f16)ix;
|
||||
}
|
||||
copy_sign_f32 :: proc(x, y: f32) -> f32 {
|
||||
copy_sign_f16le :: proc(x, y: f16le) -> f16le { return #force_inline f16le(copy_sign_f16(f16(x), f16(y))); }
|
||||
copy_sign_f16be :: proc(x, y: f16be) -> f16be { return #force_inline f16be(copy_sign_f16(f16(x), f16(y))); }
|
||||
copy_sign_f32 :: proc(x, y: f32) -> f32 {
|
||||
ix := transmute(u32)x;
|
||||
iy := transmute(u32)y;
|
||||
ix &= 0x7fff_ffff;
|
||||
ix |= iy & 0x8000_0000;
|
||||
return transmute(f32)ix;
|
||||
}
|
||||
copy_sign_f64 :: proc(x, y: f64) -> f64 {
|
||||
copy_sign_f32le :: proc(x, y: f32le) -> f32le { return #force_inline f32le(copy_sign_f32(f32(x), f32(y))); }
|
||||
copy_sign_f32be :: proc(x, y: f32be) -> f32be { return #force_inline f32be(copy_sign_f32(f32(x), f32(y))); }
|
||||
copy_sign_f64 :: proc(x, y: f64) -> f64 {
|
||||
ix := transmute(u64)x;
|
||||
iy := transmute(u64)y;
|
||||
ix &= 0x7fff_ffff_ffff_ffff;
|
||||
ix |= iy & 0x8000_0000_0000_0000;
|
||||
return transmute(f64)ix;
|
||||
}
|
||||
copy_sign :: proc{copy_sign_f16, copy_sign_f32, copy_sign_f64};
|
||||
copy_sign_f64le :: proc(x, y: f64le) -> f64le { return #force_inline f64le(copy_sign_f64(f64(x), f64(y))); }
|
||||
copy_sign_f64be :: proc(x, y: f64be) -> f64be { return #force_inline f64be(copy_sign_f64(f64(x), f64(y))); }
|
||||
copy_sign :: proc{
|
||||
copy_sign_f16, copy_sign_f16le, copy_sign_f16be,
|
||||
copy_sign_f32, copy_sign_f32le, copy_sign_f32be,
|
||||
copy_sign_f64, copy_sign_f64le, copy_sign_f64be,
|
||||
};
|
||||
|
||||
to_radians_f16 :: proc(degrees: f16) -> f16 { return degrees * RAD_PER_DEG; }
|
||||
to_radians_f16le :: proc(degrees: f16le) -> f16le { return degrees * RAD_PER_DEG; }
|
||||
to_radians_f16be :: proc(degrees: f16be) -> f16be { return degrees * RAD_PER_DEG; }
|
||||
to_radians_f32 :: proc(degrees: f32) -> f32 { return degrees * RAD_PER_DEG; }
|
||||
to_radians_f32le :: proc(degrees: f32le) -> f32le { return degrees * RAD_PER_DEG; }
|
||||
to_radians_f32be :: proc(degrees: f32be) -> f32be { return degrees * RAD_PER_DEG; }
|
||||
to_radians_f64 :: proc(degrees: f64) -> f64 { return degrees * RAD_PER_DEG; }
|
||||
to_radians_f64le :: proc(degrees: f64le) -> f64le { return degrees * RAD_PER_DEG; }
|
||||
to_radians_f64be :: proc(degrees: f64be) -> f64be { return degrees * RAD_PER_DEG; }
|
||||
to_degrees_f16 :: proc(radians: f16) -> f16 { return radians * DEG_PER_RAD; }
|
||||
to_degrees_f16le :: proc(radians: f16le) -> f16le { return radians * DEG_PER_RAD; }
|
||||
to_degrees_f16be :: proc(radians: f16be) -> f16be { return radians * DEG_PER_RAD; }
|
||||
to_degrees_f32 :: proc(radians: f32) -> f32 { return radians * DEG_PER_RAD; }
|
||||
to_degrees_f32le :: proc(radians: f32le) -> f32le { return radians * DEG_PER_RAD; }
|
||||
to_degrees_f32be :: proc(radians: f32be) -> f32be { return radians * DEG_PER_RAD; }
|
||||
to_degrees_f64 :: proc(radians: f64) -> f64 { return radians * DEG_PER_RAD; }
|
||||
to_degrees_f64le :: proc(radians: f64le) -> f64le { return radians * DEG_PER_RAD; }
|
||||
to_degrees_f64be :: proc(radians: f64be) -> f64be { return radians * DEG_PER_RAD; }
|
||||
to_radians :: proc{
|
||||
to_radians_f16, to_radians_f16le, to_radians_f16be,
|
||||
to_radians_f32, to_radians_f32le, to_radians_f32be,
|
||||
to_radians_f64, to_radians_f64le, to_radians_f64be,
|
||||
};
|
||||
to_degrees :: proc{
|
||||
to_degrees_f16, to_degrees_f16le, to_degrees_f16be,
|
||||
to_degrees_f32, to_degrees_f32le, to_degrees_f32be,
|
||||
to_degrees_f64, to_degrees_f64le, to_degrees_f64be,
|
||||
};
|
||||
|
||||
to_radians_f16 :: proc(degrees: f16) -> f16 { return degrees * RAD_PER_DEG; }
|
||||
to_radians_f32 :: proc(degrees: f32) -> f32 { return degrees * RAD_PER_DEG; }
|
||||
to_radians_f64 :: proc(degrees: f64) -> f64 { return degrees * RAD_PER_DEG; }
|
||||
to_degrees_f16 :: proc(radians: f16) -> f16 { return radians * DEG_PER_RAD; }
|
||||
to_degrees_f32 :: proc(radians: f32) -> f32 { return radians * DEG_PER_RAD; }
|
||||
to_degrees_f64 :: proc(radians: f64) -> f64 { return radians * DEG_PER_RAD; }
|
||||
to_radians :: proc{to_radians_f16, to_radians_f32, to_radians_f64};
|
||||
to_degrees :: proc{to_degrees_f16, to_degrees_f32, to_degrees_f64};
|
||||
|
||||
trunc_f16 :: proc(x: f16) -> f16 {
|
||||
trunc_f16 :: proc(x: f16) -> f16 {
|
||||
trunc_internal :: proc(f: f16) -> f16 {
|
||||
mask :: 0x1f;
|
||||
shift :: 16 - 6;
|
||||
@@ -249,8 +438,10 @@ trunc_f16 :: proc(x: f16) -> f16 {
|
||||
}
|
||||
return trunc_internal(x);
|
||||
}
|
||||
trunc_f16le :: proc(x: f16le) -> f16le { return #force_inline f16le(trunc_f16(f16(x))); }
|
||||
trunc_f16be :: proc(x: f16be) -> f16be { return #force_inline f16be(trunc_f16(f16(x))); }
|
||||
|
||||
trunc_f32 :: proc(x: f32) -> f32 {
|
||||
trunc_f32 :: proc(x: f32) -> f32 {
|
||||
trunc_internal :: proc(f: f32) -> f32 {
|
||||
mask :: 0xff;
|
||||
shift :: 32 - 9;
|
||||
@@ -279,8 +470,10 @@ trunc_f32 :: proc(x: f32) -> f32 {
|
||||
}
|
||||
return trunc_internal(x);
|
||||
}
|
||||
trunc_f32le :: proc(x: f32le) -> f32le { return #force_inline f32le(trunc_f32(f32(x))); }
|
||||
trunc_f32be :: proc(x: f32be) -> f32be { return #force_inline f32be(trunc_f32(f32(x))); }
|
||||
|
||||
trunc_f64 :: proc(x: f64) -> f64 {
|
||||
trunc_f64 :: proc(x: f64) -> f64 {
|
||||
trunc_internal :: proc(f: f64) -> f64 {
|
||||
mask :: 0x7ff;
|
||||
shift :: 64 - 12;
|
||||
@@ -309,27 +502,68 @@ trunc_f64 :: proc(x: f64) -> f64 {
|
||||
}
|
||||
return trunc_internal(x);
|
||||
}
|
||||
trunc_f64le :: proc(x: f64le) -> f64le { return #force_inline f64le(trunc_f64(f64(x))); }
|
||||
trunc_f64be :: proc(x: f64be) -> f64be { return #force_inline f64be(trunc_f64(f64(x))); }
|
||||
trunc :: proc{
|
||||
trunc_f16, trunc_f16le, trunc_f16be,
|
||||
trunc_f32, trunc_f32le, trunc_f32be,
|
||||
trunc_f64, trunc_f64le, trunc_f64be,
|
||||
};
|
||||
|
||||
trunc :: proc{trunc_f16, trunc_f32, trunc_f64};
|
||||
|
||||
round_f16 :: proc(x: f16) -> f16 {
|
||||
round_f16 :: proc(x: f16) -> f16 {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round_f32 :: proc(x: f32) -> f32 {
|
||||
round_f16le :: proc(x: f16le) -> f16le {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round_f64 :: proc(x: f64) -> f64 {
|
||||
round_f16be :: proc(x: f16be) -> f16be {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round :: proc{round_f16, round_f32, round_f64};
|
||||
|
||||
round_f32 :: proc(x: f32) -> f32 {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round_f32le :: proc(x: f32le) -> f32le {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round_f32be :: proc(x: f32be) -> f32be {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round_f64 :: proc(x: f64) -> f64 {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round_f64le :: proc(x: f64le) -> f64le {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round_f64be :: proc(x: f64be) -> f64be {
|
||||
return ceil(x - 0.5) if x < 0 else floor(x + 0.5);
|
||||
}
|
||||
round :: proc{
|
||||
round_f16, round_f16le, round_f16be,
|
||||
round_f32, round_f32le, round_f32be,
|
||||
round_f64, round_f64le, round_f64be,
|
||||
};
|
||||
|
||||
|
||||
ceil_f16 :: proc(x: f16) -> f16 { return -floor(-x); }
|
||||
ceil_f32 :: proc(x: f32) -> f32 { return -floor(-x); }
|
||||
ceil_f64 :: proc(x: f64) -> f64 { return -floor(-x); }
|
||||
ceil :: proc{ceil_f16, ceil_f32, ceil_f64};
|
||||
ceil_f16 :: proc(x: f16) -> f16 { return -floor(-x); }
|
||||
ceil_f16le :: proc(x: f16le) -> f16le { return -floor(-x); }
|
||||
ceil_f16be :: proc(x: f16be) -> f16be { return -floor(-x); }
|
||||
|
||||
floor_f16 :: proc(x: f16) -> f16 {
|
||||
ceil_f32 :: proc(x: f32) -> f32 { return -floor(-x); }
|
||||
ceil_f32le :: proc(x: f32le) -> f32le { return -floor(-x); }
|
||||
ceil_f32be :: proc(x: f32be) -> f32be { return -floor(-x); }
|
||||
|
||||
ceil_f64 :: proc(x: f64) -> f64 { return -floor(-x); }
|
||||
ceil_f64le :: proc(x: f64le) -> f64le { return -floor(-x); }
|
||||
ceil_f64be :: proc(x: f64be) -> f64be { return -floor(-x); }
|
||||
|
||||
ceil :: proc{
|
||||
ceil_f16, ceil_f16le, ceil_f16be,
|
||||
ceil_f32, ceil_f32le, ceil_f32be,
|
||||
ceil_f64, ceil_f64le, ceil_f64be,
|
||||
};
|
||||
|
||||
floor_f16 :: proc(x: f16) -> f16 {
|
||||
if x == 0 || is_nan(x) || is_inf(x) {
|
||||
return x;
|
||||
}
|
||||
@@ -343,7 +577,9 @@ floor_f16 :: proc(x: f16) -> f16 {
|
||||
d, _ := modf(x);
|
||||
return d;
|
||||
}
|
||||
floor_f32 :: proc(x: f32) -> f32 {
|
||||
floor_f16le :: proc(x: f16le) -> f16le { return #force_inline f16le(floor_f16(f16(x))); }
|
||||
floor_f16be :: proc(x: f16be) -> f16be { return #force_inline f16be(floor_f16(f16(x))); }
|
||||
floor_f32 :: proc(x: f32) -> f32 {
|
||||
if x == 0 || is_nan(x) || is_inf(x) {
|
||||
return x;
|
||||
}
|
||||
@@ -357,7 +593,9 @@ floor_f32 :: proc(x: f32) -> f32 {
|
||||
d, _ := modf(x);
|
||||
return d;
|
||||
}
|
||||
floor_f64 :: proc(x: f64) -> f64 {
|
||||
floor_f32le :: proc(x: f32le) -> f32le { return #force_inline f32le(floor_f32(f32(x))); }
|
||||
floor_f32be :: proc(x: f32be) -> f32be { return #force_inline f32be(floor_f32(f32(x))); }
|
||||
floor_f64 :: proc(x: f64) -> f64 {
|
||||
if x == 0 || is_nan(x) || is_inf(x) {
|
||||
return x;
|
||||
}
|
||||
@@ -371,7 +609,13 @@ floor_f64 :: proc(x: f64) -> f64 {
|
||||
d, _ := modf(x);
|
||||
return d;
|
||||
}
|
||||
floor :: proc{floor_f16, floor_f32, floor_f64};
|
||||
floor_f64le :: proc(x: f64le) -> f64le { return #force_inline f64le(floor_f64(f64(x))); }
|
||||
floor_f64be :: proc(x: f64be) -> f64be { return #force_inline f64be(floor_f64(f64(x))); }
|
||||
floor :: proc{
|
||||
floor_f16, floor_f16le, floor_f16be,
|
||||
floor_f32, floor_f32le, floor_f32be,
|
||||
floor_f64, floor_f64le, floor_f64be,
|
||||
};
|
||||
|
||||
|
||||
floor_div :: proc(x, y: $T) -> T
|
||||
@@ -393,7 +637,7 @@ floor_mod :: proc(x, y: $T) -> T
|
||||
return r;
|
||||
}
|
||||
|
||||
modf_f16 :: proc(x: f16) -> (int: f16, frac: f16) {
|
||||
modf_f16 :: proc(x: f16) -> (int: f16, frac: f16) {
|
||||
shift :: 16 - 5 - 1;
|
||||
mask :: 0x1f;
|
||||
bias :: 15;
|
||||
@@ -419,7 +663,15 @@ modf_f16 :: proc(x: f16) -> (int: f16, frac: f16) {
|
||||
frac = x - int;
|
||||
return;
|
||||
}
|
||||
modf_f32 :: proc(x: f32) -> (int: f32, frac: f32) {
|
||||
modf_f16le :: proc(x: f16le) -> (int: f16le, frac: f16le) {
|
||||
i, f := #force_inline modf_f16(f16(x));
|
||||
return f16le(i), f16le(f);
|
||||
}
|
||||
modf_f16be :: proc(x: f16be) -> (int: f16be, frac: f16be) {
|
||||
i, f := #force_inline modf_f16(f16(x));
|
||||
return f16be(i), f16be(f);
|
||||
}
|
||||
modf_f32 :: proc(x: f32) -> (int: f32, frac: f32) {
|
||||
shift :: 32 - 8 - 1;
|
||||
mask :: 0xff;
|
||||
bias :: 127;
|
||||
@@ -445,7 +697,15 @@ modf_f32 :: proc(x: f32) -> (int: f32, frac: f32) {
|
||||
frac = x - int;
|
||||
return;
|
||||
}
|
||||
modf_f64 :: proc(x: f64) -> (int: f64, frac: f64) {
|
||||
modf_f32le :: proc(x: f32le) -> (int: f32le, frac: f32le) {
|
||||
i, f := #force_inline modf_f32(f32(x));
|
||||
return f32le(i), f32le(f);
|
||||
}
|
||||
modf_f32be :: proc(x: f32be) -> (int: f32be, frac: f32be) {
|
||||
i, f := #force_inline modf_f32(f32(x));
|
||||
return f32be(i), f32be(f);
|
||||
}
|
||||
modf_f64 :: proc(x: f64) -> (int: f64, frac: f64) {
|
||||
shift :: 64 - 11 - 1;
|
||||
mask :: 0x7ff;
|
||||
bias :: 1023;
|
||||
@@ -471,10 +731,22 @@ modf_f64 :: proc(x: f64) -> (int: f64, frac: f64) {
|
||||
frac = x - int;
|
||||
return;
|
||||
}
|
||||
modf :: proc{modf_f16, modf_f32, modf_f64};
|
||||
modf_f64le :: proc(x: f64le) -> (int: f64le, frac: f64le) {
|
||||
i, f := #force_inline modf_f64(f64(x));
|
||||
return f64le(i), f64le(f);
|
||||
}
|
||||
modf_f64be :: proc(x: f64be) -> (int: f64be, frac: f64be) {
|
||||
i, f := #force_inline modf_f64(f64(x));
|
||||
return f64be(i), f64be(f);
|
||||
}
|
||||
modf :: proc{
|
||||
modf_f16, modf_f16le, modf_f16be,
|
||||
modf_f32, modf_f32le, modf_f32be,
|
||||
modf_f64, modf_f64le, modf_f64be,
|
||||
};
|
||||
split_decimal :: modf;
|
||||
|
||||
mod_f16 :: proc(x, y: f16) -> (n: f16) {
|
||||
mod_f16 :: proc(x, y: f16) -> (n: f16) {
|
||||
z := abs(y);
|
||||
n = remainder(abs(x), z);
|
||||
if sign(n) < 0 {
|
||||
@@ -482,7 +754,9 @@ mod_f16 :: proc(x, y: f16) -> (n: f16) {
|
||||
}
|
||||
return copy_sign(n, x);
|
||||
}
|
||||
mod_f32 :: proc(x, y: f32) -> (n: f32) {
|
||||
mod_f16le :: proc(x, y: f16le) -> (n: f16le) { return #force_inline f16le(mod_f16(f16(x), f16(y))); }
|
||||
mod_f16be :: proc(x, y: f16be) -> (n: f16be) { return #force_inline f16be(mod_f16(f16(x), f16(y))); }
|
||||
mod_f32 :: proc(x, y: f32) -> (n: f32) {
|
||||
z := abs(y);
|
||||
n = remainder(abs(x), z);
|
||||
if sign(n) < 0 {
|
||||
@@ -490,7 +764,9 @@ mod_f32 :: proc(x, y: f32) -> (n: f32) {
|
||||
}
|
||||
return copy_sign(n, x);
|
||||
}
|
||||
mod_f64 :: proc(x, y: f64) -> (n: f64) {
|
||||
mod_f32le :: proc(x, y: f32le) -> (n: f32le) { return #force_inline f32le(mod_f32(f32(x), f32(y))); }
|
||||
mod_f32be :: proc(x, y: f32be) -> (n: f32be) { return #force_inline f32be(mod_f32(f32(x), f32(y))); }
|
||||
mod_f64 :: proc(x, y: f64) -> (n: f64) {
|
||||
z := abs(y);
|
||||
n = remainder(abs(x), z);
|
||||
if sign(n) < 0 {
|
||||
@@ -498,14 +774,28 @@ mod_f64 :: proc(x, y: f64) -> (n: f64) {
|
||||
}
|
||||
return copy_sign(n, x);
|
||||
}
|
||||
mod :: proc{mod_f16, mod_f32, mod_f64};
|
||||
|
||||
remainder_f16 :: proc(x, y: f16) -> f16 { return x - round(x/y) * y; }
|
||||
remainder_f32 :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; }
|
||||
remainder_f64 :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; }
|
||||
remainder :: proc{remainder_f16, remainder_f32, remainder_f64};
|
||||
|
||||
mod_f64le :: proc(x, y: f64le) -> (n: f64le) { return #force_inline f64le(mod_f64(f64(x), f64(y))); }
|
||||
mod_f64be :: proc(x, y: f64be) -> (n: f64be) { return #force_inline f64be(mod_f64(f64(x), f64(y))); }
|
||||
mod :: proc{
|
||||
mod_f16, mod_f16le, mod_f16be,
|
||||
mod_f32, mod_f32le, mod_f32be,
|
||||
mod_f64, mod_f64le, mod_f64be,
|
||||
};
|
||||
|
||||
remainder_f16 :: proc(x, y: f16 ) -> f16 { return x - round(x/y) * y; }
|
||||
remainder_f16le :: proc(x, y: f16le) -> f16le { return x - round(x/y) * y; }
|
||||
remainder_f16be :: proc(x, y: f16be) -> f16be { return x - round(x/y) * y; }
|
||||
remainder_f32 :: proc(x, y: f32 ) -> f32 { return x - round(x/y) * y; }
|
||||
remainder_f32le :: proc(x, y: f32le) -> f32le { return x - round(x/y) * y; }
|
||||
remainder_f32be :: proc(x, y: f32be) -> f32be { return x - round(x/y) * y; }
|
||||
remainder_f64 :: proc(x, y: f64 ) -> f64 { return x - round(x/y) * y; }
|
||||
remainder_f64le :: proc(x, y: f64le) -> f64le { return x - round(x/y) * y; }
|
||||
remainder_f64be :: proc(x, y: f64be) -> f64be { return x - round(x/y) * y; }
|
||||
remainder :: proc{
|
||||
remainder_f16, remainder_f16le, remainder_f16be,
|
||||
remainder_f32, remainder_f32le, remainder_f32be,
|
||||
remainder_f64, remainder_f64le, remainder_f64be,
|
||||
};
|
||||
|
||||
gcd :: proc(x, y: $T) -> T
|
||||
where intrinsics.type_is_ordered_numeric(T) {
|
||||
@@ -522,14 +812,30 @@ lcm :: proc(x, y: $T) -> T
|
||||
return x / gcd(x, y) * y;
|
||||
}
|
||||
|
||||
frexp_f16 :: proc(x: f16) -> (significand: f16, exponent: int) {
|
||||
frexp_f16 :: proc(x: f16) -> (significand: f16, exponent: int) {
|
||||
f, e := frexp_f64(f64(x));
|
||||
return f16(f), e;
|
||||
}
|
||||
frexp_f32 :: proc(x: f32) -> (significand: f32, exponent: int) {
|
||||
frexp_f16le :: proc(x: f16le) -> (significand: f16le, exponent: int) {
|
||||
f, e := frexp_f64(f64(x));
|
||||
return f16le(f), e;
|
||||
}
|
||||
frexp_f16be :: proc(x: f16be) -> (significand: f16be, exponent: int) {
|
||||
f, e := frexp_f64(f64(x));
|
||||
return f16be(f), e;
|
||||
}
|
||||
frexp_f32 :: proc(x: f32) -> (significand: f32, exponent: int) {
|
||||
f, e := frexp_f64(f64(x));
|
||||
return f32(f), e;
|
||||
}
|
||||
frexp_f32le :: proc(x: f32le) -> (significand: f32le, exponent: int) {
|
||||
f, e := frexp_f64(f64(x));
|
||||
return f32le(f), e;
|
||||
}
|
||||
frexp_f32be :: proc(x: f32be) -> (significand: f32be, exponent: int) {
|
||||
f, e := frexp_f64(f64(x));
|
||||
return f32be(f), e;
|
||||
}
|
||||
frexp_f64 :: proc(x: f64) -> (significand: f64, exponent: int) {
|
||||
switch {
|
||||
case x == 0:
|
||||
@@ -550,7 +856,19 @@ frexp_f64 :: proc(x: f64) -> (significand: f64, exponent: int) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
frexp :: proc{frexp_f16, frexp_f32, frexp_f64};
|
||||
frexp_f64le :: proc(x: f64le) -> (significand: f64le, exponent: int) {
|
||||
f, e := frexp_f64(f64(x));
|
||||
return f64le(f), e;
|
||||
}
|
||||
frexp_f64be :: proc(x: f64be) -> (significand: f64be, exponent: int) {
|
||||
f, e := frexp_f64(f64(x));
|
||||
return f64be(f), e;
|
||||
}
|
||||
frexp :: proc{
|
||||
frexp_f16, frexp_f16le, frexp_f16be,
|
||||
frexp_f32, frexp_f32le, frexp_f32be,
|
||||
frexp_f64, frexp_f64le, frexp_f64be,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -616,7 +934,7 @@ factorial :: proc(n: int) -> int {
|
||||
return table[n];
|
||||
}
|
||||
|
||||
classify_f16 :: proc(x: f16) -> Float_Class {
|
||||
classify_f16 :: proc(x: f16) -> Float_Class {
|
||||
switch {
|
||||
case x == 0:
|
||||
i := transmute(i16)x;
|
||||
@@ -640,7 +958,9 @@ classify_f16 :: proc(x: f16) -> Float_Class {
|
||||
}
|
||||
return .Normal;
|
||||
}
|
||||
classify_f32 :: proc(x: f32) -> Float_Class {
|
||||
classify_f16le :: proc(x: f16le) -> Float_Class { return #force_inline classify_f16(f16(x)); }
|
||||
classify_f16be :: proc(x: f16be) -> Float_Class { return #force_inline classify_f16(f16(x)); }
|
||||
classify_f32 :: proc(x: f32) -> Float_Class {
|
||||
switch {
|
||||
case x == 0:
|
||||
i := transmute(i32)x;
|
||||
@@ -664,7 +984,9 @@ classify_f32 :: proc(x: f32) -> Float_Class {
|
||||
}
|
||||
return .Normal;
|
||||
}
|
||||
classify_f64 :: proc(x: f64) -> Float_Class {
|
||||
classify_f32le :: proc(x: f32le) -> Float_Class { return #force_inline classify_f32(f32(x)); }
|
||||
classify_f32be :: proc(x: f32be) -> Float_Class { return #force_inline classify_f32(f32(x)); }
|
||||
classify_f64 :: proc(x: f64) -> Float_Class {
|
||||
switch {
|
||||
case x == 0:
|
||||
i := transmute(i64)x;
|
||||
@@ -687,13 +1009,28 @@ classify_f64 :: proc(x: f64) -> Float_Class {
|
||||
}
|
||||
return .Normal;
|
||||
}
|
||||
classify :: proc{classify_f16, classify_f32, classify_f64};
|
||||
|
||||
is_nan_f16 :: proc(x: f16) -> bool { return classify(x) == .NaN; }
|
||||
is_nan_f32 :: proc(x: f32) -> bool { return classify(x) == .NaN; }
|
||||
is_nan_f64 :: proc(x: f64) -> bool { return classify(x) == .NaN; }
|
||||
is_nan :: proc{is_nan_f16, is_nan_f32, is_nan_f64};
|
||||
classify_f64le :: proc(x: f64le) -> Float_Class { return #force_inline classify_f64(f64(x)); }
|
||||
classify_f64be :: proc(x: f64be) -> Float_Class { return #force_inline classify_f64(f64(x)); }
|
||||
classify :: proc{
|
||||
classify_f16, classify_f16le, classify_f16be,
|
||||
classify_f32, classify_f32le, classify_f32be,
|
||||
classify_f64, classify_f64le, classify_f64be,
|
||||
};
|
||||
|
||||
is_nan_f16 :: proc(x: f16) -> bool { return classify(x) == .NaN; }
|
||||
is_nan_f16le :: proc(x: f16le) -> bool { return classify(x) == .NaN; }
|
||||
is_nan_f16be :: proc(x: f16be) -> bool { return classify(x) == .NaN; }
|
||||
is_nan_f32 :: proc(x: f32) -> bool { return classify(x) == .NaN; }
|
||||
is_nan_f32le :: proc(x: f32le) -> bool { return classify(x) == .NaN; }
|
||||
is_nan_f32be :: proc(x: f32be) -> bool { return classify(x) == .NaN; }
|
||||
is_nan_f64 :: proc(x: f64) -> bool { return classify(x) == .NaN; }
|
||||
is_nan_f64le :: proc(x: f64le) -> bool { return classify(x) == .NaN; }
|
||||
is_nan_f64be :: proc(x: f64be) -> bool { return classify(x) == .NaN; }
|
||||
is_nan :: proc{
|
||||
is_nan_f16, is_nan_f16le, is_nan_f16be,
|
||||
is_nan_f32, is_nan_f32le, is_nan_f32be,
|
||||
is_nan_f64, is_nan_f64le, is_nan_f64be,
|
||||
};
|
||||
|
||||
// is_inf reports whether f is an infinity, according to sign.
|
||||
// If sign > 0, is_inf reports whether f is positive infinity.
|
||||
@@ -709,6 +1046,13 @@ is_inf_f16 :: proc(x: f16, sign: int = 0) -> bool {
|
||||
}
|
||||
return class == .Inf || class == .Neg_Inf;
|
||||
}
|
||||
is_inf_f16le :: proc(x: f16le, sign: int = 0) -> bool {
|
||||
return #force_inline is_inf_f16(f16(x), sign);
|
||||
}
|
||||
is_inf_f16be :: proc(x: f16be, sign: int = 0) -> bool {
|
||||
return #force_inline is_inf_f16(f16(x), sign);
|
||||
}
|
||||
|
||||
is_inf_f32 :: proc(x: f32, sign: int = 0) -> bool {
|
||||
class := classify(abs(x));
|
||||
switch {
|
||||
@@ -719,6 +1063,13 @@ is_inf_f32 :: proc(x: f32, sign: int = 0) -> bool {
|
||||
}
|
||||
return class == .Inf || class == .Neg_Inf;
|
||||
}
|
||||
is_inf_f32le :: proc(x: f32le, sign: int = 0) -> bool {
|
||||
return #force_inline is_inf_f32(f32(x), sign);
|
||||
}
|
||||
is_inf_f32be :: proc(x: f32be, sign: int = 0) -> bool {
|
||||
return #force_inline is_inf_f32(f32(x), sign);
|
||||
}
|
||||
|
||||
is_inf_f64 :: proc(x: f64, sign: int = 0) -> bool {
|
||||
class := classify(abs(x));
|
||||
switch {
|
||||
@@ -729,16 +1080,37 @@ is_inf_f64 :: proc(x: f64, sign: int = 0) -> bool {
|
||||
}
|
||||
return class == .Inf || class == .Neg_Inf;
|
||||
}
|
||||
is_inf :: proc{is_inf_f16, is_inf_f32, is_inf_f64};
|
||||
|
||||
|
||||
inf_f16 :: proc(sign: int) -> f16 {
|
||||
return f16(inf_f16(sign));
|
||||
is_inf_f64le :: proc(x: f64le, sign: int = 0) -> bool {
|
||||
return #force_inline is_inf_f64(f64(x), sign);
|
||||
}
|
||||
inf_f32 :: proc(sign: int) -> f32 {
|
||||
is_inf_f64be :: proc(x: f64be, sign: int = 0) -> bool {
|
||||
return #force_inline is_inf_f64(f64(x), sign);
|
||||
}
|
||||
is_inf :: proc{
|
||||
is_inf_f16, is_inf_f16le, is_inf_f16be,
|
||||
is_inf_f32, is_inf_f32le, is_inf_f32be,
|
||||
is_inf_f64, is_inf_f64le, is_inf_f64be,
|
||||
};
|
||||
|
||||
inf_f16 :: proc(sign: int) -> f16 {
|
||||
return f16(inf_f64(sign));
|
||||
}
|
||||
inf_f16le :: proc(sign: int) -> f16le {
|
||||
return f16le(inf_f64(sign));
|
||||
}
|
||||
inf_f16be :: proc(sign: int) -> f16be {
|
||||
return f16be(inf_f64(sign));
|
||||
}
|
||||
inf_f32 :: proc(sign: int) -> f32 {
|
||||
return f32(inf_f64(sign));
|
||||
}
|
||||
inf_f64 :: proc(sign: int) -> f64 {
|
||||
inf_f32le :: proc(sign: int) -> f32le {
|
||||
return f32le(inf_f64(sign));
|
||||
}
|
||||
inf_f32be :: proc(sign: int) -> f32be {
|
||||
return f32be(inf_f64(sign));
|
||||
}
|
||||
inf_f64 :: proc(sign: int) -> f64 {
|
||||
v: u64;
|
||||
if sign >= 0 {
|
||||
v = 0x7ff00000_00000000;
|
||||
@@ -747,19 +1119,41 @@ inf_f64 :: proc(sign: int) -> f64 {
|
||||
}
|
||||
return transmute(f64)v;
|
||||
}
|
||||
inf_f64le :: proc(sign: int) -> f64le {
|
||||
return f64le(inf_f64(sign));
|
||||
}
|
||||
inf_f64be :: proc(sign: int) -> f64be {
|
||||
return f64be(inf_f64(sign));
|
||||
}
|
||||
|
||||
nan_f16 :: proc() -> f16 {
|
||||
nan_f16 :: proc() -> f16 {
|
||||
return f16(nan_f64());
|
||||
}
|
||||
nan_f32 :: proc() -> f32 {
|
||||
nan_f16le :: proc() -> f16le {
|
||||
return f16le(nan_f64());
|
||||
}
|
||||
nan_f16be :: proc() -> f16be {
|
||||
return f16be(nan_f64());
|
||||
}
|
||||
nan_f32 :: proc() -> f32 {
|
||||
return f32(nan_f64());
|
||||
}
|
||||
nan_f64 :: proc() -> f64 {
|
||||
nan_f32le :: proc() -> f32le {
|
||||
return f32le(nan_f64());
|
||||
}
|
||||
nan_f32be :: proc() -> f32be {
|
||||
return f32be(nan_f64());
|
||||
}
|
||||
nan_f64 :: proc() -> f64 {
|
||||
v: u64 = 0x7ff80000_00000001;
|
||||
return transmute(f64)v;
|
||||
}
|
||||
|
||||
|
||||
nan_f64le :: proc() -> f64le {
|
||||
return f64le(nan_f64());
|
||||
}
|
||||
nan_f64be :: proc() -> f64be {
|
||||
return f64be(nan_f64());
|
||||
}
|
||||
|
||||
is_power_of_two :: proc(x: int) -> bool {
|
||||
return x > 0 && (x & (x-1)) == 0;
|
||||
@@ -816,14 +1210,30 @@ cumsum :: proc(dst, src: $T/[]$E) -> T
|
||||
}
|
||||
|
||||
|
||||
atan2_f16 :: proc(y, x: f16) -> f16 {
|
||||
atan2_f16 :: proc(y, x: f16) -> f16 {
|
||||
// TODO(bill): Better atan2_f16
|
||||
return f16(atan2_f64(f64(y), f64(x)));
|
||||
}
|
||||
atan2_f32 :: proc(y, x: f32) -> f32 {
|
||||
atan2_f16le :: proc(y, x: f16le) -> f16le {
|
||||
// TODO(bill): Better atan2_f16
|
||||
return f16le(atan2_f64(f64(y), f64(x)));
|
||||
}
|
||||
atan2_f16be :: proc(y, x: f16be) -> f16be {
|
||||
// TODO(bill): Better atan2_f16
|
||||
return f16be(atan2_f64(f64(y), f64(x)));
|
||||
}
|
||||
atan2_f32 :: proc(y, x: f32) -> f32 {
|
||||
// TODO(bill): Better atan2_f32
|
||||
return f32(atan2_f64(f64(y), f64(x)));
|
||||
}
|
||||
atan2_f32le :: proc(y, x: f32le) -> f32le {
|
||||
// TODO(bill): Better atan2_f32
|
||||
return f32le(atan2_f64(f64(y), f64(x)));
|
||||
}
|
||||
atan2_f32be :: proc(y, x: f32be) -> f32be {
|
||||
// TODO(bill): Better atan2_f32
|
||||
return f32be(atan2_f64(f64(y), f64(x)));
|
||||
}
|
||||
|
||||
atan2_f64 :: proc(y, x: f64) -> f64 {
|
||||
// TODO(bill): Faster atan2_f64 if possible
|
||||
@@ -910,80 +1320,45 @@ atan2_f64 :: proc(y, x: f64) -> f64 {
|
||||
}
|
||||
return q;
|
||||
}
|
||||
atan2_f64le :: proc(y, x: f64le) -> f64le {
|
||||
// TODO(bill): Better atan2_f32
|
||||
return f64le(atan2_f64(f64(y), f64(x)));
|
||||
}
|
||||
atan2_f64be :: proc(y, x: f64be) -> f64be {
|
||||
// TODO(bill): Better atan2_f32
|
||||
return f64be(atan2_f64(f64(y), f64(x)));
|
||||
}
|
||||
|
||||
atan2 :: proc{
|
||||
atan2_f16, atan2_f16le, atan2_f16be,
|
||||
atan2_f32, atan2_f32le, atan2_f32be,
|
||||
atan2_f64, atan2_f64le, atan2_f64be,
|
||||
};
|
||||
|
||||
atan2 :: proc{atan2_f16, atan2_f32, atan2_f64};
|
||||
atan :: proc(x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return atan2(x, 1);
|
||||
}
|
||||
|
||||
atan_f16 :: proc(x: f16) -> f16 {
|
||||
return atan2_f16(x, 1);
|
||||
asin :: proc(x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return atan2(x, 1 + sqrt(1 - x*x));
|
||||
}
|
||||
atan_f32 :: proc(x: f32) -> f32 {
|
||||
return atan2_f32(x, 1);
|
||||
}
|
||||
atan_f64 :: proc(x: f64) -> f64 {
|
||||
return atan2_f64(x, 1);
|
||||
}
|
||||
atan :: proc{atan_f16, atan_f32, atan_f64};
|
||||
|
||||
asin_f16 :: proc(x: f16) -> f16 {
|
||||
return atan2_f16(x, 1 + sqrt_f16(1 - x*x));
|
||||
acos :: proc(x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return 2 * atan2(sqrt(1 - x), sqrt(1 + x));
|
||||
}
|
||||
asin_f32 :: proc(x: f32) -> f32 {
|
||||
return atan2_f32(x, 1 + sqrt_f32(1 - x*x));
|
||||
}
|
||||
asin_f64 :: proc(x: f64) -> f64 {
|
||||
return atan2_f64(x, 1 + sqrt_f64(1 - x*x));
|
||||
}
|
||||
asin :: proc{asin_f16, asin_f32, asin_f64};
|
||||
|
||||
acos_f16 :: proc(x: f16) -> f16 {
|
||||
return 2 * atan2_f16(sqrt_f16(1 - x), sqrt_f16(1 + x));
|
||||
}
|
||||
acos_f32 :: proc(x: f32) -> f32 {
|
||||
return 2 * atan2_f32(sqrt_f32(1 - x), sqrt_f32(1 + x));
|
||||
}
|
||||
acos_f64 :: proc(x: f64) -> f64 {
|
||||
return 2 * atan2_f64(sqrt_f64(1 - x), sqrt_f64(1 + x));
|
||||
}
|
||||
acos :: proc{acos_f16, acos_f32, acos_f64};
|
||||
|
||||
|
||||
sinh_f16 :: proc(x: f16) -> f16 {
|
||||
sinh :: proc(x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return (exp(x) - exp(-x))*0.5;
|
||||
}
|
||||
sinh_f32 :: proc(x: f32) -> f32 {
|
||||
return (exp(x) - exp(-x))*0.5;
|
||||
}
|
||||
sinh_f64 :: proc(x: f64) -> f64 {
|
||||
return (exp(x) - exp(-x))*0.5;
|
||||
}
|
||||
sinh :: proc{sinh_f16, sinh_f32, sinh_f64};
|
||||
|
||||
cosh_f16 :: proc(x: f16) -> f16 {
|
||||
cosh :: proc(x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return (exp(x) + exp(-x))*0.5;
|
||||
}
|
||||
cosh_f32 :: proc(x: f32) -> f32 {
|
||||
return (exp(x) + exp(-x))*0.5;
|
||||
}
|
||||
cosh_f64 :: proc(x: f64) -> f64 {
|
||||
return (exp(x) + exp(-x))*0.5;
|
||||
}
|
||||
cosh :: proc{cosh_f16, cosh_f32, cosh_f64};
|
||||
|
||||
tanh_f16 :: proc(x: f16) -> f16 {
|
||||
tanh :: proc(x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
t := exp(2*x);
|
||||
return (t - 1) / (t + 1);
|
||||
}
|
||||
tanh_f32 :: proc(x: f32) -> f32 {
|
||||
t := exp(2*x);
|
||||
return (t - 1) / (t + 1);
|
||||
}
|
||||
tanh_f64 :: proc(x: f64) -> f64 {
|
||||
t := exp(2*x);
|
||||
return (t - 1) / (t + 1);
|
||||
}
|
||||
tanh :: proc{tanh_f16, tanh_f32, tanh_f64};
|
||||
|
||||
|
||||
F16_DIG :: 3;
|
||||
F16_EPSILON :: 0.00097656;
|
||||
|
||||
@@ -6,9 +6,9 @@ Rand :: struct {
|
||||
}
|
||||
|
||||
|
||||
@(private, static)
|
||||
@(private)
|
||||
_GLOBAL_SEED_DATA := 1234567890;
|
||||
@(private, static)
|
||||
@(private)
|
||||
global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA)));
|
||||
|
||||
set_global_seed :: proc(seed: u64) {
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ Allocator_Mode_Set :: distinct bit_set[Allocator_Mode];
|
||||
Allocator_Query_Info :: runtime.Allocator_Query_Info;
|
||||
/*
|
||||
Allocator_Query_Info :: struct {
|
||||
pointer: Maybe(rawptr),
|
||||
pointer: rawptr,
|
||||
size: Maybe(int),
|
||||
alignment: Maybe(int),
|
||||
}
|
||||
|
||||
@@ -919,7 +919,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
// Small_Allocator primary allocates memory from its local buffer of size BUFFER_SIZE
|
||||
// If that buffer's memory is exhausted, it will use the backing allocator (a scratch allocator is recommended)
|
||||
// Memory allocated with Small_Allocator cannot be freed individually using 'free' and must be freed using 'free_all'
|
||||
Small_Allocator :: struct(BUFFER_SIZE: int)
|
||||
Small_Allocator :: struct($BUFFER_SIZE: int)
|
||||
where
|
||||
BUFFER_SIZE >= 2*size_of(uintptr),
|
||||
BUFFER_SIZE & (BUFFER_SIZE-1) == 0 {
|
||||
|
||||
@@ -142,6 +142,7 @@ slice_ptr :: proc(ptr: ^$T, len: int) -> []T {
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
|
||||
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
|
||||
}
|
||||
@(deprecated="use byte_slice")
|
||||
slice_ptr_to_bytes :: proc(data: rawptr, len: int) -> []byte {
|
||||
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
|
||||
}
|
||||
|
||||
+7
-11
@@ -16,16 +16,12 @@ Proc_Inlining :: enum u32 {
|
||||
No_Inline = 2,
|
||||
}
|
||||
|
||||
Proc_Calling_Convention :: enum i32 {
|
||||
Invalid = 0,
|
||||
Odin,
|
||||
Contextless,
|
||||
C_Decl,
|
||||
Std_Call,
|
||||
Fast_Call,
|
||||
None,
|
||||
|
||||
Foreign_Block_Default = -1,
|
||||
Proc_Calling_Convention_Extra :: enum i32 {
|
||||
Foreign_Block_Default,
|
||||
}
|
||||
Proc_Calling_Convention :: union {
|
||||
string,
|
||||
Proc_Calling_Convention_Extra,
|
||||
}
|
||||
|
||||
Node_State_Flag :: enum {
|
||||
@@ -69,7 +65,7 @@ File :: struct {
|
||||
pkg: ^Package,
|
||||
|
||||
fullpath: string,
|
||||
src: []byte,
|
||||
src: string,
|
||||
|
||||
docs: ^Comment_Group,
|
||||
|
||||
|
||||
@@ -28,11 +28,11 @@ Magic_String :: "odindoc\x00";
|
||||
|
||||
Header_Base :: struct {
|
||||
magic: [8]byte,
|
||||
_: u32le,
|
||||
_: u32le, // padding
|
||||
version: Version_Type,
|
||||
total_size: u32le,
|
||||
header_size: u32le,
|
||||
hash: u32le,
|
||||
total_size: u32le, // in bytes
|
||||
header_size: u32le, // in bytes
|
||||
hash: u32le, // hash of the data after the header (header_size)
|
||||
}
|
||||
|
||||
Header :: struct {
|
||||
@@ -95,16 +95,17 @@ Entity_Flag :: enum u32le {
|
||||
Foreign = 0,
|
||||
Export = 1,
|
||||
|
||||
Param_Using = 2,
|
||||
Param_Const = 3,
|
||||
Param_Auto_Cast = 4,
|
||||
Param_Ellipsis = 5,
|
||||
Param_CVararg = 6,
|
||||
Param_No_Alias = 7,
|
||||
Param_Using = 2, // using
|
||||
Param_Const = 3, // #const
|
||||
Param_Auto_Cast = 4, // auto_cast
|
||||
Param_Ellipsis = 5, // Variadic parameter
|
||||
Param_CVararg = 6, // #c_vararg
|
||||
Param_No_Alias = 7, // #no_alias
|
||||
|
||||
Type_Alias = 8,
|
||||
|
||||
Var_Thread_Local = 9,
|
||||
Var_Static = 10,
|
||||
}
|
||||
|
||||
Entity_Flags :: distinct bit_set[Entity_Flag; u32le];
|
||||
@@ -116,14 +117,25 @@ Entity :: struct {
|
||||
name: String,
|
||||
type: Type_Index,
|
||||
init_string: String,
|
||||
_: u32le,
|
||||
_: u32le, // reserved for init
|
||||
comment: String,
|
||||
docs: String,
|
||||
|
||||
// May used by:
|
||||
// .Variable
|
||||
// .Procedure
|
||||
foreign_library: Entity_Index,
|
||||
// May used by:
|
||||
// .Variable
|
||||
// .Procedure
|
||||
link_name: String,
|
||||
|
||||
attributes: Array(Attribute),
|
||||
grouped_entities: Array(Entity_Index), // Procedure Groups
|
||||
where_clauses: Array(String), // Procedures
|
||||
|
||||
// Used by: .Proc_Group
|
||||
grouped_entities: Array(Entity_Index),
|
||||
// May used by: .Procedure
|
||||
where_clauses: Array(String),
|
||||
}
|
||||
|
||||
Attribute :: struct {
|
||||
@@ -159,21 +171,70 @@ Type_Kind :: enum u32le {
|
||||
Type_Elems_Cap :: 4;
|
||||
|
||||
Type :: struct {
|
||||
kind: Type_Kind,
|
||||
flags: u32le, // Type_Kind specific
|
||||
name: String,
|
||||
kind: Type_Kind,
|
||||
// Type_Kind specific used by some types
|
||||
// Underlying flag types:
|
||||
// .Basic - Type_Flags_Basic
|
||||
// .Struct - Type_Flags_Struct
|
||||
// .Union - Type_Flags_Union
|
||||
// .Proc - Type_Flags_Proc
|
||||
// .Bit_Set - Type_Flags_Bit_Set
|
||||
flags: u32le,
|
||||
|
||||
// Used by:
|
||||
// .Basic
|
||||
// .Named
|
||||
// .Generic
|
||||
name: String,
|
||||
|
||||
// Used By: .Struct, .Union
|
||||
custom_align: String,
|
||||
|
||||
// Used by some types
|
||||
// Used by:
|
||||
// .Array - 1 count: 0=len
|
||||
// .Enumerated_Array - 1 count: 0=len
|
||||
// .SOA_Struct_Fixed - 1 count: 0=len
|
||||
// .Bit_Set - 2 count: 0=lower, 1=upper
|
||||
// .Simd_Vector - 1 count: 0=len
|
||||
elem_count_len: u32le,
|
||||
elem_counts: [Type_Elems_Cap]i64le,
|
||||
|
||||
// Each of these is esed by some types, not all
|
||||
calling_convention: String, // Procedures
|
||||
types: Array(Type_Index),
|
||||
entities: Array(Entity_Index),
|
||||
polymorphic_params: Type_Index, // Struct, Union
|
||||
where_clauses: Array(String), // Struct, Union
|
||||
// Used by: .Procedures
|
||||
// blank implies the "odin" calling convention
|
||||
calling_convention: String,
|
||||
|
||||
// Used by:
|
||||
// .Named - 1 type: 0=base type
|
||||
// .Generic - <1 type: 0=specialization
|
||||
// .Pointer - 1 type: 0=element
|
||||
// .Array - 1 type: 0=element
|
||||
// .Enumerated_Array - 2 types: 0=index and 1=element
|
||||
// .Slice - 1 type: 0=element
|
||||
// .Dynamic_Array - 1 type: 0=element
|
||||
// .Map - 2 types: 0=key, 1=value
|
||||
// .SOA_Struct_Fixed - 1 type: underlying SOA struct element
|
||||
// .SOA_Struct_Slice - 1 type: underlying SOA struct element
|
||||
// .SOA_Struct_Dynamic - 1 type: underlying SOA struct element
|
||||
// .Union - 0+ types: variants
|
||||
// .Enum - <1 type: 0=base type
|
||||
// .Proc - 2 types: 0=parameters, 1=results
|
||||
// .Bit_Set - <=2 types: 0=element type, 1=underlying type (Underlying_Type flag will be set)
|
||||
// .Simd_Vector - 1 type: 0=element
|
||||
// .Relative_Pointer - 2 types: 0=pointer type, 1=base integer
|
||||
// .Relative_Slice - 2 types: 0=slice type, 1=base integer
|
||||
types: Array(Type_Index),
|
||||
|
||||
// Used by:
|
||||
// .Named - 1 field for the definition
|
||||
// .Struct - fields
|
||||
// .Enum - fields
|
||||
// .Tuple - parameters (procedures only)
|
||||
entities: Array(Entity_Index),
|
||||
|
||||
// Used By: .Struct, .Union
|
||||
polymorphic_params: Type_Index,
|
||||
// Used By: .Struct, .Union
|
||||
where_clauses: Array(String),
|
||||
}
|
||||
|
||||
Type_Flags_Basic :: distinct bit_set[Type_Flag_Basic; u32le];
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package odin_format
|
||||
|
||||
import "core:odin/printer"
|
||||
import "core:odin/parser"
|
||||
import "core:odin/ast"
|
||||
|
||||
default_style := printer.default_style;
|
||||
|
||||
simplify :: proc(file: ^ast.File) {
|
||||
|
||||
}
|
||||
|
||||
format :: proc(filepath: string, source: string, config: printer.Config, parser_flags := parser.Flags{}, allocator := context.allocator) -> (string, bool) {
|
||||
config := config;
|
||||
|
||||
pkg := ast.Package {
|
||||
kind = .Normal,
|
||||
};
|
||||
|
||||
file := ast.File {
|
||||
pkg = &pkg,
|
||||
src = source,
|
||||
fullpath = filepath,
|
||||
};
|
||||
|
||||
config.newline_limit = clamp(config.newline_limit, 0, 16);
|
||||
config.spaces = clamp(config.spaces, 1, 16);
|
||||
config.align_length_break = clamp(config.align_length_break, 0, 64);
|
||||
|
||||
p := parser.default_parser(parser_flags);
|
||||
|
||||
ok := parser.parse_file(&p, &file);
|
||||
|
||||
if !ok || file.syntax_error_count > 0 {
|
||||
return {}, false;
|
||||
}
|
||||
|
||||
prnt := printer.make_printer(config, allocator);
|
||||
|
||||
return printer.print(&prnt, &file), true;
|
||||
}
|
||||
@@ -39,7 +39,7 @@ collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
|
||||
}
|
||||
file := ast.new(ast.File, NO_POS, NO_POS);
|
||||
file.pkg = pkg;
|
||||
file.src = src;
|
||||
file.src = string(src);
|
||||
file.fullpath = fullpath;
|
||||
pkg.files[fullpath] = file;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,21 @@ import "core:fmt"
|
||||
Warning_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any);
|
||||
Error_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any);
|
||||
|
||||
Flag :: enum u32 {
|
||||
Optional_Semicolons,
|
||||
}
|
||||
|
||||
Flags :: distinct bit_set[Flag; u32];
|
||||
|
||||
|
||||
Parser :: struct {
|
||||
file: ^ast.File,
|
||||
tok: tokenizer.Tokenizer,
|
||||
|
||||
// If .Optional_Semicolons is true, semicolons are completely as statement terminators
|
||||
// different to .Insert_Semicolon in tok.flags
|
||||
flags: Flags,
|
||||
|
||||
warn: Warning_Handler,
|
||||
err: Error_Handler,
|
||||
|
||||
@@ -100,8 +111,9 @@ end_pos :: proc(tok: tokenizer.Token) -> tokenizer.Pos {
|
||||
return pos;
|
||||
}
|
||||
|
||||
default_parser :: proc() -> Parser {
|
||||
default_parser :: proc(flags := Flags{}) -> Parser {
|
||||
return Parser {
|
||||
flags = flags,
|
||||
err = default_error_handler,
|
||||
warn = default_warning_handler,
|
||||
};
|
||||
@@ -128,6 +140,10 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
|
||||
p.line_comment = nil;
|
||||
}
|
||||
|
||||
if .Optional_Semicolons in p.flags {
|
||||
p.tok.flags += {.Insert_Semicolon};
|
||||
}
|
||||
|
||||
p.file = file;
|
||||
tokenizer.init(&p.tok, file.src, file.fullpath, p.err);
|
||||
if p.tok.ch <= 0 {
|
||||
@@ -400,6 +416,11 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
|
||||
if node == nil {
|
||||
return false;
|
||||
}
|
||||
|
||||
if .Optional_Semicolons in p.flags {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch n in node.derived {
|
||||
case ast.Empty_Stmt, ast.Block_Stmt:
|
||||
return true;
|
||||
@@ -439,14 +460,34 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
expect_semicolon_newline_error :: proc(p: ^Parser, token: tokenizer.Token, s: ^ast.Node) {
|
||||
if .Optional_Semicolons not_in p.flags && .Insert_Semicolon in p.tok.flags && token.text == "\n" {
|
||||
#partial switch token.kind {
|
||||
case .Close_Brace:
|
||||
case .Close_Paren:
|
||||
case .Else:
|
||||
return;
|
||||
}
|
||||
if is_semicolon_optional_for_node(p, s) {
|
||||
return;
|
||||
}
|
||||
|
||||
tok := token;
|
||||
tok.pos.column -= 1;
|
||||
error(p, tok.pos, "expected ';', got newline");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
|
||||
if allow_token(p, .Semicolon) {
|
||||
expect_semicolon_newline_error(p, p.prev_tok, node);
|
||||
return true;
|
||||
}
|
||||
|
||||
prev := p.prev_tok;
|
||||
if prev.kind == .Semicolon {
|
||||
expect_semicolon_newline_error(p, p.prev_tok, node);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -615,7 +656,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
|
||||
cond = parse_expr(p, false);
|
||||
} else {
|
||||
init = parse_simple_stmt(p, nil);
|
||||
if allow_token(p, .Semicolon) {
|
||||
if parse_control_statement_semicolon_separator(p) {
|
||||
cond = parse_expr(p, false);
|
||||
} else {
|
||||
cond = convert_stmt_to_expr(p, init, "boolean expression");
|
||||
@@ -668,6 +709,18 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
|
||||
return if_stmt;
|
||||
}
|
||||
|
||||
parse_control_statement_semicolon_separator :: proc(p: ^Parser) -> bool {
|
||||
tok := peek_token(p);
|
||||
if tok.kind != .Open_Brace {
|
||||
return allow_token(p, .Semicolon);
|
||||
}
|
||||
if tok.text == ";" {
|
||||
return allow_token(p, .Semicolon);
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
||||
if p.curr_proc == nil {
|
||||
error(p, p.curr_tok.pos, "you cannot use a for statement in the file scope");
|
||||
@@ -716,7 +769,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
||||
}
|
||||
}
|
||||
|
||||
if !is_range && allow_token(p, .Semicolon) {
|
||||
if !is_range && parse_control_statement_semicolon_separator(p) {
|
||||
init = cond;
|
||||
cond = nil;
|
||||
if p.curr_tok.kind != .Semicolon {
|
||||
@@ -820,7 +873,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
||||
tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In});
|
||||
if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == .In {
|
||||
is_type_switch = true;
|
||||
} else if allow_token(p, .Semicolon) {
|
||||
} else if parse_control_statement_semicolon_separator(p) {
|
||||
init = tag;
|
||||
tag = nil;
|
||||
if p.curr_tok.kind != .Open_Brace {
|
||||
@@ -831,6 +884,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
||||
}
|
||||
|
||||
|
||||
skip_possible_newline(p);
|
||||
open := expect_token(p, .Open_Brace);
|
||||
|
||||
for p.curr_tok.kind == .Case {
|
||||
@@ -958,6 +1012,7 @@ parse_foreign_block :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Foreign_Bl
|
||||
defer p.in_foreign_block = prev_in_foreign_block;
|
||||
p.in_foreign_block = true;
|
||||
|
||||
skip_possible_newline_for_literal(p);
|
||||
open := expect_token(p, .Open_Brace);
|
||||
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
|
||||
decl := parse_foreign_block_decl(p);
|
||||
@@ -1287,7 +1342,7 @@ token_precedence :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> int {
|
||||
#partial switch kind {
|
||||
case .Question, .If, .When:
|
||||
return 1;
|
||||
case .Ellipsis, .Range_Half:
|
||||
case .Ellipsis, .Range_Half, .Range_Full:
|
||||
if !p.allow_range {
|
||||
return 0;
|
||||
}
|
||||
@@ -1884,24 +1939,12 @@ parse_results :: proc(p: ^Parser) -> (list: ^ast.Field_List, diverging: bool) {
|
||||
|
||||
string_to_calling_convention :: proc(s: string) -> ast.Proc_Calling_Convention {
|
||||
if s[0] != '"' && s[0] != '`' {
|
||||
return .Invalid;
|
||||
return nil;
|
||||
}
|
||||
switch s[1:len(s)-1] {
|
||||
case "odin":
|
||||
return .Odin;
|
||||
case "contextless":
|
||||
return .Contextless;
|
||||
case "cdecl", "c":
|
||||
return .C_Decl;
|
||||
case "stdcall", "std":
|
||||
return .Std_Call;
|
||||
case "fast", "fastcall":
|
||||
return .Fast_Call;
|
||||
|
||||
case "none":
|
||||
return .None;
|
||||
if len(s) == 2 {
|
||||
return nil;
|
||||
}
|
||||
return .Invalid;
|
||||
return s;
|
||||
}
|
||||
|
||||
parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) {
|
||||
@@ -1926,21 +1969,17 @@ parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) {
|
||||
}
|
||||
|
||||
parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type {
|
||||
cc := ast.Proc_Calling_Convention.Invalid;
|
||||
cc: ast.Proc_Calling_Convention;
|
||||
if p.curr_tok.kind == .String {
|
||||
str := expect_token(p, .String);
|
||||
cc = string_to_calling_convention(str.text);
|
||||
if cc == ast.Proc_Calling_Convention.Invalid {
|
||||
if cc == nil {
|
||||
error(p, str.pos, "unknown calling convention '%s'", str.text);
|
||||
}
|
||||
}
|
||||
|
||||
if cc == ast.Proc_Calling_Convention.Invalid {
|
||||
if p.in_foreign_block {
|
||||
cc = ast.Proc_Calling_Convention.Foreign_Block_Default;
|
||||
} else {
|
||||
cc = ast.Proc_Calling_Convention.Odin;
|
||||
}
|
||||
if cc == nil && p.in_foreign_block {
|
||||
cc = .Foreign_Block_Default;
|
||||
}
|
||||
|
||||
expect_token(p, .Open_Paren);
|
||||
@@ -1976,23 +2015,6 @@ parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type {
|
||||
return pt;
|
||||
}
|
||||
|
||||
check_poly_params_for_type :: proc(p: ^Parser, poly_params: ^ast.Field_List, tok: tokenizer.Token) {
|
||||
if poly_params == nil {
|
||||
return;
|
||||
}
|
||||
for field in poly_params.list {
|
||||
for name in field.names {
|
||||
if name == nil {
|
||||
continue;
|
||||
}
|
||||
if _, ok := name.derived.(ast.Poly_Type); ok {
|
||||
error(p, name.pos, "polymorphic names are not needed for %s parameters", tok.text);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^ast.Expr {
|
||||
expr := parse_unary_expr(p, lhs);
|
||||
|
||||
@@ -2224,6 +2246,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
p.expr_level = -1;
|
||||
where_clauses = parse_rhs_expr_list(p);
|
||||
p.expr_level = prev_level;
|
||||
tags = parse_proc_tags(p);
|
||||
}
|
||||
if p.allow_type && p.expr_level < 0 {
|
||||
if where_token.kind != .Invalid {
|
||||
@@ -2233,6 +2256,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
}
|
||||
body: ^ast.Stmt;
|
||||
|
||||
skip_possible_newline_for_literal(p);
|
||||
|
||||
if allow_token(p, .Undef) {
|
||||
body = nil;
|
||||
if where_token.kind != .Invalid {
|
||||
@@ -2358,7 +2383,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
poly_params = nil;
|
||||
}
|
||||
expect_token_after(p, .Close_Paren, "parameter list");
|
||||
check_poly_params_for_type(p, poly_params, tok);
|
||||
}
|
||||
|
||||
prev_level := p.expr_level;
|
||||
@@ -2405,6 +2429,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
p.expr_level = where_prev_level;
|
||||
}
|
||||
|
||||
skip_possible_newline_for_literal(p);
|
||||
expect_token(p, .Open_Brace);
|
||||
fields, name_count = parse_field_list(p, .Close_Brace, ast.Field_Flags_Struct);
|
||||
close := expect_token(p, .Close_Brace);
|
||||
@@ -2434,7 +2459,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
poly_params = nil;
|
||||
}
|
||||
expect_token_after(p, .Close_Paren, "parameter list");
|
||||
check_poly_params_for_type(p, poly_params, tok);
|
||||
}
|
||||
|
||||
prev_level := p.expr_level;
|
||||
@@ -2473,6 +2497,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
|
||||
variants: [dynamic]^ast.Expr;
|
||||
|
||||
skip_possible_newline_for_literal(p);
|
||||
expect_token_after(p, .Open_Brace, "union");
|
||||
|
||||
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
|
||||
@@ -2503,6 +2528,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
if p.curr_tok.kind != .Open_Brace {
|
||||
base_type = parse_type(p);
|
||||
}
|
||||
|
||||
skip_possible_newline_for_literal(p);
|
||||
open := expect_token(p, .Open_Brace);
|
||||
fields := parse_elem_list(p);
|
||||
close := expect_token(p, .Close_Brace);
|
||||
@@ -2601,6 +2628,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
}
|
||||
}
|
||||
|
||||
skip_possible_newline_for_literal(p);
|
||||
open := expect_token(p, .Open_Brace);
|
||||
asm_string := parse_expr(p, false);
|
||||
expect_token(p, .Comma);
|
||||
@@ -2811,7 +2839,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
|
||||
open := expect_token(p, .Open_Bracket);
|
||||
|
||||
#partial switch p.curr_tok.kind {
|
||||
case .Colon, .Ellipsis, .Range_Half:
|
||||
case .Colon, .Ellipsis, .Range_Half, .Range_Full:
|
||||
// NOTE(bill): Do not err yet
|
||||
break;
|
||||
case:
|
||||
@@ -2819,7 +2847,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
|
||||
}
|
||||
|
||||
#partial switch p.curr_tok.kind {
|
||||
case .Ellipsis, .Range_Half:
|
||||
case .Ellipsis, .Range_Half, .Range_Full:
|
||||
error(p, p.curr_tok.pos, "expected a colon, not a range");
|
||||
fallthrough;
|
||||
case .Colon:
|
||||
@@ -3150,6 +3178,7 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
|
||||
case ast.For_Stmt: n.label = label;
|
||||
case ast.Switch_Stmt: n.label = label;
|
||||
case ast.Type_Switch_Stmt: n.label = label;
|
||||
case ast.Range_Stmt: n.label = label;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,924 @@
|
||||
package odin_printer
|
||||
|
||||
import "core:odin/ast"
|
||||
import "core:odin/tokenizer"
|
||||
import "core:strings"
|
||||
import "core:runtime"
|
||||
import "core:fmt"
|
||||
import "core:unicode/utf8"
|
||||
import "core:mem"
|
||||
|
||||
Type_Enum :: enum {Line_Comment, Value_Decl, Switch_Stmt, Struct, Assign, Call, Enum, If, For, Proc_Lit};
|
||||
|
||||
Line_Type :: bit_set[Type_Enum];
|
||||
|
||||
/*
|
||||
Represents an unwrapped line
|
||||
*/
|
||||
Line :: struct {
|
||||
format_tokens: [dynamic]Format_Token,
|
||||
finalized: bool,
|
||||
used: bool,
|
||||
depth: int,
|
||||
types: Line_Type, //for performance, so you don't have to verify what types are in it by going through the tokens - might give problems when adding linebreaking
|
||||
}
|
||||
|
||||
/*
|
||||
Represents a singular token in a unwrapped line
|
||||
*/
|
||||
Format_Token :: struct {
|
||||
kind: tokenizer.Token_Kind,
|
||||
text: string,
|
||||
type: Type_Enum,
|
||||
spaces_before: int,
|
||||
parameter_count: int,
|
||||
}
|
||||
|
||||
Printer :: struct {
|
||||
string_builder: strings.Builder,
|
||||
config: Config,
|
||||
depth: int, //the identation depth
|
||||
comments: [dynamic]^ast.Comment_Group,
|
||||
latest_comment_index: int,
|
||||
allocator: mem.Allocator,
|
||||
file: ^ast.File,
|
||||
source_position: tokenizer.Pos,
|
||||
last_source_position: tokenizer.Pos,
|
||||
lines: [dynamic]Line, //need to look into a better data structure, one that can handle inserting lines rather than appending
|
||||
skip_semicolon: bool,
|
||||
current_line: ^Line,
|
||||
current_line_index: int,
|
||||
last_line_index: int,
|
||||
last_token: ^Format_Token,
|
||||
merge_next_token: bool,
|
||||
space_next_token: bool,
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
Config :: struct {
|
||||
spaces: int, //Spaces per indentation
|
||||
newline_limit: int, //The limit of newlines between statements and declarations.
|
||||
tabs: bool, //Enable or disable tabs
|
||||
convert_do: bool, //Convert all do statements to brace blocks
|
||||
semicolons: bool, //Enable semicolons
|
||||
split_multiple_stmts: bool,
|
||||
align_switch: bool,
|
||||
brace_style: Brace_Style,
|
||||
align_assignments: bool,
|
||||
align_structs: bool,
|
||||
align_style: Alignment_Style,
|
||||
align_enums: bool,
|
||||
align_length_break: int,
|
||||
indent_cases: bool,
|
||||
newline_style: Newline_Style,
|
||||
}
|
||||
|
||||
Brace_Style :: enum {
|
||||
_1TBS,
|
||||
Allman,
|
||||
Stroustrup,
|
||||
K_And_R,
|
||||
}
|
||||
|
||||
Block_Type :: enum {
|
||||
None,
|
||||
If_Stmt,
|
||||
Proc,
|
||||
Generic,
|
||||
Comp_Lit,
|
||||
Switch_Stmt,
|
||||
}
|
||||
|
||||
Alignment_Style :: enum {
|
||||
Align_On_Type_And_Equals,
|
||||
Align_On_Colon_And_Equals,
|
||||
}
|
||||
|
||||
Newline_Style :: enum {
|
||||
CRLF,
|
||||
LF,
|
||||
}
|
||||
|
||||
default_style := Config {
|
||||
spaces = 4,
|
||||
newline_limit = 2,
|
||||
convert_do = false,
|
||||
semicolons = true,
|
||||
tabs = true,
|
||||
brace_style = ._1TBS,
|
||||
split_multiple_stmts = true,
|
||||
align_assignments = true,
|
||||
align_style = .Align_On_Type_And_Equals,
|
||||
indent_cases = false,
|
||||
align_switch = true,
|
||||
align_structs = true,
|
||||
align_enums = true,
|
||||
newline_style = .CRLF,
|
||||
align_length_break = 9,
|
||||
};
|
||||
|
||||
make_printer :: proc(config: Config, allocator := context.allocator) -> Printer {
|
||||
return {
|
||||
config = config,
|
||||
allocator = allocator,
|
||||
debug = false,
|
||||
};
|
||||
}
|
||||
|
||||
print :: proc(p: ^Printer, file: ^ast.File) -> string {
|
||||
p.comments = file.comments;
|
||||
|
||||
if len(file.decls) > 0 {
|
||||
p.lines = make([dynamic]Line, 0, (file.decls[len(file.decls) - 1].end.line - file.decls[0].pos.line) * 2, context.temp_allocator);
|
||||
}
|
||||
|
||||
set_source_position(p, file.pkg_token.pos);
|
||||
|
||||
p.last_source_position.line = 1;
|
||||
|
||||
set_line(p, 0);
|
||||
|
||||
push_generic_token(p, .Package, 0);
|
||||
push_ident_token(p, file.pkg_name, 1);
|
||||
|
||||
for decl in file.decls {
|
||||
visit_decl(p, cast(^ast.Decl)decl);
|
||||
}
|
||||
|
||||
if len(p.comments) > 0 {
|
||||
infinite := p.comments[len(p.comments) - 1].end;
|
||||
infinite.offset = 9999999;
|
||||
push_comments(p, infinite);
|
||||
}
|
||||
|
||||
fix_lines(p);
|
||||
|
||||
builder := strings.make_builder(0, mem.megabytes(5), p.allocator);
|
||||
|
||||
last_line := 0;
|
||||
|
||||
newline: string;
|
||||
|
||||
if p.config.newline_style == .LF {
|
||||
newline = "\n";
|
||||
} else {
|
||||
newline = "\r\n";
|
||||
}
|
||||
|
||||
for line, line_index in p.lines {
|
||||
diff_line := line_index - last_line;
|
||||
|
||||
for i := 0; i < diff_line; i += 1 {
|
||||
strings.write_string(&builder, newline);
|
||||
}
|
||||
|
||||
if p.config.tabs {
|
||||
for i := 0; i < line.depth; i += 1 {
|
||||
strings.write_byte(&builder, '\t');
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < line.depth * p.config.spaces; i += 1 {
|
||||
strings.write_byte(&builder, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
if p.debug {
|
||||
strings.write_string(&builder, fmt.tprintf("line %v: ", line_index));
|
||||
}
|
||||
|
||||
for format_token in line.format_tokens {
|
||||
|
||||
for i := 0; i < format_token.spaces_before; i += 1 {
|
||||
strings.write_byte(&builder, ' ');
|
||||
}
|
||||
|
||||
strings.write_string(&builder, format_token.text);
|
||||
}
|
||||
|
||||
last_line = line_index;
|
||||
}
|
||||
|
||||
strings.write_string(&builder, newline);
|
||||
|
||||
return strings.to_string(builder);
|
||||
}
|
||||
|
||||
fix_lines :: proc(p: ^Printer) {
|
||||
align_var_decls(p);
|
||||
format_generic(p);
|
||||
align_comments(p); //align them last since they rely on the other alignments
|
||||
}
|
||||
|
||||
format_value_decl :: proc(p: ^Printer, index: int) {
|
||||
|
||||
eq_found := false;
|
||||
eq_token: Format_Token;
|
||||
eq_line: int;
|
||||
largest := 0;
|
||||
|
||||
found_eq: for line, line_index in p.lines[index:] {
|
||||
for format_token in line.format_tokens {
|
||||
|
||||
largest += len(format_token.text) + format_token.spaces_before;
|
||||
|
||||
if format_token.kind == .Eq {
|
||||
eq_token = format_token;
|
||||
eq_line = line_index + index;
|
||||
eq_found = true;
|
||||
break found_eq;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !eq_found {
|
||||
return;
|
||||
}
|
||||
|
||||
align_next := false;
|
||||
|
||||
//check to see if there is a binary operator in the last token(this is guaranteed by the ast visit), otherwise it's not multilined
|
||||
for line, line_index in p.lines[eq_line:] {
|
||||
|
||||
if len(line.format_tokens) == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
if align_next {
|
||||
line.format_tokens[0].spaces_before = largest + 1;
|
||||
align_next = false;
|
||||
}
|
||||
|
||||
kind := find_last_token(line.format_tokens).kind;
|
||||
|
||||
if tokenizer.Token_Kind.B_Operator_Begin < kind && kind <= tokenizer.Token_Kind.Cmp_Or {
|
||||
align_next = true;
|
||||
}
|
||||
|
||||
if !align_next {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
find_last_token :: proc(format_tokens: [dynamic]Format_Token) -> Format_Token {
|
||||
|
||||
for i := len(format_tokens) - 1; i >= 0; i -= 1 {
|
||||
|
||||
if format_tokens[i].kind != .Comment {
|
||||
return format_tokens[i];
|
||||
}
|
||||
}
|
||||
|
||||
panic("not possible");
|
||||
}
|
||||
|
||||
format_assignment :: proc(p: ^Printer, index: int) {
|
||||
}
|
||||
|
||||
format_call :: proc(p: ^Printer, line_index: int, format_index: int) {
|
||||
|
||||
paren_found := false;
|
||||
paren_token: Format_Token;
|
||||
paren_line: int;
|
||||
paren_token_index: int;
|
||||
largest := 0;
|
||||
|
||||
found_paren: for line, i in p.lines[line_index:] {
|
||||
for format_token, j in line.format_tokens {
|
||||
|
||||
largest += len(format_token.text) + format_token.spaces_before;
|
||||
|
||||
if i == 0 && j < format_index {
|
||||
continue;
|
||||
}
|
||||
|
||||
if format_token.kind == .Open_Paren && format_token.type == .Call {
|
||||
paren_token = format_token;
|
||||
paren_line = line_index + i;
|
||||
paren_found = true;
|
||||
paren_token_index = j;
|
||||
break found_paren;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !paren_found {
|
||||
panic("Should not be possible");
|
||||
}
|
||||
|
||||
paren_count := 1;
|
||||
done := false;
|
||||
|
||||
for line, line_index in p.lines[paren_line:] {
|
||||
|
||||
if len(line.format_tokens) == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
for format_token, i in line.format_tokens {
|
||||
|
||||
if format_token.kind == .Comment {
|
||||
continue;
|
||||
}
|
||||
|
||||
if line_index == 0 && i <= paren_token_index {
|
||||
continue;
|
||||
}
|
||||
|
||||
if format_token.kind == .Open_Paren {
|
||||
paren_count += 1;
|
||||
} else if format_token.kind == .Close_Paren {
|
||||
paren_count -= 1;
|
||||
}
|
||||
|
||||
if paren_count == 0 {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if line_index != 0 {
|
||||
line.format_tokens[0].spaces_before = largest;
|
||||
}
|
||||
|
||||
if done {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, keyword: tokenizer.Token_Kind) {
|
||||
|
||||
keyword_found := false;
|
||||
keyword_token: Format_Token;
|
||||
keyword_line: int;
|
||||
|
||||
largest := 0;
|
||||
brace_count := 0;
|
||||
done := false;
|
||||
|
||||
found_keyword: for line, i in p.lines[line_index:] {
|
||||
for format_token in line.format_tokens {
|
||||
|
||||
largest += len(format_token.text) + format_token.spaces_before;
|
||||
|
||||
if format_token.kind == keyword {
|
||||
keyword_token = format_token;
|
||||
keyword_line = line_index + i;
|
||||
keyword_found = true;
|
||||
break found_keyword;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !keyword_found {
|
||||
panic("Should not be possible");
|
||||
}
|
||||
|
||||
for line, line_index in p.lines[keyword_line:] {
|
||||
|
||||
if len(line.format_tokens) == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
for format_token, i in line.format_tokens {
|
||||
|
||||
if format_token.kind == .Comment {
|
||||
break;
|
||||
} else if format_token.kind == .Undef {
|
||||
return;
|
||||
}
|
||||
|
||||
if line_index == 0 && i <= format_index {
|
||||
continue;
|
||||
}
|
||||
|
||||
if format_token.kind == .Open_Brace {
|
||||
brace_count += 1;
|
||||
} else if format_token.kind == .Close_Brace {
|
||||
brace_count -= 1;
|
||||
}
|
||||
|
||||
if brace_count == 1 {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
if line_index != 0 {
|
||||
line.format_tokens[0].spaces_before = largest + 1;
|
||||
}
|
||||
|
||||
if done {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
format_generic :: proc(p: ^Printer) {
|
||||
next_struct_line := 0;
|
||||
|
||||
for line, line_index in p.lines {
|
||||
|
||||
if len(line.format_tokens) <= 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
for format_token, token_index in line.format_tokens {
|
||||
#partial switch format_token.kind {
|
||||
case .For, .If, .When, .Switch:
|
||||
format_keyword_to_brace(p, line_index, token_index, format_token.kind);
|
||||
case .Proc:
|
||||
if format_token.type == .Proc_Lit {
|
||||
format_keyword_to_brace(p, line_index, token_index, format_token.kind);
|
||||
}
|
||||
case:
|
||||
if format_token.type == .Call {
|
||||
format_call(p, line_index, token_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if .Switch_Stmt in line.types && p.config.align_switch {
|
||||
align_switch_stmt(p, line_index);
|
||||
}
|
||||
|
||||
if .Enum in line.types && p.config.align_enums {
|
||||
align_enum(p, line_index);
|
||||
}
|
||||
|
||||
if .Struct in line.types && p.config.align_structs && next_struct_line <= 0 {
|
||||
next_struct_line = align_struct(p, line_index);
|
||||
}
|
||||
|
||||
if .Value_Decl in line.types {
|
||||
format_value_decl(p, line_index);
|
||||
}
|
||||
|
||||
if .Assign in line.types {
|
||||
format_assignment(p, line_index);
|
||||
}
|
||||
|
||||
next_struct_line -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
align_var_decls :: proc(p: ^Printer) {
|
||||
|
||||
current_line: int;
|
||||
current_typed: bool;
|
||||
current_not_mutable: bool;
|
||||
|
||||
largest_lhs := 0;
|
||||
largest_rhs := 0;
|
||||
|
||||
TokenAndLength :: struct {
|
||||
format_token: ^Format_Token,
|
||||
length: int,
|
||||
};
|
||||
|
||||
colon_tokens := make([dynamic]TokenAndLength, 0, 10, context.temp_allocator);
|
||||
type_tokens := make([dynamic]TokenAndLength, 0, 10, context.temp_allocator);
|
||||
equal_tokens := make([dynamic]TokenAndLength, 0, 10, context.temp_allocator);
|
||||
|
||||
for line, line_index in p.lines {
|
||||
|
||||
//It is only possible to align value decls that are one one line, otherwise just ignore them
|
||||
if .Value_Decl not_in line.types {
|
||||
continue;
|
||||
}
|
||||
|
||||
typed := true;
|
||||
not_mutable := false;
|
||||
continue_flag := false;
|
||||
|
||||
for i := 0; i < len(line.format_tokens); i += 1 {
|
||||
if line.format_tokens[i].kind == .Colon && line.format_tokens[min(i + 1, len(line.format_tokens) - 1)].kind == .Eq {
|
||||
typed = false;
|
||||
}
|
||||
|
||||
if line.format_tokens[i].kind == .Colon && line.format_tokens[min(i + 1, len(line.format_tokens) - 1)].kind == .Colon {
|
||||
not_mutable = true;
|
||||
}
|
||||
|
||||
if line.format_tokens[i].kind == .Union ||
|
||||
line.format_tokens[i].kind == .Enum ||
|
||||
line.format_tokens[i].kind == .Struct ||
|
||||
line.format_tokens[i].kind == .For ||
|
||||
line.format_tokens[i].kind == .If ||
|
||||
line.format_tokens[i].kind == .Comment {
|
||||
continue_flag = true;
|
||||
}
|
||||
|
||||
//enforced undef is always on the last line, if it exists
|
||||
if line.format_tokens[i].kind == .Proc && line.format_tokens[len(line.format_tokens)-1].kind != .Undef {
|
||||
continue_flag = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if continue_flag {
|
||||
continue;
|
||||
}
|
||||
|
||||
if line_index != current_line + 1 || typed != current_typed || not_mutable != current_not_mutable {
|
||||
|
||||
if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed || current_not_mutable {
|
||||
for colon_token in colon_tokens {
|
||||
colon_token.format_token.spaces_before = largest_lhs - colon_token.length + 1;
|
||||
}
|
||||
} else if p.config.align_style == .Align_On_Type_And_Equals {
|
||||
for type_token in type_tokens {
|
||||
type_token.format_token.spaces_before = largest_lhs - type_token.length + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if current_typed {
|
||||
for equal_token in equal_tokens {
|
||||
equal_token.format_token.spaces_before = largest_rhs - equal_token.length + 1;
|
||||
}
|
||||
} else {
|
||||
for equal_token in equal_tokens {
|
||||
equal_token.format_token.spaces_before = 0;
|
||||
}
|
||||
}
|
||||
|
||||
clear(&colon_tokens);
|
||||
clear(&type_tokens);
|
||||
clear(&equal_tokens);
|
||||
|
||||
largest_rhs = 0;
|
||||
largest_lhs = 0;
|
||||
current_typed = typed;
|
||||
current_not_mutable = not_mutable;
|
||||
}
|
||||
|
||||
current_line = line_index;
|
||||
|
||||
current_token_index := 0;
|
||||
lhs_length := 0;
|
||||
rhs_length := 0;
|
||||
|
||||
//calcuate the length of lhs of a value decl i.e. `a, b:`
|
||||
for; current_token_index < len(line.format_tokens); current_token_index += 1 {
|
||||
|
||||
lhs_length += len(line.format_tokens[current_token_index].text) + line.format_tokens[current_token_index].spaces_before;
|
||||
|
||||
if line.format_tokens[current_token_index].kind == .Colon {
|
||||
append(&colon_tokens, TokenAndLength {format_token = &line.format_tokens[current_token_index], length = lhs_length});
|
||||
|
||||
if len(line.format_tokens) > current_token_index && line.format_tokens[current_token_index + 1].kind != .Eq {
|
||||
append(&type_tokens, TokenAndLength {format_token = &line.format_tokens[current_token_index + 1], length = lhs_length});
|
||||
}
|
||||
|
||||
current_token_index += 1;
|
||||
largest_lhs = max(largest_lhs, lhs_length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//calcuate the length of the rhs i.e. `[dynamic]int = 123123`
|
||||
for; current_token_index < len(line.format_tokens); current_token_index += 1 {
|
||||
|
||||
rhs_length += len(line.format_tokens[current_token_index].text) + line.format_tokens[current_token_index].spaces_before;
|
||||
|
||||
if line.format_tokens[current_token_index].kind == .Eq {
|
||||
append(&equal_tokens, TokenAndLength {format_token = &line.format_tokens[current_token_index], length = rhs_length});
|
||||
largest_rhs = max(largest_rhs, rhs_length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//repeating myself, move to sub procedure
|
||||
if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed || current_not_mutable {
|
||||
for colon_token in colon_tokens {
|
||||
colon_token.format_token.spaces_before = largest_lhs - colon_token.length + 1;
|
||||
}
|
||||
} else if p.config.align_style == .Align_On_Type_And_Equals {
|
||||
for type_token in type_tokens {
|
||||
type_token.format_token.spaces_before = largest_lhs - type_token.length + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if current_typed {
|
||||
for equal_token in equal_tokens {
|
||||
equal_token.format_token.spaces_before = largest_rhs - equal_token.length + 1;
|
||||
}
|
||||
} else {
|
||||
for equal_token in equal_tokens {
|
||||
equal_token.format_token.spaces_before = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
align_switch_stmt :: proc(p: ^Printer, index: int) {
|
||||
switch_found := false;
|
||||
brace_token: Format_Token;
|
||||
brace_line: int;
|
||||
|
||||
found_switch_brace: for line, line_index in p.lines[index:] {
|
||||
for format_token in line.format_tokens {
|
||||
if format_token.kind == .Open_Brace && switch_found {
|
||||
brace_token = format_token;
|
||||
brace_line = line_index + index;
|
||||
break found_switch_brace;
|
||||
} else if format_token.kind == .Open_Brace {
|
||||
break;
|
||||
} else if format_token.kind == .Switch {
|
||||
switch_found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !switch_found {
|
||||
return;
|
||||
}
|
||||
|
||||
largest := 0;
|
||||
case_count := 0;
|
||||
|
||||
TokenAndLength :: struct {
|
||||
format_token: ^Format_Token,
|
||||
length: int,
|
||||
};
|
||||
|
||||
format_tokens := make([dynamic]TokenAndLength, 0, brace_token.parameter_count, context.temp_allocator);
|
||||
|
||||
//find all the switch cases that are one lined
|
||||
for line, line_index in p.lines[brace_line + 1:] {
|
||||
|
||||
case_found := false;
|
||||
colon_found := false;
|
||||
length := 0;
|
||||
|
||||
for format_token, i in line.format_tokens {
|
||||
|
||||
if format_token.kind == .Comment {
|
||||
break;
|
||||
}
|
||||
|
||||
//this will only happen if the case is one lined
|
||||
if case_found && colon_found {
|
||||
append(&format_tokens, TokenAndLength {format_token = &line.format_tokens[i], length = length});
|
||||
largest = max(length, largest);
|
||||
break;
|
||||
}
|
||||
|
||||
if format_token.kind == .Case {
|
||||
case_found = true;
|
||||
case_count += 1;
|
||||
} else if format_token.kind == .Colon {
|
||||
colon_found = true;
|
||||
}
|
||||
|
||||
length += len(format_token.text) + format_token.spaces_before;
|
||||
}
|
||||
|
||||
if case_count >= brace_token.parameter_count {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for token in format_tokens {
|
||||
token.format_token.spaces_before = largest - token.length + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
align_enum :: proc(p: ^Printer, index: int) {
|
||||
enum_found := false;
|
||||
brace_token: Format_Token;
|
||||
brace_line: int;
|
||||
|
||||
found_enum_brace: for line, line_index in p.lines[index:] {
|
||||
for format_token in line.format_tokens {
|
||||
if format_token.kind == .Open_Brace && enum_found {
|
||||
brace_token = format_token;
|
||||
brace_line = line_index + index;
|
||||
break found_enum_brace;
|
||||
} else if format_token.kind == .Open_Brace {
|
||||
break;
|
||||
} else if format_token.kind == .Enum {
|
||||
enum_found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !enum_found {
|
||||
return;
|
||||
}
|
||||
|
||||
largest := 0;
|
||||
comma_count := 0;
|
||||
|
||||
TokenAndLength :: struct {
|
||||
format_token: ^Format_Token,
|
||||
length: int,
|
||||
};
|
||||
|
||||
format_tokens := make([dynamic]TokenAndLength, 0, brace_token.parameter_count, context.temp_allocator);
|
||||
|
||||
for line, line_index in p.lines[brace_line + 1:] {
|
||||
length := 0;
|
||||
|
||||
for format_token, i in line.format_tokens {
|
||||
if format_token.kind == .Comment {
|
||||
break;
|
||||
}
|
||||
|
||||
if format_token.kind == .Eq {
|
||||
append(&format_tokens, TokenAndLength {format_token = &line.format_tokens[i], length = length});
|
||||
largest = max(length, largest);
|
||||
break;
|
||||
} else if format_token.kind == .Comma {
|
||||
comma_count += 1;
|
||||
}
|
||||
|
||||
length += len(format_token.text) + format_token.spaces_before;
|
||||
}
|
||||
|
||||
if comma_count >= brace_token.parameter_count {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for token in format_tokens {
|
||||
token.format_token.spaces_before = largest - token.length + 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
align_struct :: proc(p: ^Printer, index: int) -> int {
|
||||
struct_found := false;
|
||||
brace_token: Format_Token;
|
||||
brace_line: int;
|
||||
|
||||
found_struct_brace: for line, line_index in p.lines[index:] {
|
||||
for format_token in line.format_tokens {
|
||||
if format_token.kind == .Open_Brace && struct_found {
|
||||
brace_token = format_token;
|
||||
brace_line = line_index + index;
|
||||
break found_struct_brace;
|
||||
} else if format_token.kind == .Open_Brace {
|
||||
break;
|
||||
} else if format_token.kind == .Struct {
|
||||
struct_found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !struct_found {
|
||||
return 0;
|
||||
}
|
||||
|
||||
largest := 0;
|
||||
colon_count := 0;
|
||||
nested := false;
|
||||
seen_brace := false;
|
||||
|
||||
TokenAndLength :: struct {
|
||||
format_token: ^Format_Token,
|
||||
length: int,
|
||||
};
|
||||
|
||||
format_tokens := make([]TokenAndLength, brace_token.parameter_count, context.temp_allocator);
|
||||
|
||||
if brace_token.parameter_count == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
end_line_index := 0;
|
||||
|
||||
for line, line_index in p.lines[brace_line + 1:] {
|
||||
length := 0;
|
||||
|
||||
for format_token, i in line.format_tokens {
|
||||
|
||||
//give up on nested structs
|
||||
if format_token.kind == .Comment {
|
||||
break;
|
||||
} else if format_token.kind == .Open_Paren {
|
||||
break;
|
||||
} else if format_token.kind == .Open_Brace {
|
||||
seen_brace = true;
|
||||
} else if format_token.kind == .Close_Brace {
|
||||
seen_brace = false;
|
||||
} else if seen_brace {
|
||||
continue;
|
||||
}
|
||||
|
||||
if format_token.kind == .Colon {
|
||||
format_tokens[colon_count] = {format_token = &line.format_tokens[i + 1], length = length};
|
||||
|
||||
if format_tokens[colon_count].format_token.kind == .Struct {
|
||||
nested = true;
|
||||
}
|
||||
|
||||
colon_count += 1;
|
||||
largest = max(length, largest);
|
||||
}
|
||||
|
||||
length += len(format_token.text) + format_token.spaces_before;
|
||||
}
|
||||
|
||||
if nested {
|
||||
end_line_index = line_index + brace_line + 1;
|
||||
}
|
||||
|
||||
if colon_count >= brace_token.parameter_count {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//give up aligning nested, it never looks good
|
||||
if nested {
|
||||
for line, line_index in p.lines[end_line_index:] {
|
||||
for format_token in line.format_tokens {
|
||||
if format_token.kind == .Close_Brace {
|
||||
return end_line_index + line_index - index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for token in format_tokens {
|
||||
token.format_token.spaces_before = largest - token.length + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
align_comments :: proc(p: ^Printer) {
|
||||
|
||||
Comment_Align_Info :: struct {
|
||||
length: int,
|
||||
begin: int,
|
||||
end: int,
|
||||
depth: int,
|
||||
};
|
||||
|
||||
comment_infos := make([dynamic]Comment_Align_Info, 0, context.temp_allocator);
|
||||
|
||||
current_info: Comment_Align_Info;
|
||||
|
||||
for line, line_index in p.lines {
|
||||
if len(line.format_tokens) <= 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
if .Line_Comment in line.types {
|
||||
if current_info.end + 1 != line_index || current_info.depth != line.depth ||
|
||||
(current_info.begin == current_info.end && current_info.length == 0) {
|
||||
|
||||
if (current_info.begin != 0 && current_info.end != 0) || current_info.length > 0 {
|
||||
append(&comment_infos, current_info);
|
||||
}
|
||||
|
||||
current_info.begin = line_index;
|
||||
current_info.end = line_index;
|
||||
current_info.depth = line.depth;
|
||||
current_info.length = 0;
|
||||
}
|
||||
|
||||
length := 0;
|
||||
|
||||
for format_token, i in line.format_tokens {
|
||||
if format_token.kind == .Comment {
|
||||
current_info.length = max(current_info.length, length);
|
||||
current_info.end = line_index;
|
||||
}
|
||||
|
||||
length += format_token.spaces_before + len(format_token.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (current_info.begin != 0 && current_info.end != 0) || current_info.length > 0 {
|
||||
append(&comment_infos, current_info);
|
||||
}
|
||||
|
||||
for info in comment_infos {
|
||||
|
||||
if info.begin == info.end || info.length == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
for i := info.begin; i <= info.end; i += 1 {
|
||||
l := p.lines[i];
|
||||
|
||||
length := 0;
|
||||
|
||||
for format_token, i in l.format_tokens {
|
||||
if format_token.kind == .Comment {
|
||||
if len(l.format_tokens) == 1 {
|
||||
l.format_tokens[i].spaces_before = info.length + 1;
|
||||
} else {
|
||||
l.format_tokens[i].spaces_before = info.length - length + 1;
|
||||
}
|
||||
}
|
||||
|
||||
length += format_token.spaces_before + len(format_token.text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -107,6 +107,7 @@ Token_Kind :: enum u32 {
|
||||
Comma, // ,
|
||||
Ellipsis, // ..
|
||||
Range_Half, // ..<
|
||||
Range_Full, // ..=
|
||||
Back_Slash, // \
|
||||
B_Operator_End,
|
||||
|
||||
@@ -233,6 +234,7 @@ tokens := [Token_Kind.COUNT]string {
|
||||
",",
|
||||
"..",
|
||||
"..<",
|
||||
"..=",
|
||||
"\\",
|
||||
"",
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Flags :: distinct bit_set[Flag; u32];
|
||||
Tokenizer :: struct {
|
||||
// Immutable data
|
||||
path: string,
|
||||
src: []byte,
|
||||
src: string,
|
||||
err: Error_Handler,
|
||||
|
||||
flags: Flags,
|
||||
@@ -31,7 +31,7 @@ Tokenizer :: struct {
|
||||
error_count: int,
|
||||
}
|
||||
|
||||
init :: proc(t: ^Tokenizer, src: []byte, path: string, err: Error_Handler = default_error_handler) {
|
||||
init :: proc(t: ^Tokenizer, src: string, path: string, err: Error_Handler = default_error_handler) {
|
||||
t.src = src;
|
||||
t.err = err;
|
||||
t.ch = ' ';
|
||||
@@ -87,7 +87,7 @@ advance_rune :: proc(using t: ^Tokenizer) {
|
||||
case r == 0:
|
||||
error(t, t.offset, "illegal character NUL");
|
||||
case r >= utf8.RUNE_SELF:
|
||||
r, w = utf8.decode_rune(src[read_offset:]);
|
||||
r, w = utf8.decode_rune_in_string(src[read_offset:]);
|
||||
if r == utf8.RUNE_ERROR && w == 1 {
|
||||
error(t, t.offset, "illegal UTF-8 encoding");
|
||||
} else if r == utf8.RUNE_BOM && offset > 0 {
|
||||
@@ -608,7 +608,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
||||
kind = switch3(t, .And, .And_Eq, '&', .Cmp_And);
|
||||
}
|
||||
case '|': kind = switch3(t, .Or, .Or_Eq, '|', .Cmp_Or);
|
||||
case '~': kind = .Xor;
|
||||
case '~': kind = switch2(t, .Xor, .Xor_Eq);
|
||||
case '<': kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq);
|
||||
case '>': kind = switch4(t, .Gt, .Gt_Eq, '>', .Shr,.Shr_Eq);
|
||||
|
||||
@@ -623,6 +623,9 @@ scan :: proc(t: ^Tokenizer) -> Token {
|
||||
if t.ch == '<' {
|
||||
advance_rune(t);
|
||||
kind = .Range_Half;
|
||||
} else if t.ch == '=' {
|
||||
advance_rune(t);
|
||||
kind = .Range_Full;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ is_file :: proc(path: string) -> bool {
|
||||
attribs := win32.GetFileAttributesW(wpath);
|
||||
|
||||
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == win32.FILE_ATTRIBUTE_DIRECTORY;
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -283,7 +283,7 @@ is_dir :: proc(path: string) -> bool {
|
||||
attribs := win32.GetFileAttributesW(wpath);
|
||||
|
||||
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != win32.FILE_ATTRIBUTE_DIRECTORY;
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -347,7 +347,7 @@ is_abs :: proc(path: string) -> bool {
|
||||
when ODIN_OS == "windows" {
|
||||
if len(path) > 2 {
|
||||
switch path[0] {
|
||||
case 'A'..'Z', 'a'..'z':
|
||||
case 'A'..='Z', 'a'..='z':
|
||||
return path[1] == ':' && is_path_separator(path[2]);
|
||||
}
|
||||
}
|
||||
|
||||
+18
-47
@@ -1,11 +1,8 @@
|
||||
package os2
|
||||
|
||||
Platform_Error_Min_Bits :: 32;
|
||||
import "core:io"
|
||||
|
||||
Error :: enum u64 {
|
||||
None = 0,
|
||||
|
||||
// General Errors
|
||||
General_Error :: enum u32 {
|
||||
Invalid_Argument,
|
||||
|
||||
Permission_Denied,
|
||||
@@ -13,43 +10,20 @@ Error :: enum u64 {
|
||||
Not_Exist,
|
||||
Closed,
|
||||
|
||||
// Timeout Errors
|
||||
Timeout,
|
||||
|
||||
// I/O Errors
|
||||
// EOF is the error returned by `read` when no more input is available
|
||||
EOF,
|
||||
|
||||
// Unexpected_EOF means that EOF was encountered in the middle of reading a fixed-sized block of data
|
||||
Unexpected_EOF,
|
||||
|
||||
// Short_Write means that a write accepted fewer bytes than requested but failed to return an explicit error
|
||||
Short_Write,
|
||||
|
||||
// Invalid_Write means that a write returned an impossible count
|
||||
Invalid_Write,
|
||||
|
||||
// Short_Buffer means that a read required a longer buffer than was provided
|
||||
Short_Buffer,
|
||||
|
||||
// No_Progress is returned by some implementations of `io.Reader` when many calls
|
||||
// to `read` have failed to return any data or error.
|
||||
// This is usually a signed of a broken `io.Reader` implementation
|
||||
No_Progress,
|
||||
|
||||
Invalid_Whence,
|
||||
Invalid_Offset,
|
||||
Invalid_Unread,
|
||||
|
||||
Negative_Read,
|
||||
Negative_Write,
|
||||
Negative_Count,
|
||||
Buffer_Full,
|
||||
|
||||
// Platform Specific Errors
|
||||
Platform_Minimum = 1<<Platform_Error_Min_Bits,
|
||||
}
|
||||
|
||||
Platform_Error :: struct {
|
||||
err: i32,
|
||||
}
|
||||
|
||||
Error :: union {
|
||||
General_Error,
|
||||
io.Error,
|
||||
Platform_Error,
|
||||
}
|
||||
#assert(size_of(Error) == size_of(u64));
|
||||
|
||||
Path_Error :: struct {
|
||||
op: string,
|
||||
path: string,
|
||||
@@ -83,20 +57,17 @@ link_error_delete :: proc(lerr: Maybe(Link_Error)) {
|
||||
|
||||
|
||||
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
|
||||
if ferr >= .Platform_Minimum {
|
||||
err = i32(u64(ferr)>>Platform_Error_Min_Bits);
|
||||
ok = true;
|
||||
v: Platform_Error;
|
||||
if v, ok = ferr.(Platform_Error); ok {
|
||||
err = v.err;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
error_from_platform_error :: proc(errno: i32) -> Error {
|
||||
return Error(u64(errno) << Platform_Error_Min_Bits);
|
||||
}
|
||||
|
||||
error_string :: proc(ferr: Error) -> string {
|
||||
#partial switch ferr {
|
||||
case .None: return "";
|
||||
switch ferr {
|
||||
case nil: return "";
|
||||
case .Invalid_Argument: return "invalid argument";
|
||||
case .Permission_Denied: return "permission denied";
|
||||
case .Exist: return "file already exists";
|
||||
|
||||
@@ -10,23 +10,14 @@ file_to_stream :: proc(fd: Handle) -> (s: io.Stream) {
|
||||
|
||||
@(private)
|
||||
error_to_io_error :: proc(ferr: Error) -> io.Error {
|
||||
#partial switch ferr {
|
||||
case .None: return .None;
|
||||
case .EOF: return .EOF;
|
||||
case .Unexpected_EOF: return .Unexpected_EOF;
|
||||
case .Short_Write: return .Short_Write;
|
||||
case .Invalid_Write: return .Invalid_Write;
|
||||
case .Short_Buffer: return .Short_Buffer;
|
||||
case .No_Progress: return .No_Progress;
|
||||
case .Invalid_Whence: return .Invalid_Whence;
|
||||
case .Invalid_Offset: return .Invalid_Offset;
|
||||
case .Invalid_Unread: return .Invalid_Unread;
|
||||
case .Negative_Read: return .Negative_Read;
|
||||
case .Negative_Write: return .Negative_Write;
|
||||
case .Negative_Count: return .Negative_Count;
|
||||
case .Buffer_Full: return .Buffer_Full;
|
||||
if ferr == nil {
|
||||
return .None;
|
||||
}
|
||||
return .Unknown;
|
||||
err, ok := ferr.(io.Error);
|
||||
if !ok {
|
||||
err = .Unknown;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package os2
|
||||
|
||||
import "core:mem"
|
||||
import "core:io"
|
||||
import "core:strconv"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
|
||||
@@ -5,19 +5,19 @@ import "core:io"
|
||||
import "core:time"
|
||||
|
||||
_create :: proc(name: string) -> (Handle, Error) {
|
||||
return 0, .None;
|
||||
return 0, nil;
|
||||
}
|
||||
|
||||
_open :: proc(name: string) -> (Handle, Error) {
|
||||
return 0, .None;
|
||||
return 0, nil;
|
||||
}
|
||||
|
||||
_open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) {
|
||||
return 0, .None;
|
||||
return 0, nil;
|
||||
}
|
||||
|
||||
_close :: proc(fd: Handle) -> Error {
|
||||
return .None;
|
||||
return nil;
|
||||
}
|
||||
|
||||
_name :: proc(fd: Handle, allocator := context.allocator) -> string {
|
||||
@@ -58,11 +58,11 @@ _file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
|
||||
|
||||
|
||||
_sync :: proc(fd: Handle) -> Error {
|
||||
return .None;
|
||||
return nil;
|
||||
}
|
||||
|
||||
_flush :: proc(fd: Handle) -> Error {
|
||||
return .None;
|
||||
return nil;
|
||||
}
|
||||
|
||||
_truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) {
|
||||
@@ -92,20 +92,20 @@ _read_link :: proc(name: string) -> (string, Maybe(Path_Error)) {
|
||||
|
||||
|
||||
_chdir :: proc(fd: Handle) -> Error {
|
||||
return .None;
|
||||
return nil;
|
||||
}
|
||||
|
||||
_chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
|
||||
return .None;
|
||||
return nil;
|
||||
}
|
||||
|
||||
_chown :: proc(fd: Handle, uid, gid: int) -> Error {
|
||||
return .None;
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return .None;
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ heap_allocator :: proc() -> runtime.Allocator {
|
||||
|
||||
heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
return _heap_allocator_proc(allocator_data, mode, size, alignment, old_memory, old_size, flags, loc);
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, runtime.Allocator_Error) {
|
||||
return _heap_allocator_proc(allocator_data, mode, size, alignment, old_memory, old_size, loc);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ heap_free :: proc(ptr: rawptr) {
|
||||
win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
|
||||
}
|
||||
|
||||
_heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
_heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) {
|
||||
//
|
||||
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
||||
@@ -37,7 +37,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mod
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> rawptr {
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr));
|
||||
space := size + a - 1;
|
||||
|
||||
@@ -54,13 +54,13 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mod
|
||||
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
|
||||
diff := int(aligned_ptr - ptr);
|
||||
if (size + diff) > space {
|
||||
return nil;
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
|
||||
aligned_mem = rawptr(aligned_ptr);
|
||||
mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
|
||||
|
||||
return aligned_mem;
|
||||
return mem.byte_slice(aligned_mem, size), nil;
|
||||
}
|
||||
|
||||
aligned_free :: proc(p: rawptr) {
|
||||
@@ -69,9 +69,9 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mod
|
||||
}
|
||||
}
|
||||
|
||||
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> rawptr {
|
||||
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, mem.Allocator_Error) {
|
||||
if p == nil {
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
return aligned_alloc(new_size, new_alignment, p);
|
||||
}
|
||||
@@ -93,15 +93,15 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mod
|
||||
return aligned_resize(old_memory, old_size, size, alignment);
|
||||
|
||||
case .Query_Features:
|
||||
set := (^runtime.Allocator_Mode_Set)(old_memory);
|
||||
set := (^mem.Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features};
|
||||
}
|
||||
return set;
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil;
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import win32 "core:sys/windows"
|
||||
_pipe :: proc() -> (r, w: Handle, err: Error) {
|
||||
p: [2]win32.HANDLE;
|
||||
if !win32.CreatePipe(&p[0], &p[1], nil, 0) {
|
||||
return 0, 0, error_from_platform_error(i32(win32.GetLastError()));
|
||||
return 0, 0, Platform_Error{i32(win32.GetLastError())};
|
||||
}
|
||||
return Handle(p[0]), Handle(p[1]), nil;
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool {
|
||||
|
||||
|
||||
_stat_errno :: proc(errno: win32.DWORD) -> Path_Error {
|
||||
return Path_Error{err = error_from_platform_error(i32(errno))};
|
||||
return Path_Error{err = Platform_Error{i32(errno)}};
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co
|
||||
fd: win32.WIN32_FIND_DATAW;
|
||||
sh := win32.FindFirstFileW(wname, &fd);
|
||||
if sh == win32.INVALID_HANDLE_VALUE {
|
||||
e = Path_Error{err = error_from_platform_error(i32(win32.GetLastError()))};
|
||||
e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}};
|
||||
return;
|
||||
}
|
||||
win32.FindClose(sh);
|
||||
@@ -99,7 +99,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co
|
||||
|
||||
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil);
|
||||
if h == win32.INVALID_HANDLE_VALUE {
|
||||
e = Path_Error{err = error_from_platform_error(i32(win32.GetLastError()))};
|
||||
e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}};
|
||||
return;
|
||||
}
|
||||
defer win32.CloseHandle(h);
|
||||
@@ -322,7 +322,7 @@ _is_abs :: proc(path: string) -> bool {
|
||||
}
|
||||
if len(path) > 2 {
|
||||
switch path[0] {
|
||||
case 'A'..'Z', 'a'..'z':
|
||||
case 'A'..='Z', 'a'..='z':
|
||||
return path[1] == ':' && is_path_separator(path[2]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,11 @@ package os2
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
_create_temp :: proc(dir, pattern: string) -> (Handle, Error) {
|
||||
return 0, .None;
|
||||
return 0, nil;
|
||||
}
|
||||
|
||||
_mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) {
|
||||
return "", .None;
|
||||
return "", nil;
|
||||
}
|
||||
|
||||
_temp_dir :: proc(allocator := context.allocator) -> string {
|
||||
|
||||
@@ -302,7 +302,7 @@ foreign libc {
|
||||
@(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---;
|
||||
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---;
|
||||
|
||||
@(link_name="exit") _unix_exit :: proc(status: int) ---;
|
||||
@(link_name="exit") _unix_exit :: proc(status: int) -> ! ---;
|
||||
}
|
||||
|
||||
foreign dl {
|
||||
@@ -337,7 +337,7 @@ write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
return 0, 0;
|
||||
}
|
||||
bytes_written := _unix_write(fd, raw_data(data), len(data));
|
||||
if(bytes_written == -1) {
|
||||
if bytes_written == -1 {
|
||||
return 0, 1;
|
||||
}
|
||||
return bytes_written, 0;
|
||||
|
||||
@@ -10,7 +10,7 @@ import "core:c"
|
||||
Handle :: distinct i32;
|
||||
File_Time :: distinct u64;
|
||||
Errno :: distinct i32;
|
||||
Syscall :: distinct int;
|
||||
Syscall :: distinct i32;
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0);
|
||||
|
||||
|
||||
@@ -35,8 +35,9 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errn
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
return 0, 0;
|
||||
}
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import "core:strconv"
|
||||
Handle :: distinct i32;
|
||||
File_Time :: distinct u64;
|
||||
Errno :: distinct i32;
|
||||
Syscall :: distinct int;
|
||||
Syscall :: distinct i32;
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0);
|
||||
|
||||
@@ -269,7 +269,7 @@ SYS_GETTID: Syscall : 186;
|
||||
|
||||
foreign libc {
|
||||
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---;
|
||||
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
|
||||
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> i32 ---;
|
||||
|
||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---;
|
||||
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---;
|
||||
@@ -595,7 +595,7 @@ exit :: proc "contextless" (code: int) -> ! {
|
||||
}
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return syscall(SYS_GETTID);
|
||||
return cast(int)syscall(SYS_GETTID);
|
||||
}
|
||||
|
||||
dlopen :: proc(filename: string, flags: int) -> rawptr {
|
||||
|
||||
@@ -11,14 +11,14 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
|
||||
}
|
||||
p := win32.utf8_to_utf16(name, context.temp_allocator);
|
||||
buf := make([dynamic]u16, 100, allocator);
|
||||
defer delete(buf);
|
||||
for {
|
||||
n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil);
|
||||
if n == 0 {
|
||||
delete(buf);
|
||||
return "", Errno(win32.GetLastError());
|
||||
}
|
||||
if n <= u32(len(buf)) {
|
||||
return win32.utf16_to_utf8(buf[:n]), ERROR_NONE;
|
||||
return win32.utf16_to_utf8(buf[:n], allocator), ERROR_NONE;
|
||||
}
|
||||
resize(&buf, len(buf)*2);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ volume_name_len :: proc(path: string) -> int {
|
||||
c := path[0];
|
||||
if path[1] == ':' {
|
||||
switch c {
|
||||
case 'a'..'z', 'A'..'Z':
|
||||
case 'a'..='z', 'A'..='Z':
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
@@ -170,6 +170,7 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
|
||||
cleaned, new_allocation := from_slash(s);
|
||||
if new_allocation {
|
||||
delete(s);
|
||||
lazy_buffer_destroy(out);
|
||||
}
|
||||
return cleaned;
|
||||
}
|
||||
@@ -206,23 +207,23 @@ Relative_Error :: enum {
|
||||
|
||||
rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (string, Relative_Error) {
|
||||
context.allocator = allocator;
|
||||
base_vol, target_vol := volume_name(base_path), volume_name(target_path);
|
||||
base, target := clean(base_path), clean(target_path);
|
||||
base_clean, target_clean := clean(base_path), clean(target_path);
|
||||
|
||||
delete_target := true;
|
||||
defer {
|
||||
if delete_target {
|
||||
delete(target);
|
||||
delete(target_clean);
|
||||
}
|
||||
delete(base);
|
||||
delete(base_clean);
|
||||
}
|
||||
|
||||
if strings.equal_fold(target, base) {
|
||||
if strings.equal_fold(target_clean, base_clean) {
|
||||
return strings.clone("."), .None;
|
||||
}
|
||||
|
||||
base = base[len(base_vol):];
|
||||
target = target[len(target_vol):];
|
||||
base_vol, target_vol := volume_name(base_path), volume_name(target_path);
|
||||
base := base_clean[len(base_vol):];
|
||||
target := target_clean[len(target_vol):];
|
||||
if base == "." {
|
||||
base = "";
|
||||
}
|
||||
@@ -395,3 +396,8 @@ lazy_buffer_string :: proc(lb: ^Lazy_Buffer) -> string {
|
||||
copy(z[len(x):], y);
|
||||
return string(z);
|
||||
}
|
||||
@(private)
|
||||
lazy_buffer_destroy :: proc(lb: ^Lazy_Buffer) {
|
||||
delete(lb.b);
|
||||
lb^ = {};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import "core:mem"
|
||||
_ :: runtime;
|
||||
_ :: mem;
|
||||
|
||||
Map_Entry_Info :: struct(Key, Value: typeid) {
|
||||
Map_Entry_Info :: struct($Key, $Value: typeid) {
|
||||
hash: uintptr,
|
||||
key: Key,
|
||||
value: Value,
|
||||
|
||||
+63
-21
@@ -33,6 +33,8 @@ Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector;
|
||||
Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer;
|
||||
Type_Info_Relative_Slice :: runtime.Type_Info_Relative_Slice;
|
||||
|
||||
Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value;
|
||||
|
||||
|
||||
Type_Kind :: enum {
|
||||
Invalid,
|
||||
@@ -111,7 +113,7 @@ backing_type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
}
|
||||
|
||||
|
||||
type_info_base :: proc(info: ^runtime.Type_Info) -> ^runtime.Type_Info {
|
||||
type_info_base :: proc(info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil { return nil; }
|
||||
|
||||
base := info;
|
||||
@@ -125,7 +127,7 @@ type_info_base :: proc(info: ^runtime.Type_Info) -> ^runtime.Type_Info {
|
||||
}
|
||||
|
||||
|
||||
type_info_core :: proc(info: ^runtime.Type_Info) -> ^runtime.Type_Info {
|
||||
type_info_core :: proc(info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil { return nil; }
|
||||
|
||||
base := info;
|
||||
@@ -344,7 +346,7 @@ Struct_Tag :: distinct string;
|
||||
|
||||
Struct_Field :: struct {
|
||||
name: string,
|
||||
type: typeid,
|
||||
type: ^Type_Info,
|
||||
tag: Struct_Tag,
|
||||
offset: uintptr,
|
||||
is_using: bool,
|
||||
@@ -355,7 +357,7 @@ struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
if 0 <= i && i < len(s.names) {
|
||||
field.name = s.names[i];
|
||||
field.type = s.types[i].id;
|
||||
field.type = s.types[i];
|
||||
field.tag = Struct_Tag(s.tags[i]);
|
||||
field.offset = s.offsets[i];
|
||||
field.is_using = s.usings[i];
|
||||
@@ -370,7 +372,7 @@ struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
|
||||
for fname, i in s.names {
|
||||
if fname == name {
|
||||
field.name = s.names[i];
|
||||
field.type = s.types[i].id;
|
||||
field.type = s.types[i];
|
||||
field.tag = Struct_Tag(s.tags[i]);
|
||||
field.offset = s.offsets[i];
|
||||
field.is_using = s.usings[i];
|
||||
@@ -381,7 +383,7 @@ struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct_field_value_by_name :: proc(a: any, field: string, recurse := false) -> any {
|
||||
struct_field_value_by_name :: proc(a: any, field: string, allow_using := false) -> any {
|
||||
if a == nil { return nil; }
|
||||
|
||||
ti := runtime.type_info_base(type_info_of(a.id));
|
||||
@@ -395,13 +397,13 @@ struct_field_value_by_name :: proc(a: any, field: string, recurse := false) -> a
|
||||
};
|
||||
}
|
||||
|
||||
if recurse && s.usings[i] {
|
||||
if allow_using && s.usings[i] {
|
||||
f := any{
|
||||
rawptr(uintptr(a.data) + s.offsets[i]),
|
||||
s.types[i].id,
|
||||
};
|
||||
|
||||
if res := struct_field_value_by_name(f, field, recurse); res != nil {
|
||||
if res := struct_field_value_by_name(f, field, allow_using); res != nil {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -420,7 +422,7 @@ struct_field_names :: proc(T: typeid) -> []string {
|
||||
return nil;
|
||||
}
|
||||
|
||||
struct_field_types :: proc(T: typeid) -> []^runtime.Type_Info {
|
||||
struct_field_types :: proc(T: typeid) -> []^Type_Info {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
return s.types;
|
||||
@@ -445,6 +447,20 @@ struct_field_offsets :: proc(T: typeid) -> []uintptr {
|
||||
return nil;
|
||||
}
|
||||
|
||||
struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
return soa_zip(
|
||||
name = s.names,
|
||||
type = s.types,
|
||||
tag = transmute([]Struct_Tag)s.tags,
|
||||
offset = s.offsets,
|
||||
is_using = s.usings,
|
||||
);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag) {
|
||||
@@ -468,7 +484,7 @@ struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag, o
|
||||
switch t[i] {
|
||||
case ':', '"':
|
||||
break loop;
|
||||
case 0x00 ..< ' ', 0x7f .. 0x9f: // break if control character is found
|
||||
case 0x00 ..< ' ', 0x7f ..= 0x9f: // break if control character is found
|
||||
break loop;
|
||||
}
|
||||
i += 1;
|
||||
@@ -516,7 +532,7 @@ enum_string :: proc(a: any) -> string {
|
||||
if e, ok := ti.variant.(runtime.Type_Info_Enum); ok {
|
||||
v, _ := as_i64(a);
|
||||
for value, i in e.values {
|
||||
if value == runtime.Type_Info_Enum_Value(v) {
|
||||
if value == Type_Info_Enum_Value(v) {
|
||||
return e.names[i];
|
||||
}
|
||||
}
|
||||
@@ -528,26 +544,24 @@ enum_string :: proc(a: any) -> string {
|
||||
}
|
||||
|
||||
// Given a enum type and a value name, get the enum value.
|
||||
enum_from_name :: proc($EnumType: typeid, name: string) -> (value: EnumType, ok: bool) {
|
||||
ti := type_info_base(type_info_of(EnumType));
|
||||
enum_from_name :: proc($Enum_Type: typeid, name: string) -> (value: Enum_Type, ok: bool) {
|
||||
ti := type_info_base(type_info_of(Enum_Type));
|
||||
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
|
||||
for value_name, i in eti.names {
|
||||
if value_name != name {
|
||||
continue;
|
||||
}
|
||||
v := eti.values[i];
|
||||
value = EnumType(v);
|
||||
value = Enum_Type(v);
|
||||
ok = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
panic("expected enum type to reflect.enum_from_name");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
enum_from_name_any :: proc(EnumType: typeid, name: string) -> (value: runtime.Type_Info_Enum_Value, ok: bool) {
|
||||
ti := runtime.type_info_base(type_info_of(EnumType));
|
||||
enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info_Enum_Value, ok: bool) {
|
||||
ti := runtime.type_info_base(type_info_of(Enum_Type));
|
||||
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
|
||||
for value_name, i in eti.names {
|
||||
if value_name != name {
|
||||
@@ -557,14 +571,42 @@ enum_from_name_any :: proc(EnumType: typeid, name: string) -> (value: runtime.Ty
|
||||
ok = true;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
panic("expected enum type to reflect.enum_from_name_any");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
union_variant_type_info :: proc(a: any) -> ^runtime.Type_Info {
|
||||
enum_field_names :: proc(Enum_Type: typeid) -> []string {
|
||||
ti := runtime.type_info_base(type_info_of(Enum_Type));
|
||||
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
|
||||
return eti.names;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
enum_field_values :: proc(Enum_Type: typeid) -> []Type_Info_Enum_Value {
|
||||
ti := runtime.type_info_base(type_info_of(Enum_Type));
|
||||
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
|
||||
return eti.values;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
Enum_Field :: struct {
|
||||
name: string,
|
||||
value: Type_Info_Enum_Value,
|
||||
}
|
||||
|
||||
enum_fields_zipped :: proc(Enum_Type: typeid) -> (fields: #soa[]Enum_Field) {
|
||||
ti := runtime.type_info_base(type_info_of(Enum_Type));
|
||||
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
|
||||
return soa_zip(name=eti.names, value=eti.values);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
union_variant_type_info :: proc(a: any) -> ^Type_Info {
|
||||
id := union_variant_typeid(a);
|
||||
return type_info_of(id);
|
||||
}
|
||||
|
||||
+14
-20
@@ -20,6 +20,8 @@
|
||||
//
|
||||
package runtime
|
||||
|
||||
import "intrinsics"
|
||||
|
||||
// NOTE(bill): This must match the compiler's
|
||||
Calling_Convention :: enum u8 {
|
||||
Invalid = 0,
|
||||
@@ -30,6 +32,7 @@ Calling_Convention :: enum u8 {
|
||||
Fast_Call = 5,
|
||||
|
||||
None = 6,
|
||||
Naked = 7,
|
||||
}
|
||||
|
||||
Type_Info_Enum_Value :: distinct i64;
|
||||
@@ -118,6 +121,9 @@ Type_Info_Union :: struct {
|
||||
variants: []^Type_Info,
|
||||
tag_offset: uintptr,
|
||||
tag_type: ^Type_Info,
|
||||
|
||||
equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set
|
||||
|
||||
custom_align: bool,
|
||||
no_nil: bool,
|
||||
maybe: bool,
|
||||
@@ -249,7 +255,7 @@ Source_Code_Location :: struct {
|
||||
procedure: string,
|
||||
}
|
||||
|
||||
Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location);
|
||||
Assertion_Failure_Proc :: #type proc(prefix, message: string, loc: Source_Code_Location) -> !;
|
||||
|
||||
// Allocation Stuff
|
||||
Allocator_Mode :: enum byte {
|
||||
@@ -323,8 +329,6 @@ Context :: struct {
|
||||
assertion_failure_proc: Assertion_Failure_Proc,
|
||||
logger: Logger,
|
||||
|
||||
thread_id: int,
|
||||
|
||||
user_data: any,
|
||||
user_ptr: rawptr,
|
||||
user_index: int,
|
||||
@@ -407,7 +411,7 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
|
||||
}
|
||||
type_info_base_without_enum :: type_info_core;
|
||||
|
||||
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info {
|
||||
__type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check {
|
||||
MASK :: 1<<(8*size_of(typeid) - 8) - 1;
|
||||
data := transmute(uintptr)id;
|
||||
n := int(data & MASK);
|
||||
@@ -430,17 +434,9 @@ typeid_base_without_enum :: typeid_core;
|
||||
|
||||
|
||||
|
||||
@(default_calling_convention = "none")
|
||||
foreign {
|
||||
@(link_name="llvm.debugtrap")
|
||||
debug_trap :: proc() ---;
|
||||
|
||||
@(link_name="llvm.trap")
|
||||
trap :: proc() -> ! ---;
|
||||
|
||||
@(link_name="llvm.readcyclecounter")
|
||||
read_cycle_counter :: proc() -> u64 ---;
|
||||
}
|
||||
debug_trap :: intrinsics.debug_trap;
|
||||
trap :: intrinsics.trap;
|
||||
read_cycle_counter :: intrinsics.read_cycle_counter;
|
||||
|
||||
|
||||
|
||||
@@ -481,15 +477,13 @@ __init_context :: proc "contextless" (c: ^Context) {
|
||||
c.temp_allocator.procedure = default_temp_allocator_proc;
|
||||
c.temp_allocator.data = &global_default_temp_allocator_data;
|
||||
|
||||
c.thread_id = current_thread_id(); // NOTE(bill): This is "contextless" so it is okay to call
|
||||
c.assertion_failure_proc = default_assertion_failure_proc;
|
||||
|
||||
c.logger.procedure = default_logger_proc;
|
||||
c.logger.data = nil;
|
||||
}
|
||||
|
||||
|
||||
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) {
|
||||
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {
|
||||
print_caller_location(loc);
|
||||
print_string(" ");
|
||||
print_string(prefix);
|
||||
@@ -498,6 +492,6 @@ default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code
|
||||
print_string(message);
|
||||
}
|
||||
print_byte('\n');
|
||||
debug_trap();
|
||||
// trap();
|
||||
// debug_trap();
|
||||
trap();
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package runtime
|
||||
|
||||
import "intrinsics"
|
||||
|
||||
@builtin
|
||||
Maybe :: union(T: typeid) #maybe {T};
|
||||
Maybe :: union($T: typeid) #maybe {T};
|
||||
|
||||
@thread_local global_default_temp_allocator_data: Default_Temp_Allocator;
|
||||
|
||||
@@ -15,7 +17,7 @@ init_global_temporary_allocator :: proc(size: int, backup_allocator := context.a
|
||||
copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
|
||||
n := max(0, min(len(dst), len(src)));
|
||||
if n > 0 {
|
||||
mem_copy(raw_data(dst), raw_data(src), n*size_of(E));
|
||||
intrinsics.mem_copy(raw_data(dst), raw_data(src), n*size_of(E));
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -23,7 +25,7 @@ copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
|
||||
copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
|
||||
n := max(0, min(len(dst), len(src)));
|
||||
if n > 0 {
|
||||
mem_copy(raw_data(dst), raw_data(src), n);
|
||||
intrinsics.mem_copy(raw_data(dst), raw_data(src), n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -33,26 +35,26 @@ copy :: proc{copy_slice, copy_from_string};
|
||||
|
||||
|
||||
@builtin
|
||||
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
|
||||
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
|
||||
bounds_check_error_loc(loc, index, len(array));
|
||||
n := len(array)-1;
|
||||
if index != n {
|
||||
array[index] = array[n];
|
||||
}
|
||||
pop(array);
|
||||
(^Raw_Dynamic_Array)(array).len -= 1;
|
||||
}
|
||||
|
||||
@builtin
|
||||
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) {
|
||||
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
|
||||
bounds_check_error_loc(loc, index, len(array));
|
||||
if index+1 < len(array) {
|
||||
copy(array[index:], array[index+1:]);
|
||||
}
|
||||
pop(array);
|
||||
(^Raw_Dynamic_Array)(array).len -= 1;
|
||||
}
|
||||
|
||||
@builtin
|
||||
remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) {
|
||||
remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) #no_bounds_check {
|
||||
slice_expr_error_lo_hi_loc(loc, lo, hi, len(array));
|
||||
n := max(hi-lo, 0);
|
||||
if n > 0 {
|
||||
@@ -283,22 +285,18 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) {
|
||||
return;
|
||||
}
|
||||
|
||||
arg_len := 1;
|
||||
|
||||
if cap(array) < len(array)+arg_len {
|
||||
cap := 2 * cap(array) + max(8, arg_len);
|
||||
if cap(array) < len(array)+1 {
|
||||
cap := 2 * cap(array) + max(8, 1);
|
||||
_ = reserve(array, cap, loc);
|
||||
}
|
||||
arg_len = min(cap(array)-len(array), arg_len);
|
||||
if arg_len > 0 {
|
||||
if cap(array)-len(array) > 0 {
|
||||
a := (^Raw_Dynamic_Array)(array);
|
||||
if size_of(E) != 0 {
|
||||
when size_of(E) != 0 {
|
||||
data := (^E)(a.data);
|
||||
assert(data != nil);
|
||||
val := arg;
|
||||
mem_copy(ptr_offset(data, a.len), &val, size_of(E));
|
||||
assert(condition=data != nil, loc=loc);
|
||||
intrinsics.ptr_offset(data, a.len)^ = arg;
|
||||
}
|
||||
a.len += arg_len;
|
||||
a.len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,10 +319,10 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
|
||||
arg_len = min(cap(array)-len(array), arg_len);
|
||||
if arg_len > 0 {
|
||||
a := (^Raw_Dynamic_Array)(array);
|
||||
if size_of(E) != 0 {
|
||||
when size_of(E) != 0 {
|
||||
data := (^E)(a.data);
|
||||
assert(data != nil);
|
||||
mem_copy(ptr_offset(data, a.len), &args[0], size_of(E) * arg_len);
|
||||
assert(condition=data != nil, loc=loc);
|
||||
intrinsics.mem_copy(intrinsics.ptr_offset(data, a.len), &args[0], size_of(E) * arg_len);
|
||||
}
|
||||
a.len += arg_len;
|
||||
}
|
||||
@@ -539,20 +537,15 @@ excl_bit_set :: proc(s: ^$S/bit_set[$E; $U], other: S) {
|
||||
@builtin
|
||||
card :: proc(s: $S/bit_set[$E; $U]) -> int {
|
||||
when size_of(S) == 1 {
|
||||
foreign { @(link_name="llvm.ctpop.i8") count_ones :: proc(i: u8) -> u8 --- }
|
||||
return int(count_ones(transmute(u8)s));
|
||||
return int(intrinsics.count_ones(transmute(u8)s));
|
||||
} else when size_of(S) == 2 {
|
||||
foreign { @(link_name="llvm.ctpop.i16") count_ones :: proc(i: u16) -> u16 --- }
|
||||
return int(count_ones(transmute(u16)s));
|
||||
return int(intrinsics.count_ones(transmute(u16)s));
|
||||
} else when size_of(S) == 4 {
|
||||
foreign { @(link_name="llvm.ctpop.i32") count_ones :: proc(i: u32) -> u32 --- }
|
||||
return int(count_ones(transmute(u32)s));
|
||||
return int(intrinsics.count_ones(transmute(u32)s));
|
||||
} else when size_of(S) == 8 {
|
||||
foreign { @(link_name="llvm.ctpop.i64") count_ones :: proc(i: u64) -> u64 --- }
|
||||
return int(count_ones(transmute(u64)s));
|
||||
return int(intrinsics.count_ones(transmute(u64)s));
|
||||
} else when size_of(S) == 16 {
|
||||
foreign { @(link_name="llvm.ctpop.i128") count_ones :: proc(i: u128) -> u128 --- }
|
||||
return int(count_ones(transmute(u128)s));
|
||||
return int(intrinsics.count_ones(transmute(u128)s));
|
||||
} else {
|
||||
#panic("Unhandled card bit_set size");
|
||||
}
|
||||
|
||||
@@ -1,205 +0,0 @@
|
||||
package runtime
|
||||
|
||||
when ODIN_DEFAULT_TO_NIL_ALLOCATOR || ODIN_OS == "freestanding" {
|
||||
// mem.nil_allocator reimplementation
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
} else when ODIN_OS != "windows" {
|
||||
// TODO(bill): reimplement these procedures in the os_specific stuff
|
||||
import "core:os"
|
||||
|
||||
default_allocator_proc :: os.heap_allocator_proc;
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return os.heap_allocator();
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
|
||||
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
|
||||
}
|
||||
|
||||
|
||||
DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22);
|
||||
|
||||
|
||||
Default_Temp_Allocator :: struct {
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_allocation: rawptr,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic][]byte,
|
||||
}
|
||||
|
||||
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
|
||||
s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator);
|
||||
s.curr_offset = 0;
|
||||
s.prev_allocation = nil;
|
||||
s.backup_allocator = backup_allocator;
|
||||
s.leaked_allocations.allocator = backup_allocator;
|
||||
}
|
||||
|
||||
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
|
||||
if s == nil {
|
||||
return;
|
||||
}
|
||||
for ptr in s.leaked_allocations {
|
||||
free(raw_data(ptr), s.backup_allocator);
|
||||
}
|
||||
delete(s.leaked_allocations);
|
||||
delete(s.data, s.backup_allocator);
|
||||
s^ = {};
|
||||
}
|
||||
|
||||
default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
|
||||
s := (^Default_Temp_Allocator)(allocator_data);
|
||||
|
||||
if s.data == nil {
|
||||
default_temp_allocator_init(s, DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, default_allocator());
|
||||
}
|
||||
|
||||
size := size;
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
size = align_forward_int(size, alignment);
|
||||
|
||||
switch {
|
||||
case s.curr_offset+size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data));
|
||||
ptr := start + uintptr(s.curr_offset);
|
||||
ptr = align_forward_uintptr(ptr, uintptr(alignment));
|
||||
mem_zero(rawptr(ptr), size);
|
||||
|
||||
s.prev_allocation = rawptr(ptr);
|
||||
offset := int(ptr - start);
|
||||
s.curr_offset = offset + size;
|
||||
return byte_slice(rawptr(ptr), size), .None;
|
||||
|
||||
case size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data));
|
||||
ptr := align_forward_uintptr(start, uintptr(alignment));
|
||||
mem_zero(rawptr(ptr), size);
|
||||
|
||||
s.prev_allocation = rawptr(ptr);
|
||||
offset := int(ptr - start);
|
||||
s.curr_offset = offset + size;
|
||||
return byte_slice(rawptr(ptr), size), .None;
|
||||
}
|
||||
a := s.backup_allocator;
|
||||
if a.procedure == nil {
|
||||
a = context.allocator;
|
||||
s.backup_allocator = a;
|
||||
}
|
||||
|
||||
data, err := mem_alloc_bytes(size, alignment, a, loc);
|
||||
if err != nil {
|
||||
return data, err;
|
||||
}
|
||||
if s.leaked_allocations == nil {
|
||||
s.leaked_allocations = make([dynamic][]byte, a);
|
||||
}
|
||||
append(&s.leaked_allocations, data);
|
||||
|
||||
// TODO(bill): Should leaks be notified about?
|
||||
if logger := context.logger; logger.lowest_level <= .Warning {
|
||||
if logger.procedure != nil {
|
||||
logger.procedure(logger.data, .Warning, "default temp allocator resorted to backup_allocator" , logger.options, loc);
|
||||
}
|
||||
}
|
||||
|
||||
return data, .None;
|
||||
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
old_ptr := uintptr(old_memory);
|
||||
|
||||
if s.prev_allocation == old_memory {
|
||||
s.curr_offset = int(uintptr(s.prev_allocation) - start);
|
||||
s.prev_allocation = nil;
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
if start <= old_ptr && old_ptr < end {
|
||||
// NOTE(bill): Cannot free this pointer but it is valid
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
if len(s.leaked_allocations) != 0 {
|
||||
for data, i in s.leaked_allocations {
|
||||
ptr := raw_data(data);
|
||||
if ptr == old_memory {
|
||||
free(ptr, s.backup_allocator);
|
||||
ordered_remove(&s.leaked_allocations, i);
|
||||
return nil, .None;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, .Invalid_Pointer;
|
||||
// panic("invalid pointer passed to default_temp_allocator");
|
||||
|
||||
case .Free_All:
|
||||
s.curr_offset = 0;
|
||||
s.prev_allocation = nil;
|
||||
for data in s.leaked_allocations {
|
||||
free(raw_data(data), s.backup_allocator);
|
||||
}
|
||||
clear(&s.leaked_allocations);
|
||||
|
||||
case .Resize:
|
||||
begin := uintptr(raw_data(s.data));
|
||||
end := begin + uintptr(len(s.data));
|
||||
old_ptr := uintptr(old_memory);
|
||||
if old_memory == s.prev_allocation && old_ptr & uintptr(alignment)-1 == 0 {
|
||||
if old_ptr+uintptr(size) < end {
|
||||
s.curr_offset = int(old_ptr-begin)+size;
|
||||
return byte_slice(old_memory, size), .None;
|
||||
}
|
||||
}
|
||||
ptr, err := default_temp_allocator_proc(allocator_data, .Alloc, size, alignment, old_memory, old_size, loc);
|
||||
if err == .None {
|
||||
copy(ptr, byte_slice(old_memory, old_size));
|
||||
_, err = default_temp_allocator_proc(allocator_data, .Free, 0, alignment, old_memory, old_size, loc);
|
||||
}
|
||||
return ptr, err;
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_temp_allocator_proc,
|
||||
data = allocator,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//+build !windows
|
||||
//+build !freestanding
|
||||
package runtime
|
||||
|
||||
when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
// mem.nil_allocator reimplementation
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// TODO(bill): reimplement these procedures in the os_specific stuff
|
||||
import "core:os"
|
||||
|
||||
default_allocator_proc :: os.heap_allocator_proc;
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return os.heap_allocator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
//+build freestanding
|
||||
package runtime
|
||||
|
||||
// mem.nil_allocator reimplementation
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
return nil, .None;
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//+build windows
|
||||
package runtime
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
data, err = _windows_default_alloc(size, alignment);
|
||||
|
||||
case .Free:
|
||||
_windows_default_free(old_memory);
|
||||
|
||||
case .Free_All:
|
||||
// NOTE(tetra): Do nothing.
|
||||
|
||||
case .Resize:
|
||||
data, err = _windows_default_resize(old_memory, old_size, size, alignment);
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features};
|
||||
}
|
||||
|
||||
case .Query_Info:
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package runtime
|
||||
|
||||
@(private)
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
|
||||
return transmute([]u8)Raw_Slice{data=data, len=max(len, 0)};
|
||||
}
|
||||
|
||||
|
||||
DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22);
|
||||
|
||||
|
||||
Default_Temp_Allocator :: struct {
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_allocation: rawptr,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic][]byte,
|
||||
}
|
||||
|
||||
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
|
||||
s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator);
|
||||
s.curr_offset = 0;
|
||||
s.prev_allocation = nil;
|
||||
s.backup_allocator = backup_allocator;
|
||||
s.leaked_allocations.allocator = backup_allocator;
|
||||
}
|
||||
|
||||
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
|
||||
if s == nil {
|
||||
return;
|
||||
}
|
||||
for ptr in s.leaked_allocations {
|
||||
free(raw_data(ptr), s.backup_allocator);
|
||||
}
|
||||
delete(s.leaked_allocations);
|
||||
delete(s.data, s.backup_allocator);
|
||||
s^ = {};
|
||||
}
|
||||
|
||||
@(private)
|
||||
default_temp_allocator_alloc :: proc(s: ^Default_Temp_Allocator, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
size := size;
|
||||
size = align_forward_int(size, alignment);
|
||||
|
||||
switch {
|
||||
case s.curr_offset+size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data));
|
||||
ptr := start + uintptr(s.curr_offset);
|
||||
ptr = align_forward_uintptr(ptr, uintptr(alignment));
|
||||
mem_zero(rawptr(ptr), size);
|
||||
|
||||
s.prev_allocation = rawptr(ptr);
|
||||
offset := int(ptr - start);
|
||||
s.curr_offset = offset + size;
|
||||
return byte_slice(rawptr(ptr), size), .None;
|
||||
|
||||
case size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data));
|
||||
ptr := align_forward_uintptr(start, uintptr(alignment));
|
||||
mem_zero(rawptr(ptr), size);
|
||||
|
||||
s.prev_allocation = rawptr(ptr);
|
||||
offset := int(ptr - start);
|
||||
s.curr_offset = offset + size;
|
||||
return byte_slice(rawptr(ptr), size), .None;
|
||||
}
|
||||
a := s.backup_allocator;
|
||||
if a.procedure == nil {
|
||||
a = context.allocator;
|
||||
s.backup_allocator = a;
|
||||
}
|
||||
|
||||
data, err := mem_alloc_bytes(size, alignment, a, loc);
|
||||
if err != nil {
|
||||
return data, err;
|
||||
}
|
||||
if s.leaked_allocations == nil {
|
||||
s.leaked_allocations = make([dynamic][]byte, a);
|
||||
}
|
||||
append(&s.leaked_allocations, data);
|
||||
|
||||
// TODO(bill): Should leaks be notified about?
|
||||
if logger := context.logger; logger.lowest_level <= .Warning {
|
||||
if logger.procedure != nil {
|
||||
logger.procedure(logger.data, .Warning, "default temp allocator resorted to backup_allocator" , logger.options, loc);
|
||||
}
|
||||
}
|
||||
|
||||
return data, .None;
|
||||
}
|
||||
|
||||
@(private)
|
||||
default_temp_allocator_free :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, loc := #caller_location) -> Allocator_Error {
|
||||
if old_memory == nil {
|
||||
return .None;
|
||||
}
|
||||
|
||||
start := uintptr(raw_data(s.data));
|
||||
end := start + uintptr(len(s.data));
|
||||
old_ptr := uintptr(old_memory);
|
||||
|
||||
if s.prev_allocation == old_memory {
|
||||
s.curr_offset = int(uintptr(s.prev_allocation) - start);
|
||||
s.prev_allocation = nil;
|
||||
return .None;
|
||||
}
|
||||
|
||||
if start <= old_ptr && old_ptr < end {
|
||||
// NOTE(bill): Cannot free this pointer but it is valid
|
||||
return .None;
|
||||
}
|
||||
|
||||
if len(s.leaked_allocations) != 0 {
|
||||
for data, i in s.leaked_allocations {
|
||||
ptr := raw_data(data);
|
||||
if ptr == old_memory {
|
||||
free(ptr, s.backup_allocator);
|
||||
ordered_remove(&s.leaked_allocations, i);
|
||||
return .None;
|
||||
}
|
||||
}
|
||||
}
|
||||
return .Invalid_Pointer;
|
||||
// panic("invalid pointer passed to default_temp_allocator");
|
||||
}
|
||||
|
||||
@(private)
|
||||
default_temp_allocator_free_all :: proc(s: ^Default_Temp_Allocator, loc := #caller_location) {
|
||||
s.curr_offset = 0;
|
||||
s.prev_allocation = nil;
|
||||
for data in s.leaked_allocations {
|
||||
free(raw_data(data), s.backup_allocator);
|
||||
}
|
||||
clear(&s.leaked_allocations);
|
||||
}
|
||||
|
||||
@(private)
|
||||
default_temp_allocator_resize :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, old_size, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
begin := uintptr(raw_data(s.data));
|
||||
end := begin + uintptr(len(s.data));
|
||||
old_ptr := uintptr(old_memory);
|
||||
if old_memory == s.prev_allocation && old_ptr & uintptr(alignment)-1 == 0 {
|
||||
if old_ptr+uintptr(size) < end {
|
||||
s.curr_offset = int(old_ptr-begin)+size;
|
||||
return byte_slice(old_memory, size), .None;
|
||||
}
|
||||
}
|
||||
data, err := default_temp_allocator_alloc(s, size, alignment, loc);
|
||||
if err == .None {
|
||||
copy(data, byte_slice(old_memory, old_size));
|
||||
err = default_temp_allocator_free(s, old_memory, loc);
|
||||
}
|
||||
return data, err;
|
||||
}
|
||||
|
||||
default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
|
||||
s := (^Default_Temp_Allocator)(allocator_data);
|
||||
|
||||
if s.data == nil {
|
||||
default_temp_allocator_init(s, DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, default_allocator());
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
data, err = default_temp_allocator_alloc(s, size, alignment, loc);
|
||||
case .Free:
|
||||
err = default_temp_allocator_free(s, old_memory, loc);
|
||||
|
||||
case .Free_All:
|
||||
default_temp_allocator_free_all(s, loc);
|
||||
|
||||
case .Resize:
|
||||
data, err = default_temp_allocator_resize(s, old_memory, old_size, size, alignment, loc);
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features};
|
||||
}
|
||||
|
||||
case .Query_Info:
|
||||
// Nothing to give
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_temp_allocator_proc,
|
||||
data = allocator,
|
||||
};
|
||||
}
|
||||
@@ -362,7 +362,7 @@ __dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds
|
||||
curr := __dynamic_map_get_entry(h, fr.entry_index);
|
||||
prev.next = curr.next;
|
||||
}
|
||||
if (fr.entry_index == m.entries.len-1) {
|
||||
if fr.entry_index == m.entries.len-1 {
|
||||
// NOTE(bill): No need to do anything else, just pop
|
||||
} else {
|
||||
old := __dynamic_map_get_entry(h, fr.entry_index);
|
||||
|
||||
@@ -22,7 +22,6 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
|
||||
return;
|
||||
}
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) {
|
||||
context = default_context();
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""});
|
||||
print_string(" Index ");
|
||||
print_i64(i64(index));
|
||||
@@ -35,7 +34,6 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
|
||||
}
|
||||
|
||||
slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! {
|
||||
context = default_context();
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""});
|
||||
print_string(" Invalid slice indices: ");
|
||||
print_i64(i64(lo));
|
||||
@@ -66,7 +64,6 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
|
||||
return;
|
||||
}
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) {
|
||||
context = default_context();
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""});
|
||||
print_string(" Invalid dynamic array values: ");
|
||||
print_i64(i64(low));
|
||||
@@ -86,7 +83,6 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column
|
||||
return;
|
||||
}
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) {
|
||||
context = default_context();
|
||||
print_caller_location(Source_Code_Location{file, line, column, ""});
|
||||
print_string(" Invalid type assertion from ");
|
||||
print_typeid(from);
|
||||
@@ -131,7 +127,6 @@ type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, colum
|
||||
}
|
||||
|
||||
handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
|
||||
context = default_context();
|
||||
|
||||
actual := variant_type(from, from_data);
|
||||
|
||||
@@ -156,7 +151,6 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio
|
||||
return;
|
||||
}
|
||||
handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) {
|
||||
context = default_context();
|
||||
print_caller_location(loc);
|
||||
print_string(" Invalid slice length for make: ");
|
||||
print_i64(i64(len));
|
||||
@@ -171,7 +165,6 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (using loc := #
|
||||
return;
|
||||
}
|
||||
handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) {
|
||||
context = default_context();
|
||||
print_caller_location(loc);
|
||||
print_string(" Invalid dynamic array parameters for make: ");
|
||||
print_i64(i64(len));
|
||||
@@ -188,7 +181,6 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca
|
||||
return;
|
||||
}
|
||||
handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) {
|
||||
context = default_context();
|
||||
print_caller_location(loc);
|
||||
print_string(" Invalid map capacity for make: ");
|
||||
print_i64(i64(cap));
|
||||
|
||||
+125
-102
@@ -97,7 +97,7 @@ mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
if len < 0 {
|
||||
return data;
|
||||
}
|
||||
memset(data, 0, len);
|
||||
intrinsics.mem_zero(data, len);
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -105,27 +105,9 @@ mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if src == nil {
|
||||
return dst;
|
||||
}
|
||||
|
||||
// NOTE(bill): This _must_ be implemented like C's memmove
|
||||
foreign _ {
|
||||
when ODIN_USE_LLVM_API {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i64")
|
||||
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i32")
|
||||
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
||||
}
|
||||
} else {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i64")
|
||||
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i32")
|
||||
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||
}
|
||||
}
|
||||
}
|
||||
llvm_memmove(dst, src, len);
|
||||
intrinsics.mem_copy(dst, src, len);
|
||||
return dst;
|
||||
}
|
||||
|
||||
@@ -133,27 +115,9 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
|
||||
if src == nil {
|
||||
return dst;
|
||||
}
|
||||
|
||||
// NOTE(bill): This _must_ be implemented like C's memcpy
|
||||
foreign _ {
|
||||
when ODIN_USE_LLVM_API {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
|
||||
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
|
||||
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
|
||||
}
|
||||
} else {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
|
||||
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
|
||||
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||
}
|
||||
}
|
||||
}
|
||||
llvm_memcpy(dst, src, len);
|
||||
intrinsics.mem_copy_non_overlapping(dst, src, len);
|
||||
return dst;
|
||||
}
|
||||
|
||||
@@ -336,6 +300,9 @@ cstring_to_string :: proc "contextless" (s: cstring) -> string {
|
||||
}
|
||||
|
||||
|
||||
complex32_eq :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) == real(b) && imag(a) == imag(b); }
|
||||
complex32_ne :: #force_inline proc "contextless" (a, b: complex32) -> bool { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
complex64_eq :: #force_inline proc "contextless" (a, b: complex64) -> bool { return real(a) == real(b) && imag(a) == imag(b); }
|
||||
complex64_ne :: #force_inline proc "contextless" (a, b: complex64) -> bool { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
@@ -343,6 +310,9 @@ complex128_eq :: #force_inline proc "contextless" (a, b: complex128) -> bool { r
|
||||
complex128_ne :: #force_inline proc "contextless" (a, b: complex128) -> bool { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
|
||||
quaternion64_eq :: #force_inline proc "contextless" (a, b: quaternion64) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); }
|
||||
quaternion64_ne :: #force_inline proc "contextless" (a, b: quaternion64) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); }
|
||||
|
||||
quaternion128_eq :: #force_inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); }
|
||||
quaternion128_ne :: #force_inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); }
|
||||
|
||||
@@ -429,90 +399,58 @@ string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int
|
||||
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
|
||||
}
|
||||
|
||||
@(default_calling_convention = "none")
|
||||
foreign {
|
||||
@(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 ---
|
||||
@(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 ---
|
||||
}
|
||||
abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 {
|
||||
foreign {
|
||||
@(link_name="llvm.fabs.f16") _abs :: proc "none" (x: f16) -> f16 ---
|
||||
}
|
||||
return _abs(x);
|
||||
return -x if x < 0 else x;
|
||||
}
|
||||
abs_f32 :: #force_inline proc "contextless" (x: f32) -> f32 {
|
||||
foreign {
|
||||
@(link_name="llvm.fabs.f32") _abs :: proc "none" (x: f32) -> f32 ---
|
||||
}
|
||||
return _abs(x);
|
||||
return -x if x < 0 else x;
|
||||
}
|
||||
abs_f64 :: #force_inline proc "contextless" (x: f64) -> f64 {
|
||||
foreign {
|
||||
@(link_name="llvm.fabs.f64") _abs :: proc "none" (x: f64) -> f64 ---
|
||||
}
|
||||
return _abs(x);
|
||||
return -x if x < 0 else x;
|
||||
}
|
||||
|
||||
min_f16 :: proc(a, b: f16) -> f16 {
|
||||
foreign {
|
||||
@(link_name="llvm.minnum.f16") _min :: proc "none" (a, b: f16) -> f16 ---
|
||||
}
|
||||
return _min(a, b);
|
||||
min_f16 :: #force_inline proc "contextless" (a, b: f16) -> f16 {
|
||||
return a if a < b else b;
|
||||
}
|
||||
min_f32 :: proc(a, b: f32) -> f32 {
|
||||
foreign {
|
||||
@(link_name="llvm.minnum.f32") _min :: proc "none" (a, b: f32) -> f32 ---
|
||||
}
|
||||
return _min(a, b);
|
||||
min_f32 :: #force_inline proc "contextless" (a, b: f32) -> f32 {
|
||||
return a if a < b else b;
|
||||
}
|
||||
min_f64 :: proc(a, b: f64) -> f64 {
|
||||
foreign {
|
||||
@(link_name="llvm.minnum.f64") _min :: proc "none" (a, b: f64) -> f64 ---
|
||||
}
|
||||
return _min(a, b);
|
||||
min_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 {
|
||||
return a if a < b else b;
|
||||
}
|
||||
max_f16 :: proc(a, b: f16) -> f16 {
|
||||
foreign {
|
||||
@(link_name="llvm.maxnum.f16") _max :: proc "none" (a, b: f16) -> f16 ---
|
||||
}
|
||||
return _max(a, b);
|
||||
max_f16 :: #force_inline proc "contextless" (a, b: f16) -> f16 {
|
||||
return a if a > b else b;
|
||||
}
|
||||
max_f32 :: proc(a, b: f32) -> f32 {
|
||||
foreign {
|
||||
@(link_name="llvm.maxnum.f32") _max :: proc "none" (a, b: f32) -> f32 ---
|
||||
}
|
||||
return _max(a, b);
|
||||
max_f32 :: #force_inline proc "contextless" (a, b: f32) -> f32 {
|
||||
return a if a > b else b;
|
||||
}
|
||||
max_f64 :: proc(a, b: f64) -> f64 {
|
||||
foreign {
|
||||
@(link_name="llvm.maxnum.f64") _max :: proc "none" (a, b: f64) -> f64 ---
|
||||
}
|
||||
return _max(a, b);
|
||||
max_f64 :: #force_inline proc "contextless" (a, b: f64) -> f64 {
|
||||
return a if a > b else b;
|
||||
}
|
||||
|
||||
abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
|
||||
r, i := real(x), imag(x);
|
||||
return f16(_sqrt_f32(f32(r*r + i*i)));
|
||||
return f16(intrinsics.sqrt(f32(r*r + i*i)));
|
||||
}
|
||||
abs_complex64 :: #force_inline proc "contextless" (x: complex64) -> f32 {
|
||||
r, i := real(x), imag(x);
|
||||
return _sqrt_f32(r*r + i*i);
|
||||
return intrinsics.sqrt(r*r + i*i);
|
||||
}
|
||||
abs_complex128 :: #force_inline proc "contextless" (x: complex128) -> f64 {
|
||||
r, i := real(x), imag(x);
|
||||
return _sqrt_f64(r*r + i*i);
|
||||
return intrinsics.sqrt(r*r + i*i);
|
||||
}
|
||||
abs_quaternion64 :: #force_inline proc "contextless" (x: quaternion64) -> f16 {
|
||||
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
|
||||
return f16(_sqrt_f32(f32(r*r + i*i + j*j + k*k)));
|
||||
return f16(intrinsics.sqrt(f32(r*r + i*i + j*j + k*k)));
|
||||
}
|
||||
abs_quaternion128 :: #force_inline proc "contextless" (x: quaternion128) -> f32 {
|
||||
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
|
||||
return _sqrt_f32(r*r + i*i + j*j + k*k);
|
||||
return intrinsics.sqrt(r*r + i*i + j*j + k*k);
|
||||
}
|
||||
abs_quaternion256 :: #force_inline proc "contextless" (x: quaternion256) -> f64 {
|
||||
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
|
||||
return _sqrt_f64(r*r + i*i + j*j + k*k);
|
||||
return intrinsics.sqrt(r*r + i*i + j*j + k*k);
|
||||
}
|
||||
|
||||
|
||||
@@ -662,19 +600,19 @@ truncsfhf2 :: proc "c" (value: f32) -> u16 {
|
||||
m = i & 0x007fffff;
|
||||
|
||||
|
||||
if (e <= 0) {
|
||||
if (e < -10) {
|
||||
if e <= 0 {
|
||||
if e < -10 {
|
||||
return u16(s);
|
||||
}
|
||||
m = (m | 0x00800000) >> u32(1 - e);
|
||||
|
||||
if (m & 0x00001000) != 0 {
|
||||
if m & 0x00001000 != 0 {
|
||||
m += 0x00002000;
|
||||
}
|
||||
|
||||
return u16(s | (m >> 13));
|
||||
} else if (e == 0xff - (127 - 15)) {
|
||||
if (m == 0) {
|
||||
} else if e == 0xff - (127 - 15) {
|
||||
if m == 0 {
|
||||
return u16(s | 0x7c00); /* NOTE(bill): infinity */
|
||||
} else {
|
||||
/* NOTE(bill): NAN */
|
||||
@@ -682,7 +620,7 @@ truncsfhf2 :: proc "c" (value: f32) -> u16 {
|
||||
return u16(s | 0x7c00 | m | i32(m == 0));
|
||||
}
|
||||
} else {
|
||||
if (m & 0x00001000) != 0 {
|
||||
if m & 0x00001000 != 0 {
|
||||
m += 0x00002000;
|
||||
if (m & 0x00800000) != 0 {
|
||||
m = 0;
|
||||
@@ -690,8 +628,8 @@ truncsfhf2 :: proc "c" (value: f32) -> u16 {
|
||||
}
|
||||
}
|
||||
|
||||
if (e > 30) {
|
||||
f := 1e12;
|
||||
if e > 30 {
|
||||
f := i64(1e12);
|
||||
for j := 0; j < 10; j += 1 {
|
||||
/* NOTE(bill): Cause overflow */
|
||||
g := intrinsics.volatile_load(&f);
|
||||
@@ -740,3 +678,88 @@ gnu_f2h_ieee :: proc "c" (value: f32) -> u16 {
|
||||
extendhfsf2 :: proc "c" (value: u16) -> f32 {
|
||||
return gnu_h2f_ieee(value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(link_name="__floattidf")
|
||||
floattidf :: proc(a: i128) -> f64 {
|
||||
DBL_MANT_DIG :: 53;
|
||||
if a == 0 {
|
||||
return 0.0;
|
||||
}
|
||||
a := a;
|
||||
N :: size_of(i128) * 8;
|
||||
s := a >> (N-1);
|
||||
a = (a ~ s) - s;
|
||||
sd: = N - intrinsics.count_leading_zeros(a); // number of significant digits
|
||||
e := u32(sd - 1); // exponent
|
||||
if sd > DBL_MANT_DIG {
|
||||
switch sd {
|
||||
case DBL_MANT_DIG + 1:
|
||||
a <<= 1;
|
||||
case DBL_MANT_DIG + 2:
|
||||
// okay
|
||||
case:
|
||||
a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
|
||||
i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
|
||||
};
|
||||
|
||||
a |= i128((a & 4) != 0);
|
||||
a += 1;
|
||||
a >>= 2;
|
||||
|
||||
if a & (1 << DBL_MANT_DIG) != 0 {
|
||||
a >>= 1;
|
||||
e += 1;
|
||||
}
|
||||
} else {
|
||||
a <<= u128(DBL_MANT_DIG - sd);
|
||||
}
|
||||
fb: [2]u32;
|
||||
fb[1] = (u32(s) & 0x80000000) | // sign
|
||||
((e + 1023) << 20) | // exponent
|
||||
u32((u64(a) >> 32) & 0x000FFFFF); // mantissa-high
|
||||
fb[1] = u32(a); // mantissa-low
|
||||
return transmute(f64)fb;
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__floattidf_unsigned")
|
||||
floattidf_unsigned :: proc(a: u128) -> f64 {
|
||||
DBL_MANT_DIG :: 53;
|
||||
if a == 0 {
|
||||
return 0.0;
|
||||
}
|
||||
a := a;
|
||||
N :: size_of(u128) * 8;
|
||||
sd: = N - intrinsics.count_leading_zeros(a); // number of significant digits
|
||||
e := u32(sd - 1); // exponent
|
||||
if sd > DBL_MANT_DIG {
|
||||
switch sd {
|
||||
case DBL_MANT_DIG + 1:
|
||||
a <<= 1;
|
||||
case DBL_MANT_DIG + 2:
|
||||
// okay
|
||||
case:
|
||||
a = u128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
|
||||
u128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
|
||||
};
|
||||
|
||||
a |= u128((a & 4) != 0);
|
||||
a += 1;
|
||||
a >>= 2;
|
||||
|
||||
if a & (1 << DBL_MANT_DIG) != 0 {
|
||||
a >>= 1;
|
||||
e += 1;
|
||||
}
|
||||
} else {
|
||||
a <<= u128(DBL_MANT_DIG - sd);
|
||||
}
|
||||
fb: [2]u32;
|
||||
fb[1] = (0) | // sign
|
||||
((e + 1023) << 20) | // exponent
|
||||
u32((u64(a) >> 32) & 0x000FFFFF); // mantissa-high
|
||||
fb[1] = u32(a); // mantissa-low
|
||||
return transmute(f64)fb;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package runtime
|
||||
|
||||
import "intrinsics"
|
||||
|
||||
@(link_name="__umodti3")
|
||||
umodti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
r: u128 = ---;
|
||||
@@ -85,51 +87,3 @@ fixdfti :: proc(a: u64) -> i128 {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@(default_calling_convention = "none")
|
||||
foreign {
|
||||
@(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__floattidf")
|
||||
floattidf :: proc(a: i128) -> f64 {
|
||||
DBL_MANT_DIG :: 53;
|
||||
if a == 0 {
|
||||
return 0.0;
|
||||
}
|
||||
a := a;
|
||||
N :: size_of(i128) * 8;
|
||||
s := a >> (N-1);
|
||||
a = (a ~ s) - s;
|
||||
sd: = N - _clz_i128(a); // number of significant digits
|
||||
e := u32(sd - 1); // exponent
|
||||
if sd > DBL_MANT_DIG {
|
||||
switch sd {
|
||||
case DBL_MANT_DIG + 1:
|
||||
a <<= 1;
|
||||
case DBL_MANT_DIG + 2:
|
||||
// okay
|
||||
case:
|
||||
a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
|
||||
i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
|
||||
};
|
||||
|
||||
a |= i128((a & 4) != 0);
|
||||
a += 1;
|
||||
a >>= 2;
|
||||
|
||||
if a & (1 << DBL_MANT_DIG) != 0 {
|
||||
a >>= 1;
|
||||
e += 1;
|
||||
}
|
||||
} else {
|
||||
a <<= u128(DBL_MANT_DIG - sd);
|
||||
}
|
||||
fb: [2]u32;
|
||||
fb[1] = (u32(s) & 0x80000000) | // sign
|
||||
((e + 1023) << 20) | // exponent
|
||||
u32((u64(a) >> 32) & 0x000FFFFF); // mantissa-high
|
||||
fb[1] = u32(a); // mantissa-low
|
||||
return transmute(f64)fb;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package runtime
|
||||
|
||||
import "intrinsics"
|
||||
|
||||
@(link_name="__umodti3")
|
||||
umodti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
r: u128 = ---;
|
||||
@@ -85,51 +87,3 @@ fixdfti :: proc(a: u64) -> i128 {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@(default_calling_convention = "none")
|
||||
foreign {
|
||||
@(link_name="llvm.ctlz.i128") _clz_i128 :: proc(x: i128, is_zero_undef := false) -> i128 ---
|
||||
}
|
||||
|
||||
|
||||
@(link_name="__floattidf")
|
||||
floattidf :: proc(a: i128) -> f64 {
|
||||
DBL_MANT_DIG :: 53;
|
||||
if a == 0 {
|
||||
return 0.0;
|
||||
}
|
||||
a := a;
|
||||
N :: size_of(i128) * 8;
|
||||
s := a >> (N-1);
|
||||
a = (a ~ s) - s;
|
||||
sd: = N - _clz_i128(a); // number of significant digits
|
||||
e := u32(sd - 1); // exponent
|
||||
if sd > DBL_MANT_DIG {
|
||||
switch sd {
|
||||
case DBL_MANT_DIG + 1:
|
||||
a <<= 1;
|
||||
case DBL_MANT_DIG + 2:
|
||||
// okay
|
||||
case:
|
||||
a = i128(u128(a) >> u128(sd - (DBL_MANT_DIG+2))) |
|
||||
i128(u128(a) & (~u128(0) >> u128(N + DBL_MANT_DIG+2 - sd)) != 0);
|
||||
};
|
||||
|
||||
a |= i128((a & 4) != 0);
|
||||
a += 1;
|
||||
a >>= 2;
|
||||
|
||||
if a & (1 << DBL_MANT_DIG) != 0 {
|
||||
a >>= 1;
|
||||
e += 1;
|
||||
}
|
||||
} else {
|
||||
a <<= u128(DBL_MANT_DIG - sd);
|
||||
}
|
||||
fb: [2]u32;
|
||||
fb[1] = (u32(s) & 0x80000000) | // sign
|
||||
((e + 1023) << 20) | // exponent
|
||||
u32((u64(a) >> 32) & 0x000FFFFF); // mantissa-high
|
||||
fb[1] = u32(a); // mantissa-low
|
||||
return transmute(f64)fb;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
package runtime
|
||||
|
||||
_OS_Errno :: distinct int;
|
||||
|
||||
os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
return _os_write(data);
|
||||
}
|
||||
|
||||
@@ -6,12 +6,8 @@ import "core:os"
|
||||
|
||||
// TODO(bill): reimplement `os.write` so that it does not rely on package os
|
||||
// NOTE: Use os_specific_linux.odin, os_specific_darwin.odin, etc
|
||||
os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
context = default_context();
|
||||
n, err := os.write(os.stderr, data);
|
||||
return int(n), _OS_Errno(err);
|
||||
}
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return os.current_thread_id();
|
||||
}
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
package runtime
|
||||
|
||||
// TODO(bill): reimplement `os.write`
|
||||
os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
return 0, -1;
|
||||
}
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
//+private
|
||||
//+build windows
|
||||
package runtime
|
||||
|
||||
import "intrinsics"
|
||||
|
||||
foreign import kernel32 "system:Kernel32.lib"
|
||||
|
||||
@(private="file")
|
||||
@@ -14,9 +17,6 @@ foreign kernel32 {
|
||||
WriteFile :: proc(hFile: rawptr, lpBuffer: rawptr, nNumberOfBytesToWrite: u32, lpNumberOfBytesWritten: ^u32, lpOverlapped: rawptr) -> b32 ---
|
||||
GetLastError :: proc() -> u32 ---
|
||||
|
||||
// current_thread_id
|
||||
GetCurrentThreadId :: proc() -> u32 ---
|
||||
|
||||
// default_allocator
|
||||
GetProcessHeap :: proc() -> rawptr ---
|
||||
HeapAlloc :: proc(hHeap: rawptr, dwFlags: u32, dwBytes: uint) -> rawptr ---
|
||||
@@ -24,7 +24,7 @@ foreign kernel32 {
|
||||
HeapFree :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr) -> b32 ---
|
||||
}
|
||||
|
||||
os_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) {
|
||||
_os_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) #no_bounds_check {
|
||||
if len(data) == 0 {
|
||||
return 0, 0;
|
||||
}
|
||||
@@ -58,11 +58,6 @@ os_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) {
|
||||
return;
|
||||
}
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return int(GetCurrentThreadId());
|
||||
}
|
||||
|
||||
|
||||
heap_alloc :: proc "contextless" (size: int) -> rawptr {
|
||||
HEAP_ZERO_MEMORY :: 0x00000008;
|
||||
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, uint(size));
|
||||
@@ -86,89 +81,58 @@ heap_free :: proc "contextless" (ptr: rawptr) {
|
||||
HeapFree(GetProcessHeap(), 0, ptr);
|
||||
}
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
|
||||
//
|
||||
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
||||
// padding. We also store the original pointer returned by heap_alloc right before
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
//
|
||||
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
||||
// padding. We also store the original pointer returned by heap_alloc right before
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr));
|
||||
space := size + a - 1;
|
||||
|
||||
allocated_mem: rawptr;
|
||||
if old_ptr != nil {
|
||||
original_old_ptr := ptr_offset((^rawptr)(old_ptr), -1)^;
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr));
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr));
|
||||
}
|
||||
aligned_mem := rawptr(ptr_offset((^u8)(allocated_mem), size_of(rawptr)));
|
||||
|
||||
ptr := uintptr(aligned_mem);
|
||||
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
|
||||
diff := int(aligned_ptr - ptr);
|
||||
if (size + diff) > space {
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
|
||||
aligned_mem = rawptr(aligned_ptr);
|
||||
ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
|
||||
|
||||
return byte_slice(aligned_mem, size), nil;
|
||||
}
|
||||
|
||||
aligned_free :: proc "contextless" (p: rawptr) {
|
||||
if p != nil {
|
||||
heap_free(ptr_offset((^rawptr)(p), -1)^);
|
||||
}
|
||||
}
|
||||
|
||||
aligned_resize :: proc "contextless" (p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, Allocator_Error) {
|
||||
if p == nil {
|
||||
return nil, nil;
|
||||
}
|
||||
return aligned_alloc(new_size, new_alignment, p);
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return aligned_alloc(size, alignment);
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory);
|
||||
|
||||
case .Free_All:
|
||||
// NOTE(tetra): Do nothing.
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return aligned_alloc(size, alignment);
|
||||
}
|
||||
return aligned_resize(old_memory, old_size, size, alignment);
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory);
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features};
|
||||
}
|
||||
return nil, nil;
|
||||
|
||||
case .Query_Info:
|
||||
_windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, Allocator_Error) {
|
||||
if size == 0 {
|
||||
_windows_default_free(old_ptr);
|
||||
return nil, nil;
|
||||
}
|
||||
|
||||
return nil, nil;
|
||||
a := max(alignment, align_of(rawptr));
|
||||
space := size + a - 1;
|
||||
|
||||
allocated_mem: rawptr;
|
||||
if old_ptr != nil {
|
||||
original_old_ptr := intrinsics.ptr_offset((^rawptr)(old_ptr), -1)^;
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr));
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr));
|
||||
}
|
||||
aligned_mem := rawptr(intrinsics.ptr_offset((^u8)(allocated_mem), size_of(rawptr)));
|
||||
|
||||
ptr := uintptr(aligned_mem);
|
||||
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
|
||||
diff := int(aligned_ptr - ptr);
|
||||
if (size + diff) > space {
|
||||
return nil, .Out_Of_Memory;
|
||||
}
|
||||
|
||||
aligned_mem = rawptr(aligned_ptr);
|
||||
intrinsics.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
|
||||
|
||||
return byte_slice(aligned_mem, size), nil;
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
data = nil,
|
||||
};
|
||||
_windows_default_alloc :: proc "contextless" (size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
return _windows_default_alloc_or_resize(size, alignment, nil);
|
||||
}
|
||||
|
||||
|
||||
_windows_default_free :: proc "contextless" (ptr: rawptr) {
|
||||
if ptr != nil {
|
||||
heap_free(intrinsics.ptr_offset((^rawptr)(ptr), -1)^);
|
||||
}
|
||||
}
|
||||
|
||||
_windows_default_resize :: proc "contextless" (p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, Allocator_Error) {
|
||||
return _windows_default_alloc_or_resize(new_size, new_alignment, p);
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ print_encoded_rune :: proc "contextless" (r: rune) {
|
||||
print_byte('\'');
|
||||
}
|
||||
|
||||
print_rune :: proc "contextless" (r: rune) -> (int, _OS_Errno) {
|
||||
print_rune :: proc "contextless" (r: rune) -> (int, _OS_Errno) #no_bounds_check {
|
||||
RUNE_SELF :: 0x80;
|
||||
|
||||
if r < RUNE_SELF {
|
||||
@@ -98,7 +98,7 @@ print_rune :: proc "contextless" (r: rune) -> (int, _OS_Errno) {
|
||||
}
|
||||
|
||||
|
||||
print_u64 :: proc "contextless" (x: u64) {
|
||||
print_u64 :: proc "contextless" (x: u64) #no_bounds_check {
|
||||
digits := _INTEGER_DIGITS;
|
||||
|
||||
a: [129]byte;
|
||||
@@ -115,7 +115,7 @@ print_u64 :: proc "contextless" (x: u64) {
|
||||
}
|
||||
|
||||
|
||||
print_i64 :: proc "contextless" (x: i64) {
|
||||
print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
|
||||
digits := _INTEGER_DIGITS;
|
||||
b :: i64(10);
|
||||
|
||||
|
||||
@@ -1,18 +1,22 @@
|
||||
package runtime
|
||||
|
||||
import "core:sys/es"
|
||||
|
||||
@(link_name="memset")
|
||||
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
|
||||
return es.CRTmemset(ptr, val, len);
|
||||
addr := 0x1000 + 196 * size_of(int);
|
||||
fp := (rawptr(((^uintptr)(uintptr(addr)))^));
|
||||
return ((proc "c" (rawptr, i32, int) -> rawptr)(fp))(ptr, val, len);
|
||||
}
|
||||
|
||||
@(link_name="memmove")
|
||||
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||
return es.CRTmemmove(dst, src, len);
|
||||
addr := 0x1000 + 195 * size_of(int);
|
||||
fp := (rawptr(((^uintptr)(uintptr(addr)))^));
|
||||
return ((proc "c" (rawptr, rawptr, int) -> rawptr)(fp))(dst, src, len);
|
||||
}
|
||||
|
||||
@(link_name="memcpy")
|
||||
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||
return es.CRTmemcpy(dst, src, len);
|
||||
addr := 0x1000 + 194 * size_of(int);
|
||||
fp := (rawptr(((^uintptr)(uintptr(addr)))^));
|
||||
return ((proc "c" (rawptr, rawptr, int) -> rawptr)(fp))(dst, src, len);
|
||||
}
|
||||
|
||||
@@ -1,41 +1,17 @@
|
||||
package runtime
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.cttz.i8") _ctz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
|
||||
@(link_name="llvm.cttz.i16") _ctz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
|
||||
@(link_name="llvm.cttz.i32") _ctz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
|
||||
@(link_name="llvm.cttz.i64") _ctz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
|
||||
}
|
||||
_ctz :: proc{
|
||||
_ctz_u8,
|
||||
_ctz_u16,
|
||||
_ctz_u32,
|
||||
_ctz_u64,
|
||||
};
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.ctlz.i8") _clz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
|
||||
@(link_name="llvm.ctlz.i16") _clz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
|
||||
@(link_name="llvm.ctlz.i32") _clz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
|
||||
@(link_name="llvm.ctlz.i64") _clz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
|
||||
}
|
||||
_clz :: proc{
|
||||
_clz_u8,
|
||||
_clz_u16,
|
||||
_clz_u32,
|
||||
_clz_u64,
|
||||
};
|
||||
|
||||
import "intrinsics"
|
||||
|
||||
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||
_ctz :: intrinsics.count_trailing_zeros;
|
||||
_clz :: intrinsics.count_leading_zeros;
|
||||
|
||||
n := transmute([2]u64)a;
|
||||
d := transmute([2]u64)b;
|
||||
q, r: [2]u64 = ---, ---;
|
||||
sr: u32 = 0;
|
||||
|
||||
low :: ODIN_ENDIAN == "big" ? 1 : 0;
|
||||
low :: 1 when ODIN_ENDIAN == "big" else 0;
|
||||
high :: 1 - low;
|
||||
U64_BITS :: 8*size_of(u64);
|
||||
U128_BITS :: 8*size_of(u128);
|
||||
|
||||
+2
-2
@@ -27,12 +27,12 @@ map_values :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (values:
|
||||
return;
|
||||
}
|
||||
|
||||
Map_Entry :: struct(Key, Value: typeid) {
|
||||
Map_Entry :: struct($Key, $Value: typeid) {
|
||||
key: Key,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
Map_Entry_Info :: struct(Key, Value: typeid) {
|
||||
Map_Entry_Info :: struct($Key, $Value: typeid) {
|
||||
hash: uintptr,
|
||||
key: Key,
|
||||
value: Value,
|
||||
|
||||
@@ -16,9 +16,9 @@ _digit_value :: proc(r: rune) -> int {
|
||||
ri := int(r);
|
||||
v: int = 16;
|
||||
switch r {
|
||||
case '0'..'9': v = ri-'0';
|
||||
case 'a'..'z': v = ri-'a'+10;
|
||||
case 'A'..'Z': v = ri-'A'+10;
|
||||
case '0'..='9': v = ri-'0';
|
||||
case 'a'..='z': v = ri-'a'+10;
|
||||
case 'A'..='Z': v = ri-'A'+10;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
@@ -557,9 +557,9 @@ quote_rune :: proc(buf: []byte, r: rune) -> string {
|
||||
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
|
||||
hex_to_int :: proc(c: byte) -> int {
|
||||
switch c {
|
||||
case '0'..'9': return int(c-'0');
|
||||
case 'a'..'f': return int(c-'a')+10;
|
||||
case 'A'..'F': return int(c-'A')+10;
|
||||
case '0'..='9': return int(c-'0');
|
||||
case 'a'..='f': return int(c-'a')+10;
|
||||
case 'A'..='F': return int(c-'A')+10;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@@ -597,7 +597,7 @@ unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool
|
||||
case '"': r = '"';
|
||||
case '\'': r = '\'';
|
||||
|
||||
case '0'..'7':
|
||||
case '0'..='7':
|
||||
v := int(c-'0');
|
||||
if len(s) < 2 {
|
||||
return;
|
||||
|
||||
@@ -221,7 +221,7 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
|
||||
}
|
||||
|
||||
|
||||
@(private, static)
|
||||
@(private)
|
||||
DIGITS_LOWER := "0123456789abcdefx";
|
||||
|
||||
write_quoted_string :: proc{
|
||||
@@ -314,9 +314,9 @@ write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe
|
||||
is_printable :: proc(r: rune) -> bool {
|
||||
if r <= 0xff {
|
||||
switch r {
|
||||
case 0x20..0x7e:
|
||||
case 0x20..=0x7e:
|
||||
return true;
|
||||
case 0xa1..0xff: // ¡ through ÿ except for the soft hyphen
|
||||
case 0xa1..=0xff: // ¡ through ÿ except for the soft hyphen
|
||||
return r != 0xad; //
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user