mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-17 03:12:22 -07:00
Merge remote-tracking branch 'offical/master'
This commit is contained in:
+69
-166
@@ -29,186 +29,101 @@ jobs:
|
||||
gmake release
|
||||
./odin version
|
||||
./odin report
|
||||
gmake -C vendor/stb/src
|
||||
gmake -C vendor/cgltf/src
|
||||
gmake -C vendor/miniaudio/src
|
||||
./odin check examples/all -vet -strict-style -target:netbsd_amd64
|
||||
./odin check examples/all -vet -strict-style -target:netbsd_arm64
|
||||
(cd tests/core; gmake all_bsd)
|
||||
(cd tests/internal; gmake all_bsd)
|
||||
./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
|
||||
./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false
|
||||
./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
|
||||
./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false
|
||||
(cd tests/issues; ./run.sh)
|
||||
(cd tests/benchmark; gmake all)
|
||||
build_linux:
|
||||
name: Ubuntu Build, Check, and Test
|
||||
runs-on: ubuntu-latest
|
||||
ci:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# MacOS 13 runs on Intel, 14 runs on ARM
|
||||
os: [ubuntu-latest, macos-13, macos-14]
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: ${{ matrix.os == 'macos-14' && 'MacOS ARM' || (matrix.os == 'macos-13' && 'MacOS Intel' || 'Ubuntu') }} Build, Check, and Test
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Download LLVM (Linux)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 17
|
||||
echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
run: ./build_odin.sh release
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Compile needed Vendor
|
||||
run: |
|
||||
make -C $(./odin root)/vendor/stb/src
|
||||
make -C $(./odin root)/vendor/cgltf/src
|
||||
make -C $(./odin root)/vendor/miniaudio/src
|
||||
timeout-minutes: 10
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo -vet
|
||||
timeout-minutes: 10
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo
|
||||
timeout-minutes: 10
|
||||
- name: Odin run -debug
|
||||
run: ./odin run examples/demo -debug
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all
|
||||
run: ./odin check examples/all -strict-style
|
||||
timeout-minutes: 10
|
||||
- name: Core library tests
|
||||
run: |
|
||||
cd tests/core
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Vendor library tests
|
||||
run: |
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin internals tests
|
||||
run: |
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin core library benchmarks
|
||||
run: |
|
||||
cd tests/benchmark
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Linux i386
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Linux arm64
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for FreeBSD amd64
|
||||
run: ./odin check examples/all -vet -strict-style -target:freebsd_amd64
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for OpenBSD amd64
|
||||
run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
|
||||
timeout-minutes: 10
|
||||
build_macOS:
|
||||
name: MacOS Build, Check, and Test
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM, and setup PATH
|
||||
|
||||
- name: Download LLVM (MacOS Intel)
|
||||
if: matrix.os == 'macos-13'
|
||||
run: |
|
||||
brew install llvm@17
|
||||
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
run: ./build_odin.sh release
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Compile needed Vendor
|
||||
run: |
|
||||
make -C $(./odin root)/vendor/stb/src
|
||||
make -C $(./odin root)/vendor/cgltf/src
|
||||
make -C $(./odin root)/vendor/miniaudio/src
|
||||
timeout-minutes: 10
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo -vet
|
||||
timeout-minutes: 10
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo
|
||||
timeout-minutes: 10
|
||||
- name: Odin run -debug
|
||||
run: ./odin run examples/demo -debug
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all
|
||||
run: ./odin check examples/all -strict-style
|
||||
timeout-minutes: 10
|
||||
- name: Core library tests
|
||||
run: |
|
||||
cd tests/core
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin internals tests
|
||||
run: |
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin core library benchmarks
|
||||
run: |
|
||||
cd tests/benchmark
|
||||
make
|
||||
timeout-minutes: 10
|
||||
build_macOS_arm:
|
||||
name: MacOS ARM Build, Check, and Test
|
||||
runs-on: macos-14 # This is an arm/m1 runner.
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
|
||||
- name: Download LLVM (MacOS ARM)
|
||||
if: matrix.os == 'macos-14'
|
||||
run: |
|
||||
brew install llvm@17
|
||||
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
|
||||
- name: Build Odin
|
||||
run: ./build_odin.sh release
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Compile needed Vendor
|
||||
run: |
|
||||
make -C $(./odin root)/vendor/stb/src
|
||||
make -C $(./odin root)/vendor/cgltf/src
|
||||
make -C $(./odin root)/vendor/miniaudio/src
|
||||
timeout-minutes: 10
|
||||
make -C vendor/stb/src
|
||||
make -C vendor/cgltf/src
|
||||
make -C vendor/miniaudio/src
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo -vet
|
||||
timeout-minutes: 10
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo
|
||||
timeout-minutes: 10
|
||||
- name: Odin run -debug
|
||||
run: ./odin run examples/demo -debug
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all
|
||||
run: ./odin check examples/all -strict-style
|
||||
timeout-minutes: 10
|
||||
- name: Core library tests
|
||||
- name: Normal Core library tests
|
||||
run: ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
|
||||
- name: Optimized Core library tests
|
||||
run: ./odin test tests/core/speed.odin -o:speed -file -all-packages -define:ODIN_TEST_FANCY=false
|
||||
- name: Vendor library tests
|
||||
run: ./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
|
||||
- name: Internals tests
|
||||
run: ./odin test tests/internal -all-packages -define:ODIN_TEST_FANCY=false
|
||||
- name: Core library benchmarks
|
||||
run: ./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false
|
||||
- name: GitHub Issue tests
|
||||
run: |
|
||||
cd tests/core
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin internals tests
|
||||
run: |
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin core library benchmarks
|
||||
run: |
|
||||
cd tests/benchmark
|
||||
make
|
||||
timeout-minutes: 10
|
||||
cd tests/issues
|
||||
./run.sh
|
||||
|
||||
- name: Odin check examples/all for Linux i386
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
- name: Odin check examples/all for Linux arm64
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
- name: Odin check examples/all for FreeBSD amd64
|
||||
run: ./odin check examples/all -vet -strict-style -target:freebsd_amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
- name: Odin check examples/all for OpenBSD amd64
|
||||
run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
|
||||
build_windows:
|
||||
name: Windows Build, Check, and Test
|
||||
runs-on: windows-2022
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -216,79 +131,67 @@ jobs:
|
||||
./build.bat 1
|
||||
- name: Odin version
|
||||
run: ./odin version
|
||||
timeout-minutes: 1
|
||||
- name: Odin report
|
||||
run: ./odin report
|
||||
timeout-minutes: 1
|
||||
- name: Odin check
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check examples/demo -vet
|
||||
timeout-minutes: 10
|
||||
- name: Odin run
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin run examples/demo
|
||||
timeout-minutes: 10
|
||||
- name: Odin run -debug
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin run examples/demo -debug
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check examples/all -strict-style
|
||||
timeout-minutes: 10
|
||||
- name: Core library tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\core
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
|
||||
- name: Optimized core library tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin test tests/core/speed.odin -o:speed -file -all-packages -define:ODIN_TEST_FANCY=false
|
||||
- name: Core library benchmarks
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\benchmark
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false
|
||||
- name: Vendor library tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\vendor
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
|
||||
- name: Odin internals tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\internal
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
odin test tests/internal -all-packages -define:ODIN_TEST_FANCY=false
|
||||
- name: Odin documentation tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\documentation
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: core:math/big tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\core\math\big
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Windows 32bits
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check examples/all -strict-style -target:windows_i386
|
||||
timeout-minutes: 10
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
if: github.repository == 'odin-lang/Odin'
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
if: github.repository == 'odin-lang/Odin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
- name: (Linux) Download LLVM
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
@@ -79,7 +79,7 @@ jobs:
|
||||
if: github.repository == 'odin-lang/Odin'
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm@17 dylibbundler
|
||||
@@ -113,7 +113,7 @@ jobs:
|
||||
if: github.repository == 'odin-lang/Odin'
|
||||
runs-on: macos-14 # ARM machine
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm@17 dylibbundler
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
runs-on: [ubuntu-latest]
|
||||
needs: [build_windows, build_macos, build_macos_arm, build_ubuntu]
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.8.x'
|
||||
@@ -193,4 +193,4 @@ jobs:
|
||||
python3 ci/nightly.py artifact macos-amd64 macos_artifacts/dist.zip
|
||||
python3 ci/nightly.py artifact macos-arm64 macos_arm_artifacts/dist.zip
|
||||
python3 ci/nightly.py prune
|
||||
python3 ci/nightly.py json
|
||||
python3 ci/nightly.py json
|
||||
|
||||
+13
-2
@@ -701,7 +701,7 @@ default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code
|
||||
when ODIN_OS == .Freestanding {
|
||||
// Do nothing
|
||||
} else {
|
||||
when !ODIN_DISABLE_ASSERT {
|
||||
when ODIN_OS != .Orca && !ODIN_DISABLE_ASSERT {
|
||||
print_caller_location(loc)
|
||||
print_string(" ")
|
||||
}
|
||||
@@ -710,7 +710,18 @@ default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code
|
||||
print_string(": ")
|
||||
print_string(message)
|
||||
}
|
||||
print_byte('\n')
|
||||
|
||||
when ODIN_OS == .Orca {
|
||||
assert_fail(
|
||||
cstring(raw_data(loc.file_path)),
|
||||
cstring(raw_data(loc.procedure)),
|
||||
loc.line,
|
||||
"",
|
||||
cstring(raw_data(orca_stderr_buffer[:orca_stderr_buffer_idx])),
|
||||
)
|
||||
} else {
|
||||
print_byte('\n')
|
||||
}
|
||||
}
|
||||
trap()
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
} else when ODIN_DEFAULT_TO_PANIC_ALLOCATOR {
|
||||
default_allocator_proc :: panic_allocator_proc
|
||||
default_allocator :: panic_allocator
|
||||
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
|
||||
} else when ODIN_OS != .Orca && (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32) {
|
||||
default_allocator :: default_wasm_allocator
|
||||
default_allocator_proc :: wasm_allocator_proc
|
||||
} else {
|
||||
|
||||
@@ -6,15 +6,29 @@ package runtime
|
||||
import "base:intrinsics"
|
||||
|
||||
when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
|
||||
@(link_name="_start", linkage="strong", require, export)
|
||||
_start :: proc "c" () {
|
||||
context = default_context()
|
||||
#force_no_inline _startup_runtime()
|
||||
intrinsics.__entry_point()
|
||||
when ODIN_OS == .Orca {
|
||||
@(linkage="strong", require, export)
|
||||
oc_on_init :: proc "c" () {
|
||||
context = default_context()
|
||||
#force_no_inline _startup_runtime()
|
||||
intrinsics.__entry_point()
|
||||
}
|
||||
@(linkage="strong", require, export)
|
||||
oc_on_terminate :: proc "c" () {
|
||||
context = default_context()
|
||||
#force_no_inline _cleanup_runtime()
|
||||
}
|
||||
} else {
|
||||
@(link_name="_start", linkage="strong", require, export)
|
||||
_start :: proc "c" () {
|
||||
context = default_context()
|
||||
#force_no_inline _startup_runtime()
|
||||
intrinsics.__entry_point()
|
||||
}
|
||||
@(link_name="_end", linkage="strong", require, export)
|
||||
_end :: proc "c" () {
|
||||
context = default_context()
|
||||
#force_no_inline _cleanup_runtime()
|
||||
}
|
||||
}
|
||||
@(link_name="_end", linkage="strong", require, export)
|
||||
_end :: proc "c" () {
|
||||
context = default_context()
|
||||
#force_no_inline _cleanup_runtime()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ package runtime
|
||||
bounds_trap :: proc "contextless" () -> ! {
|
||||
when ODIN_OS == .Windows {
|
||||
windows_trap_array_bounds()
|
||||
} else when ODIN_OS == .Orca {
|
||||
abort_ext("", "", 0, "bounds trap")
|
||||
} else {
|
||||
trap()
|
||||
}
|
||||
@@ -13,6 +15,8 @@ bounds_trap :: proc "contextless" () -> ! {
|
||||
type_assertion_trap :: proc "contextless" () -> ! {
|
||||
when ODIN_OS == .Windows {
|
||||
windows_trap_type_assertion()
|
||||
} else when ODIN_OS == .Orca {
|
||||
abort_ext("", "", 0, "type assertion trap")
|
||||
} else {
|
||||
trap()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
//+build orca
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
foreign {
|
||||
@(link_name="malloc") _orca_malloc :: proc "c" (size: int) -> rawptr ---
|
||||
@(link_name="calloc") _orca_calloc :: proc "c" (num, size: int) -> rawptr ---
|
||||
@(link_name="free") _orca_free :: proc "c" (ptr: rawptr) ---
|
||||
@(link_name="realloc") _orca_realloc :: proc "c" (ptr: rawptr, size: int) -> rawptr ---
|
||||
}
|
||||
|
||||
_heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _orca_calloc(1, size)
|
||||
} else {
|
||||
return _orca_malloc(size)
|
||||
}
|
||||
}
|
||||
|
||||
_heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
return _orca_realloc(ptr, new_size)
|
||||
}
|
||||
|
||||
_heap_free :: proc(ptr: rawptr) {
|
||||
_orca_free(ptr)
|
||||
}
|
||||
@@ -12,4 +12,4 @@ _heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
|
||||
_heap_free :: proc(ptr: rawptr) {
|
||||
unimplemented("base:runtime 'heap_free' procedure is not supported on this platform")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -483,7 +483,7 @@ quaternion256_ne :: #force_inline proc "contextless" (a, b: quaternion256) -> bo
|
||||
string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int) {
|
||||
// NOTE(bill): Duplicated here to remove dependency on package unicode/utf8
|
||||
|
||||
@static accept_sizes := [256]u8{
|
||||
@(static, rodata) accept_sizes := [256]u8{
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
|
||||
@@ -504,7 +504,7 @@ string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int
|
||||
}
|
||||
Accept_Range :: struct {lo, hi: u8}
|
||||
|
||||
@static accept_ranges := [5]Accept_Range{
|
||||
@(static, rodata) accept_ranges := [5]Accept_Range{
|
||||
{0x80, 0xbf},
|
||||
{0xa0, 0xbf},
|
||||
{0x80, 0x9f},
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
//+build orca
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
// Constants allowing to specify the level of logging verbosity.
|
||||
log_level :: enum u32 {
|
||||
// Only errors are logged.
|
||||
ERROR = 0,
|
||||
// Only warnings and errors are logged.
|
||||
WARNING = 1,
|
||||
// All messages are logged.
|
||||
INFO = 2,
|
||||
COUNT = 3,
|
||||
}
|
||||
|
||||
@(default_calling_convention="c", link_prefix="oc_")
|
||||
foreign {
|
||||
abort_ext :: proc(file: cstring, function: cstring, line: i32, fmt: cstring, #c_vararg args: ..any) -> ! ---
|
||||
assert_fail :: proc(file: cstring, function: cstring, line: i32, src: cstring, fmt: cstring, #c_vararg args: ..any) -> ! ---
|
||||
log_ext :: proc(level: log_level, function: cstring, file: cstring, line: i32, fmt: cstring, #c_vararg args: ..any) ---
|
||||
}
|
||||
|
||||
// NOTE: This is all pretty gross, don't look.
|
||||
|
||||
// WASM is single threaded so this should be fine.
|
||||
orca_stderr_buffer: [4096]byte
|
||||
orca_stderr_buffer_idx: int
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
for b in data {
|
||||
orca_stderr_buffer[orca_stderr_buffer_idx] = b
|
||||
orca_stderr_buffer_idx += 1
|
||||
|
||||
if b == '\n' || orca_stderr_buffer_idx == len(orca_stderr_buffer)-1 {
|
||||
log_ext(.ERROR, "", "", 0, cstring(raw_data(orca_stderr_buffer[:orca_stderr_buffer_idx])))
|
||||
orca_stderr_buffer_idx = 0
|
||||
}
|
||||
}
|
||||
|
||||
return len(data), 0
|
||||
}
|
||||
@@ -25,7 +25,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
||||
RtlMoveMemory(dst, src, len)
|
||||
return dst
|
||||
}
|
||||
} else when ODIN_NO_CRT || (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32) {
|
||||
} else when ODIN_NO_CRT || (ODIN_OS != .Orca && (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32)) {
|
||||
// NOTE: on wasm, calls to these procs are generated (by LLVM) with type `i32` instead of `int`.
|
||||
//
|
||||
// NOTE: `#any_int` is also needed, because calls that we generate (and package code)
|
||||
|
||||
@@ -7,20 +7,20 @@ import "base:intrinsics"
|
||||
Port of emmalloc, modified for use in Odin.
|
||||
|
||||
Invariants:
|
||||
- Per-allocation header overhead is 8 bytes, smallest allocated payload
|
||||
amount is 8 bytes, and a multiple of 4 bytes.
|
||||
- Acquired memory blocks are subdivided into disjoint regions that lie
|
||||
next to each other.
|
||||
- A region is either in used or free.
|
||||
Used regions may be adjacent, and a used and unused region
|
||||
may be adjacent, but not two unused ones - they would be
|
||||
merged.
|
||||
- Memory allocation takes constant time, unless the alloc needs to wasm_memory_grow()
|
||||
or memory is very close to being exhausted.
|
||||
- Free and used regions are managed inside "root regions", which are slabs
|
||||
of memory acquired via wasm_memory_grow().
|
||||
- Memory retrieved using wasm_memory_grow() can not be given back to the OS.
|
||||
Therefore, frees are internal to the allocator.
|
||||
- Per-allocation header overhead is 8 bytes, smallest allocated payload
|
||||
amount is 8 bytes, and a multiple of 4 bytes.
|
||||
- Acquired memory blocks are subdivided into disjoint regions that lie
|
||||
next to each other.
|
||||
- A region is either in used or free.
|
||||
Used regions may be adjacent, and a used and unused region
|
||||
may be adjacent, but not two unused ones - they would be
|
||||
merged.
|
||||
- Memory allocation takes constant time, unless the alloc needs to wasm_memory_grow()
|
||||
or memory is very close to being exhausted.
|
||||
- Free and used regions are managed inside "root regions", which are slabs
|
||||
of memory acquired via wasm_memory_grow().
|
||||
- Memory retrieved using wasm_memory_grow() can not be given back to the OS.
|
||||
Therefore, frees are internal to the allocator.
|
||||
|
||||
Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file.
|
||||
|
||||
|
||||
+1
-1
@@ -1495,7 +1495,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
|
||||
u := u64(uintptr(p))
|
||||
switch verb {
|
||||
case 'p', 'v', 'w':
|
||||
if !fi.hash && verb == 'v' {
|
||||
if !fi.hash {
|
||||
io.write_string(fi.writer, "0x", &fi.n)
|
||||
}
|
||||
_fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//+build !freestanding
|
||||
//+build !js
|
||||
//+build !orca
|
||||
package fmt
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@@ -0,0 +1,746 @@
|
||||
// package bmp implements a Microsoft BMP image reader
|
||||
package core_image_bmp
|
||||
|
||||
import "core:image"
|
||||
import "core:bytes"
|
||||
import "core:compress"
|
||||
import "core:mem"
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
|
||||
Error :: image.Error
|
||||
Image :: image.Image
|
||||
Options :: image.Options
|
||||
|
||||
RGB_Pixel :: image.RGB_Pixel
|
||||
RGBA_Pixel :: image.RGBA_Pixel
|
||||
|
||||
FILE_HEADER_SIZE :: 14
|
||||
INFO_STUB_SIZE :: FILE_HEADER_SIZE + size_of(image.BMP_Version)
|
||||
|
||||
save_to_buffer :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
if img == nil {
|
||||
return .Invalid_Input_Image
|
||||
}
|
||||
|
||||
if output == nil {
|
||||
return .Invalid_Output
|
||||
}
|
||||
|
||||
pixels := img.width * img.height
|
||||
if pixels == 0 || pixels > image.MAX_DIMENSIONS {
|
||||
return .Invalid_Input_Image
|
||||
}
|
||||
|
||||
// While the BMP spec (and our loader) support more fanciful image types,
|
||||
// `bmp.save` supports only 3 and 4 channel images with a bit depth of 8.
|
||||
if img.depth != 8 || img.channels < 3 || img.channels > 4 {
|
||||
return .Invalid_Input_Image
|
||||
}
|
||||
|
||||
if img.channels * pixels != len(img.pixels.buf) {
|
||||
return .Invalid_Input_Image
|
||||
}
|
||||
|
||||
// Calculate and allocate size.
|
||||
header_size := u32le(image.BMP_Version.V3)
|
||||
total_header_size := header_size + 14 // file header = 14
|
||||
pixel_count_bytes := u32le(align4(img.width * img.channels) * img.height)
|
||||
|
||||
header := image.BMP_Header{
|
||||
// File header
|
||||
magic = .Bitmap,
|
||||
size = total_header_size + pixel_count_bytes,
|
||||
_res1 = 0,
|
||||
_res2 = 0,
|
||||
pixel_offset = total_header_size,
|
||||
// V3
|
||||
info_size = .V3,
|
||||
width = i32le(img.width),
|
||||
height = i32le(img.height),
|
||||
planes = 1,
|
||||
bpp = u16le(8 * img.channels),
|
||||
compression = .RGB,
|
||||
image_size = pixel_count_bytes,
|
||||
pels_per_meter = {2835, 2835}, // 72 DPI
|
||||
colors_used = 0,
|
||||
colors_important = 0,
|
||||
}
|
||||
written := 0
|
||||
|
||||
if resize(&output.buf, int(header.size)) != nil {
|
||||
return .Unable_To_Allocate_Or_Resize
|
||||
}
|
||||
|
||||
header_bytes := transmute([size_of(image.BMP_Header)]u8)header
|
||||
written += int(total_header_size)
|
||||
copy(output.buf[:], header_bytes[:written])
|
||||
|
||||
switch img.channels {
|
||||
case 3:
|
||||
row_bytes := img.width * img.channels
|
||||
row_padded := align4(row_bytes)
|
||||
pixels := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
|
||||
for y in 0..<img.height {
|
||||
row_offset := row_padded * (img.height - y - 1) + written
|
||||
for x in 0..<img.width {
|
||||
pix_offset := 3 * x
|
||||
output.buf[row_offset + pix_offset + 0] = pixels[0].b
|
||||
output.buf[row_offset + pix_offset + 1] = pixels[0].g
|
||||
output.buf[row_offset + pix_offset + 2] = pixels[0].r
|
||||
pixels = pixels[1:]
|
||||
}
|
||||
}
|
||||
|
||||
case 4:
|
||||
row_bytes := img.width * img.channels
|
||||
pixels := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:])
|
||||
for y in 0..<img.height {
|
||||
row_offset := row_bytes * (img.height - y - 1) + written
|
||||
for x in 0..<img.width {
|
||||
pix_offset := 4 * x
|
||||
output.buf[row_offset + pix_offset + 0] = pixels[0].b
|
||||
output.buf[row_offset + pix_offset + 1] = pixels[0].g
|
||||
output.buf[row_offset + pix_offset + 2] = pixels[0].r
|
||||
output.buf[row_offset + pix_offset + 3] = pixels[0].a
|
||||
pixels = pixels[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
ctx := &compress.Context_Memory_Input{
|
||||
input_data = data,
|
||||
}
|
||||
|
||||
img, err = load_from_context(ctx, options, allocator)
|
||||
return img, err
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
options := options
|
||||
|
||||
// For compress.read_slice(), until that's rewritten to not use temp allocator
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
|
||||
if .info in options {
|
||||
options |= {.return_metadata, .do_not_decompress_image}
|
||||
options -= {.info}
|
||||
}
|
||||
|
||||
if .return_header in options && .return_metadata in options {
|
||||
options -= {.return_header}
|
||||
}
|
||||
|
||||
info_buf: [size_of(image.BMP_Header)]u8
|
||||
|
||||
// Read file header (14) + info size (4)
|
||||
stub_data := compress.read_slice(ctx, INFO_STUB_SIZE) or_return
|
||||
copy(info_buf[:], stub_data[:])
|
||||
stub_info := transmute(image.BMP_Header)info_buf
|
||||
|
||||
if stub_info.magic != .Bitmap {
|
||||
for v in image.BMP_Magic {
|
||||
if stub_info.magic == v {
|
||||
return img, .Unsupported_OS2_File
|
||||
}
|
||||
}
|
||||
return img, .Invalid_Signature
|
||||
}
|
||||
|
||||
info: image.BMP_Header
|
||||
switch stub_info.info_size {
|
||||
case .OS2_v1:
|
||||
// Read the remainder of the header
|
||||
os2_data := compress.read_data(ctx, image.OS2_Header) or_return
|
||||
|
||||
info = transmute(image.BMP_Header)info_buf
|
||||
info.width = i32le(os2_data.width)
|
||||
info.height = i32le(os2_data.height)
|
||||
info.planes = os2_data.planes
|
||||
info.bpp = os2_data.bpp
|
||||
|
||||
switch info.bpp {
|
||||
case 1, 4, 8, 24:
|
||||
case:
|
||||
return img, .Unsupported_BPP
|
||||
}
|
||||
|
||||
case .ABBR_16 ..= .V5:
|
||||
// Sizes include V3, V4, V5 and OS2v2 outright, but can also handle truncated headers.
|
||||
// Sometimes called BITMAPV2INFOHEADER or BITMAPV3INFOHEADER.
|
||||
// Let's just try to process it.
|
||||
|
||||
to_read := int(stub_info.info_size) - size_of(image.BMP_Version)
|
||||
info_data := compress.read_slice(ctx, to_read) or_return
|
||||
copy(info_buf[INFO_STUB_SIZE:], info_data[:])
|
||||
|
||||
// Update info struct with the rest of the data we read
|
||||
info = transmute(image.BMP_Header)info_buf
|
||||
|
||||
case:
|
||||
return img, .Unsupported_BMP_Version
|
||||
}
|
||||
|
||||
/* TODO(Jeroen): Add a "strict" option to catch these non-issues that violate spec?
|
||||
if info.planes != 1 {
|
||||
return img, .Invalid_Planes_Value
|
||||
}
|
||||
*/
|
||||
|
||||
if img == nil {
|
||||
img = new(Image)
|
||||
}
|
||||
img.which = .BMP
|
||||
|
||||
img.metadata = new_clone(image.BMP_Info{
|
||||
info = info,
|
||||
})
|
||||
|
||||
img.width = abs(int(info.width))
|
||||
img.height = abs(int(info.height))
|
||||
img.channels = 3
|
||||
img.depth = 8
|
||||
|
||||
if img.width == 0 || img.height == 0 {
|
||||
return img, .Invalid_Image_Dimensions
|
||||
}
|
||||
|
||||
total_pixels := abs(img.width * img.height)
|
||||
if total_pixels > image.MAX_DIMENSIONS {
|
||||
return img, .Image_Dimensions_Too_Large
|
||||
}
|
||||
|
||||
// TODO(Jeroen): Handle RGBA.
|
||||
switch info.compression {
|
||||
case .Bit_Fields, .Alpha_Bit_Fields:
|
||||
switch info.bpp {
|
||||
case 16, 32:
|
||||
make_output(img, allocator) or_return
|
||||
decode_rgb(ctx, img, info, allocator) or_return
|
||||
case:
|
||||
if is_os2(info.info_size) {
|
||||
return img, .Unsupported_Compression
|
||||
}
|
||||
return img, .Unsupported_BPP
|
||||
}
|
||||
case .RGB:
|
||||
make_output(img, allocator) or_return
|
||||
decode_rgb(ctx, img, info, allocator) or_return
|
||||
case .RLE4, .RLE8:
|
||||
make_output(img, allocator) or_return
|
||||
decode_rle(ctx, img, info, allocator) or_return
|
||||
case .CMYK, .CMYK_RLE4, .CMYK_RLE8: fallthrough
|
||||
case .PNG, .JPEG: fallthrough
|
||||
case: return img, .Unsupported_Compression
|
||||
}
|
||||
|
||||
// Flipped vertically
|
||||
if info.height < 0 {
|
||||
pixels := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
|
||||
for y in 0..<img.height / 2 {
|
||||
for x in 0..<img.width {
|
||||
top := y * img.width + x
|
||||
bot := (img.height - y - 1) * img.width + x
|
||||
|
||||
pixels[top], pixels[bot] = pixels[bot], pixels[top]
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
is_os2 :: proc(version: image.BMP_Version) -> (res: bool) {
|
||||
#partial switch version {
|
||||
case .OS2_v1, .OS2_v2: return true
|
||||
case: return false
|
||||
}
|
||||
}
|
||||
|
||||
make_output :: proc(img: ^Image, allocator := context.allocator) -> (err: Error) {
|
||||
assert(img != nil)
|
||||
bytes_needed := img.channels * img.height * img.width
|
||||
img.pixels.buf = make([dynamic]u8, bytes_needed, allocator)
|
||||
if len(img.pixels.buf) != bytes_needed {
|
||||
return .Unable_To_Allocate_Or_Resize
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
write :: proc(img: ^Image, x, y: int, pix: RGB_Pixel) -> (err: Error) {
|
||||
if y >= img.height || x >= img.width {
|
||||
return .Corrupt
|
||||
}
|
||||
out := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
|
||||
assert(img.height >= 1 && img.width >= 1)
|
||||
out[(img.height - y - 1) * img.width + x] = pix
|
||||
return
|
||||
}
|
||||
|
||||
Bitmask :: struct {
|
||||
mask: [4]u32le `fmt:"b"`,
|
||||
shift: [4]u32le,
|
||||
bits: [4]u32le,
|
||||
}
|
||||
|
||||
read_or_make_bit_masks :: proc(ctx: ^$C, info: image.BMP_Header) -> (res: Bitmask, read: int, err: Error) {
|
||||
ctz :: intrinsics.count_trailing_zeros
|
||||
c1s :: intrinsics.count_ones
|
||||
|
||||
#partial switch info.compression {
|
||||
case .RGB:
|
||||
switch info.bpp {
|
||||
case 16:
|
||||
return {
|
||||
mask = {31 << 10, 31 << 5, 31, 0},
|
||||
shift = { 10, 5, 0, 0},
|
||||
bits = { 5, 5, 5, 0},
|
||||
}, int(4 * info.colors_used), nil
|
||||
|
||||
case 32:
|
||||
return {
|
||||
mask = {255 << 16, 255 << 8, 255, 255 << 24},
|
||||
shift = { 16, 8, 0, 24},
|
||||
bits = { 8, 8, 8, 8},
|
||||
}, int(4 * info.colors_used), nil
|
||||
|
||||
case: return {}, 0, .Unsupported_BPP
|
||||
}
|
||||
case .Bit_Fields, .Alpha_Bit_Fields:
|
||||
bf := info.masks
|
||||
alpha_mask := false
|
||||
bit_count: u32le
|
||||
|
||||
#partial switch info.info_size {
|
||||
case .ABBR_52 ..= .V5:
|
||||
// All possible BMP header sizes 52+ bytes long, includes V4 + V5
|
||||
// Bit fields were read as part of the header
|
||||
// V3 header is 40 bytes. We need 56 at a minimum for RGBA bit fields in the next section.
|
||||
if info.info_size >= .ABBR_56 {
|
||||
alpha_mask = true
|
||||
}
|
||||
|
||||
case .V3:
|
||||
// Version 3 doesn't have a bit field embedded, but can still have a 3 or 4 color bit field.
|
||||
// Because it wasn't read as part of the header, we need to read it now.
|
||||
|
||||
if info.compression == .Alpha_Bit_Fields {
|
||||
bf = compress.read_data(ctx, [4]u32le) or_return
|
||||
alpha_mask = true
|
||||
read = 16
|
||||
} else {
|
||||
bf.xyz = compress.read_data(ctx, [3]u32le) or_return
|
||||
read = 12
|
||||
}
|
||||
|
||||
case:
|
||||
// Bit fields are unhandled for this BMP version
|
||||
return {}, 0, .Bitfield_Version_Unhandled
|
||||
}
|
||||
|
||||
if alpha_mask {
|
||||
res = {
|
||||
mask = {bf.r, bf.g, bf.b, bf.a},
|
||||
shift = {ctz(bf.r), ctz(bf.g), ctz(bf.b), ctz(bf.a)},
|
||||
bits = {c1s(bf.r), c1s(bf.g), c1s(bf.b), c1s(bf.a)},
|
||||
}
|
||||
|
||||
bit_count = res.bits.r + res.bits.g + res.bits.b + res.bits.a
|
||||
} else {
|
||||
res = {
|
||||
mask = {bf.r, bf.g, bf.b, 0},
|
||||
shift = {ctz(bf.r), ctz(bf.g), ctz(bf.b), 0},
|
||||
bits = {c1s(bf.r), c1s(bf.g), c1s(bf.b), 0},
|
||||
}
|
||||
|
||||
bit_count = res.bits.r + res.bits.g + res.bits.b
|
||||
}
|
||||
|
||||
if bit_count > u32le(info.bpp) {
|
||||
err = .Bitfield_Sum_Exceeds_BPP
|
||||
}
|
||||
|
||||
overlapped := res.mask.r | res.mask.g | res.mask.b | res.mask.a
|
||||
if c1s(overlapped) < bit_count {
|
||||
err = .Bitfield_Overlapped
|
||||
}
|
||||
return res, read, err
|
||||
|
||||
case:
|
||||
return {}, 0, .Unsupported_Compression
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
scale :: proc(val: $T, mask, shift, bits: u32le) -> (res: u8) {
|
||||
if bits == 0 { return 0 } // Guard against malformed bit fields
|
||||
v := (u32le(val) & mask) >> shift
|
||||
mask_in := u32le(1 << bits) - 1
|
||||
return u8(v * 255 / mask_in)
|
||||
}
|
||||
|
||||
decode_rgb :: proc(ctx: ^$C, img: ^Image, info: image.BMP_Header, allocator := context.allocator) -> (err: Error) {
|
||||
pixel_offset := int(info.pixel_offset)
|
||||
pixel_offset -= int(info.info_size) + FILE_HEADER_SIZE
|
||||
|
||||
palette: [256]RGBA_Pixel
|
||||
|
||||
// Palette size is info.colors_used if populated. If not it's min(1 << bpp, offset to the pixels / channel count)
|
||||
colors_used := min(256, 1 << info.bpp if info.colors_used == 0 else info.colors_used)
|
||||
max_colors := pixel_offset / 3 if info.info_size == .OS2_v1 else pixel_offset / 4
|
||||
colors_used = min(colors_used, u32le(max_colors))
|
||||
|
||||
switch info.bpp {
|
||||
case 1:
|
||||
if info.info_size == .OS2_v1 {
|
||||
// 2 x RGB palette of instead of variable RGBA palette
|
||||
for i in 0..<colors_used {
|
||||
palette[i].rgb = image.read_data(ctx, RGB_Pixel) or_return
|
||||
}
|
||||
pixel_offset -= int(3 * colors_used)
|
||||
} else {
|
||||
for i in 0..<colors_used {
|
||||
palette[i] = image.read_data(ctx, RGBA_Pixel) or_return
|
||||
}
|
||||
pixel_offset -= int(4 * colors_used)
|
||||
}
|
||||
skip_space(ctx, pixel_offset)
|
||||
|
||||
stride := (img.width + 7) / 8
|
||||
for y in 0..<img.height {
|
||||
data := compress.read_slice(ctx, stride) or_return
|
||||
for x in 0..<img.width {
|
||||
shift := u8(7 - (x & 0x07))
|
||||
p := (data[x / 8] >> shift) & 0x01
|
||||
write(img, x, y, palette[p].bgr) or_return
|
||||
}
|
||||
}
|
||||
|
||||
case 2: // Non-standard on modern Windows, but was allowed on WinCE
|
||||
for i in 0..<colors_used {
|
||||
palette[i] = image.read_data(ctx, RGBA_Pixel) or_return
|
||||
}
|
||||
pixel_offset -= int(4 * colors_used)
|
||||
skip_space(ctx, pixel_offset)
|
||||
|
||||
stride := (img.width + 3) / 4
|
||||
for y in 0..<img.height {
|
||||
data := compress.read_slice(ctx, stride) or_return
|
||||
for x in 0..<img.width {
|
||||
shift := 6 - (x & 0x03) << 1
|
||||
p := (data[x / 4] >> u8(shift)) & 0x03
|
||||
write(img, x, y, palette[p].bgr) or_return
|
||||
}
|
||||
}
|
||||
|
||||
case 4:
|
||||
if info.info_size == .OS2_v1 {
|
||||
// 16 x RGB palette of instead of variable RGBA palette
|
||||
for i in 0..<colors_used {
|
||||
palette[i].rgb = image.read_data(ctx, RGB_Pixel) or_return
|
||||
}
|
||||
pixel_offset -= int(3 * colors_used)
|
||||
} else {
|
||||
for i in 0..<colors_used {
|
||||
palette[i] = image.read_data(ctx, RGBA_Pixel) or_return
|
||||
}
|
||||
pixel_offset -= int(4 * colors_used)
|
||||
}
|
||||
skip_space(ctx, pixel_offset)
|
||||
|
||||
stride := (img.width + 1) / 2
|
||||
for y in 0..<img.height {
|
||||
data := compress.read_slice(ctx, stride) or_return
|
||||
for x in 0..<img.width {
|
||||
p := data[x / 2] >> 4 if x & 1 == 0 else data[x / 2]
|
||||
write(img, x, y, palette[p & 0x0f].bgr) or_return
|
||||
}
|
||||
}
|
||||
|
||||
case 8:
|
||||
if info.info_size == .OS2_v1 {
|
||||
// 256 x RGB palette of instead of variable RGBA palette
|
||||
for i in 0..<colors_used {
|
||||
palette[i].rgb = image.read_data(ctx, RGB_Pixel) or_return
|
||||
}
|
||||
pixel_offset -= int(3 * colors_used)
|
||||
} else {
|
||||
for i in 0..<colors_used {
|
||||
palette[i] = image.read_data(ctx, RGBA_Pixel) or_return
|
||||
}
|
||||
pixel_offset -= int(4 * colors_used)
|
||||
}
|
||||
skip_space(ctx, pixel_offset)
|
||||
|
||||
stride := align4(img.width)
|
||||
for y in 0..<img.height {
|
||||
data := compress.read_slice(ctx, stride) or_return
|
||||
for x in 0..<img.width {
|
||||
write(img, x, y, palette[data[x]].bgr) or_return
|
||||
}
|
||||
}
|
||||
|
||||
case 16:
|
||||
bm, read := read_or_make_bit_masks(ctx, info) or_return
|
||||
// Skip optional palette and other data
|
||||
pixel_offset -= read
|
||||
skip_space(ctx, pixel_offset)
|
||||
|
||||
stride := align4(img.width * 2)
|
||||
for y in 0..<img.height {
|
||||
data := compress.read_slice(ctx, stride) or_return
|
||||
pixels := mem.slice_data_cast([]u16le, data)
|
||||
for x in 0..<img.width {
|
||||
v := pixels[x]
|
||||
r := scale(v, bm.mask.r, bm.shift.r, bm.bits.r)
|
||||
g := scale(v, bm.mask.g, bm.shift.g, bm.bits.g)
|
||||
b := scale(v, bm.mask.b, bm.shift.b, bm.bits.b)
|
||||
write(img, x, y, RGB_Pixel{r, g, b}) or_return
|
||||
}
|
||||
}
|
||||
|
||||
case 24:
|
||||
// Eat useless palette and other padding
|
||||
skip_space(ctx, pixel_offset)
|
||||
|
||||
stride := align4(img.width * 3)
|
||||
for y in 0..<img.height {
|
||||
data := compress.read_slice(ctx, stride) or_return
|
||||
pixels := mem.slice_data_cast([]RGB_Pixel, data)
|
||||
for x in 0..<img.width {
|
||||
write(img, x, y, pixels[x].bgr) or_return
|
||||
}
|
||||
}
|
||||
|
||||
case 32:
|
||||
bm, read := read_or_make_bit_masks(ctx, info) or_return
|
||||
// Skip optional palette and other data
|
||||
pixel_offset -= read
|
||||
skip_space(ctx, pixel_offset)
|
||||
|
||||
for y in 0..<img.height {
|
||||
data := compress.read_slice(ctx, img.width * size_of(RGBA_Pixel)) or_return
|
||||
pixels := mem.slice_data_cast([]u32le, data)
|
||||
for x in 0..<img.width {
|
||||
v := pixels[x]
|
||||
r := scale(v, bm.mask.r, bm.shift.r, bm.bits.r)
|
||||
g := scale(v, bm.mask.g, bm.shift.g, bm.bits.g)
|
||||
b := scale(v, bm.mask.b, bm.shift.b, bm.bits.b)
|
||||
write(img, x, y, RGB_Pixel{r, g, b}) or_return
|
||||
}
|
||||
}
|
||||
|
||||
case:
|
||||
return .Unsupported_BPP
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
decode_rle :: proc(ctx: ^$C, img: ^Image, info: image.BMP_Header, allocator := context.allocator) -> (err: Error) {
|
||||
pixel_offset := int(info.pixel_offset)
|
||||
pixel_offset -= int(info.info_size) + FILE_HEADER_SIZE
|
||||
|
||||
bytes_needed := size_of(RGB_Pixel) * img.height * img.width
|
||||
if resize(&img.pixels.buf, bytes_needed) != nil {
|
||||
return .Unable_To_Allocate_Or_Resize
|
||||
}
|
||||
out := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
|
||||
assert(len(out) == img.height * img.width)
|
||||
|
||||
palette: [256]RGBA_Pixel
|
||||
|
||||
switch info.bpp {
|
||||
case 4:
|
||||
colors_used := info.colors_used if info.colors_used > 0 else 16
|
||||
colors_used = min(colors_used, 16)
|
||||
|
||||
for i in 0..<colors_used {
|
||||
palette[i] = image.read_data(ctx, RGBA_Pixel) or_return
|
||||
pixel_offset -= size_of(RGBA_Pixel)
|
||||
}
|
||||
skip_space(ctx, pixel_offset)
|
||||
|
||||
pixel_size := info.size - info.pixel_offset
|
||||
remaining := compress.input_size(ctx) or_return
|
||||
if remaining < i64(pixel_size) {
|
||||
return .Corrupt
|
||||
}
|
||||
|
||||
data := make([]u8, int(pixel_size) + 4)
|
||||
defer delete(data)
|
||||
|
||||
for i in 0..<pixel_size {
|
||||
data[i] = image.read_u8(ctx) or_return
|
||||
}
|
||||
|
||||
y, x := 0, 0
|
||||
index := 0
|
||||
for {
|
||||
if len(data[index:]) < 2 {
|
||||
return .Corrupt
|
||||
}
|
||||
|
||||
if data[index] > 0 {
|
||||
for count in 0..<data[index] {
|
||||
if count & 1 == 1 {
|
||||
write(img, x, y, palette[(data[index + 1] >> 0) & 0x0f].bgr)
|
||||
} else {
|
||||
write(img, x, y, palette[(data[index + 1] >> 4) & 0x0f].bgr)
|
||||
}
|
||||
x += 1
|
||||
}
|
||||
index += 2
|
||||
} else {
|
||||
switch data[index + 1] {
|
||||
case 0: // EOL
|
||||
x = 0; y += 1
|
||||
index += 2
|
||||
case 1: // EOB
|
||||
return
|
||||
case 2: // MOVE
|
||||
x += int(data[index + 2])
|
||||
y += int(data[index + 3])
|
||||
index += 4
|
||||
case: // Literals
|
||||
run_length := int(data[index + 1])
|
||||
aligned := (align4(run_length) >> 1) + 2
|
||||
|
||||
if index + aligned >= len(data) {
|
||||
return .Corrupt
|
||||
}
|
||||
|
||||
for count in 0..<run_length {
|
||||
val := data[index + 2 + count / 2]
|
||||
if count & 1 == 1 {
|
||||
val &= 0xf
|
||||
} else {
|
||||
val = val >> 4
|
||||
}
|
||||
write(img, x, y, palette[val].bgr)
|
||||
x += 1
|
||||
}
|
||||
index += aligned
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 8:
|
||||
colors_used := info.colors_used if info.colors_used > 0 else 256
|
||||
colors_used = min(colors_used, 256)
|
||||
|
||||
for i in 0..<colors_used {
|
||||
palette[i] = image.read_data(ctx, RGBA_Pixel) or_return
|
||||
pixel_offset -= size_of(RGBA_Pixel)
|
||||
}
|
||||
skip_space(ctx, pixel_offset)
|
||||
|
||||
pixel_size := info.size - info.pixel_offset
|
||||
remaining := compress.input_size(ctx) or_return
|
||||
if remaining < i64(pixel_size) {
|
||||
return .Corrupt
|
||||
}
|
||||
|
||||
data := make([]u8, int(pixel_size) + 4)
|
||||
defer delete(data)
|
||||
|
||||
for i in 0..<pixel_size {
|
||||
data[i] = image.read_u8(ctx) or_return
|
||||
}
|
||||
|
||||
y, x := 0, 0
|
||||
index := 0
|
||||
for {
|
||||
if len(data[index:]) < 2 {
|
||||
return .Corrupt
|
||||
}
|
||||
|
||||
if data[index] > 0 {
|
||||
for _ in 0..<data[index] {
|
||||
write(img, x, y, palette[data[index + 1]].bgr)
|
||||
x += 1
|
||||
}
|
||||
index += 2
|
||||
} else {
|
||||
switch data[index + 1] {
|
||||
case 0: // EOL
|
||||
x = 0; y += 1
|
||||
index += 2
|
||||
case 1: // EOB
|
||||
return
|
||||
case 2: // MOVE
|
||||
x += int(data[index + 2])
|
||||
y += int(data[index + 3])
|
||||
index += 4
|
||||
case: // Literals
|
||||
run_length := int(data[index + 1])
|
||||
aligned := align2(run_length) + 2
|
||||
|
||||
if index + aligned >= len(data) {
|
||||
return .Corrupt
|
||||
}
|
||||
for count in 0..<run_length {
|
||||
write(img, x, y, palette[data[index + 2 + count]].bgr)
|
||||
x += 1
|
||||
}
|
||||
index += aligned
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case:
|
||||
return .Unsupported_BPP
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
align2 :: proc(width: int) -> (stride: int) {
|
||||
stride = width
|
||||
if width & 1 != 0 {
|
||||
stride += 2 - (width & 1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
align4 :: proc(width: int) -> (stride: int) {
|
||||
stride = width
|
||||
if width & 3 != 0 {
|
||||
stride += 4 - (width & 3)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
skip_space :: proc(ctx: ^$C, bytes_to_skip: int) -> (err: Error) {
|
||||
if bytes_to_skip < 0 {
|
||||
return .Corrupt
|
||||
}
|
||||
for _ in 0..<bytes_to_skip {
|
||||
image.read_u8(ctx) or_return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Cleanup of image-specific data.
|
||||
destroy :: proc(img: ^Image) {
|
||||
if img == nil {
|
||||
// Nothing to do. Load must've returned with an error.
|
||||
return
|
||||
}
|
||||
|
||||
bytes.buffer_destroy(&img.pixels)
|
||||
if v, ok := img.metadata.(^image.BMP_Info); ok {
|
||||
free(v)
|
||||
}
|
||||
free(img)
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
_register :: proc() {
|
||||
image.register(.BMP, load_from_bytes, destroy)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
//+build js
|
||||
package core_image_bmp
|
||||
|
||||
load :: proc{load_from_bytes, load_from_context}
|
||||
@@ -0,0 +1,34 @@
|
||||
//+build !js
|
||||
package core_image_bmp
|
||||
|
||||
import "core:os"
|
||||
import "core:bytes"
|
||||
|
||||
load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
defer delete(data)
|
||||
|
||||
if ok {
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
|
||||
save :: proc{save_to_buffer, save_to_file}
|
||||
|
||||
save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
defer bytes.buffer_destroy(out)
|
||||
|
||||
save_to_buffer(out, img, options) or_return
|
||||
write_ok := os.write_entire_file(output, out.buf[:])
|
||||
|
||||
return nil if write_ok else .Unable_To_Write_File
|
||||
}
|
||||
+161
-1
@@ -12,6 +12,7 @@ package image
|
||||
|
||||
import "core:bytes"
|
||||
import "core:mem"
|
||||
import "core:io"
|
||||
import "core:compress"
|
||||
import "base:runtime"
|
||||
|
||||
@@ -62,6 +63,7 @@ Image_Metadata :: union #shared_nil {
|
||||
^PNG_Info,
|
||||
^QOI_Info,
|
||||
^TGA_Info,
|
||||
^BMP_Info,
|
||||
}
|
||||
|
||||
|
||||
@@ -159,11 +161,13 @@ Error :: union #shared_nil {
|
||||
Netpbm_Error,
|
||||
PNG_Error,
|
||||
QOI_Error,
|
||||
BMP_Error,
|
||||
|
||||
compress.Error,
|
||||
compress.General_Error,
|
||||
compress.Deflate_Error,
|
||||
compress.ZLIB_Error,
|
||||
io.Error,
|
||||
runtime.Allocator_Error,
|
||||
}
|
||||
|
||||
@@ -196,6 +200,128 @@ General_Image_Error :: enum {
|
||||
Unable_To_Allocate_Or_Resize,
|
||||
}
|
||||
|
||||
/*
|
||||
BMP-specific
|
||||
*/
|
||||
BMP_Error :: enum {
|
||||
None = 0,
|
||||
Invalid_File_Size,
|
||||
Unsupported_BMP_Version,
|
||||
Unsupported_OS2_File,
|
||||
Unsupported_Compression,
|
||||
Unsupported_BPP,
|
||||
Invalid_Stride,
|
||||
Invalid_Color_Count,
|
||||
Implausible_File_Size,
|
||||
Bitfield_Version_Unhandled, // We don't (yet) handle bit fields for this BMP version.
|
||||
Bitfield_Sum_Exceeds_BPP, // Total mask bit count > bpp
|
||||
Bitfield_Overlapped, // Channel masks overlap
|
||||
}
|
||||
|
||||
// img.metadata is wrapped in a struct in case we need to add to it later
|
||||
// without putting it in BMP_Header
|
||||
BMP_Info :: struct {
|
||||
info: BMP_Header,
|
||||
}
|
||||
|
||||
BMP_Magic :: enum u16le {
|
||||
Bitmap = 0x4d42, // 'BM'
|
||||
OS2_Bitmap_Array = 0x4142, // 'BA'
|
||||
OS2_Icon = 0x4349, // 'IC',
|
||||
OS2_Color_Icon = 0x4943, // 'CI'
|
||||
OS2_Pointer = 0x5450, // 'PT'
|
||||
OS2_Color_Pointer = 0x5043, // 'CP'
|
||||
}
|
||||
|
||||
// See: http://justsolve.archiveteam.org/wiki/BMP#Well-known_versions
|
||||
BMP_Version :: enum u32le {
|
||||
OS2_v1 = 12, // BITMAPCOREHEADER (Windows V2 / OS/2 version 1.0)
|
||||
OS2_v2 = 64, // BITMAPCOREHEADER2 (OS/2 version 2.x)
|
||||
V3 = 40, // BITMAPINFOHEADER
|
||||
V4 = 108, // BITMAPV4HEADER
|
||||
V5 = 124, // BITMAPV5HEADER
|
||||
|
||||
ABBR_16 = 16, // Abbreviated
|
||||
ABBR_24 = 24, // ..
|
||||
ABBR_48 = 48, // ..
|
||||
ABBR_52 = 52, // ..
|
||||
ABBR_56 = 56, // ..
|
||||
}
|
||||
|
||||
BMP_Header :: struct #packed {
|
||||
// File header
|
||||
magic: BMP_Magic,
|
||||
size: u32le,
|
||||
_res1: u16le, // Reserved; must be zero
|
||||
_res2: u16le, // Reserved; must be zero
|
||||
pixel_offset: u32le, // Offset in bytes, from the beginning of BMP_Header to the pixel data
|
||||
// V3
|
||||
info_size: BMP_Version,
|
||||
width: i32le,
|
||||
height: i32le,
|
||||
planes: u16le,
|
||||
bpp: u16le,
|
||||
compression: BMP_Compression,
|
||||
image_size: u32le,
|
||||
pels_per_meter: [2]u32le,
|
||||
colors_used: u32le,
|
||||
colors_important: u32le, // OS2_v2 is equal up to here
|
||||
// V4
|
||||
masks: [4]u32le `fmt:"32b"`,
|
||||
colorspace: BMP_Logical_Color_Space,
|
||||
endpoints: BMP_CIEXYZTRIPLE,
|
||||
gamma: [3]BMP_GAMMA16_16,
|
||||
// V5
|
||||
intent: BMP_Gamut_Mapping_Intent,
|
||||
profile_data: u32le,
|
||||
profile_size: u32le,
|
||||
reserved: u32le,
|
||||
}
|
||||
#assert(size_of(BMP_Header) == 138)
|
||||
|
||||
OS2_Header :: struct #packed {
|
||||
// BITMAPCOREHEADER minus info_size field
|
||||
width: i16le,
|
||||
height: i16le,
|
||||
planes: u16le,
|
||||
bpp: u16le,
|
||||
}
|
||||
#assert(size_of(OS2_Header) == 8)
|
||||
|
||||
BMP_Compression :: enum u32le {
|
||||
RGB = 0x0000,
|
||||
RLE8 = 0x0001,
|
||||
RLE4 = 0x0002,
|
||||
Bit_Fields = 0x0003, // If Windows
|
||||
Huffman1D = 0x0003, // If OS2v2
|
||||
JPEG = 0x0004, // If Windows
|
||||
RLE24 = 0x0004, // If OS2v2
|
||||
PNG = 0x0005,
|
||||
Alpha_Bit_Fields = 0x0006,
|
||||
CMYK = 0x000B,
|
||||
CMYK_RLE8 = 0x000C,
|
||||
CMYK_RLE4 = 0x000D,
|
||||
}
|
||||
|
||||
BMP_Logical_Color_Space :: enum u32le {
|
||||
CALIBRATED_RGB = 0x00000000,
|
||||
sRGB = 0x73524742, // 'sRGB'
|
||||
WINDOWS_COLOR_SPACE = 0x57696E20, // 'Win '
|
||||
}
|
||||
|
||||
BMP_FXPT2DOT30 :: u32le
|
||||
BMP_CIEXYZ :: [3]BMP_FXPT2DOT30
|
||||
BMP_CIEXYZTRIPLE :: [3]BMP_CIEXYZ
|
||||
BMP_GAMMA16_16 :: [2]u16le
|
||||
|
||||
BMP_Gamut_Mapping_Intent :: enum u32le {
|
||||
INVALID = 0x00000000, // If not V5, this field will just be zero-initialized and not valid.
|
||||
ABS_COLORIMETRIC = 0x00000008,
|
||||
BUSINESS = 0x00000001,
|
||||
GRAPHICS = 0x00000002,
|
||||
IMAGES = 0x00000004,
|
||||
}
|
||||
|
||||
/*
|
||||
Netpbm-specific definitions
|
||||
*/
|
||||
@@ -1133,6 +1259,40 @@ apply_palette_rgba :: proc(img: ^Image, palette: [256]RGBA_Pixel, allocator := c
|
||||
}
|
||||
apply_palette :: proc{apply_palette_rgb, apply_palette_rgba}
|
||||
|
||||
blend_single_channel :: #force_inline proc(fg, alpha, bg: $T) -> (res: T) where T == u8 || T == u16 {
|
||||
MAX :: 256 when T == u8 else 65536
|
||||
|
||||
c := u32(fg) * (MAX - u32(alpha)) + u32(bg) * (1 + u32(alpha))
|
||||
return T(c & (MAX - 1))
|
||||
}
|
||||
|
||||
blend_pixel :: #force_inline proc(fg: [$N]$T, alpha: T, bg: [N]T) -> (res: [N]T) where (T == u8 || T == u16), N >= 1 && N <= 4 {
|
||||
MAX :: 256 when T == u8 else 65536
|
||||
|
||||
when N == 1 {
|
||||
r := u32(fg.r) * (MAX - u32(alpha)) + u32(bg.r) * (1 + u32(alpha))
|
||||
return {T(r & (MAX - 1))}
|
||||
}
|
||||
when N == 2 {
|
||||
r := u32(fg.r) * (MAX - u32(alpha)) + u32(bg.r) * (1 + u32(alpha))
|
||||
g := u32(fg.g) * (MAX - u32(alpha)) + u32(bg.g) * (1 + u32(alpha))
|
||||
return {T(r & (MAX - 1)), T(g & (MAX - 1))}
|
||||
}
|
||||
when N == 3 || N == 4 {
|
||||
r := u32(fg.r) * (MAX - u32(alpha)) + u32(bg.r) * (1 + u32(alpha))
|
||||
g := u32(fg.g) * (MAX - u32(alpha)) + u32(bg.g) * (1 + u32(alpha))
|
||||
b := u32(fg.b) * (MAX - u32(alpha)) + u32(bg.b) * (1 + u32(alpha))
|
||||
|
||||
when N == 3 {
|
||||
return {T(r & (MAX - 1)), T(g & (MAX - 1)), T(b & (MAX - 1))}
|
||||
} else {
|
||||
return {T(r & (MAX - 1)), T(g & (MAX - 1)), T(b & (MAX - 1)), MAX - 1}
|
||||
}
|
||||
}
|
||||
unreachable()
|
||||
}
|
||||
blend :: proc{blend_single_channel, blend_pixel}
|
||||
|
||||
|
||||
// Replicates grayscale values into RGB(A) 8- or 16-bit images as appropriate.
|
||||
// Returns early with `false` if already an RGB(A) image.
|
||||
@@ -1245,4 +1405,4 @@ write_bytes :: proc(buf: ^bytes.Buffer, data: []u8) -> (err: compress.General_Er
|
||||
return .Resize_Failed
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
+28
-40
@@ -597,7 +597,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
dsc := depth_scale_table
|
||||
scale := dsc[info.header.bit_depth]
|
||||
if scale != 1 {
|
||||
key := mem.slice_data_cast([]u16be, c.data)[0] * u16be(scale)
|
||||
key := (^u16be)(raw_data(c.data))^ * u16be(scale)
|
||||
c.data = []u8{0, u8(key & 255)}
|
||||
}
|
||||
}
|
||||
@@ -735,59 +735,48 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
return {}, .Unable_To_Allocate_Or_Resize
|
||||
}
|
||||
|
||||
i := 0; j := 0
|
||||
|
||||
// If we don't have transparency or drop it without applying it, we can do this:
|
||||
if (!seen_trns || (seen_trns && .alpha_drop_if_present in options && .alpha_premultiply not_in options)) && .alpha_add_if_missing not_in options {
|
||||
for h := 0; h < int(img.height); h += 1 {
|
||||
for w := 0; w < int(img.width); w += 1 {
|
||||
c := _plte.entries[temp.buf[i]]
|
||||
t.buf[j ] = c.r
|
||||
t.buf[j+1] = c.g
|
||||
t.buf[j+2] = c.b
|
||||
i += 1; j += 3
|
||||
}
|
||||
output := mem.slice_data_cast([]image.RGB_Pixel, t.buf[:])
|
||||
for pal_idx, idx in temp.buf {
|
||||
output[idx] = _plte.entries[pal_idx]
|
||||
}
|
||||
} else if add_alpha || .alpha_drop_if_present in options {
|
||||
bg := [3]f32{0, 0, 0}
|
||||
bg := PLTE_Entry{0, 0, 0}
|
||||
if premultiply && seen_bkgd {
|
||||
c16 := img.background.([3]u16)
|
||||
bg = [3]f32{f32(c16.r), f32(c16.g), f32(c16.b)}
|
||||
bg = {u8(c16.r), u8(c16.g), u8(c16.b)}
|
||||
}
|
||||
|
||||
no_alpha := (.alpha_drop_if_present in options || premultiply) && .alpha_add_if_missing not_in options
|
||||
blend_background := seen_bkgd && .blend_background in options
|
||||
|
||||
for h := 0; h < int(img.height); h += 1 {
|
||||
for w := 0; w < int(img.width); w += 1 {
|
||||
index := temp.buf[i]
|
||||
|
||||
c := _plte.entries[index]
|
||||
a := int(index) < len(trns.data) ? trns.data[index] : 255
|
||||
alpha := f32(a) / 255.0
|
||||
if no_alpha {
|
||||
output := mem.slice_data_cast([]image.RGB_Pixel, t.buf[:])
|
||||
for orig, idx in temp.buf {
|
||||
c := _plte.entries[orig]
|
||||
a := int(orig) < len(trns.data) ? trns.data[orig] : 255
|
||||
|
||||
if blend_background {
|
||||
c.r = u8((1.0 - alpha) * bg[0] + f32(c.r) * alpha)
|
||||
c.g = u8((1.0 - alpha) * bg[1] + f32(c.g) * alpha)
|
||||
c.b = u8((1.0 - alpha) * bg[2] + f32(c.b) * alpha)
|
||||
output[idx] = image.blend(c, a, bg)
|
||||
} else if premultiply {
|
||||
output[idx] = image.blend(PLTE_Entry{}, a, c)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
output := mem.slice_data_cast([]image.RGBA_Pixel, t.buf[:])
|
||||
for orig, idx in temp.buf {
|
||||
c := _plte.entries[orig]
|
||||
a := int(orig) < len(trns.data) ? trns.data[orig] : 255
|
||||
|
||||
if blend_background {
|
||||
c = image.blend(c, a, bg)
|
||||
a = 255
|
||||
} else if premultiply {
|
||||
c.r = u8(f32(c.r) * alpha)
|
||||
c.g = u8(f32(c.g) * alpha)
|
||||
c.b = u8(f32(c.b) * alpha)
|
||||
c = image.blend(PLTE_Entry{}, a, c)
|
||||
}
|
||||
|
||||
t.buf[j ] = c.r
|
||||
t.buf[j+1] = c.g
|
||||
t.buf[j+2] = c.b
|
||||
i += 1
|
||||
|
||||
if no_alpha {
|
||||
j += 3
|
||||
} else {
|
||||
t.buf[j+3] = u8(a)
|
||||
j += 4
|
||||
}
|
||||
output[idx] = {c.r, c.g, c.b, u8(a)}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1015,8 +1004,8 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
return {}, .Unable_To_Allocate_Or_Resize
|
||||
}
|
||||
|
||||
p := mem.slice_data_cast([]u8, temp.buf[:])
|
||||
o := mem.slice_data_cast([]u8, t.buf[:])
|
||||
p := temp.buf[:]
|
||||
o := t.buf[:]
|
||||
|
||||
switch raw_image_channels {
|
||||
case 1:
|
||||
@@ -1627,7 +1616,6 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@(init, private)
|
||||
_register :: proc() {
|
||||
image.register(.PNG, load_from_bytes, destroy)
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package math_big
|
||||
|
||||
/*
|
||||
With `n` items, calculate how many ways that `r` of them can be ordered.
|
||||
*/
|
||||
permutations_with_repetition :: int_pow_int
|
||||
|
||||
/*
|
||||
With `n` items, calculate how many ways that `r` of them can be ordered without any repeats.
|
||||
*/
|
||||
permutations_without_repetition :: proc(dest: ^Int, n, r: int) -> (error: Error) {
|
||||
if n == r {
|
||||
return factorial(dest, n)
|
||||
}
|
||||
|
||||
tmp := &Int{}
|
||||
defer internal_destroy(tmp)
|
||||
|
||||
// n!
|
||||
// --------
|
||||
// (n - r)!
|
||||
factorial(dest, n) or_return
|
||||
factorial(tmp, n - r) or_return
|
||||
div(dest, dest, tmp) or_return
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
With `n` items, calculate how many ways that `r` of them can be chosen.
|
||||
|
||||
Also known as the multiset coefficient or (n multichoose k).
|
||||
*/
|
||||
combinations_with_repetition :: proc(dest: ^Int, n, r: int) -> (error: Error) {
|
||||
// (n + r - 1)!
|
||||
// ------------
|
||||
// r! (n - 1)!
|
||||
return combinations_without_repetition(dest, n + r - 1, r)
|
||||
}
|
||||
|
||||
/*
|
||||
With `n` items, calculate how many ways that `r` of them can be chosen without any repeats.
|
||||
|
||||
Also known as the binomial coefficient or (n choose k).
|
||||
*/
|
||||
combinations_without_repetition :: proc(dest: ^Int, n, r: int) -> (error: Error) {
|
||||
tmp_a, tmp_b := &Int{}, &Int{}
|
||||
defer internal_destroy(tmp_a, tmp_b)
|
||||
|
||||
// n!
|
||||
// ------------
|
||||
// r! (n - r)!
|
||||
factorial(dest, n) or_return
|
||||
factorial(tmp_a, r) or_return
|
||||
factorial(tmp_b, n - r) or_return
|
||||
mul(tmp_a, tmp_a, tmp_b) or_return
|
||||
div(dest, dest, tmp_a) or_return
|
||||
|
||||
return
|
||||
}
|
||||
@@ -315,6 +315,7 @@ int_atoi :: proc(res: ^Int, input: string, radix := i8(10), allocator := context
|
||||
|
||||
|
||||
atoi :: proc { int_atoi, }
|
||||
string_to_int :: int_atoi
|
||||
|
||||
/*
|
||||
We size for `string` by default.
|
||||
|
||||
@@ -350,7 +350,7 @@ _reduce_pi_f64 :: proc "contextless" (x: f64) -> f64 #no_bounds_check {
|
||||
// that is, 1/PI = SUM bdpi[i]*2^(-64*i).
|
||||
// 19 64-bit digits give 1216 bits of precision
|
||||
// to handle the largest possible f64 exponent.
|
||||
@static bdpi := [?]u64{
|
||||
@(static, rodata) bdpi := [?]u64{
|
||||
0x0000000000000000,
|
||||
0x517cc1b727220a94,
|
||||
0xfe13abe8fa9a6ee0,
|
||||
|
||||
+9
-9
@@ -130,10 +130,10 @@ pow10 :: proc{
|
||||
|
||||
@(require_results)
|
||||
pow10_f16 :: proc "contextless" (n: f16) -> f16 {
|
||||
@static pow10_pos_tab := [?]f16{
|
||||
@(static, rodata) pow10_pos_tab := [?]f16{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04,
|
||||
}
|
||||
@static pow10_neg_tab := [?]f16{
|
||||
@(static, rodata) pow10_neg_tab := [?]f16{
|
||||
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07,
|
||||
}
|
||||
|
||||
@@ -151,13 +151,13 @@ pow10_f16 :: proc "contextless" (n: f16) -> f16 {
|
||||
|
||||
@(require_results)
|
||||
pow10_f32 :: proc "contextless" (n: f32) -> f32 {
|
||||
@static pow10_pos_tab := [?]f32{
|
||||
@(static, rodata) pow10_pos_tab := [?]f32{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
|
||||
1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38,
|
||||
}
|
||||
@static pow10_neg_tab := [?]f32{
|
||||
@(static, rodata) pow10_neg_tab := [?]f32{
|
||||
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09,
|
||||
1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18, 1e-19,
|
||||
1e-20, 1e-21, 1e-22, 1e-23, 1e-24, 1e-25, 1e-26, 1e-27, 1e-28, 1e-29,
|
||||
@@ -179,16 +179,16 @@ pow10_f32 :: proc "contextless" (n: f32) -> f32 {
|
||||
|
||||
@(require_results)
|
||||
pow10_f64 :: proc "contextless" (n: f64) -> f64 {
|
||||
@static pow10_tab := [?]f64{
|
||||
@(static, rodata) pow10_tab := [?]f64{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
|
||||
1e30, 1e31,
|
||||
}
|
||||
@static pow10_pos_tab32 := [?]f64{
|
||||
@(static, rodata) pow10_pos_tab32 := [?]f64{
|
||||
1e00, 1e32, 1e64, 1e96, 1e128, 1e160, 1e192, 1e224, 1e256, 1e288,
|
||||
}
|
||||
@static pow10_neg_tab32 := [?]f64{
|
||||
@(static, rodata) pow10_neg_tab32 := [?]f64{
|
||||
1e-00, 1e-32, 1e-64, 1e-96, 1e-128, 1e-160, 1e-192, 1e-224, 1e-256, 1e-288, 1e-320,
|
||||
}
|
||||
|
||||
@@ -1274,7 +1274,7 @@ binomial :: proc "contextless" (n, k: int) -> int {
|
||||
@(require_results)
|
||||
factorial :: proc "contextless" (n: int) -> int {
|
||||
when size_of(int) == size_of(i64) {
|
||||
@static table := [21]int{
|
||||
@(static, rodata) table := [21]int{
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
@@ -1298,7 +1298,7 @@ factorial :: proc "contextless" (n: int) -> int {
|
||||
2_432_902_008_176_640_000,
|
||||
}
|
||||
} else {
|
||||
@static table := [13]int{
|
||||
@(static, rodata) table := [13]int{
|
||||
1,
|
||||
1,
|
||||
2,
|
||||
|
||||
@@ -67,7 +67,7 @@ package math
|
||||
// masks any imprecision in the polynomial.
|
||||
@(private="file", require_results)
|
||||
stirling :: proc "contextless" (x: f64) -> (f64, f64) {
|
||||
@(static) gamS := [?]f64{
|
||||
@(static, rodata) gamS := [?]f64{
|
||||
+7.87311395793093628397e-04,
|
||||
-2.29549961613378126380e-04,
|
||||
-2.68132617805781232825e-03,
|
||||
@@ -103,7 +103,7 @@ gamma_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
return false
|
||||
}
|
||||
|
||||
@(static) gamP := [?]f64{
|
||||
@(static, rodata) gamP := [?]f64{
|
||||
1.60119522476751861407e-04,
|
||||
1.19135147006586384913e-03,
|
||||
1.04213797561761569935e-02,
|
||||
@@ -112,7 +112,7 @@ gamma_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
4.94214826801497100753e-01,
|
||||
9.99999999999999996796e-01,
|
||||
}
|
||||
@(static) gamQ := [?]f64{
|
||||
@(static, rodata) gamQ := [?]f64{
|
||||
-2.31581873324120129819e-05,
|
||||
+5.39605580493303397842e-04,
|
||||
-4.45641913851797240494e-03,
|
||||
|
||||
@@ -123,7 +123,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
|
||||
return -x
|
||||
}
|
||||
|
||||
@static lgamA := [?]f64{
|
||||
@(static, rodata) lgamA := [?]f64{
|
||||
0h3FB3C467E37DB0C8,
|
||||
0h3FD4A34CC4A60FAD,
|
||||
0h3FB13E001A5562A7,
|
||||
@@ -137,7 +137,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
|
||||
0h3EFA7074428CFA52,
|
||||
0h3F07858E90A45837,
|
||||
}
|
||||
@static lgamR := [?]f64{
|
||||
@(static, rodata) lgamR := [?]f64{
|
||||
1.0,
|
||||
0h3FF645A762C4AB74,
|
||||
0h3FE71A1893D3DCDC,
|
||||
@@ -146,7 +146,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
|
||||
0h3F497DDACA41A95B,
|
||||
0h3EDEBAF7A5B38140,
|
||||
}
|
||||
@static lgamS := [?]f64{
|
||||
@(static, rodata) lgamS := [?]f64{
|
||||
0hBFB3C467E37DB0C8,
|
||||
0h3FCB848B36E20878,
|
||||
0h3FD4D98F4F139F59,
|
||||
@@ -155,7 +155,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
|
||||
0h3F5E26B67368F239,
|
||||
0h3F00BFECDD17E945,
|
||||
}
|
||||
@static lgamT := [?]f64{
|
||||
@(static, rodata) lgamT := [?]f64{
|
||||
0h3FDEF72BC8EE38A2,
|
||||
0hBFC2E4278DC6C509,
|
||||
0h3FB08B4294D5419B,
|
||||
@@ -172,7 +172,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
|
||||
0hBF347F24ECC38C38,
|
||||
0h3F35FD3EE8C2D3F4,
|
||||
}
|
||||
@static lgamU := [?]f64{
|
||||
@(static, rodata) lgamU := [?]f64{
|
||||
0hBFB3C467E37DB0C8,
|
||||
0h3FE4401E8B005DFF,
|
||||
0h3FF7475CD119BD6F,
|
||||
@@ -180,7 +180,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
|
||||
0h3FCD4EAEF6010924,
|
||||
0h3F8B678BBF2BAB09,
|
||||
}
|
||||
@static lgamV := [?]f64{
|
||||
@(static, rodata) lgamV := [?]f64{
|
||||
1.0,
|
||||
0h4003A5D7C2BD619C,
|
||||
0h40010725A42B18F5,
|
||||
@@ -188,7 +188,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
|
||||
0h3FBAAE55D6537C88,
|
||||
0h3F6A5ABB57D0CF61,
|
||||
}
|
||||
@static lgamW := [?]f64{
|
||||
@(static, rodata) lgamW := [?]f64{
|
||||
0h3FDACFE390C97D69,
|
||||
0h3FB555555555553B,
|
||||
0hBF66C16C16B02E5C,
|
||||
|
||||
@@ -234,7 +234,7 @@ _trig_reduce_f64 :: proc "contextless" (x: f64) -> (j: u64, z: f64) #no_bounds_c
|
||||
// that is, 4/pi = Sum bd_pi4[i]*2^(-64*i)
|
||||
// 19 64-bit digits and the leading one bit give 1217 bits
|
||||
// of precision to handle the largest possible f64 exponent.
|
||||
@static bd_pi4 := [?]u64{
|
||||
@(static, rodata) bd_pi4 := [?]u64{
|
||||
0x0000000000000001,
|
||||
0x45f306dc9c882a53,
|
||||
0xf84eafa3ea69bb81,
|
||||
|
||||
@@ -19,7 +19,7 @@ import "core:math"
|
||||
exp_float64 :: proc(r: ^Rand = nil) -> f64 {
|
||||
re :: 7.69711747013104972
|
||||
|
||||
@(static)
|
||||
@(static, rodata)
|
||||
ke := [256]u32{
|
||||
0xe290a139, 0x0, 0x9beadebc, 0xc377ac71, 0xd4ddb990,
|
||||
0xde893fb8, 0xe4a8e87c, 0xe8dff16a, 0xebf2deab, 0xee49a6e8,
|
||||
@@ -74,7 +74,7 @@ exp_float64 :: proc(r: ^Rand = nil) -> f64 {
|
||||
0xf7b577d2, 0xf69c650c, 0xf51530f0, 0xf2cb0e3c, 0xeeefb15d,
|
||||
0xe6da6ecf,
|
||||
}
|
||||
@(static)
|
||||
@(static, rodata)
|
||||
we := [256]f32{
|
||||
2.0249555e-09, 1.486674e-11, 2.4409617e-11, 3.1968806e-11,
|
||||
3.844677e-11, 4.4228204e-11, 4.9516443e-11, 5.443359e-11,
|
||||
@@ -141,7 +141,7 @@ exp_float64 :: proc(r: ^Rand = nil) -> f64 {
|
||||
1.2393786e-09, 1.276585e-09, 1.3193139e-09, 1.3695435e-09,
|
||||
1.4305498e-09, 1.508365e-09, 1.6160854e-09, 1.7921248e-09,
|
||||
}
|
||||
@(static)
|
||||
@(static, rodata)
|
||||
fe := [256]f32{
|
||||
1, 0.9381437, 0.90046996, 0.87170434, 0.8477855, 0.8269933,
|
||||
0.8084217, 0.7915276, 0.77595687, 0.7614634, 0.7478686,
|
||||
|
||||
@@ -21,7 +21,7 @@ import "core:math"
|
||||
norm_float64 :: proc(r: ^Rand = nil) -> f64 {
|
||||
rn :: 3.442619855899
|
||||
|
||||
@(static)
|
||||
@(static, rodata)
|
||||
kn := [128]u32{
|
||||
0x76ad2212, 0x00000000, 0x600f1b53, 0x6ce447a6, 0x725b46a2,
|
||||
0x7560051d, 0x774921eb, 0x789a25bd, 0x799045c3, 0x7a4bce5d,
|
||||
@@ -50,7 +50,7 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
|
||||
0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a,
|
||||
0x7ba90bdc, 0x7a722176, 0x77d664e5,
|
||||
}
|
||||
@(static)
|
||||
@(static, rodata)
|
||||
wn := [128]f32{
|
||||
1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10,
|
||||
2.2232431e-10, 2.4244937e-10, 2.601613e-10, 2.7611988e-10,
|
||||
@@ -85,7 +85,7 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
|
||||
1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09,
|
||||
1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09,
|
||||
}
|
||||
@(static)
|
||||
@(static, rodata)
|
||||
fn := [128]f32{
|
||||
1.00000000, 0.9635997, 0.9362827, 0.9130436, 0.89228165,
|
||||
0.87324303, 0.8555006, 0.8387836, 0.8229072, 0.8077383,
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
Original BSD-3 license:
|
||||
|
||||
Two Level Segregated Fit memory allocator, version 3.1.
|
||||
Written by Matthew Conte
|
||||
http://tlsf.baisoku.org
|
||||
|
||||
Based on the original documentation by Miguel Masmano:
|
||||
http://www.gii.upv.es/tlsf/main/docs
|
||||
|
||||
This implementation was written to the specification
|
||||
of the document, therefore no GPL restrictions apply.
|
||||
|
||||
Copyright (c) 2006-2016, Matthew Conte
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holder nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL MATTHEW CONTE BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
Copyright 2024 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Matt Conte: Original C implementation, see LICENSE file in this package
|
||||
Jeroen van Rijn: Source port
|
||||
*/
|
||||
|
||||
// package mem_tlsf implements a Two Level Segregated Fit memory allocator.
|
||||
package mem_tlsf
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
Error :: enum byte {
|
||||
None = 0,
|
||||
Invalid_Backing_Allocator = 1,
|
||||
Invalid_Alignment = 2,
|
||||
Backing_Buffer_Too_Small = 3,
|
||||
Backing_Buffer_Too_Large = 4,
|
||||
Backing_Allocator_Error = 5,
|
||||
}
|
||||
|
||||
|
||||
Allocator :: struct {
|
||||
// Empty lists point at this block to indicate they are free.
|
||||
block_null: Block_Header,
|
||||
|
||||
// Bitmaps for free lists.
|
||||
fl_bitmap: u32 `fmt:"-"`,
|
||||
sl_bitmap: [FL_INDEX_COUNT]u32 `fmt:"-"`,
|
||||
|
||||
// Head of free lists.
|
||||
blocks: [FL_INDEX_COUNT][SL_INDEX_COUNT]^Block_Header `fmt:"-"`,
|
||||
|
||||
// Keep track of pools so we can deallocate them.
|
||||
// If `pool.allocator` is blank, we don't do anything.
|
||||
// We also use this linked list of pools to report
|
||||
// statistics like how much memory is still available,
|
||||
// fragmentation, etc.
|
||||
pool: Pool,
|
||||
}
|
||||
#assert(size_of(Allocator) % ALIGN_SIZE == 0)
|
||||
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
allocator :: proc(t: ^Allocator) -> runtime.Allocator {
|
||||
return runtime.Allocator{
|
||||
procedure = allocator_proc,
|
||||
data = t,
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
init_from_buffer :: proc(control: ^Allocator, buf: []byte) -> Error {
|
||||
assert(control != nil)
|
||||
if uintptr(raw_data(buf)) % ALIGN_SIZE != 0 {
|
||||
return .Invalid_Alignment
|
||||
}
|
||||
|
||||
pool_bytes := align_down(len(buf) - POOL_OVERHEAD, ALIGN_SIZE)
|
||||
if pool_bytes < BLOCK_SIZE_MIN {
|
||||
return .Backing_Buffer_Too_Small
|
||||
} else if pool_bytes > BLOCK_SIZE_MAX {
|
||||
return .Backing_Buffer_Too_Large
|
||||
}
|
||||
|
||||
clear(control)
|
||||
return pool_add(control, buf[:])
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
init_from_allocator :: proc(control: ^Allocator, backing: runtime.Allocator, initial_pool_size: int, new_pool_size := 0) -> Error {
|
||||
assert(control != nil)
|
||||
pool_bytes := align_up(uint(initial_pool_size) + POOL_OVERHEAD, ALIGN_SIZE)
|
||||
if pool_bytes < BLOCK_SIZE_MIN {
|
||||
return .Backing_Buffer_Too_Small
|
||||
} else if pool_bytes > BLOCK_SIZE_MAX {
|
||||
return .Backing_Buffer_Too_Large
|
||||
}
|
||||
|
||||
buf, backing_err := runtime.make_aligned([]byte, pool_bytes, ALIGN_SIZE, backing)
|
||||
if backing_err != nil {
|
||||
return .Backing_Allocator_Error
|
||||
}
|
||||
err := init_from_buffer(control, buf)
|
||||
control.pool = Pool{
|
||||
data = buf,
|
||||
allocator = backing,
|
||||
}
|
||||
return err
|
||||
}
|
||||
init :: proc{init_from_buffer, init_from_allocator}
|
||||
|
||||
destroy :: proc(control: ^Allocator) {
|
||||
if control == nil { return }
|
||||
|
||||
// No need to call `pool_remove` or anything, as they're they're embedded in the backing memory.
|
||||
// We do however need to free the `Pool` tracking entities and the backing memory itself.
|
||||
// As `Allocator` is embedded in the first backing slice, the `control` pointer will be
|
||||
// invalid after this call.
|
||||
for p := control.pool.next; p != nil; {
|
||||
next := p.next
|
||||
|
||||
// Free the allocation on the backing allocator
|
||||
runtime.delete(p.data, p.allocator)
|
||||
free(p, p.allocator)
|
||||
|
||||
p = next
|
||||
}
|
||||
}
|
||||
|
||||
allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, runtime.Allocator_Error) {
|
||||
|
||||
control := (^Allocator)(allocator_data)
|
||||
if control == nil {
|
||||
return nil, .Invalid_Argument
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return alloc_bytes(control, uint(size), uint(alignment))
|
||||
case .Alloc_Non_Zeroed:
|
||||
return alloc_bytes_non_zeroed(control, uint(size), uint(alignment))
|
||||
|
||||
case .Free:
|
||||
free_with_size(control, old_memory, uint(old_size))
|
||||
return nil, nil
|
||||
|
||||
case .Free_All:
|
||||
clear(control)
|
||||
return nil, nil
|
||||
|
||||
case .Resize:
|
||||
return resize(control, old_memory, uint(old_size), uint(size), uint(alignment))
|
||||
|
||||
case .Resize_Non_Zeroed:
|
||||
return resize_non_zeroed(control, old_memory, uint(old_size), uint(size), uint(alignment))
|
||||
|
||||
case .Query_Features:
|
||||
set := (^runtime.Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
case .Query_Info:
|
||||
return nil, .Mode_Not_Implemented
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
@@ -0,0 +1,738 @@
|
||||
/*
|
||||
Copyright 2024 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Matt Conte: Original C implementation, see LICENSE file in this package
|
||||
Jeroen van Rijn: Source port
|
||||
*/
|
||||
|
||||
|
||||
package mem_tlsf
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
// import "core:fmt"
|
||||
|
||||
// log2 of number of linear subdivisions of block sizes.
|
||||
// Larger values require more memory in the control structure.
|
||||
// Values of 4 or 5 are typical.
|
||||
TLSF_SL_INDEX_COUNT_LOG2 :: #config(TLSF_SL_INDEX_COUNT_LOG2, 5)
|
||||
|
||||
// All allocation sizes and addresses are aligned to 4/8 bytes
|
||||
ALIGN_SIZE_LOG2 :: 3 when size_of(uintptr) == 8 else 2
|
||||
|
||||
// We can increase this to support larger allocation sizes,
|
||||
// at the expense of more overhead in the TLSF structure
|
||||
FL_INDEX_MAX :: 32 when size_of(uintptr) == 8 else 30
|
||||
#assert(FL_INDEX_MAX < 36)
|
||||
|
||||
ALIGN_SIZE :: 1 << ALIGN_SIZE_LOG2
|
||||
SL_INDEX_COUNT :: 1 << TLSF_SL_INDEX_COUNT_LOG2
|
||||
FL_INDEX_SHIFT :: TLSF_SL_INDEX_COUNT_LOG2 + ALIGN_SIZE_LOG2
|
||||
FL_INDEX_COUNT :: FL_INDEX_MAX - FL_INDEX_SHIFT + 1
|
||||
SMALL_BLOCK_SIZE :: 1 << FL_INDEX_SHIFT
|
||||
|
||||
/*
|
||||
We support allocations of sizes up to (1 << `FL_INDEX_MAX`) bits.
|
||||
However, because we linearly subdivide the second-level lists, and
|
||||
our minimum size granularity is 4 bytes, it doesn't make sense to
|
||||
create first-level lists for sizes smaller than `SL_INDEX_COUNT` * 4,
|
||||
or (1 << (`TLSF_SL_INDEX_COUNT_LOG2` + 2)) bytes, as there we will be
|
||||
trying to split size ranges into more slots than we have available.
|
||||
Instead, we calculate the minimum threshold size, and place all
|
||||
blocks below that size into the 0th first-level list.
|
||||
*/
|
||||
|
||||
// SL_INDEX_COUNT must be <= number of bits in sl_bitmap's storage tree
|
||||
#assert(size_of(uint) * 8 >= SL_INDEX_COUNT)
|
||||
|
||||
// Ensure we've properly tuned our sizes.
|
||||
#assert(ALIGN_SIZE == SMALL_BLOCK_SIZE / SL_INDEX_COUNT)
|
||||
|
||||
#assert(size_of(Allocator) % ALIGN_SIZE == 0)
|
||||
|
||||
Pool :: struct {
|
||||
data: []u8 `fmt:"-"`,
|
||||
allocator: runtime.Allocator,
|
||||
next: ^Pool,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Block header structure.
|
||||
|
||||
There are several implementation subtleties involved:
|
||||
- The `prev_phys_block` field is only valid if the previous block is free.
|
||||
- The `prev_phys_block` field is actually stored at the end of the
|
||||
previous block. It appears at the beginning of this structure only to
|
||||
simplify the implementation.
|
||||
- The `next_free` / `prev_free` fields are only valid if the block is free.
|
||||
*/
|
||||
Block_Header :: struct {
|
||||
prev_phys_block: ^Block_Header,
|
||||
size: uint, // The size of this block, excluding the block header
|
||||
|
||||
// Next and previous free blocks.
|
||||
next_free: ^Block_Header,
|
||||
prev_free: ^Block_Header,
|
||||
}
|
||||
#assert(offset_of(Block_Header, prev_phys_block) == 0)
|
||||
|
||||
/*
|
||||
Since block sizes are always at least a multiple of 4, the two least
|
||||
significant bits of the size field are used to store the block status:
|
||||
- bit 0: whether block is busy or free
|
||||
- bit 1: whether previous block is busy or free
|
||||
*/
|
||||
BLOCK_HEADER_FREE :: uint(1 << 0)
|
||||
BLOCK_HEADER_PREV_FREE :: uint(1 << 1)
|
||||
|
||||
/*
|
||||
The size of the block header exposed to used blocks is the `size` field.
|
||||
The `prev_phys_block` field is stored *inside* the previous free block.
|
||||
*/
|
||||
BLOCK_HEADER_OVERHEAD :: uint(size_of(uint))
|
||||
|
||||
POOL_OVERHEAD :: 2 * BLOCK_HEADER_OVERHEAD
|
||||
|
||||
// User data starts directly after the size field in a used block.
|
||||
BLOCK_START_OFFSET :: offset_of(Block_Header, size) + size_of(Block_Header{}.size)
|
||||
|
||||
/*
|
||||
A free block must be large enough to store its header minus the size of
|
||||
the `prev_phys_block` field, and no larger than the number of addressable
|
||||
bits for `FL_INDEX`.
|
||||
*/
|
||||
BLOCK_SIZE_MIN :: uint(size_of(Block_Header) - size_of(^Block_Header))
|
||||
BLOCK_SIZE_MAX :: uint(1) << FL_INDEX_MAX
|
||||
|
||||
/*
|
||||
TLSF achieves O(1) cost for `alloc` and `free` operations by limiting
|
||||
the search for a free block to a free list of guaranteed size
|
||||
adequate to fulfill the request, combined with efficient free list
|
||||
queries using bitmasks and architecture-specific bit-manipulation
|
||||
routines.
|
||||
|
||||
NOTE: TLSF spec relies on ffs/fls returning value 0..31.
|
||||
*/
|
||||
|
||||
@(require_results)
|
||||
ffs :: proc "contextless" (word: u32) -> (bit: i32) {
|
||||
return -1 if word == 0 else i32(intrinsics.count_trailing_zeros(word))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fls :: proc "contextless" (word: u32) -> (bit: i32) {
|
||||
N :: (size_of(u32) * 8) - 1
|
||||
return i32(N - intrinsics.count_leading_zeros(word))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fls_uint :: proc "contextless" (size: uint) -> (bit: i32) {
|
||||
N :: (size_of(uint) * 8) - 1
|
||||
return i32(N - intrinsics.count_leading_zeros(size))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
block_size :: proc "contextless" (block: ^Block_Header) -> (size: uint) {
|
||||
return block.size &~ (BLOCK_HEADER_FREE | BLOCK_HEADER_PREV_FREE)
|
||||
}
|
||||
|
||||
block_set_size :: proc "contextless" (block: ^Block_Header, size: uint) {
|
||||
old_size := block.size
|
||||
block.size = size | (old_size & (BLOCK_HEADER_FREE | BLOCK_HEADER_PREV_FREE))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
block_is_last :: proc "contextless" (block: ^Block_Header) -> (is_last: bool) {
|
||||
return block_size(block) == 0
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
block_is_free :: proc "contextless" (block: ^Block_Header) -> (is_free: bool) {
|
||||
return (block.size & BLOCK_HEADER_FREE) == BLOCK_HEADER_FREE
|
||||
}
|
||||
|
||||
block_set_free :: proc "contextless" (block: ^Block_Header) {
|
||||
block.size |= BLOCK_HEADER_FREE
|
||||
}
|
||||
|
||||
block_set_used :: proc "contextless" (block: ^Block_Header) {
|
||||
block.size &~= BLOCK_HEADER_FREE
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
block_is_prev_free :: proc "contextless" (block: ^Block_Header) -> (is_prev_free: bool) {
|
||||
return (block.size & BLOCK_HEADER_PREV_FREE) == BLOCK_HEADER_PREV_FREE
|
||||
}
|
||||
|
||||
block_set_prev_free :: proc "contextless" (block: ^Block_Header) {
|
||||
block.size |= BLOCK_HEADER_PREV_FREE
|
||||
}
|
||||
|
||||
block_set_prev_used :: proc "contextless" (block: ^Block_Header) {
|
||||
block.size &~= BLOCK_HEADER_PREV_FREE
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
block_from_ptr :: proc(ptr: rawptr) -> (block_ptr: ^Block_Header) {
|
||||
return (^Block_Header)(uintptr(ptr) - BLOCK_START_OFFSET)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
block_to_ptr :: proc(block: ^Block_Header) -> (ptr: rawptr) {
|
||||
return rawptr(uintptr(block) + BLOCK_START_OFFSET)
|
||||
}
|
||||
|
||||
// Return location of next block after block of given size.
|
||||
@(require_results)
|
||||
offset_to_block :: proc(ptr: rawptr, size: uint) -> (block: ^Block_Header) {
|
||||
return (^Block_Header)(uintptr(ptr) + uintptr(size))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
offset_to_block_backwards :: proc(ptr: rawptr, size: uint) -> (block: ^Block_Header) {
|
||||
return (^Block_Header)(uintptr(ptr) - uintptr(size))
|
||||
}
|
||||
|
||||
// Return location of previous block.
|
||||
@(require_results)
|
||||
block_prev :: proc(block: ^Block_Header) -> (prev: ^Block_Header) {
|
||||
assert(block_is_prev_free(block), "previous block must be free")
|
||||
return block.prev_phys_block
|
||||
}
|
||||
|
||||
// Return location of next existing block.
|
||||
@(require_results)
|
||||
block_next :: proc(block: ^Block_Header) -> (next: ^Block_Header) {
|
||||
return offset_to_block(block_to_ptr(block), block_size(block) - BLOCK_HEADER_OVERHEAD)
|
||||
}
|
||||
|
||||
// Link a new block with its physical neighbor, return the neighbor.
|
||||
@(require_results)
|
||||
block_link_next :: proc(block: ^Block_Header) -> (next: ^Block_Header) {
|
||||
next = block_next(block)
|
||||
next.prev_phys_block = block
|
||||
return
|
||||
}
|
||||
|
||||
block_mark_as_free :: proc(block: ^Block_Header) {
|
||||
// Link the block to the next block, first.
|
||||
next := block_link_next(block)
|
||||
block_set_prev_free(next)
|
||||
block_set_free(block)
|
||||
}
|
||||
|
||||
block_mark_as_used :: proc(block: ^Block_Header) {
|
||||
next := block_next(block)
|
||||
block_set_prev_used(next)
|
||||
block_set_used(block)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
align_up :: proc(x, align: uint) -> (aligned: uint) {
|
||||
assert(0 == (align & (align - 1)), "must align to a power of two")
|
||||
return (x + (align - 1)) &~ (align - 1)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
align_down :: proc(x, align: uint) -> (aligned: uint) {
|
||||
assert(0 == (align & (align - 1)), "must align to a power of two")
|
||||
return x - (x & (align - 1))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
align_ptr :: proc(ptr: rawptr, align: uint) -> (aligned: rawptr) {
|
||||
assert(0 == (align & (align - 1)), "must align to a power of two")
|
||||
align_mask := uintptr(align) - 1
|
||||
_ptr := uintptr(ptr)
|
||||
_aligned := (_ptr + align_mask) &~ (align_mask)
|
||||
return rawptr(_aligned)
|
||||
}
|
||||
|
||||
// Adjust an allocation size to be aligned to word size, and no smaller than internal minimum.
|
||||
@(require_results)
|
||||
adjust_request_size :: proc(size, align: uint) -> (adjusted: uint) {
|
||||
if size == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// aligned size must not exceed `BLOCK_SIZE_MAX`, or we'll go out of bounds on `sl_bitmap`.
|
||||
if aligned := align_up(size, align); aligned < BLOCK_SIZE_MAX {
|
||||
adjusted = min(aligned, BLOCK_SIZE_MAX)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Adjust an allocation size to be aligned to word size, and no smaller than internal minimum.
|
||||
@(require_results)
|
||||
adjust_request_size_with_err :: proc(size, align: uint) -> (adjusted: uint, err: runtime.Allocator_Error) {
|
||||
if size == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// aligned size must not exceed `BLOCK_SIZE_MAX`, or we'll go out of bounds on `sl_bitmap`.
|
||||
if aligned := align_up(size, align); aligned < BLOCK_SIZE_MAX {
|
||||
adjusted = min(aligned, BLOCK_SIZE_MAX)
|
||||
} else {
|
||||
err = .Out_Of_Memory
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// TLSF utility functions. In most cases these are direct translations of
|
||||
// the documentation in the research paper.
|
||||
|
||||
@(optimization_mode="speed", require_results)
|
||||
mapping_insert :: proc(size: uint) -> (fl, sl: i32) {
|
||||
if size < SMALL_BLOCK_SIZE {
|
||||
// Store small blocks in first list.
|
||||
sl = i32(size) / (SMALL_BLOCK_SIZE / SL_INDEX_COUNT)
|
||||
} else {
|
||||
fl = fls_uint(size)
|
||||
sl = i32(size >> (uint(fl) - TLSF_SL_INDEX_COUNT_LOG2)) ~ (1 << TLSF_SL_INDEX_COUNT_LOG2)
|
||||
fl -= (FL_INDEX_SHIFT - 1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(optimization_mode="speed", require_results)
|
||||
mapping_round :: #force_inline proc(size: uint) -> (rounded: uint) {
|
||||
rounded = size
|
||||
if size >= SMALL_BLOCK_SIZE {
|
||||
round := uint(1 << (uint(fls_uint(size) - TLSF_SL_INDEX_COUNT_LOG2))) - 1
|
||||
rounded += round
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// This version rounds up to the next block size (for allocations)
|
||||
@(optimization_mode="speed", require_results)
|
||||
mapping_search :: proc(size: uint) -> (fl, sl: i32) {
|
||||
return mapping_insert(mapping_round(size))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
search_suitable_block :: proc(control: ^Allocator, fli, sli: ^i32) -> (block: ^Block_Header) {
|
||||
// First, search for a block in the list associated with the given fl/sl index.
|
||||
fl := fli^; sl := sli^
|
||||
|
||||
sl_map := control.sl_bitmap[fli^] & (~u32(0) << uint(sl))
|
||||
if sl_map == 0 {
|
||||
// No block exists. Search in the next largest first-level list.
|
||||
fl_map := control.fl_bitmap & (~u32(0) << uint(fl + 1))
|
||||
if fl_map == 0 {
|
||||
// No free blocks available, memory has been exhausted.
|
||||
return {}
|
||||
}
|
||||
|
||||
fl = ffs(fl_map)
|
||||
fli^ = fl
|
||||
sl_map = control.sl_bitmap[fl]
|
||||
}
|
||||
assert(sl_map != 0, "internal error - second level bitmap is null")
|
||||
sl = ffs(sl_map)
|
||||
sli^ = sl
|
||||
|
||||
// Return the first block in the free list.
|
||||
return control.blocks[fl][sl]
|
||||
}
|
||||
|
||||
// Remove a free block from the free list.
|
||||
remove_free_block :: proc(control: ^Allocator, block: ^Block_Header, fl: i32, sl: i32) {
|
||||
prev := block.prev_free
|
||||
next := block.next_free
|
||||
assert(prev != nil, "prev_free can not be nil")
|
||||
assert(next != nil, "next_free can not be nil")
|
||||
next.prev_free = prev
|
||||
prev.next_free = next
|
||||
|
||||
// If this block is the head of the free list, set new head.
|
||||
if control.blocks[fl][sl] == block {
|
||||
control.blocks[fl][sl] = next
|
||||
|
||||
// If the new head is nil, clear the bitmap
|
||||
if next == &control.block_null {
|
||||
control.sl_bitmap[fl] &~= (u32(1) << uint(sl))
|
||||
|
||||
// If the second bitmap is now empty, clear the fl bitmap
|
||||
if control.sl_bitmap[fl] == 0 {
|
||||
control.fl_bitmap &~= (u32(1) << uint(fl))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Insert a free block into the free block list.
|
||||
insert_free_block :: proc(control: ^Allocator, block: ^Block_Header, fl: i32, sl: i32) {
|
||||
current := control.blocks[fl][sl]
|
||||
assert(current != nil, "free lists cannot have a nil entry")
|
||||
assert(block != nil, "cannot insert a nil entry into the free list")
|
||||
block.next_free = current
|
||||
block.prev_free = &control.block_null
|
||||
current.prev_free = block
|
||||
|
||||
assert(block_to_ptr(block) == align_ptr(block_to_ptr(block), ALIGN_SIZE), "block not properly aligned")
|
||||
|
||||
// Insert the new block at the head of the list, and mark the first- and second-level bitmaps appropriately.
|
||||
control.blocks[fl][sl] = block
|
||||
control.fl_bitmap |= (u32(1) << uint(fl))
|
||||
control.sl_bitmap[fl] |= (u32(1) << uint(sl))
|
||||
}
|
||||
|
||||
// Remove a given block from the free list.
|
||||
block_remove :: proc(control: ^Allocator, block: ^Block_Header) {
|
||||
fl, sl := mapping_insert(block_size(block))
|
||||
remove_free_block(control, block, fl, sl)
|
||||
}
|
||||
|
||||
// Insert a given block into the free list.
|
||||
block_insert :: proc(control: ^Allocator, block: ^Block_Header) {
|
||||
fl, sl := mapping_insert(block_size(block))
|
||||
insert_free_block(control, block, fl, sl)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
block_can_split :: proc(block: ^Block_Header, size: uint) -> (can_split: bool) {
|
||||
return block_size(block) >= size_of(Block_Header) + size
|
||||
}
|
||||
|
||||
// Split a block into two, the second of which is free.
|
||||
@(require_results)
|
||||
block_split :: proc(block: ^Block_Header, size: uint) -> (remaining: ^Block_Header) {
|
||||
// Calculate the amount of space left in the remaining block.
|
||||
remaining = offset_to_block(block_to_ptr(block), size - BLOCK_HEADER_OVERHEAD)
|
||||
|
||||
remain_size := block_size(block) - (size + BLOCK_HEADER_OVERHEAD)
|
||||
|
||||
assert(block_to_ptr(remaining) == align_ptr(block_to_ptr(remaining), ALIGN_SIZE),
|
||||
"remaining block not aligned properly")
|
||||
|
||||
assert(block_size(block) == remain_size + size + BLOCK_HEADER_OVERHEAD)
|
||||
block_set_size(remaining, remain_size)
|
||||
assert(block_size(remaining) >= BLOCK_SIZE_MIN, "block split with invalid size")
|
||||
|
||||
block_set_size(block, size)
|
||||
block_mark_as_free(remaining)
|
||||
|
||||
return remaining
|
||||
}
|
||||
|
||||
// Absorb a free block's storage into an adjacent previous free block.
|
||||
@(require_results)
|
||||
block_absorb :: proc(prev: ^Block_Header, block: ^Block_Header) -> (absorbed: ^Block_Header) {
|
||||
assert(!block_is_last(prev), "previous block can't be last")
|
||||
// Note: Leaves flags untouched.
|
||||
prev.size += block_size(block) + BLOCK_HEADER_OVERHEAD
|
||||
_ = block_link_next(prev)
|
||||
return prev
|
||||
}
|
||||
|
||||
// Merge a just-freed block with an adjacent previous free block.
|
||||
@(require_results)
|
||||
block_merge_prev :: proc(control: ^Allocator, block: ^Block_Header) -> (merged: ^Block_Header) {
|
||||
merged = block
|
||||
if (block_is_prev_free(block)) {
|
||||
prev := block_prev(block)
|
||||
assert(prev != nil, "prev physical block can't be nil")
|
||||
assert(block_is_free(prev), "prev block is not free though marked as such")
|
||||
block_remove(control, prev)
|
||||
merged = block_absorb(prev, block)
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
// Merge a just-freed block with an adjacent free block.
|
||||
@(require_results)
|
||||
block_merge_next :: proc(control: ^Allocator, block: ^Block_Header) -> (merged: ^Block_Header) {
|
||||
merged = block
|
||||
next := block_next(block)
|
||||
assert(next != nil, "next physical block can't be nil")
|
||||
|
||||
if (block_is_free(next)) {
|
||||
assert(!block_is_last(block), "previous block can't be last")
|
||||
block_remove(control, next)
|
||||
merged = block_absorb(block, next)
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
// Trim any trailing block space off the end of a free block, return to pool.
|
||||
block_trim_free :: proc(control: ^Allocator, block: ^Block_Header, size: uint) {
|
||||
assert(block_is_free(block), "block must be free")
|
||||
if (block_can_split(block, size)) {
|
||||
remaining_block := block_split(block, size)
|
||||
_ = block_link_next(block)
|
||||
block_set_prev_free(remaining_block)
|
||||
block_insert(control, remaining_block)
|
||||
}
|
||||
}
|
||||
|
||||
// Trim any trailing block space off the end of a used block, return to pool.
|
||||
block_trim_used :: proc(control: ^Allocator, block: ^Block_Header, size: uint) {
|
||||
assert(!block_is_free(block), "Block must be used")
|
||||
if (block_can_split(block, size)) {
|
||||
// If the next block is free, we must coalesce.
|
||||
remaining_block := block_split(block, size)
|
||||
block_set_prev_used(remaining_block)
|
||||
|
||||
remaining_block = block_merge_next(control, remaining_block)
|
||||
block_insert(control, remaining_block)
|
||||
}
|
||||
}
|
||||
|
||||
// Trim leading block space, return to pool.
|
||||
@(require_results)
|
||||
block_trim_free_leading :: proc(control: ^Allocator, block: ^Block_Header, size: uint) -> (remaining: ^Block_Header) {
|
||||
remaining = block
|
||||
if block_can_split(block, size) {
|
||||
// We want the 2nd block.
|
||||
remaining = block_split(block, size - BLOCK_HEADER_OVERHEAD)
|
||||
block_set_prev_free(remaining)
|
||||
|
||||
_ = block_link_next(block)
|
||||
block_insert(control, block)
|
||||
}
|
||||
return remaining
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
block_locate_free :: proc(control: ^Allocator, size: uint) -> (block: ^Block_Header) {
|
||||
fl, sl: i32
|
||||
if size != 0 {
|
||||
fl, sl = mapping_search(size)
|
||||
|
||||
/*
|
||||
`mapping_search` can futz with the size, so for excessively large sizes it can sometimes wind up
|
||||
with indices that are off the end of the block array. So, we protect against that here,
|
||||
since this is the only call site of `mapping_search`. Note that we don't need to check `sl`,
|
||||
as it comes from a modulo operation that guarantees it's always in range.
|
||||
*/
|
||||
if fl < FL_INDEX_COUNT {
|
||||
block = search_suitable_block(control, &fl, &sl)
|
||||
}
|
||||
}
|
||||
|
||||
if block != nil {
|
||||
assert(block_size(block) >= size)
|
||||
remove_free_block(control, block, fl, sl)
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
block_prepare_used :: proc(control: ^Allocator, block: ^Block_Header, size: uint) -> (res: []byte, err: runtime.Allocator_Error) {
|
||||
if block != nil {
|
||||
assert(size != 0, "Size must be non-zero")
|
||||
block_trim_free(control, block, size)
|
||||
block_mark_as_used(block)
|
||||
res = ([^]byte)(block_to_ptr(block))[:size]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Clear control structure and point all empty lists at the null block
|
||||
clear :: proc(control: ^Allocator) {
|
||||
control.block_null.next_free = &control.block_null
|
||||
control.block_null.prev_free = &control.block_null
|
||||
|
||||
control.fl_bitmap = 0
|
||||
for i in 0..<FL_INDEX_COUNT {
|
||||
control.sl_bitmap[i] = 0
|
||||
for j in 0..<SL_INDEX_COUNT {
|
||||
control.blocks[i][j] = &control.block_null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
pool_add :: proc(control: ^Allocator, pool: []u8) -> (err: Error) {
|
||||
assert(uintptr(raw_data(pool)) % ALIGN_SIZE == 0, "Added memory must be aligned")
|
||||
|
||||
pool_overhead := POOL_OVERHEAD
|
||||
pool_bytes := align_down(len(pool) - pool_overhead, ALIGN_SIZE)
|
||||
|
||||
if pool_bytes < BLOCK_SIZE_MIN {
|
||||
return .Backing_Buffer_Too_Small
|
||||
} else if pool_bytes > BLOCK_SIZE_MAX {
|
||||
return .Backing_Buffer_Too_Large
|
||||
}
|
||||
|
||||
// Create the main free block. Offset the start of the block slightly,
|
||||
// so that the `prev_phys_block` field falls outside of the pool -
|
||||
// it will never be used.
|
||||
block := offset_to_block_backwards(raw_data(pool), BLOCK_HEADER_OVERHEAD)
|
||||
|
||||
block_set_size(block, pool_bytes)
|
||||
block_set_free(block)
|
||||
block_set_prev_used(block)
|
||||
block_insert(control, block)
|
||||
|
||||
// Split the block to create a zero-size sentinel block
|
||||
next := block_link_next(block)
|
||||
block_set_size(next, 0)
|
||||
block_set_used(next)
|
||||
block_set_prev_free(next)
|
||||
return
|
||||
}
|
||||
|
||||
pool_remove :: proc(control: ^Allocator, pool: []u8) {
|
||||
block := offset_to_block_backwards(raw_data(pool), BLOCK_HEADER_OVERHEAD)
|
||||
|
||||
assert(block_is_free(block), "Block should be free")
|
||||
assert(!block_is_free(block_next(block)), "Next block should not be free")
|
||||
assert(block_size(block_next(block)) == 0, "Next block size should be zero")
|
||||
|
||||
fl, sl := mapping_insert(block_size(block))
|
||||
remove_free_block(control, block, fl, sl)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
alloc_bytes_non_zeroed :: proc(control: ^Allocator, size: uint, align: uint) -> (res: []byte, err: runtime.Allocator_Error) {
|
||||
assert(control != nil)
|
||||
adjust := adjust_request_size(size, ALIGN_SIZE)
|
||||
|
||||
GAP_MINIMUM :: size_of(Block_Header)
|
||||
size_with_gap := adjust_request_size(adjust + align + GAP_MINIMUM, align)
|
||||
|
||||
aligned_size := size_with_gap if adjust != 0 && align > ALIGN_SIZE else adjust
|
||||
if aligned_size == 0 && size > 0 {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
block := block_locate_free(control, aligned_size)
|
||||
if block == nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
ptr := block_to_ptr(block)
|
||||
aligned := align_ptr(ptr, align)
|
||||
gap := uint(int(uintptr(aligned)) - int(uintptr(ptr)))
|
||||
|
||||
if gap != 0 && gap < GAP_MINIMUM {
|
||||
gap_remain := GAP_MINIMUM - gap
|
||||
offset := uintptr(max(gap_remain, align))
|
||||
next_aligned := rawptr(uintptr(aligned) + offset)
|
||||
|
||||
aligned = align_ptr(next_aligned, align)
|
||||
|
||||
gap = uint(int(uintptr(aligned)) - int(uintptr(ptr)))
|
||||
}
|
||||
|
||||
if gap != 0 {
|
||||
assert(gap >= GAP_MINIMUM, "gap size too small")
|
||||
block = block_trim_free_leading(control, block, gap)
|
||||
}
|
||||
|
||||
return block_prepare_used(control, block, adjust)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
alloc_bytes :: proc(control: ^Allocator, size: uint, align: uint) -> (res: []byte, err: runtime.Allocator_Error) {
|
||||
res, err = alloc_bytes_non_zeroed(control, size, align)
|
||||
if err != nil {
|
||||
intrinsics.mem_zero(raw_data(res), len(res))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
free_with_size :: proc(control: ^Allocator, ptr: rawptr, size: uint) {
|
||||
assert(control != nil)
|
||||
// `size` is currently ignored
|
||||
if ptr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
block := block_from_ptr(ptr)
|
||||
assert(!block_is_free(block), "block already marked as free") // double free
|
||||
block_mark_as_free(block)
|
||||
block = block_merge_prev(control, block)
|
||||
block = block_merge_next(control, block)
|
||||
block_insert(control, block)
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
resize :: proc(control: ^Allocator, ptr: rawptr, old_size, new_size: uint, alignment: uint) -> (res: []byte, err: runtime.Allocator_Error) {
|
||||
assert(control != nil)
|
||||
if ptr != nil && new_size == 0 {
|
||||
free_with_size(control, ptr, old_size)
|
||||
return
|
||||
} else if ptr == nil {
|
||||
return alloc_bytes(control, new_size, alignment)
|
||||
}
|
||||
|
||||
block := block_from_ptr(ptr)
|
||||
next := block_next(block)
|
||||
|
||||
curr_size := block_size(block)
|
||||
combined := curr_size + block_size(next) + BLOCK_HEADER_OVERHEAD
|
||||
adjust := adjust_request_size(new_size, max(ALIGN_SIZE, alignment))
|
||||
|
||||
assert(!block_is_free(block), "block already marked as free") // double free
|
||||
|
||||
min_size := min(curr_size, new_size, old_size)
|
||||
|
||||
if adjust > curr_size && (!block_is_free(next) || adjust > combined) {
|
||||
res = alloc_bytes(control, new_size, alignment) or_return
|
||||
if res != nil {
|
||||
copy(res, ([^]byte)(ptr)[:min_size])
|
||||
free_with_size(control, ptr, curr_size)
|
||||
}
|
||||
return
|
||||
}
|
||||
if adjust > curr_size {
|
||||
_ = block_merge_next(control, block)
|
||||
block_mark_as_used(block)
|
||||
}
|
||||
|
||||
block_trim_used(control, block, adjust)
|
||||
res = ([^]byte)(ptr)[:new_size]
|
||||
|
||||
if min_size < new_size {
|
||||
to_zero := ([^]byte)(ptr)[min_size:new_size]
|
||||
runtime.mem_zero(raw_data(to_zero), len(to_zero))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
resize_non_zeroed :: proc(control: ^Allocator, ptr: rawptr, old_size, new_size: uint, alignment: uint) -> (res: []byte, err: runtime.Allocator_Error) {
|
||||
assert(control != nil)
|
||||
if ptr != nil && new_size == 0 {
|
||||
free_with_size(control, ptr, old_size)
|
||||
return
|
||||
} else if ptr == nil {
|
||||
return alloc_bytes_non_zeroed(control, new_size, alignment)
|
||||
}
|
||||
|
||||
block := block_from_ptr(ptr)
|
||||
next := block_next(block)
|
||||
|
||||
curr_size := block_size(block)
|
||||
combined := curr_size + block_size(next) + BLOCK_HEADER_OVERHEAD
|
||||
adjust := adjust_request_size(new_size, max(ALIGN_SIZE, alignment))
|
||||
|
||||
assert(!block_is_free(block), "block already marked as free") // double free
|
||||
|
||||
min_size := min(curr_size, new_size, old_size)
|
||||
|
||||
if adjust > curr_size && (!block_is_free(next) || adjust > combined) {
|
||||
res = alloc_bytes_non_zeroed(control, new_size, alignment) or_return
|
||||
if res != nil {
|
||||
copy(res, ([^]byte)(ptr)[:min_size])
|
||||
free_with_size(control, ptr, old_size)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if adjust > curr_size {
|
||||
_ = block_merge_next(control, block)
|
||||
block_mark_as_used(block)
|
||||
}
|
||||
|
||||
block_trim_used(control, block, adjust)
|
||||
res = ([^]byte)(ptr)[:new_size]
|
||||
return
|
||||
}
|
||||
@@ -87,8 +87,12 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
|
||||
find_data := &win32.WIN32_FIND_DATAW{}
|
||||
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
|
||||
if find_handle == win32.INVALID_HANDLE_VALUE {
|
||||
err = Errno(win32.GetLastError())
|
||||
return dfi[:], err
|
||||
}
|
||||
defer win32.FindClose(find_handle)
|
||||
for n != 0 && find_handle != nil {
|
||||
for n != 0 {
|
||||
fi: File_Info
|
||||
fi = find_data_to_file_info(path, find_data)
|
||||
if fi.name != "" {
|
||||
|
||||
@@ -111,7 +111,7 @@ next_random :: proc(r: ^[2]u64) -> u64 {
|
||||
|
||||
@(require_results)
|
||||
random_string :: proc(buf: []byte) -> string {
|
||||
@static digits := "0123456789"
|
||||
@(static, rodata) digits := "0123456789"
|
||||
|
||||
u := next_random(&random_string_seed)
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package slice
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
// An in-place permutation iterator.
|
||||
Permutation_Iterator :: struct($T: typeid) {
|
||||
index: int,
|
||||
slice: []T,
|
||||
counters: []int,
|
||||
}
|
||||
|
||||
/*
|
||||
Make an iterator to permute a slice in-place.
|
||||
|
||||
*Allocates Using Provided Allocator*
|
||||
|
||||
This procedure allocates some state to assist in permutation and does not make
|
||||
a copy of the underlying slice. If you want to permute a slice without altering
|
||||
the underlying data, use `clone` to create a copy, then permute that instead.
|
||||
|
||||
Inputs:
|
||||
- slice: The slice to permute.
|
||||
- allocator: (default is context.allocator)
|
||||
|
||||
Returns:
|
||||
- iter: The iterator, to be passed to `permute`.
|
||||
- error: An `Allocator_Error`, if allocation failed.
|
||||
*/
|
||||
make_permutation_iterator :: proc(
|
||||
slice: []$T,
|
||||
allocator := context.allocator,
|
||||
) -> (
|
||||
iter: Permutation_Iterator(T),
|
||||
error: runtime.Allocator_Error,
|
||||
) #optional_allocator_error {
|
||||
iter.slice = slice
|
||||
iter.counters = make([]int, len(iter.slice), allocator) or_return
|
||||
|
||||
return
|
||||
}
|
||||
/*
|
||||
Free the state allocated by `make_permutation_iterator`.
|
||||
|
||||
Inputs:
|
||||
- iter: The iterator created by `make_permutation_iterator`.
|
||||
- allocator: The allocator used to create the iterator. (default is context.allocator)
|
||||
*/
|
||||
destroy_permutation_iterator :: proc(
|
||||
iter: Permutation_Iterator($T),
|
||||
allocator := context.allocator,
|
||||
) {
|
||||
delete(iter.counters, allocator = allocator)
|
||||
}
|
||||
/*
|
||||
Permute a slice in-place.
|
||||
|
||||
Note that the first iteration will always be the original, unpermuted slice.
|
||||
|
||||
Inputs:
|
||||
- iter: The iterator created by `make_permutation_iterator`.
|
||||
|
||||
Returns:
|
||||
- ok: True if the permutation succeeded, false if the iteration is complete.
|
||||
*/
|
||||
permute :: proc(iter: ^Permutation_Iterator($T)) -> (ok: bool) {
|
||||
// This is an iterative, resumable implementation of Heap's algorithm.
|
||||
//
|
||||
// The original algorithm was described by B. R. Heap as "Permutations by
|
||||
// interchanges" in The Computer Journal, 1963.
|
||||
//
|
||||
// This implementation is based on the nonrecursive version described by
|
||||
// Robert Sedgewick in "Permutation Generation Methods" which was published
|
||||
// in ACM Computing Surveys in 1977.
|
||||
|
||||
i := iter.index
|
||||
|
||||
if i == 0 {
|
||||
iter.index = 1
|
||||
return true
|
||||
}
|
||||
|
||||
n := len(iter.counters)
|
||||
#no_bounds_check for i < n {
|
||||
if iter.counters[i] < i {
|
||||
if i & 1 == 0 {
|
||||
iter.slice[0], iter.slice[i] = iter.slice[i], iter.slice[0]
|
||||
} else {
|
||||
iter.slice[iter.counters[i]], iter.slice[i] = iter.slice[i], iter.slice[iter.counters[i]]
|
||||
}
|
||||
|
||||
iter.counters[i] += 1
|
||||
i = 1
|
||||
|
||||
break
|
||||
} else {
|
||||
iter.counters[i] = 0
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
if i == n {
|
||||
return false
|
||||
}
|
||||
iter.index = i
|
||||
return true
|
||||
}
|
||||
@@ -375,7 +375,7 @@ decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64
|
||||
return
|
||||
}
|
||||
|
||||
@static power_table := [?]int{1, 3, 6, 9, 13, 16, 19, 23, 26}
|
||||
@(static, rodata) power_table := [?]int{1, 3, 6, 9, 13, 16, 19, 23, 26}
|
||||
|
||||
exp = 0
|
||||
for d.decimal_point > 0 {
|
||||
|
||||
@@ -1095,7 +1095,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
|
||||
}
|
||||
|
||||
trunc_block: if !trunc {
|
||||
@static pow10 := [?]f64{
|
||||
@(static, rodata) pow10 := [?]f64{
|
||||
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22,
|
||||
|
||||
@@ -52,7 +52,7 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati
|
||||
}
|
||||
} else {
|
||||
|
||||
timeout_ns := u32(duration) * 1000
|
||||
timeout_ns := u32(duration)
|
||||
s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns)
|
||||
if s >= 0 {
|
||||
return true
|
||||
|
||||
@@ -527,6 +527,7 @@ macos_release_map: map[string]Darwin_To_Release = {
|
||||
"23D60" = {{23, 3, 0}, "macOS", {"Sonoma", {14, 3, 1}}},
|
||||
"23E214" = {{23, 4, 0}, "macOS", {"Sonoma", {14, 4, 0}}},
|
||||
"23E224" = {{23, 4, 0}, "macOS", {"Sonoma", {14, 4, 1}}},
|
||||
"23F79" = {{23, 5, 0}, "macOS", {"Sonoma", {14, 5, 0}}},
|
||||
}
|
||||
|
||||
@(private)
|
||||
|
||||
@@ -53,6 +53,9 @@ get_log_level :: #force_inline proc() -> runtime.Logger_Level {
|
||||
else when LOG_LEVEL == "warning" { return .Warning }
|
||||
else when LOG_LEVEL == "error" { return .Error }
|
||||
else when LOG_LEVEL == "fatal" { return .Fatal }
|
||||
else {
|
||||
#panic("Unknown `ODIN_TEST_LOG_LEVEL`: \"" + LOG_LEVEL + "\", possible levels are: \"debug\", \"info\", \"warning\", \"error\", or \"fatal\".")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
// +private
|
||||
package thread
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:sync"
|
||||
import "core:sys/unix"
|
||||
import "core:time"
|
||||
|
||||
CAS :: intrinsics.atomic_compare_exchange_strong
|
||||
CAS :: sync.atomic_compare_exchange_strong
|
||||
|
||||
// NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t.
|
||||
// Also see core/sys/darwin/mach_darwin.odin/semaphore_t.
|
||||
@@ -32,11 +32,13 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
|
||||
t.id = sync.current_thread_id()
|
||||
|
||||
for (.Started not_in t.flags) {
|
||||
sync.wait(&t.cond, &t.mutex)
|
||||
for (.Started not_in sync.atomic_load(&t.flags)) {
|
||||
// HACK: use a timeout so in the event that the condition is signalled at THIS comment's exact point
|
||||
// (after checking flags, before starting the wait) it gets itself out of that deadlock after a ms.
|
||||
sync.wait_with_timeout(&t.cond, &t.mutex, time.Millisecond)
|
||||
}
|
||||
|
||||
if .Joined in t.flags {
|
||||
if .Joined in sync.atomic_load(&t.flags) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -60,11 +62,11 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
t.procedure(t)
|
||||
}
|
||||
|
||||
intrinsics.atomic_store(&t.flags, t.flags + { .Done })
|
||||
sync.atomic_or(&t.flags, { .Done })
|
||||
|
||||
sync.unlock(&t.mutex)
|
||||
|
||||
if .Self_Cleanup in t.flags {
|
||||
if .Self_Cleanup in sync.atomic_load(&t.flags) {
|
||||
t.unix_thread = {}
|
||||
// NOTE(ftphikari): It doesn't matter which context 'free' received, right?
|
||||
context = {}
|
||||
@@ -122,13 +124,12 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
}
|
||||
|
||||
_start :: proc(t: ^Thread) {
|
||||
// sync.guard(&t.mutex)
|
||||
t.flags += { .Started }
|
||||
sync.atomic_or(&t.flags, { .Started })
|
||||
sync.signal(&t.cond)
|
||||
}
|
||||
|
||||
_is_done :: proc(t: ^Thread) -> bool {
|
||||
return .Done in intrinsics.atomic_load(&t.flags)
|
||||
return .Done in sync.atomic_load(&t.flags)
|
||||
}
|
||||
|
||||
_join :: proc(t: ^Thread) {
|
||||
@@ -139,7 +140,7 @@ _join :: proc(t: ^Thread) {
|
||||
}
|
||||
|
||||
// Preserve other flags besides `.Joined`, like `.Started`.
|
||||
unjoined := intrinsics.atomic_load(&t.flags) - {.Joined}
|
||||
unjoined := sync.atomic_load(&t.flags) - {.Joined}
|
||||
joined := unjoined + {.Joined}
|
||||
|
||||
// Try to set `t.flags` from unjoined to joined. If it returns joined,
|
||||
|
||||
@@ -389,6 +389,7 @@ is_leap_year :: proc "contextless" (year: int) -> (leap: bool) {
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
|
||||
}
|
||||
|
||||
@(rodata)
|
||||
days_before := [?]i32{
|
||||
0,
|
||||
31,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
//+private
|
||||
//+build orca
|
||||
package time
|
||||
|
||||
_IS_SUPPORTED :: false
|
||||
|
||||
_now :: proc "contextless" () -> Time {
|
||||
return {}
|
||||
}
|
||||
|
||||
_sleep :: proc "contextless" (d: Duration) {
|
||||
}
|
||||
|
||||
_tick_now :: proc "contextless" () -> Tick {
|
||||
// mul_div_u64 :: proc "contextless" (val, num, den: i64) -> i64 {
|
||||
// q := val / den
|
||||
// r := val % den
|
||||
// return q * num + r * num / den
|
||||
// }
|
||||
return {}
|
||||
}
|
||||
|
||||
_yield :: proc "contextless" () {
|
||||
}
|
||||
@@ -12,6 +12,7 @@ package unicode
|
||||
@(private) pLo :: pLl | pLu // a letter that is neither upper nor lower case.
|
||||
@(private) pLmask :: pLo
|
||||
|
||||
@(rodata)
|
||||
char_properties := [MAX_LATIN1+1]u8{
|
||||
0x00 = pC, // '\x00'
|
||||
0x01 = pC, // '\x01'
|
||||
@@ -272,6 +273,7 @@ char_properties := [MAX_LATIN1+1]u8{
|
||||
}
|
||||
|
||||
|
||||
@(rodata)
|
||||
alpha_ranges := [?]i32{
|
||||
0x00d8, 0x00f6,
|
||||
0x00f8, 0x01f5,
|
||||
@@ -427,6 +429,7 @@ alpha_ranges := [?]i32{
|
||||
0xffda, 0xffdc,
|
||||
}
|
||||
|
||||
@(rodata)
|
||||
alpha_singlets := [?]i32{
|
||||
0x00aa,
|
||||
0x00b5,
|
||||
@@ -462,6 +465,7 @@ alpha_singlets := [?]i32{
|
||||
0xfe74,
|
||||
}
|
||||
|
||||
@(rodata)
|
||||
space_ranges := [?]i32{
|
||||
0x0009, 0x000d, // tab and newline
|
||||
0x0020, 0x0020, // space
|
||||
@@ -477,6 +481,7 @@ space_ranges := [?]i32{
|
||||
0xfeff, 0xfeff,
|
||||
}
|
||||
|
||||
@(rodata)
|
||||
unicode_spaces := [?]i32{
|
||||
0x0009, // tab
|
||||
0x000a, // LF
|
||||
@@ -494,6 +499,7 @@ unicode_spaces := [?]i32{
|
||||
0xfeff, // unknown
|
||||
}
|
||||
|
||||
@(rodata)
|
||||
to_upper_ranges := [?]i32{
|
||||
0x0061, 0x007a, 468, // a-z A-Z
|
||||
0x00e0, 0x00f6, 468,
|
||||
@@ -532,6 +538,7 @@ to_upper_ranges := [?]i32{
|
||||
0xff41, 0xff5a, 468,
|
||||
}
|
||||
|
||||
@(rodata)
|
||||
to_upper_singlets := [?]i32{
|
||||
0x00ff, 621,
|
||||
0x0101, 499,
|
||||
@@ -875,6 +882,7 @@ to_upper_singlets := [?]i32{
|
||||
0x1ff3, 509,
|
||||
}
|
||||
|
||||
@(rodata)
|
||||
to_lower_ranges := [?]i32{
|
||||
0x0041, 0x005a, 532, // A-Z a-z
|
||||
0x00c0, 0x00d6, 532, // - -
|
||||
@@ -914,6 +922,7 @@ to_lower_ranges := [?]i32{
|
||||
0xff21, 0xff3a, 532, // - -
|
||||
}
|
||||
|
||||
@(rodata)
|
||||
to_lower_singlets := [?]i32{
|
||||
0x0100, 501,
|
||||
0x0102, 501,
|
||||
@@ -1250,6 +1259,7 @@ to_lower_singlets := [?]i32{
|
||||
0x1ffc, 491,
|
||||
}
|
||||
|
||||
@(rodata)
|
||||
to_title_singlets := [?]i32{
|
||||
0x01c4, 501,
|
||||
0x01c6, 499,
|
||||
|
||||
@@ -910,6 +910,7 @@ gb_internal void report_os_info() {
|
||||
{"23D60", {23, 3, 0}, "macOS", {"Sonoma", {14, 3, 1}}},
|
||||
{"23E214", {23, 4, 0}, "macOS", {"Sonoma", {14, 4, 0}}},
|
||||
{"23E224", {23, 4, 0}, "macOS", {"Sonoma", {14, 4, 1}}},
|
||||
{"23F79", {23, 5, 0}, "macOS", {"Sonoma", {14, 5, 0}}},
|
||||
};
|
||||
|
||||
|
||||
|
||||
+22
-6
@@ -1088,7 +1088,6 @@ gb_global TargetMetrics target_orca_wasm32 = {
|
||||
TargetArch_wasm32,
|
||||
4, 4, 8, 16,
|
||||
str_lit("wasm32-wasi-js"),
|
||||
// str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"),
|
||||
};
|
||||
|
||||
|
||||
@@ -1138,6 +1137,14 @@ gb_global TargetMetrics target_freestanding_arm64 = {
|
||||
str_lit("aarch64-none-elf"),
|
||||
};
|
||||
|
||||
gb_global TargetMetrics target_freestanding_arm32 = {
|
||||
TargetOs_freestanding,
|
||||
TargetArch_arm32,
|
||||
4, 4, 4, 8,
|
||||
str_lit("arm-unknown-unknown-gnueabihf"),
|
||||
};
|
||||
|
||||
|
||||
struct NamedTargetMetrics {
|
||||
String name;
|
||||
TargetMetrics *metrics;
|
||||
@@ -1170,6 +1177,7 @@ gb_global NamedTargetMetrics named_targets[] = {
|
||||
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
|
||||
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
|
||||
{ str_lit("js_wasm32"), &target_js_wasm32 },
|
||||
{ str_lit("orca_wasm32"), &target_orca_wasm32 },
|
||||
|
||||
{ str_lit("freestanding_wasm64p32"), &target_freestanding_wasm64p32 },
|
||||
{ str_lit("js_wasm64p32"), &target_js_wasm64p32 },
|
||||
@@ -1179,6 +1187,7 @@ gb_global NamedTargetMetrics named_targets[] = {
|
||||
{ str_lit("freestanding_amd64_win64"), &target_freestanding_amd64_win64 },
|
||||
|
||||
{ str_lit("freestanding_arm64"), &target_freestanding_arm64 },
|
||||
{ str_lit("freestanding_arm32"), &target_freestanding_arm32 },
|
||||
};
|
||||
|
||||
gb_global NamedTargetMetrics *selected_target_metrics;
|
||||
@@ -2035,6 +2044,9 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
|
||||
bc->link_flags = str_lit("/machine:x86 ");
|
||||
break;
|
||||
}
|
||||
} else if (bc->metrics.os == TargetOs_darwin) {
|
||||
bc->link_flags = concatenate3_strings(permanent_allocator(),
|
||||
str_lit("-target "), bc->metrics.target_triplet, str_lit(" "));
|
||||
} else if (is_arch_wasm()) {
|
||||
gbString link_flags = gb_string_make(heap_allocator(), " ");
|
||||
// link_flags = gb_string_appendc(link_flags, "--export-all ");
|
||||
@@ -2045,16 +2057,20 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
|
||||
// }
|
||||
if (bc->no_entry_point || bc->metrics.os == TargetOs_orca) {
|
||||
link_flags = gb_string_appendc(link_flags, "--no-entry ");
|
||||
bc->no_entry_point = true; // just in case for the "orca" target
|
||||
}
|
||||
|
||||
|
||||
bc->link_flags = make_string_c(link_flags);
|
||||
|
||||
|
||||
// Disallow on wasm
|
||||
bc->use_separate_modules = false;
|
||||
} else {
|
||||
bc->link_flags = concatenate3_strings(permanent_allocator(),
|
||||
str_lit("-target "), bc->metrics.target_triplet, str_lit(" "));
|
||||
// NOTE: for targets other than darwin, we don't specify a `-target` link flag.
|
||||
// This is because we don't support cross-linking and clang is better at figuring
|
||||
// out what the actual target for linking is,
|
||||
// for example, on x86/alpine/musl it HAS to be `x86_64-alpine-linux-musl` to link correctly.
|
||||
//
|
||||
// Note that codegen will still target the triplet we specify, but the intricate details of
|
||||
// a target shouldn't matter as much to codegen (if it does at all) as it does to linking.
|
||||
}
|
||||
|
||||
// NOTE: needs to be done after adding the -target flag to the linker flags so the linker
|
||||
|
||||
+11
-23
@@ -2433,6 +2433,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
if (arg_count > max_count) {
|
||||
error(call, "Too many 'swizzle' indices, %td > %td", arg_count, max_count);
|
||||
return false;
|
||||
} else if (arg_count < 2) {
|
||||
error(call, "Not enough 'swizzle' indices, %td < 2", arg_count);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type->kind == Type_Array) {
|
||||
@@ -5926,15 +5929,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
if (operand->mode != Addressing_Type) {
|
||||
error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name));
|
||||
} else {
|
||||
Type *bt = base_type(operand->type);
|
||||
if (bt->kind == Type_Struct) {
|
||||
if (bt->Struct.polymorphic_params != nullptr) {
|
||||
operand->value = exact_value_i64(bt->Struct.polymorphic_params->Tuple.variables.count);
|
||||
}
|
||||
} else if (bt->kind == Type_Union) {
|
||||
if (bt->Union.polymorphic_params != nullptr) {
|
||||
operand->value = exact_value_i64(bt->Union.polymorphic_params->Tuple.variables.count);
|
||||
}
|
||||
TypeTuple *tuple = get_record_polymorphic_params(operand->type);
|
||||
if (tuple) {
|
||||
operand->value = exact_value_i64(tuple->variables.count);
|
||||
} else {
|
||||
error(operand->expr, "Expected a record type for '%.*s'", LIT(builtin_name));
|
||||
}
|
||||
@@ -5966,20 +5963,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
Entity *param = nullptr;
|
||||
i64 count = 0;
|
||||
|
||||
Type *bt = base_type(operand->type);
|
||||
if (bt->kind == Type_Struct) {
|
||||
if (bt->Struct.polymorphic_params != nullptr) {
|
||||
count = bt->Struct.polymorphic_params->Tuple.variables.count;
|
||||
if (index < count) {
|
||||
param = bt->Struct.polymorphic_params->Tuple.variables[cast(isize)index];
|
||||
}
|
||||
}
|
||||
} else if (bt->kind == Type_Union) {
|
||||
if (bt->Union.polymorphic_params != nullptr) {
|
||||
count = bt->Union.polymorphic_params->Tuple.variables.count;
|
||||
if (index < count) {
|
||||
param = bt->Union.polymorphic_params->Tuple.variables[cast(isize)index];
|
||||
}
|
||||
TypeTuple *tuple = get_record_polymorphic_params(operand->type);
|
||||
if (tuple) {
|
||||
count = tuple->variables.count;
|
||||
if (index < count) {
|
||||
param = tuple->variables[cast(isize)index];
|
||||
}
|
||||
} else {
|
||||
error(operand->expr, "Expected a specialized polymorphic record type for '%.*s'", LIT(builtin_name));
|
||||
|
||||
@@ -1264,6 +1264,9 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
|
||||
if (ac.is_static) {
|
||||
error(e->token, "@(static) is not supported for global variables, nor required");
|
||||
}
|
||||
if (ac.rodata) {
|
||||
e->Variable.is_rodata = true;
|
||||
}
|
||||
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix, ac.link_suffix);
|
||||
|
||||
if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) {
|
||||
@@ -1350,6 +1353,9 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
|
||||
Operand o = {};
|
||||
check_expr_with_type_hint(ctx, &o, init_expr, e->type);
|
||||
check_init_variable(ctx, e, &o, str_lit("variable declaration"));
|
||||
if (e->Variable.is_rodata && o.mode != Addressing_Constant) {
|
||||
error(o.expr, "Variables declared with @(rodata) must have constant initialization");
|
||||
}
|
||||
|
||||
check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
|
||||
}
|
||||
|
||||
+22
-11
@@ -281,8 +281,20 @@ gb_internal void error_operand_not_expression(Operand *o) {
|
||||
|
||||
gb_internal void error_operand_no_value(Operand *o) {
|
||||
if (o->mode == Addressing_NoValue) {
|
||||
gbString err = expr_to_string(o->expr);
|
||||
Ast *x = unparen_expr(o->expr);
|
||||
|
||||
if (x->kind == Ast_CallExpr) {
|
||||
Ast *p = unparen_expr(x->CallExpr.proc);
|
||||
if (p->kind == Ast_BasicDirective) {
|
||||
String tag = p->BasicDirective.name.string;
|
||||
if (tag == "panic" ||
|
||||
tag == "assert") {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gbString err = expr_to_string(o->expr);
|
||||
if (x->kind == Ast_CallExpr) {
|
||||
error(o->expr, "'%s' call does not return a value and cannot be used as a value", err);
|
||||
} else {
|
||||
@@ -3338,6 +3350,9 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
|
||||
if (is_type_untyped(x->type)) {
|
||||
Type *final_type = type;
|
||||
if (is_const_expr && !is_type_constant_type(type)) {
|
||||
if (is_type_union(type)) {
|
||||
convert_to_typed(c, x, type);
|
||||
}
|
||||
final_type = default_type(x->type);
|
||||
}
|
||||
update_untyped_expr_type(c, x->expr, final_type, true);
|
||||
@@ -4286,7 +4301,8 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
|
||||
} else {
|
||||
switch (operand->type->Basic.kind) {
|
||||
case Basic_UntypedBool:
|
||||
if (!is_type_boolean(target_type)) {
|
||||
if (!is_type_boolean(target_type) &&
|
||||
!is_type_integer(target_type)) {
|
||||
operand->mode = Addressing_Invalid;
|
||||
convert_untyped_error(c, operand, target_type);
|
||||
return;
|
||||
@@ -7331,14 +7347,9 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
|
||||
gbString s = gb_string_make_reserve(heap_allocator(), e->token.string.len+3);
|
||||
s = gb_string_append_fmt(s, "%.*s(", LIT(e->token.string));
|
||||
|
||||
Type *params = nullptr;
|
||||
switch (bt->kind) {
|
||||
case Type_Struct: params = bt->Struct.polymorphic_params; break;
|
||||
case Type_Union: params = bt->Union.polymorphic_params; break;
|
||||
}
|
||||
|
||||
if (params != nullptr) for_array(i, params->Tuple.variables) {
|
||||
Entity *v = params->Tuple.variables[i];
|
||||
TypeTuple *tuple = get_record_polymorphic_params(e->type);
|
||||
if (tuple != nullptr) for_array(i, tuple->variables) {
|
||||
Entity *v = tuple->variables[i];
|
||||
String name = v->token.string;
|
||||
if (i > 0) {
|
||||
s = gb_string_append_fmt(s, ", ");
|
||||
@@ -8344,7 +8355,7 @@ gb_internal ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, A
|
||||
name == "assert" ||
|
||||
name == "defined" ||
|
||||
name == "config" ||
|
||||
name == "exists" ||
|
||||
name == "exists" ||
|
||||
name == "load" ||
|
||||
name == "load_hash" ||
|
||||
name == "load_directory" ||
|
||||
|
||||
+14
-2
@@ -501,6 +501,9 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
|
||||
return nullptr;
|
||||
|
||||
case Addressing_Variable:
|
||||
if (e && e->kind == Entity_Variable && e->Variable.is_rodata) {
|
||||
error(lhs->expr, "Assignment to variable '%.*s' marked as @(rodata) is not allowed", LIT(e->token.string));
|
||||
}
|
||||
break;
|
||||
|
||||
case Addressing_MapIndex: {
|
||||
@@ -1252,8 +1255,6 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
|
||||
error_line("\t%.*s\n", LIT(f->token.string));
|
||||
}
|
||||
}
|
||||
error_line("\n");
|
||||
|
||||
error_line("\tSuggestion: Was '#partial switch' wanted?\n");
|
||||
}
|
||||
}
|
||||
@@ -2055,6 +2056,13 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ac.rodata) {
|
||||
if (ac.is_static) {
|
||||
e->Variable.is_rodata = true;
|
||||
} else {
|
||||
error(e->token, "Only global or @(static) variables can have @(rodata) applied");
|
||||
}
|
||||
}
|
||||
if (ac.thread_local_model != "") {
|
||||
String name = e->token.string;
|
||||
if (name == "_") {
|
||||
@@ -2493,6 +2501,10 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
|
||||
unsafe_return_error(o, "the address of an indexed variable", f->type);
|
||||
}
|
||||
}
|
||||
} else if (o.mode == Addressing_Constant && is_type_slice(o.type)) {
|
||||
ERROR_BLOCK();
|
||||
unsafe_return_error(o, "a compound literal of a slice");
|
||||
error_line("\tNote: A constant slice value will use the memory of the current stack frame\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-18
@@ -564,19 +564,7 @@ gb_internal bool check_record_poly_operand_specialization(CheckerContext *ctx, T
|
||||
gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array<Operand> const &ordered_operands) {
|
||||
for (Entity *e : found_gen_types->types) {
|
||||
Type *t = base_type(e->type);
|
||||
TypeTuple *tuple = nullptr;
|
||||
switch (t->kind) {
|
||||
case Type_Struct:
|
||||
if (t->Struct.polymorphic_params) {
|
||||
tuple = &t->Struct.polymorphic_params->Tuple;
|
||||
}
|
||||
break;
|
||||
case Type_Union:
|
||||
if (t->Union.polymorphic_params) {
|
||||
tuple = &t->Union.polymorphic_params->Tuple;
|
||||
}
|
||||
break;
|
||||
}
|
||||
TypeTuple *tuple = get_record_polymorphic_params(t);
|
||||
GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t));
|
||||
GB_ASSERT(param_count == tuple->variables.count);
|
||||
|
||||
@@ -663,6 +651,8 @@ gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *
|
||||
&struct_type->Struct.is_polymorphic,
|
||||
node, poly_operands
|
||||
);
|
||||
wait_signal_set(&struct_type->Struct.polymorphic_wait_signal);
|
||||
|
||||
struct_type->Struct.is_poly_specialized = check_record_poly_operand_specialization(ctx, struct_type, poly_operands, &struct_type->Struct.is_polymorphic);
|
||||
if (original_type_for_poly) {
|
||||
GB_ASSERT(named_type != nullptr);
|
||||
@@ -712,6 +702,8 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no
|
||||
&union_type->Union.is_polymorphic,
|
||||
node, poly_operands
|
||||
);
|
||||
wait_signal_set(&union_type->Union.polymorphic_wait_signal);
|
||||
|
||||
union_type->Union.is_poly_specialized = check_record_poly_operand_specialization(ctx, union_type, poly_operands, &union_type->Union.is_polymorphic);
|
||||
if (original_type_for_poly) {
|
||||
GB_ASSERT(named_type != nullptr);
|
||||
@@ -784,7 +776,7 @@ gb_internal void check_union_type(CheckerContext *ctx, Type *union_type, Ast *no
|
||||
}
|
||||
}
|
||||
if (variants.count < 2) {
|
||||
error(ut->align, "A union with #no_nil must have at least 2 variants");
|
||||
error(node, "A union with #no_nil must have at least 2 variants");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1457,8 +1449,8 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special
|
||||
s->Struct.polymorphic_params != nullptr &&
|
||||
t->Struct.polymorphic_params != nullptr) {
|
||||
|
||||
TypeTuple *s_tuple = &s->Struct.polymorphic_params->Tuple;
|
||||
TypeTuple *t_tuple = &t->Struct.polymorphic_params->Tuple;
|
||||
TypeTuple *s_tuple = get_record_polymorphic_params(s);
|
||||
TypeTuple *t_tuple = get_record_polymorphic_params(t);
|
||||
GB_ASSERT(t_tuple->variables.count == s_tuple->variables.count);
|
||||
for_array(i, s_tuple->variables) {
|
||||
Entity *s_e = s_tuple->variables[i];
|
||||
@@ -1510,8 +1502,8 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special
|
||||
s->Union.polymorphic_params != nullptr &&
|
||||
t->Union.polymorphic_params != nullptr) {
|
||||
|
||||
TypeTuple *s_tuple = &s->Union.polymorphic_params->Tuple;
|
||||
TypeTuple *t_tuple = &t->Union.polymorphic_params->Tuple;
|
||||
TypeTuple *s_tuple = get_record_polymorphic_params(s);
|
||||
TypeTuple *t_tuple = get_record_polymorphic_params(t);
|
||||
GB_ASSERT(t_tuple->variables.count == s_tuple->variables.count);
|
||||
for_array(i, s_tuple->variables) {
|
||||
Entity *s_e = s_tuple->variables[i];
|
||||
|
||||
@@ -3628,6 +3628,12 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
|
||||
}
|
||||
ac->is_static = true;
|
||||
return true;
|
||||
} else if (name == "rodata") {
|
||||
if (value != nullptr) {
|
||||
error(elem, "'rodata' does not have any parameters");
|
||||
}
|
||||
ac->rodata = true;
|
||||
return true;
|
||||
} else if (name == "thread_local") {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ac->init_expr_list_count > 0) {
|
||||
|
||||
@@ -133,6 +133,7 @@ struct AttributeContext {
|
||||
bool entry_point_only : 1;
|
||||
bool instrumentation_enter : 1;
|
||||
bool instrumentation_exit : 1;
|
||||
bool rodata : 1;
|
||||
u32 optimization_mode; // ProcedureOptimizationMode
|
||||
i64 foreign_import_priority_index;
|
||||
String extra_linker_flags;
|
||||
|
||||
@@ -230,6 +230,7 @@ struct Entity {
|
||||
bool is_foreign;
|
||||
bool is_export;
|
||||
bool is_global;
|
||||
bool is_rodata;
|
||||
} Variable;
|
||||
struct {
|
||||
Type * type_parameter_specialization;
|
||||
|
||||
+33
-32
@@ -390,8 +390,6 @@ gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va
|
||||
error_out_empty();
|
||||
} else {
|
||||
error_out_pos(pos);
|
||||
}
|
||||
if (has_ansi_terminal_colours()) {
|
||||
error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
|
||||
}
|
||||
error_out_va(fmt, va);
|
||||
@@ -407,29 +405,31 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt,
|
||||
error_va(pos, end, fmt, va);
|
||||
return;
|
||||
}
|
||||
if (global_ignore_warnings()) {
|
||||
return;
|
||||
}
|
||||
|
||||
global_error_collector.warning_count.fetch_add(1);
|
||||
mutex_lock(&global_error_collector.mutex);
|
||||
|
||||
push_error_value(pos, ErrorValue_Warning);
|
||||
|
||||
if (!global_ignore_warnings()) {
|
||||
if (pos.line == 0) {
|
||||
if (pos.line == 0) {
|
||||
error_out_empty();
|
||||
error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
} else {
|
||||
// global_error_collector.prev = pos;
|
||||
if (json_errors()) {
|
||||
error_out_empty();
|
||||
error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
} else {
|
||||
// global_error_collector.prev = pos;
|
||||
if (json_errors()) {
|
||||
error_out_empty();
|
||||
} else {
|
||||
error_out_pos(pos);
|
||||
}
|
||||
error_out_pos(pos);
|
||||
error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
show_error_on_line(pos, end);
|
||||
}
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
show_error_on_line(pos, end);
|
||||
}
|
||||
try_pop_error_value();
|
||||
mutex_unlock(&global_error_collector.mutex);
|
||||
@@ -544,30 +544,31 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const
|
||||
syntax_error_va(pos, end, fmt, va);
|
||||
return;
|
||||
}
|
||||
if (global_ignore_warnings()) {
|
||||
return;
|
||||
}
|
||||
mutex_lock(&global_error_collector.mutex);
|
||||
global_error_collector.warning_count++;
|
||||
|
||||
|
||||
push_error_value(pos, ErrorValue_Warning);
|
||||
|
||||
if (!global_ignore_warnings()) {
|
||||
if (pos.line == 0) {
|
||||
if (pos.line == 0) {
|
||||
error_out_empty();
|
||||
error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
} else {
|
||||
// global_error_collector.prev = pos;
|
||||
if (json_errors()) {
|
||||
error_out_empty();
|
||||
error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
} else {
|
||||
// global_error_collector.prev = pos;
|
||||
if (json_errors()) {
|
||||
error_out_empty();
|
||||
} else {
|
||||
error_out_pos(pos);
|
||||
}
|
||||
error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
// show_error_on_line(pos, end);
|
||||
error_out_pos(pos);
|
||||
}
|
||||
error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow);
|
||||
error_out_va(fmt, va);
|
||||
error_out("\n");
|
||||
// show_error_on_line(pos, end);
|
||||
}
|
||||
|
||||
try_pop_error_value();
|
||||
@@ -838,4 +839,4 @@ gb_internal void print_all_errors(void) {
|
||||
gb_file_write(f, res, gb_string_length(res));
|
||||
|
||||
errors_already_printed = true;
|
||||
}
|
||||
}
|
||||
|
||||
+29
-15
@@ -13,6 +13,7 @@ struct LinkerData {
|
||||
};
|
||||
|
||||
gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...);
|
||||
gb_internal bool system_exec_command_line_app_output(char const *command, gbString *output);
|
||||
|
||||
#if defined(GB_SYSTEM_OSX)
|
||||
gb_internal void linker_enable_system_library_linking(LinkerData *ld) {
|
||||
@@ -69,27 +70,40 @@ gb_internal i32 linker_stage(LinkerData *gen) {
|
||||
if (is_arch_wasm()) {
|
||||
timings_start_section(timings, str_lit("wasm-ld"));
|
||||
|
||||
String extra_orca_flags = {};
|
||||
gbString extra_orca_flags = gb_string_make(temporary_allocator(), "");
|
||||
|
||||
gbString inputs = gb_string_make(temporary_allocator(), "");
|
||||
inputs = gb_string_append_fmt(inputs, "\"%.*s.o\"", LIT(output_filename));
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
if (build_context.metrics.os == TargetOs_orca) {
|
||||
extra_orca_flags = str_lit(" W:/orca/installation/dev-afb9591/bin/liborca_wasm.a --export-dynamic");
|
||||
gbString orca_sdk_path = gb_string_make(temporary_allocator(), "");
|
||||
if (!system_exec_command_line_app_output("orca sdk-path", &orca_sdk_path)) {
|
||||
gb_printf_err("executing `orca sdk-path` failed, make sure Orca is installed and added to your path\n");
|
||||
return 1;
|
||||
}
|
||||
if (gb_string_length(orca_sdk_path) == 0) {
|
||||
gb_printf_err("executing `orca sdk-path` did not produce output\n");
|
||||
return 1;
|
||||
}
|
||||
inputs = gb_string_append_fmt(inputs, " \"%s/orca-libc/lib/crt1.o\" \"%s/orca-libc/lib/libc.o\"", orca_sdk_path, orca_sdk_path);
|
||||
|
||||
extra_orca_flags = gb_string_append_fmt(extra_orca_flags, " -L \"%s/bin\" -lorca_wasm --export-dynamic", orca_sdk_path);
|
||||
}
|
||||
|
||||
result = system_exec_command_line_app("wasm-ld",
|
||||
"\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s %.*s",
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags),
|
||||
LIT(extra_orca_flags));
|
||||
#else
|
||||
if (build_context.metrics.os == TargetOs_orca) {
|
||||
extra_orca_flags = str_lit(" -L . -lorca --export-dynamic");
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
result = system_exec_command_line_app("wasm-ld",
|
||||
"wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s %.*s",
|
||||
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags),
|
||||
LIT(extra_orca_flags));
|
||||
"\"%.*s\\bin\\wasm-ld\" %s -o \"%.*s\" %.*s %.*s %s",
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
inputs, LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags),
|
||||
extra_orca_flags);
|
||||
#else
|
||||
result = system_exec_command_line_app("wasm-ld",
|
||||
"wasm-ld %s -o \"%.*s\" %.*s %.*s %s",
|
||||
inputs, LIT(output_filename),
|
||||
LIT(build_context.link_flags),
|
||||
LIT(build_context.extra_linker_flags),
|
||||
extra_orca_flags);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
+9
-1
@@ -900,7 +900,15 @@ namespace lbAbiAmd64SysV {
|
||||
}
|
||||
|
||||
switch (LLVMGetTypeKind(t)) {
|
||||
case LLVMIntegerTypeKind:
|
||||
case LLVMIntegerTypeKind: {
|
||||
i64 s = t_size;
|
||||
while (s > 0) {
|
||||
unify(cls, ix + off/8, RegClass_Int);
|
||||
off += 8;
|
||||
s -= 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LLVMPointerTypeKind:
|
||||
case LLVMHalfTypeKind:
|
||||
unify(cls, ix + off/8, RegClass_Int);
|
||||
|
||||
+18
-2
@@ -1160,6 +1160,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
|
||||
if (is_type_untyped_nil(init.type)) {
|
||||
LLVMSetInitializer(var.var.value, LLVMConstNull(global_type));
|
||||
var.is_initialized = true;
|
||||
|
||||
if (e->Variable.is_rodata) {
|
||||
LLVMSetGlobalConstant(var.var.value, true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
GB_PANIC("Invalid init value, got %s", expr_to_string(init_expr));
|
||||
@@ -1174,6 +1178,10 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
|
||||
}
|
||||
LLVMSetInitializer(var.var.value, init.value);
|
||||
var.is_initialized = true;
|
||||
|
||||
if (e->Variable.is_rodata) {
|
||||
LLVMSetGlobalConstant(var.var.value, true);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
@@ -1206,8 +1214,9 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
|
||||
|
||||
var.is_initialized = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
CheckerInfo *info = main_module->gen->info;
|
||||
|
||||
for (Entity *e : info->init_procedures) {
|
||||
@@ -3210,14 +3219,21 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
lbValue init = lb_const_value(m, tav.type, v);
|
||||
LLVMSetInitializer(g.value, init.value);
|
||||
var.is_initialized = true;
|
||||
if (e->kind == Entity_Variable && e->Variable.is_rodata) {
|
||||
LLVMSetGlobalConstant(g.value, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!var.is_initialized && is_type_untyped_nil(tav.type)) {
|
||||
var.is_initialized = true;
|
||||
if (e->kind == Entity_Variable && e->Variable.is_rodata) {
|
||||
LLVMSetGlobalConstant(g.value, true);
|
||||
}
|
||||
}
|
||||
} else if (e->kind == Entity_Variable && e->Variable.is_rodata) {
|
||||
LLVMSetGlobalConstant(g.value, true);
|
||||
}
|
||||
|
||||
array_add(&global_variables, var);
|
||||
|
||||
lb_add_entity(m, e, g);
|
||||
|
||||
@@ -504,6 +504,10 @@ gb_internal bool lb_is_matrix_simdable(Type *t) {
|
||||
if ((mt->Matrix.row_count & 1) ^ (mt->Matrix.column_count & 1)) {
|
||||
return false;
|
||||
}
|
||||
if (mt->Matrix.is_row_major) {
|
||||
// TODO(bill): make #row_major matrices work with SIMD
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elem->kind == Type_Basic) {
|
||||
switch (elem->Basic.kind) {
|
||||
@@ -1869,13 +1873,40 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
|
||||
lbValue res_i128 = lb_emit_runtime_call(p, call, args);
|
||||
return lb_emit_conv(p, res_i128, t);
|
||||
}
|
||||
i64 sz = type_size_of(src);
|
||||
|
||||
lbValue res = {};
|
||||
res.type = t;
|
||||
if (is_type_unsigned(dst)) {
|
||||
res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t), "");
|
||||
switch (sz) {
|
||||
case 2:
|
||||
case 4:
|
||||
res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t_u32), "");
|
||||
res.value = LLVMBuildIntCast2(p->builder, res.value, lb_type(m, t), false, "");
|
||||
break;
|
||||
case 8:
|
||||
res.value = LLVMBuildFPToUI(p->builder, value.value, lb_type(m, t_u64), "");
|
||||
res.value = LLVMBuildIntCast2(p->builder, res.value, lb_type(m, t), false, "");
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("Unhandled float type");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t), "");
|
||||
switch (sz) {
|
||||
case 2:
|
||||
case 4:
|
||||
res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t_i32), "");
|
||||
res.value = LLVMBuildIntCast2(p->builder, res.value, lb_type(m, t), true, "");
|
||||
break;
|
||||
case 8:
|
||||
res.value = LLVMBuildFPToSI(p->builder, value.value, lb_type(m, t_i64), "");
|
||||
res.value = LLVMBuildIntCast2(p->builder, res.value, lb_type(m, t), true, "");
|
||||
break;
|
||||
default:
|
||||
GB_PANIC("Unhandled float type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1383,8 +1383,6 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
|
||||
|
||||
LLVMTypeRef vector_type = nullptr;
|
||||
if (lb_try_vector_cast(p->module, addr.addr, &vector_type)) {
|
||||
LLVMSetAlignment(res.addr.value, cast(unsigned)lb_alignof(vector_type));
|
||||
|
||||
LLVMValueRef vp = LLVMBuildPointerCast(p->builder, addr.addr.value, LLVMPointerType(vector_type, 0), "");
|
||||
LLVMValueRef v = LLVMBuildLoad2(p->builder, vector_type, vp, "");
|
||||
LLVMValueRef scalars[4] = {};
|
||||
@@ -1394,6 +1392,8 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
|
||||
LLVMValueRef mask = LLVMConstVector(scalars, addr.swizzle.count);
|
||||
LLVMValueRef sv = llvm_basic_shuffle(p, v, mask);
|
||||
|
||||
LLVMSetAlignment(res.addr.value, cast(unsigned)lb_alignof(LLVMTypeOf(sv)));
|
||||
|
||||
LLVMValueRef dst = LLVMBuildPointerCast(p->builder, ptr.value, LLVMPointerType(LLVMTypeOf(sv), 0), "");
|
||||
LLVMBuildStore(p->builder, sv, dst);
|
||||
} else {
|
||||
|
||||
@@ -710,13 +710,12 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
|
||||
lb_set_debug_position_to_procedure_begin(p);
|
||||
if (p->debug_info != nullptr) {
|
||||
if (p->context_stack.count != 0) {
|
||||
lbBlock *prev_block = p->curr_block;
|
||||
p->curr_block = p->decl_block;
|
||||
lb_add_debug_context_variable(p, lb_find_or_generate_context_ptr(p));
|
||||
p->curr_block = prev_block;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lb_start_block(p, p->entry_block);
|
||||
}
|
||||
|
||||
gb_internal void lb_end_procedure_body(lbProcedure *p) {
|
||||
|
||||
@@ -1850,7 +1850,9 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) {
|
||||
LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type)));
|
||||
if (value.value != nullptr) {
|
||||
LLVMSetInitializer(global, value.value);
|
||||
} else {
|
||||
}
|
||||
if (e->Variable.is_rodata) {
|
||||
LLVMSetGlobalConstant(global, true);
|
||||
}
|
||||
if (e->Variable.thread_local_model != "") {
|
||||
LLVMSetThreadLocal(global, true);
|
||||
|
||||
@@ -155,6 +155,38 @@ gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt,
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
#define popen _popen
|
||||
#define pclose _pclose
|
||||
#endif
|
||||
|
||||
gb_internal bool system_exec_command_line_app_output(char const *command, gbString *output) {
|
||||
GB_ASSERT(output);
|
||||
|
||||
u8 buffer[256];
|
||||
FILE *stream;
|
||||
stream = popen(command, "r");
|
||||
if (!stream) {
|
||||
return false;
|
||||
}
|
||||
defer (pclose(stream));
|
||||
|
||||
while (!feof(stream)) {
|
||||
size_t n = fread(buffer, 1, 255, stream);
|
||||
*output = gb_string_append_length(*output, buffer, n);
|
||||
|
||||
if (ferror(stream)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (build_context.show_system_calls) {
|
||||
gb_printf_err("[SYSTEM CALL OUTPUT] %s -> %s\n", command, *output);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
gb_internal Array<String> setup_args(int argc, char const **argv) {
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
|
||||
+8
-2
@@ -140,6 +140,7 @@ struct TypeStruct {
|
||||
i64 custom_field_align;
|
||||
Type * polymorphic_params; // Type_Tuple
|
||||
Type * polymorphic_parent;
|
||||
Wait_Signal polymorphic_wait_signal;
|
||||
|
||||
Type * soa_elem;
|
||||
i32 soa_count;
|
||||
@@ -167,6 +168,7 @@ struct TypeUnion {
|
||||
i64 custom_align;
|
||||
Type * polymorphic_params; // Type_Tuple
|
||||
Type * polymorphic_parent;
|
||||
Wait_Signal polymorphic_wait_signal;
|
||||
|
||||
i16 tag_size;
|
||||
bool is_polymorphic;
|
||||
@@ -1093,6 +1095,7 @@ gb_internal Type *alloc_type_struct() {
|
||||
gb_internal Type *alloc_type_struct_complete() {
|
||||
Type *t = alloc_type(Type_Struct);
|
||||
wait_signal_set(&t->Struct.fields_wait_signal);
|
||||
wait_signal_set(&t->Struct.polymorphic_wait_signal);
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -1491,10 +1494,10 @@ gb_internal i64 matrix_align_of(Type *t, struct TypePath *tp) {
|
||||
i64 total_expected_size = row_count*t->Matrix.column_count*elem_size;
|
||||
// i64 min_alignment = prev_pow2(elem_align * row_count);
|
||||
i64 min_alignment = prev_pow2(total_expected_size);
|
||||
while ((total_expected_size % min_alignment) != 0) {
|
||||
while (total_expected_size != 0 && (total_expected_size % min_alignment) != 0) {
|
||||
min_alignment >>= 1;
|
||||
}
|
||||
GB_ASSERT(min_alignment >= elem_align);
|
||||
min_alignment = gb_max(min_alignment, elem_align);
|
||||
|
||||
i64 align = gb_min(min_alignment, build_context.max_simd_align);
|
||||
return align;
|
||||
@@ -2136,15 +2139,18 @@ gb_internal bool is_type_polymorphic_record_unspecialized(Type *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
gb_internal TypeTuple *get_record_polymorphic_params(Type *t) {
|
||||
t = base_type(t);
|
||||
switch (t->kind) {
|
||||
case Type_Struct:
|
||||
wait_signal_until_available(&t->Struct.polymorphic_wait_signal);
|
||||
if (t->Struct.polymorphic_params) {
|
||||
return &t->Struct.polymorphic_params->Tuple;
|
||||
}
|
||||
break;
|
||||
case Type_Union:
|
||||
wait_signal_until_available(&t->Union.polymorphic_wait_signal);
|
||||
if (t->Union.polymorphic_params) {
|
||||
return &t->Union.polymorphic_params->Tuple;
|
||||
}
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
ODIN=../../odin
|
||||
COMMON=-no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false
|
||||
|
||||
all: crypto_bench \
|
||||
hash_bench
|
||||
|
||||
crypto_bench:
|
||||
$(ODIN) test crypto $(COMMON) -o:speed -out:bench_crypto
|
||||
|
||||
hash_bench:
|
||||
$(ODIN) test hash $(COMMON) -o:speed -out:bench_hash
|
||||
|
||||
clean:
|
||||
rm bench_*
|
||||
@@ -0,0 +1,4 @@
|
||||
package benchmarks
|
||||
|
||||
@(require) import "crypto"
|
||||
@(require) import "hash"
|
||||
@@ -1,13 +0,0 @@
|
||||
@echo off
|
||||
set COMMON=-no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false
|
||||
set PATH_TO_ODIN==..\..\odin
|
||||
|
||||
echo ---
|
||||
echo Running core:crypto benchmarks
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test crypto %COMMON% -o:speed -out:bench_crypto.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:hash benchmarks
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test hash %COMMON% -o:speed -out:bench_hash.exe || exit /b
|
||||
@@ -1,3 +1,4 @@
|
||||
*.bmp
|
||||
*.zip
|
||||
*.png
|
||||
math_big_test_library.*
|
||||
@@ -1,115 +0,0 @@
|
||||
ODIN=../../odin
|
||||
PYTHON=$(shell which python3)
|
||||
COMMON=-no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false
|
||||
|
||||
all: all_bsd \
|
||||
net_test
|
||||
|
||||
all_bsd: download_test_assets \
|
||||
c_libc_test \
|
||||
compress_test \
|
||||
container_test \
|
||||
crypto_test \
|
||||
encoding_test \
|
||||
filepath_test \
|
||||
fmt_test \
|
||||
hash_test \
|
||||
i18n_test \
|
||||
image_test \
|
||||
linalg_glsl_math_test \
|
||||
match_test \
|
||||
math_test \
|
||||
noise_test \
|
||||
odin_test \
|
||||
os_exit_test \
|
||||
reflect_test \
|
||||
runtime_test \
|
||||
slice_test \
|
||||
strconv_test \
|
||||
strings_test \
|
||||
thread_test \
|
||||
time_test
|
||||
|
||||
download_test_assets:
|
||||
$(PYTHON) download_assets.py
|
||||
|
||||
c_libc_test:
|
||||
$(ODIN) test c/libc $(COMMON) -out:test_core_libc
|
||||
|
||||
compress_test:
|
||||
$(ODIN) test compress $(COMMON) -out:test_core_compress
|
||||
|
||||
container_test:
|
||||
$(ODIN) test container $(COMMON) -out:test_core_container
|
||||
|
||||
crypto_test:
|
||||
$(ODIN) test crypto $(COMMON) -o:speed -out:test_crypto
|
||||
|
||||
encoding_test:
|
||||
$(ODIN) test encoding/base64 $(COMMON) -out:test_base64
|
||||
$(ODIN) test encoding/cbor $(COMMON) -out:test_cbor
|
||||
$(ODIN) test encoding/hex $(COMMON) -out:test_hex
|
||||
$(ODIN) test encoding/hxa $(COMMON) -out:test_hxa
|
||||
$(ODIN) test encoding/json $(COMMON) -out:test_json
|
||||
$(ODIN) test encoding/varint $(COMMON) -out:test_varint
|
||||
$(ODIN) test encoding/xml $(COMMON) -out:test_xml
|
||||
|
||||
filepath_test:
|
||||
$(ODIN) test path/filepath $(COMMON) -out:test_core_filepath
|
||||
|
||||
fmt_test:
|
||||
$(ODIN) test fmt $(COMMON) -out:test_core_fmt
|
||||
|
||||
hash_test:
|
||||
$(ODIN) test hash $(COMMON) -o:speed -out:test_hash
|
||||
|
||||
image_test:
|
||||
$(ODIN) test image $(COMMON) -out:test_core_image
|
||||
|
||||
i18n_test:
|
||||
$(ODIN) test text/i18n $(COMMON) -out:test_core_i18n
|
||||
|
||||
match_test:
|
||||
$(ODIN) test text/match $(COMMON) -out:test_core_match
|
||||
|
||||
math_test:
|
||||
$(ODIN) test math $(COMMON) -out:test_core_math
|
||||
|
||||
linalg_glsl_math_test:
|
||||
$(ODIN) test math/linalg/glsl $(COMMON) -out:test_linalg_glsl_math
|
||||
|
||||
noise_test:
|
||||
$(ODIN) test math/noise $(COMMON) -out:test_noise
|
||||
|
||||
net_test:
|
||||
$(ODIN) test net $(COMMON) -out:test_core_net
|
||||
|
||||
os_exit_test:
|
||||
$(ODIN) run os/test_core_os_exit.odin -file -out:test_core_os_exit && exit 1 || exit 0
|
||||
|
||||
odin_test:
|
||||
$(ODIN) test odin $(COMMON) -out:test_core_odin
|
||||
|
||||
reflect_test:
|
||||
$(ODIN) test reflect $(COMMON) -out:test_core_reflect
|
||||
|
||||
runtime_test:
|
||||
$(ODIN) test runtime $(COMMON) -out:test_core_runtime
|
||||
|
||||
slice_test:
|
||||
$(ODIN) test slice $(COMMON) -out:test_core_slice
|
||||
|
||||
strconv_test:
|
||||
$(ODIN) test strconv $(COMMON) -out:test_core_strconv
|
||||
|
||||
strings_test:
|
||||
$(ODIN) test strings $(COMMON) -out:test_core_strings
|
||||
|
||||
thread_test:
|
||||
$(ODIN) test thread $(COMMON) -out:test_core_thread
|
||||
|
||||
time_test:
|
||||
$(ODIN) test time $(COMMON) -out:test_core_time
|
||||
|
||||
clean:
|
||||
rm test_*
|
||||
@@ -1,124 +0,0 @@
|
||||
@echo off
|
||||
set COMMON=-no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false
|
||||
set PATH_TO_ODIN==..\..\odin
|
||||
python3 download_assets.py
|
||||
echo ---
|
||||
echo Running core:c/libc tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test c\libc %COMMON% -out:test_libc.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:compress tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test compress %COMMON% -out:test_core_compress.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:container tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test container %COMMON% -out:test_core_container.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:crypto tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test crypto %COMMON% -o:speed -out:test_crypto.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:encoding tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test encoding/base64 %COMMON% -out:test_base64.exe || exit /b
|
||||
%PATH_TO_ODIN% test encoding/cbor %COMMON% -out:test_cbor.exe || exit /b
|
||||
%PATH_TO_ODIN% test encoding/hex %COMMON% -out:test_hex.exe || exit /b
|
||||
%PATH_TO_ODIN% test encoding/hxa %COMMON% -out:test_hxa.exe || exit /b
|
||||
%PATH_TO_ODIN% test encoding/json %COMMON% -out:test_json.exe || exit /b
|
||||
%PATH_TO_ODIN% test encoding/varint %COMMON% -out:test_varint.exe || exit /b
|
||||
%PATH_TO_ODIN% test encoding/xml %COMMON% -out:test_xml.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:path/filepath tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test path/filepath %COMMON% -out:test_core_filepath.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:fmt tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test fmt %COMMON% -out:test_core_fmt.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:hash tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test hash %COMMON% -o:speed -out:test_core_hash.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:image tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test image %COMMON% -out:test_core_image.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:text/i18n tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test text\i18n %COMMON% -out:test_core_i18n.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running text:match tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test text/match %COMMON% -out:test_core_match.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:math tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test math %COMMON% -out:test_core_math.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:math/linalg/glsl tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test math/linalg/glsl %COMMON% -out:test_linalg_glsl.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:math/noise tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test math/noise %COMMON% -out:test_noise.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:net
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test net %COMMON% -out:test_core_net.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:odin tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test odin %COMMON% -o:size -out:test_core_odin.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:reflect tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test reflect %COMMON% -out:test_core_reflect.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:runtime tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test runtime %COMMON% -out:test_core_runtime.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:slice tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test slice %COMMON% -out:test_core_slice.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:strconv tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test strconv %COMMON% -out:test_core_strconv.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:strings tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test strings %COMMON% -out:test_core_strings.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:thread tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test thread %COMMON% -out:test_core_thread.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:time tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test time %COMMON% -out:test_core_time.exe || exit /b
|
||||
@@ -7,8 +7,8 @@ import zipfile
|
||||
import hashlib
|
||||
import hmac
|
||||
|
||||
TEST_SUITES = ['PNG', 'XML']
|
||||
DOWNLOAD_BASE_PATH = "assets/{}"
|
||||
TEST_SUITES = ['PNG', 'XML', 'BMP']
|
||||
DOWNLOAD_BASE_PATH = sys.argv[1] + "/{}"
|
||||
ASSETS_BASE_URL = "https://raw.githubusercontent.com/odin-lang/test-assets/master/{}/{}"
|
||||
HMAC_KEY = "https://odin-lang.org"
|
||||
HMAC_HASH = hashlib.sha3_512
|
||||
@@ -192,6 +192,94 @@ HMAC_DIGESTS = {
|
||||
'z06n2c08.png': "94268c1998de1f4304d24219e31175def7375cc26e2bbfc7d1ac20465a42fae49bcc8ff7626873138b537588e8bce21b6d5e1373efaade1f83cae455334074aa",
|
||||
'z09n2c08.png': "3cbb1bb58d78ecc9dd5568a8e9093ba020b63449ef3ab102f98fac4220fc9619feaa873336a25f3c1ad99cfb3e5d32bcfe52d966bc8640d1d5ba4e061741743e",
|
||||
|
||||
'ba-bm.bmp': "2f76d46b1b9bea62e08e7fc5306452a495616cb7af7a0cbb79237ed457b083418d5859c9e6cfd0d9fbf1fe24495319b6f206135f36f2bd19330de01a8eaf20c8",
|
||||
'badbitcount.bmp': "2d37e22aa2e659416c950815841e5a402f2e9c21eb677390fc026eefaeb5be64345a7ef0fac2965a2cae8abe78c1e12086a7d93d8e62cc8659b35168c82f6d5f",
|
||||
'badbitssize.bmp': "f59cc30827bcb56f7e946dcffcaab22a5e197f2e3884cf80a2e596f5653f5203b3927674d9d5190486239964e65228f4e3f359cdd2f7d061b09846f5f26bfaa9",
|
||||
'baddens1.bmp': "aa84bebc41b3d50329269da9ee61fd7e1518ffd0e8f733af6872323bc46ace6ed1c9931a65a367d97b8b2cb2aa772ccd94fd3def0a79fd1c0baf185d669c386f",
|
||||
'baddens2.bmp': "5c254a8cde716fae77ebf20294a404383fd6afc705d783c5418762e7c4138aa621625bc6d08a8946ee3f1e8c40c767681a39806735bb3b3026fee5eb91d8fadc",
|
||||
'badfilesize.bmp': "9019b6853a91f69bd246f9b28da47007aec871c0e46fea7cd6ab5c30460a6938a1b09da8fa7ba8895650e37ce14a79d4183e9f2401eb510f60455410e2266eb5",
|
||||
'badheadersize.bmp': "90412d7c3bff7336d5e0c7ae899d8a53b82235072034f00783fb2403479447cd2959644f7ec70ae0988f99cc49b63356c8710b808ddd2280e19dca484f34074e",
|
||||
'badpalettesize.bmp': "d914a89f7b78fcdd6ab4433c176355755687b65c3cfc23db57de9d04447c440fa31d993db184940c1dc09b37e8e044324d8237877d3d1b1ad5657c4929d8435a",
|
||||
'badplanes.bmp': "46f583d4a43ef0c9964765b9d8820369955f0568a4eae0bf215434f508e8e03457bd759b73c344c2f88de7f33fc5379517ce3cf5b2e5a16ebc20c05df73aa723",
|
||||
'badrle.bmp': "a64e1551fd60159ff469ce25e1f5b4575dc462684f4ff66c7ea69b2990c7c9d2547b72237020e2d001f69dfd31f1ac45e0a9630d0ddd11c77584881f3e25609e",
|
||||
'badrle4.bmp': "2bd22418010b1ac3eac50932ed06e578411ac2741bfa50a9edd1b360686efa28c74df8b14d92e05b711eeb88a5e826256c6a5cf5a0176a29369fb92b336efb93",
|
||||
'badrle4bis.bmp': "d7a24ab095e1ca5e888dd1bcb732b19bb1983f787c64c1eb5a273da0f58c4b8cd137197df9ac47572a74c3026aab5af1f08551a2121af37b8941cffa71df1951",
|
||||
'badrle4ter.bmp': "825cc5361378d44524205b117825f95228c4d093d39ac2fc2ab755be743df78784529f2019418deca31059f3e46889a66658e7424b4f896668ee4cfa281574bc",
|
||||
'badrlebis.bmp': "f41acfd4f989302bb5ec42a2e759a56f71a5ecac5a814842e32542742ca015464f8579ebeec0e7e9cea45e2aafe51456cfe18b48b509bc3704f992bcc9d321af",
|
||||
'badrleter.bmp': "a8f3e0b0668fc4f43353028d5fca87d6cac6ff0c917c4e7a61c624918360ff598ec9eaa32f5c6a070da9bf6e90c58426f5a901fdab9dfb0a4fdca0c72ba67de4",
|
||||
'badwidth.bmp': "68f192a55b8de66f8e13fe316647262a5e4641365eb77d4987c84ab1eae35b7cba20827277cd569583543819de70ec75f383367f72cd229e48743ad1e45bfa9e",
|
||||
'pal1.bmp': "0194c9b501ac7e043fab78746e6f142e0c880917d0fd6dbb7215765b8fc1ce4403ad85146c555665ba6e37d3b47edad5e687b9260e7a61a27d8a059bc81bb525",
|
||||
'pal1bg.bmp': "3aafc29122bd6e97d88d740be1f61cb9febe8373d19ae6d731f4af776c868dd489260287bf0cf1c960f9d9afcbc7448e83e45435d3e42e913823c0f5c2a80d9f",
|
||||
'pal1huffmsb.bmp': "4e122f602c3556f4f5ab45f9e13a617d8210d81f587d08cbd6c2110dc6231573aec92a6344aeb4734c00d3dcf380130f53a887002756811d8edd6bc5aabbafc0",
|
||||
'pal1p1.bmp': "33e2b2b1c1bed43ba64888d7739eb830c7789857352513de08b6e35718ac0e421afcdae0e7bab97c25d1ad972eb4f09e2c6556c416d4d7367c545330c4123df0",
|
||||
'pal1wb.bmp': "bc583ad4eaae40f5d2e3a6280aeb3c62ee11b2cf05ba7c8386f9578587e29b66819293992bdcd31c2750c21cd9bf97daa603ce1051fbfdd40fadbc1860156853",
|
||||
'pal2.bmp': "7b560ba972cf58ca1ed01910fa4f630ca74e657d46d134e2ac0df733eb5773d0a1788e745d5240efa18f182bd8dce22c7ac7cee6f99ddc946a27b65297762764",
|
||||
'pal2color.bmp': "b868a8aaa22fac3aa86bbd5270eb5ffee06959461be8177880829d838be0391d9617d11d73fab1643520a92364dc333c25c0510bb2628c8fb945719518d2675f",
|
||||
'pal4.bmp': "53a39fdb86630c828d9003a1e95dbd59c47524c4ec044d8ce72e1b643166b4c2b6ec06ab5191cb25d17be2fcb18bd7a9e0b7ec169722e6d89b725609a15b1df1",
|
||||
'pal4gs.bmp': "ab4c2078943afdf19bcc02b1ebbe5a69cfa93d1152f7882db6176c39b917191a2760fbb2127e5207b0bfb3dafd711593a6aed61d312807605913062aa1ce9c2f",
|
||||
'pal4rle.bmp': "c86c86280b75a252ccf484e4bba2df45d3747dc1e4879795e925613959a0c451e2fc4890532e8aef9911e38e45e7d6a8baf29d57e573d26c20923a5823700443",
|
||||
'pal4rlecut.bmp': "f38d485dbb8e67bdeaefba181f9a05556a986ed3f834edca723c088e813764bb2b42240d4fbb938a1775370b79b9ea2f14277ffe9c7247c1e0e77766fec27189",
|
||||
'pal4rletrns.bmp': "b81e7fed38854d201a3199ce50ca05e92ca287c860797142857ac20b4a2f28952b058e21687c0fae60712f5784cd2c950ce70148ba1316efe31d4f3fc4006817",
|
||||
'pal8-0.bmp': "f39a4f1827c52bb620d975f8c72f5e95f90ac6c65ae0a6776ff1ad95808c090de17cbd182188a85157396fd9649ea4b5d84bb7c9175ab49ce2845da214c16bff",
|
||||
'pal8.bmp': "be27e55a866cbb655fdd917435cd6a5b62c20ae0d6ef7c1533c5a01dd9a893f058cc4ba2d902ab9315380009808e06b7f180116c9b790587cf62aa770c7a4a07",
|
||||
'pal8badindex.bmp': "bd5fc036985ae705182915a560dee2e5dfb3bd8b50932337b9085e190259c66e6bae5fbc813a261d352a60dcb0755798bdc251d6c2a0b638a7e337ba58811811",
|
||||
'pal8gs.bmp': "228f944b3e45359f62a2839d4e7b94d7f3a1074ad9e25661fdb9e8fff4c15581c85a7bb0ac75c92b95c7537ececc9d80b835cfe55bc7560a513118224a9ed36f",
|
||||
'pal8nonsquare.bmp': "b8adc9b03880975b232849ea1e8f87705937929d743df3d35420902b32557065354ab71d0d8176646bf0ad72c583c884cfcd1511017260ffca8c41d5a358a3eb",
|
||||
'pal8offs.bmp': "c92f1e1835d753fd8484be5198b2b8a2660d5e54117f6c4fc6d2ebc8d1def72a8d09cd820b1b8dcee15740b47151d62b8b7aca0b843e696252e28226b51361cf",
|
||||
'pal8os2-hs.bmp': "477c04048787eb412f192e7fe47ae96f14d7995391e78b10cc4c365f8c762f60c54cad7ef9d1705a78bd490a578fb346ee0a383c3a3fdf790558a12589eb04eb",
|
||||
'pal8os2-sz.bmp': "fd0eeb733be9b39f492d0f67dd28fc67207149e41691c206d4de4c693b5dea9458b88699a781383e7050a3b343259659aae64fec0616c98f3f8555cbf5c9e46c",
|
||||
'pal8os2.bmp': "cdab3ed7bc9f38d89117332a21418b3c916a99a8d8fb6b7ce456d54288c96152af12c0380293b04e96594a7867b83be5c99913d224c9750c7d38295924e0735a",
|
||||
'pal8os2sp.bmp': "f6e595a6db992ab7d1f79442d31f39f648061e7de13e51b07933283df065ce405c0208e6101ac916e4eb0613e412116f008510021a2d17543aa7f0a32349c96f",
|
||||
'pal8os2v2-16.bmp': "f52877d434218aa6b772a7aa0aaba4c2ae6ce35ecfa6876943bb350fdf9554f1f763a8d5bb054362fb8f9848eb71ce14a371f4a76da4b9475cdcee4f286109a4",
|
||||
'pal8os2v2-40sz.bmp': "9481691ada527df1f529316d44b5857c6a840c5dafa7e9795c9cb92dac02c6cc35739d3f6ce33d4ab6ff6bcd6b949741e89dc8c42cf52ad4546ff58cd3b5b66a",
|
||||
'pal8os2v2-sz.bmp': "99cd2836f90591cd27b0c8696ecff1e7a1debcef284bbe5d21e68759270c1bfe1f32ee8f576c49f3e64d8f4e4d9096574f3c8c79bfdae0545689da18364de3e7",
|
||||
'pal8os2v2.bmp': "7859b265956c7d369db7a0a357ce09bcda74e98954de88f454cae5e7cb021222146687a7770ce0cc2c58f1439c7c21c45c0c27464944e73913e1c88afc836c8a",
|
||||
'pal8oversizepal.bmp': "e778864e0669a33fce27c0ccd5b6460b572a5db01975e8d56acec8a9447e1c58d6051ad3516cfa96a39f4eb7f2576154188ea62ec187bcf4ae323883499383c0",
|
||||
'pal8rle.bmp': "88942a1cd2e36d1e0f0e2748a888034057919c7ec0f8d9b2664deb1daf1a6e45ed3e722dff5d810f413d6fc182e700a16d6563dd25f67dc6d135d751cd736dea",
|
||||
'pal8rlecut.bmp': "cda9fa274cde590aeaca81516e0465684cfae84e934eb983301801e978e6e2e9c590d22af992d9912e51bb9c2761945276bdbe0b6c47f3a021514414e1f3f455",
|
||||
'pal8rletrns.bmp': "0b2d5618dc9c81caa72c070070a4245dd9cd3de5d344b76ce9c15d0eeb72e2675efc264201f8709dfcffd234df09e76d6f328f16f2ad873ba846f870cadfa486",
|
||||
'pal8topdown.bmp': "d470a2b7556fa88eac820427cb900f59a121732cdb4a7f3530ed457798139c946a884a34ab79d822feb84c2ca6f4d9a65f6e792994eafc3a189948b9e4543546",
|
||||
'pal8v4.bmp': "0382610e32c49d5091a096cb48a54ebbf44d9ba1def96e2f30826fd3ddf249f5aed70ca5b74c484b6cdc3924f4d4bfed2f5194ad0bcf1d99bfaa3a619e299d86",
|
||||
'pal8v5.bmp': "50fadaa93aac2a377b565c4dc852fd4602538863b913cb43155f5ad7cf79928127ca28b33e5a3b0230076ea4a6e339e3bf57f019333f42c4e9f003a8f2376325",
|
||||
'pal8w124.bmp': "e54a901b9badda655cad828d226b381831aea7e36aec8729147e9e95a9f2b21a9d74d93756e908e812902a01197f1379fe7e35498dbafed02e27c853a24097b7",
|
||||
'pal8w125.bmp': "d7a04d45ef5b3830da071ca598f1e2a413c46834968b2db7518985cf8d8c7380842145899e133e71355b6b7d040ee9e97adec1e928ce4739282e0533058467c0",
|
||||
'pal8w126.bmp': "4b93845a98797679423c669c541a248b4cdfee80736f01cec29d8b40584bf55a27835c80656a2bf5c7ad3ed211c1f7d3c7d5831a6726904b39f10043a76b658d",
|
||||
'reallybig.bmp': "babbf0335bac63fd2e95a89210c61ae6bbaaeeab5f07974034e76b4dc2a5c755f77501e3d056479357445aac442e2807d7170ec44067bab8fd35742f0e7b8440",
|
||||
'rgb16-231.bmp': "611a87cb5d29f16ef71971714a3b0e3863a6df51fff16ce4d4df8ee028442f9ce03669fb5d7a6a838a12a75d8a887b56b5a2e44a3ad62f4ef3fc2e238c33f6a1",
|
||||
'rgb16-3103.bmp': "7fdff66f4d94341da522b4e40586b3b8c327be9778e461bca1600e938bfbaa872b484192b35cd84d9430ca20aa922ec0301567a74fb777c808400819db90b09d",
|
||||
'rgb16-565.bmp': "777883f64b9ae80d77bf16c6d062082b7a4702f8260c183680afee6ec26e48681bcca75f0f81c470be1ac8fcb55620b3af5ce31d9e114b582dfd82300a3d6654",
|
||||
'rgb16-565pal.bmp': "57e9dcf159415b3574a1b343a484391b0859ab2f480e22157f2a84bc188fde141a48826f960c6f30b1d1f17ef6503ec3afc883a2f25ff09dd50c437244f9ae7f",
|
||||
'rgb16-880.bmp': "8d61183623002da4f7a0a66b42aa58a120e3a91578bb0c4a5e2c5ba7d08b875d43a22f2b5b3a449d3caf4cc303cb05111dd1d5169953f288493b7ea3c2423d24",
|
||||
'rgb16.bmp': "1c0fe56661d4998edd76efedda520a441171d42ae4dad95b350e3b61deda984c3a3255392481fe1903e5e751357da3f35164935e323377f015774280036ba39e",
|
||||
'rgb16bfdef.bmp': "ed55d086e27ba472076df418be0046b740944958afeb84d05aa2bbe578dec27ced122ffefb6d549e1d07e05eb608979b3ac9b1bd809f8237cf0984ffdaa24716",
|
||||
'rgb16faketrns.bmp': "9cd4a8e05fe125649e360715851ef912e78a56d30e0ea1b1cfb6eaafd386437d45de9c1e1a845dd8d63ff5a414832355b8ae0e2a96d72a42a7205e8a2742d37c",
|
||||
'rgb24.bmp': "4f0ce2978bbfea948798b2fdcc4bdbe8983a6c94d1b7326f39daa6059368e08ebf239260984b64eeb0948f7c8089a523e74b7fa6b0437f9205d8af8891340389",
|
||||
'rgb24largepal.bmp': "b377aee1594c5d9fc806a70bc62ee83cf8d1852b4a2b18fd3e9409a31aa3b5a4cf5e3b4af2cbdebcef2b5861b7985a248239684a72072437c50151adc524e9df",
|
||||
'rgb24pal.bmp': "f40bb6e01f6ecb3d55aa992bf1d1e2988ea5eb11e3e58a0c59a4fea2448de26f231f45e9f378b7ee1bdd529ec57a1de38ea536e397f5e1ac6998921e066ab427",
|
||||
'rgb24png.bmp': "c60890bbd79c12472205665959eb6e2dd2103671571f80117b9e963f897cffca103181374a4797f53c7768af01a705e830a0de4dd8fab7241d24c17bee4a4dbe",
|
||||
'rgb24rle24.bmp': "ea0ff3f512dd04176d14b43dfbee73ac7f1913aa1b77587e187e271781c7dacec880cec73850c4127ea9f8dd885f069e281f159bb5232e93cbb2d1ee9cb50438",
|
||||
'rgb32-111110.bmp': "732884e300d4edbcf31556a038947beefc0c5e749131a66d2d7aa1d4ec8c8eba07833133038a03bbe4c4fa61a805a5df1f797b5853339ee6a8140478f5f70a76",
|
||||
'rgb32-7187.bmp': "4c55aab2e4ecf63dc30b04e5685c5d9fba7354ca0e1327d7c4b15d6da10ac66ca1cea6f0303f9c5f046da3bcd2566275384e0e7bb14fcc5196ec39ca90fac194",
|
||||
'rgb32-xbgr.bmp': "1e9f240eaec6ac2833f8c719f1fb53cc7551809936620e871ccacfab26402e1afc6503b9f707e4ec25f15529e3ce6433c7f999d5714af31dfb856eb67e772f64",
|
||||
'rgb32.bmp': "32033dbf9462e5321b1182ba739624ed535aa4d33b775ffeeaf09d2d4cb663e4c3505e8c05489d940f754dde4b50a2e0b0688b21d06755e717e6e511b0947525",
|
||||
'rgb32bf.bmp': "7243c215827a9b4a1d7d52d67fb04ffb43b0b176518fbdab43d413e2a0c18883b106797f1acd85ba68d494ec939b0caab8789564670d058caf0e1175ce7983fb",
|
||||
'rgb32bfdef.bmp': "a81433babb67ce714285346a77bfccd19cf6203ac1d8245288855aff20cf38146a783f4a7eac221db63d1ee31345da1329e945b432f0e7bcf279ea88ff5bb302",
|
||||
'rgb32fakealpha.bmp': "abecaf1b5bfad322de7aec897efe7aa6525f2a77a0af86cc0a0a366ed1650da703cf4b7b117a7ba34f21d03a8a0045e5821248cdefa00b0c78e01d434b55e746",
|
||||
'rgb32h52.bmp': "707d734393c83384bc75720330075ec9ffefc69167343259ebf95f9393948364a40f33712619f962e7056483b73334584570962c16da212cd5291f764b3f2cd1",
|
||||
'rgba16-1924.bmp': "3e41a5d8d951bac580c780370ca21e0783de8154f4081106bc58d1185bb2815fc5b7f08f2a1c75cd205fc52c888e9d07c91856651603a2d756c9cfc392585598",
|
||||
'rgba16-4444.bmp': "a6662186b23bd434a7e019d2a71cd95f53a47b64a1afea4c27ae1120685d041a9ff98800a43a9d8f332682670585bdb2fa77ff77b6def65139fe725323f91561",
|
||||
'rgba16-5551.bmp': "a7d9f8ae7f8349cd6df651ab9d814411939fa2a235506ddfdd0df5a8f8abcf75552c32481ea035ff29e683bdcd34da68eb23730857e0716b79af51d69a60757b",
|
||||
'rgba32-1.bmp': "3958d18d2a7f32ada69cb11e0b4397821225a5c40acc7b6d36ff28ee4565e150cc508971278a2ddf8948aaff86f66ec6a0c24513db44962d81b79c4239b3e612",
|
||||
'rgba32-1010102.bmp': "59a28db5563caf954d31b20a1d1cc59366fcfd258b7ba2949f7281978460a3d94bedcc314c089243dd7463bb18d36a9473355158a7d903912cb25b98eab6b068",
|
||||
'rgba32-2.bmp': "9b7e5965ff9888f011540936ab6b3022edf9f6c5d7e541d6882cb81820cf1d68326d65695a6f0d01999ac10a496a504440906aa45e413da593405563c54c1a05",
|
||||
'rgba32-61754.bmp': "784ae0a32b19fa925e0c86dbff3bd38d80904d0fa7dc3b03e9d4f707d42b1604c1f54229e901ccc249cab8c2976d58b1e16980157d9bf3dbc4e035e2b2fd1779",
|
||||
'rgba32-81284.bmp': "fcfca645017c0d15d44b08598a90d238d063763fd06db665d9a8e36ef5099ce0bf4d440e615c6d6b1bf99f38230d4848318bfa1e6d9bfdd6dfd521cc633ba110",
|
||||
'rgba32abf.bmp': "2883d676966d298d235196f102e044e52ef18f3cb5bb0dd84738c679f0a1901181483ca2df1cccf6e4b3b4e98be39e81de69c9a58f0d70bc3ebb0fcea80daa0c",
|
||||
'rgba32h56.bmp': "507d0caf29ccb011c83c0c069c21105ea1d58e06b92204f9c612f26102123a7680eae53fef023c701952d903e11b61f8aa07618c381ea08f6808c523f5a84546",
|
||||
'rgba64.bmp': "d01f14f649c1c33e3809508cc6f089dd2ab0a538baf833a91042f2e54eca3f8e409908e15fa8763b059d7fa022cf5c074d9f5720eed5293a4c922e131c2eae68",
|
||||
'rletopdown.bmp': "37500893aad0b40656aa80fd5c7c5f9b35d033018b8070d8b1d7baeb34c90f90462288b13295204b90aa3e5c9be797d22a328e3714ab259334e879a09a3de175",
|
||||
'shortfile.bmp': "be3ffade7999304f00f9b7d152b5b27811ad1166d0fd43004392467a28f44b6a4ec02a23c0296bacd4f02f8041cd824b9ca6c9fc31fed27e36e572113bb47d73",
|
||||
|
||||
'unicode.xml': "e0cdc94f07fdbb15eea811ed2ae6dcf494a83d197dafe6580c740270feb0d8f5f7146d4a7d4c2d2ea25f8bd9678bc986123484b39399819a6b7262687959d1ae",
|
||||
}
|
||||
|
||||
@@ -233,6 +321,7 @@ def try_download_and_unpack_zip(suite):
|
||||
|
||||
hmac_digest = hmac.new(HMAC_KEY.encode(), file_data, HMAC_HASH).hexdigest()
|
||||
print("{} *{}".format(hmac_digest, file.filename))
|
||||
|
||||
if not hmac.compare_digest(hmac_digest, HMAC_DIGESTS[file.filename]):
|
||||
print("FAIL! Expected: {}".format(HMAC_DIGESTS[file.filename]))
|
||||
return 4
|
||||
|
||||
@@ -221,8 +221,45 @@ test_fmt_python_syntax :: proc(t: ^testing.T) {
|
||||
check(t, "%!(MISSING CLOSE BRACE)%!(EXTRA 1)", "{0", 1 )
|
||||
}
|
||||
|
||||
@(test)
|
||||
test_pointers :: proc(t: ^testing.T) {
|
||||
S :: struct { i: int }
|
||||
a: rawptr
|
||||
b: ^int
|
||||
c: ^S
|
||||
d: ^S = cast(^S)cast(uintptr)0xFFFF
|
||||
|
||||
check(t, "0x0", "%p", a)
|
||||
check(t, "0x0", "%p", b)
|
||||
check(t, "0x0", "%p", c)
|
||||
check(t, "0xFFFF", "%p", d)
|
||||
|
||||
check(t, "0x0", "%#p", a)
|
||||
check(t, "0x0", "%#p", b)
|
||||
check(t, "0x0", "%#p", c)
|
||||
check(t, "0xFFFF", "%#p", d)
|
||||
|
||||
check(t, "0x0", "%v", a)
|
||||
check(t, "0x0", "%v", b)
|
||||
check(t, "<nil>", "%v", c)
|
||||
|
||||
check(t, "0x0", "%#v", a)
|
||||
check(t, "0x0", "%#v", b)
|
||||
check(t, "<nil>", "%#v", c)
|
||||
|
||||
check(t, "0x0000", "%4p", a)
|
||||
check(t, "0x0000", "%4p", b)
|
||||
check(t, "0x0000", "%4p", c)
|
||||
check(t, "0xFFFF", "%4p", d)
|
||||
|
||||
check(t, "0x0000", "%#4p", a)
|
||||
check(t, "0x0000", "%#4p", b)
|
||||
check(t, "0x0000", "%#4p", c)
|
||||
check(t, "0xFFFF", "%#4p", d)
|
||||
}
|
||||
|
||||
@(private)
|
||||
check :: proc(t: ^testing.T, exp: string, format: string, args: ..any) {
|
||||
check :: proc(t: ^testing.T, exp: string, format: string, args: ..any, loc := #caller_location) {
|
||||
got := fmt.tprintf(format, ..args)
|
||||
testing.expectf(t, got == exp, "(%q, %v): %q != %q", format, args, got, exp)
|
||||
}
|
||||
testing.expectf(t, got == exp, "(%q, %v): %q != %q", format, args, got, exp, loc = loc)
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
@echo off
|
||||
odin test . -define:ODIN_TEST_TRACK_MEMORY=true -define:ODIN_TEST_PROGRESS_WIDTH=12 -vet -strict-style
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@
|
||||
|
||||
This file exports procedures for use with the test.py test suite.
|
||||
*/
|
||||
package math_big_tests
|
||||
package test_core_math_big
|
||||
|
||||
/*
|
||||
TODO: Write tests for `internal_*` and test reusing parameters with the public implementations.
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package test_core_math_big
|
||||
|
||||
import "core:math/big"
|
||||
import "core:testing"
|
||||
|
||||
@(test)
|
||||
test_permutations_and_combinations :: proc(t: ^testing.T) {
|
||||
{
|
||||
calc, exp := &big.Int{}, &big.Int{}
|
||||
defer big.destroy(calc, exp)
|
||||
big.permutations_without_repetition(calc, 9000, 10)
|
||||
big.int_atoi(exp, "3469387884476822917768284664849390080000")
|
||||
equals, error := big.equals(calc, exp)
|
||||
testing.expect(t, equals)
|
||||
testing.expect_value(t, error, nil)
|
||||
}
|
||||
|
||||
{
|
||||
calc, exp := &big.Int{}, &big.Int{}
|
||||
defer big.destroy(calc, exp)
|
||||
big.combinations_with_repetition(calc, 9000, 10)
|
||||
big.int_atoi(exp, "965678962435231708695393645683400")
|
||||
equals, error := big.equals(calc, exp)
|
||||
testing.expect(t, equals)
|
||||
testing.expect_value(t, error, nil)
|
||||
}
|
||||
|
||||
{
|
||||
calc, exp := &big.Int{}, &big.Int{}
|
||||
defer big.destroy(calc, exp)
|
||||
big.combinations_without_repetition(calc, 9000, 10)
|
||||
big.int_atoi(exp, "956070294443568925751842114431600")
|
||||
equals, error := big.equals(calc, exp)
|
||||
testing.expect(t, equals)
|
||||
testing.expect_value(t, error, nil)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package test_core_mem
|
||||
|
||||
import "core:mem/tlsf"
|
||||
import "core:testing"
|
||||
|
||||
@test
|
||||
test_tlsf_bitscan :: proc(t: ^testing.T) {
|
||||
Vector :: struct {
|
||||
op: enum{ffs, fls, fls_uint},
|
||||
v: union{u32, uint},
|
||||
exp: i32,
|
||||
}
|
||||
Tests := []Vector{
|
||||
{.ffs, u32 (0x0000_0000_0000_0000), -1},
|
||||
{.ffs, u32 (0x0000_0000_0000_0000), -1},
|
||||
{.fls, u32 (0x0000_0000_0000_0000), -1},
|
||||
{.ffs, u32 (0x0000_0000_0000_0001), 0},
|
||||
{.fls, u32 (0x0000_0000_0000_0001), 0},
|
||||
{.ffs, u32 (0x0000_0000_8000_0000), 31},
|
||||
{.ffs, u32 (0x0000_0000_8000_8000), 15},
|
||||
{.fls, u32 (0x0000_0000_8000_0008), 31},
|
||||
{.fls, u32 (0x0000_0000_7FFF_FFFF), 30},
|
||||
{.fls_uint, uint(0x0000_0000_8000_0000), 31},
|
||||
{.fls_uint, uint(0x0000_0001_0000_0000), 32},
|
||||
{.fls_uint, uint(0xffff_ffff_ffff_ffff), 63},
|
||||
}
|
||||
|
||||
for test in Tests {
|
||||
switch test.op {
|
||||
case .ffs:
|
||||
res := tlsf.ffs(test.v.?)
|
||||
testing.expectf(t, res == test.exp, "Expected tlsf.ffs(0x%08x) == %v, got %v", test.v, test.exp, res)
|
||||
case .fls:
|
||||
res := tlsf.fls(test.v.?)
|
||||
testing.expectf(t, res == test.exp, "Expected tlsf.fls(0x%08x) == %v, got %v", test.v, test.exp, res)
|
||||
case .fls_uint:
|
||||
res := tlsf.fls_uint(test.v.?)
|
||||
testing.expectf(t, res == test.exp, "Expected tlsf.fls_uint(0x%16x) == %v, got %v", test.v, test.exp, res)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
A test suite for `core:net`
|
||||
*/
|
||||
//+build !netbsd
|
||||
package test_core_net
|
||||
|
||||
import "core:testing"
|
||||
@@ -552,4 +553,4 @@ binstr_to_address :: proc(t: ^testing.T, binstr: string) -> (address: net.Addres
|
||||
return nil
|
||||
}
|
||||
panic("Invalid test case")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package tests_core
|
||||
|
||||
import rlibc "core:c/libc"
|
||||
|
||||
@(init)
|
||||
download_assets :: proc() {
|
||||
if rlibc.system("python3 " + ODIN_ROOT + "tests/core/download_assets.py " + ODIN_ROOT + "tests/core/assets") != 0 {
|
||||
panic("downloading test assets failed!")
|
||||
}
|
||||
}
|
||||
|
||||
@(require) import "c/libc"
|
||||
@(require) import "compress"
|
||||
@(require) import "container"
|
||||
@(require) import "encoding/base64"
|
||||
@(require) import "encoding/cbor"
|
||||
@(require) import "encoding/hex"
|
||||
@(require) import "encoding/hxa"
|
||||
@(require) import "encoding/json"
|
||||
@(require) import "encoding/varint"
|
||||
@(require) import "encoding/xml"
|
||||
@(require) import "fmt"
|
||||
@(require) import "math"
|
||||
@(require) import "math/big"
|
||||
@(require) import "math/linalg/glsl"
|
||||
@(require) import "math/noise"
|
||||
@(require) import "mem"
|
||||
@(require) import "net"
|
||||
@(require) import "odin"
|
||||
@(require) import "path/filepath"
|
||||
@(require) import "reflect"
|
||||
@(require) import "runtime"
|
||||
@(require) import "slice"
|
||||
@(require) import "strconv"
|
||||
@(require) import "strings"
|
||||
@(require) import "text/i18n"
|
||||
@(require) import "text/match"
|
||||
@(require) import "thread"
|
||||
@(require) import "time"
|
||||
@@ -1,10 +0,0 @@
|
||||
// Tests that Odin run returns exit code of built executable on Unix
|
||||
// Needs exit status to be inverted to return 0 on success, e.g.
|
||||
// $(./odin run tests/core/os/test_core_os_exit.odin && exit 1 || exit 0)
|
||||
package test_core_os_exit
|
||||
|
||||
import "core:os"
|
||||
|
||||
main :: proc() {
|
||||
os.exit(1)
|
||||
}
|
||||
@@ -184,4 +184,34 @@ test_binary_search :: proc(t: ^testing.T) {
|
||||
index, found = slice.binary_search(b, 0)
|
||||
testing.expect(t, index == 0, "Expected index to be 0.")
|
||||
testing.expect(t, found == false, "Expected found to be false.")
|
||||
}
|
||||
}
|
||||
|
||||
@test
|
||||
test_permutation_iterator :: proc(t: ^testing.T) {
|
||||
// Big enough to do some sanity checking but not overly large.
|
||||
FAC_5 :: 120
|
||||
s := []int{1, 2, 3, 4, 5}
|
||||
seen: map[int]bool
|
||||
defer delete(seen)
|
||||
|
||||
iter := slice.make_permutation_iterator(s)
|
||||
defer slice.destroy_permutation_iterator(iter)
|
||||
|
||||
permutations_counted: int
|
||||
for slice.permute(&iter) {
|
||||
n := 0
|
||||
for item, index in s {
|
||||
n *= 10
|
||||
n += item
|
||||
}
|
||||
if n in seen {
|
||||
testing.fail_now(t, "Permutation iterator made a duplicate permutation.")
|
||||
return
|
||||
}
|
||||
seen[n] = true
|
||||
permutations_counted += 1
|
||||
}
|
||||
|
||||
testing.expect_value(t, len(seen), FAC_5)
|
||||
testing.expect_value(t, permutations_counted, FAC_5)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// Tests intended to be ran with optimizations on
|
||||
package tests_core
|
||||
|
||||
@(require) import "crypto"
|
||||
@(require) import "hash"
|
||||
@(require) import "image"
|
||||
@@ -1,24 +0,0 @@
|
||||
ODIN=../../odin
|
||||
COMMON=-file -vet -strict-style -o:minimal
|
||||
|
||||
all: all_bsd asan_test
|
||||
|
||||
all_bsd: rtti_test map_test pow_test 128_test string_compare_test
|
||||
|
||||
rtti_test:
|
||||
$(ODIN) test test_rtti.odin $(COMMON)
|
||||
|
||||
map_test:
|
||||
$(ODIN) test test_map.odin $(COMMON)
|
||||
|
||||
pow_test:
|
||||
$(ODIN) test test_pow.odin $(COMMON)
|
||||
|
||||
128_test:
|
||||
$(ODIN) test test_128.odin $(COMMON)
|
||||
|
||||
string_compare_test:
|
||||
$(ODIN) test test_string_compare.odin $(COMMON)
|
||||
|
||||
asan_test:
|
||||
$(ODIN) test test_asan.odin $(COMMON) -sanitize:address -debug
|
||||
@@ -1,9 +0,0 @@
|
||||
@echo off
|
||||
set PATH_TO_ODIN==..\..\odin
|
||||
set COMMON=-file -vet -strict-style -o:minimal
|
||||
%PATH_TO_ODIN% test test_rtti.odin %COMMON% || exit /b
|
||||
%PATH_TO_ODIN% test test_map.odin %COMMON% || exit /b
|
||||
%PATH_TO_ODIN% test test_pow.odin %COMMON% || exit /b
|
||||
%PATH_TO_ODIN% test test_asan.odin %COMMON% || exit /b
|
||||
%PATH_TO_ODIN% test test_128.odin %COMMON% || exit /b
|
||||
%PATH_TO_ODIN% test test_string_compare.odin %COMMON% || exit /b
|
||||
@@ -1,4 +1,4 @@
|
||||
package test_128
|
||||
package test_internal
|
||||
|
||||
import "core:testing"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Intended to contain code that would trigger asan easily if the abi was set up badly.
|
||||
package test_asan
|
||||
package test_internal
|
||||
|
||||
import "core:testing"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package test_internal_map
|
||||
package test_internal
|
||||
|
||||
import "core:log"
|
||||
import "base:intrinsics"
|
||||
@@ -309,4 +309,4 @@ set_delete_random_key_value :: proc(t: ^testing.T) {
|
||||
}
|
||||
seed_incr += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package test_internal_math_pow
|
||||
package test_internal
|
||||
|
||||
@(require) import "core:log"
|
||||
import "core:math"
|
||||
@@ -42,4 +42,4 @@ pow_test :: proc(t: ^testing.T) {
|
||||
testing.expectf(t, _v1 == _v2, "Expected math.pow2_f16(%d) == math.pow(2, %d) (= %04x), got %04x", exp, exp, _v1, _v2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package test_internal_rtti
|
||||
package test_internal
|
||||
|
||||
import "core:fmt"
|
||||
import "core:testing"
|
||||
@@ -43,4 +43,4 @@ rtti_test :: proc(t: ^testing.T) {
|
||||
l_s := fmt.tprintf("%s", l_buggy)
|
||||
testing.expectf(t, g_s == EXPECTED_REPR, "Expected fmt.tprintf(\"%%s\", g_s)) to return \"%v\", got \"%v\"", EXPECTED_REPR, g_s)
|
||||
testing.expectf(t, l_s == EXPECTED_REPR, "Expected fmt.tprintf(\"%%s\", l_s)) to return \"%v\", got \"%v\"", EXPECTED_REPR, l_s)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package test_internal_string_compare
|
||||
package test_internal
|
||||
|
||||
import "core:testing"
|
||||
|
||||
@@ -54,4 +54,4 @@ string_compare :: proc(t: ^testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style
|
||||
..\..\..\odin test ..\test_issue_829.odin %COMMON% || exit /b
|
||||
..\..\..\odin test ..\test_issue_1592.odin %COMMON% || exit /b
|
||||
..\..\..\odin test ..\test_issue_2056.odin %COMMON% || exit /b
|
||||
..\..\..\odin test ..\test_issue_2087.odin %COMMON% || exit /b
|
||||
..\..\..\odin build ..\test_issue_2113.odin %COMMON% -debug || exit /b
|
||||
..\..\..\odin test ..\test_issue_2466.odin %COMMON% || exit /b
|
||||
..\..\..\odin test ..\test_issue_2615.odin %COMMON% || exit /b
|
||||
|
||||
+2
-4
@@ -6,23 +6,21 @@ pushd build
|
||||
ODIN=../../../odin
|
||||
COMMON="-define:ODIN_TEST_FANCY=false -file -vet -strict-style"
|
||||
|
||||
NO_NIL_ERR="Error: "
|
||||
|
||||
set -x
|
||||
|
||||
$ODIN test ../test_issue_829.odin $COMMON
|
||||
$ODIN test ../test_issue_1592.odin $COMMON
|
||||
$ODIN test ../test_issue_2056.odin $COMMON
|
||||
$ODIN test ../test_issue_2087.odin $COMMON
|
||||
$ODIN build ../test_issue_2113.odin $COMMON -debug
|
||||
$ODIN test ../test_issue_2466.odin $COMMON
|
||||
$ODIN test ../test_issue_2615.odin $COMMON
|
||||
$ODIN test ../test_issue_2637.odin $COMMON
|
||||
$ODIN test ../test_issue_2666.odin $COMMON
|
||||
if [[ $($ODIN build ../test_issue_2395.odin $COMMON 2>&1 >/dev/null | grep -c "$NO_NIL_ERR") -eq 2 ]] ; then
|
||||
if [[ $($ODIN build ../test_issue_2395.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]] ; then
|
||||
echo "SUCCESSFUL 1/1"
|
||||
else
|
||||
echo "SUCCESSFUL 0/1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set +x
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
// exactly 2 errors from the invalid unions
|
||||
package test_issues
|
||||
|
||||
import "core:testing"
|
||||
|
||||
ValidUnion :: union($T: typeid) #no_nil {
|
||||
T,
|
||||
f32,
|
||||
|
||||
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
ODIN=../../odin
|
||||
ODINFLAGS=
|
||||
|
||||
OS=$(shell uname)
|
||||
|
||||
ifeq ($(OS), OpenBSD)
|
||||
ODINFLAGS:=$(ODINFLAGS) -extra-linker-flags:-L/usr/local/lib
|
||||
endif
|
||||
|
||||
all:
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
package tests_vendor
|
||||
|
||||
@(require) import "glfw"
|
||||
Vendored
-8
@@ -1,8 +0,0 @@
|
||||
@echo off
|
||||
set COMMON=-show-timings -no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false
|
||||
set PATH_TO_ODIN==..\..\odin
|
||||
|
||||
echo ---
|
||||
echo Running vendor:glfw tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% test glfw %COMMON% -out:vendor_glfw.exe || exit /b
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
//+build darwin, windows
|
||||
package test_vendor_glfw
|
||||
|
||||
import "core:testing"
|
||||
|
||||
Vendored
+3
-3
@@ -622,7 +622,7 @@ push_command :: proc(ctx: ^Context, $Type: typeid, extra_size := 0) -> ^Type {
|
||||
return cmd
|
||||
}
|
||||
|
||||
next_command :: proc(ctx: ^Context, pcmd: ^^Command) -> bool {
|
||||
next_command :: proc "contextless" (ctx: ^Context, pcmd: ^^Command) -> bool {
|
||||
cmd := pcmd^
|
||||
defer pcmd^ = cmd
|
||||
if cmd != nil {
|
||||
@@ -630,7 +630,7 @@ next_command :: proc(ctx: ^Context, pcmd: ^^Command) -> bool {
|
||||
} else {
|
||||
cmd = (^Command)(&ctx.command_list.items[0])
|
||||
}
|
||||
invalid_command :: #force_inline proc(ctx: ^Context) -> ^Command {
|
||||
invalid_command :: #force_inline proc "contextless" (ctx: ^Context) -> ^Command {
|
||||
return (^Command)(&ctx.command_list.items[ctx.command_list.idx])
|
||||
}
|
||||
for cmd != invalid_command(ctx) {
|
||||
@@ -643,7 +643,7 @@ next_command :: proc(ctx: ^Context, pcmd: ^^Command) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
next_command_iterator :: proc(ctx: ^Context, pcm: ^^Command) -> (Command_Variant, bool) {
|
||||
next_command_iterator :: proc "contextless" (ctx: ^Context, pcm: ^^Command) -> (Command_Variant, bool) {
|
||||
if next_command(ctx, pcm) {
|
||||
return pcm^.variant, true
|
||||
}
|
||||
|
||||
Vendored
+2
-2
@@ -118,8 +118,8 @@ when ODIN_OS == .Windows {
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import lib {
|
||||
"macos" +
|
||||
"-arm64" when ODIN_ARCH == .arm64 else "" +
|
||||
"/libraylib" + ".500.dylib" when RAYLIB_SHARED else ".a",
|
||||
("-arm64" when ODIN_ARCH == .arm64 else "") +
|
||||
"/libraylib" + (".500.dylib" when RAYLIB_SHARED else ".a"),
|
||||
"system:Cocoa.framework",
|
||||
"system:OpenGL.framework",
|
||||
"system:IOKit.framework",
|
||||
|
||||
Vendored
+1
-1
@@ -76,7 +76,7 @@ foreign lib {
|
||||
GetRenderer :: proc(window: ^Window) -> ^Renderer ---
|
||||
GetRendererInfo :: proc(renderer: ^Renderer, info: ^RendererInfo) -> c.int ---
|
||||
GetRendererOutputSize :: proc(renderer: ^Renderer, w, h: ^c.int) -> c.int ---
|
||||
CreateTexture :: proc(renderer: ^Renderer, format: u32, access: TextureAccess, w, h: c.int) -> ^Texture ---
|
||||
CreateTexture :: proc(renderer: ^Renderer, format: PixelFormatEnum, access: TextureAccess, w, h: c.int) -> ^Texture ---
|
||||
CreateTextureFromSurface :: proc(renderer: ^Renderer, surface: ^Surface) -> ^Texture ---
|
||||
QueryTexture :: proc(texture: ^Texture, format: ^u32, access, w, h: ^c.int) -> c.int ---
|
||||
SetTextureColorMod :: proc(texture: ^Texture, r, g, b: u8) -> c.int ---
|
||||
|
||||
Vendored
+5
@@ -17,6 +17,11 @@ AllTemporary :: 0
|
||||
CurrentTime :: 0
|
||||
NoSymbol :: 0
|
||||
|
||||
PropModeReplace :: 0
|
||||
PropModePrepend :: 1
|
||||
PropModeAppend :: 2
|
||||
|
||||
XA_ATOM :: Atom(4)
|
||||
XA_WM_CLASS :: Atom(67)
|
||||
XA_WM_CLIENT_MACHINE :: Atom(36)
|
||||
XA_WM_COMMAND :: Atom(34)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user