Merge branch 'master' into tlsf-allocator

This commit is contained in:
Jeroen van Rijn
2024-06-09 16:42:23 +02:00
100 changed files with 2649 additions and 999 deletions
+71 -168
View File
@@ -10,7 +10,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Build, Check, and Test
timeout-minutes: 25
timeout-minutes: 15
uses: vmactions/netbsd-vm@v1
with:
release: "10.0"
@@ -24,191 +24,106 @@ jobs:
/usr/sbin/pkg_add https://github.com/andreas-jonsson/llvm17-netbsd-bin/releases/download/pkgsrc-current/llvm-17.0.6.tgz
/usr/sbin/pkg_add https://github.com/andreas-jonsson/llvm17-netbsd-bin/releases/download/pkgsrc-current/clang-17.0.6.tgz
ln -s /usr/pkg/bin/python3.11 /usr/bin/python3
ln -s /usr/pkg/bin/bash /bin/bash
run: |
git config --global --add safe.directory $(pwd)
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
(cd tests/core; gmake all_bsd)
(cd tests/internal; gmake all_bsd)
./odin check examples/all -vet -strict-style -target:netbsd_arm64
./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
+13 -28
View File
@@ -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,16 +146,16 @@ 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'
- name: Install B2 CLI
- name: Install B2 SDK
shell: bash
run: |
python -m pip install --upgrade pip
pip install --upgrade b2
pip install --upgrade b2sdk
- name: Display Python version
run: python -c "import sys; print(sys.version)"
@@ -188,24 +188,9 @@ jobs:
BUCKET: ${{ secrets.B2_BUCKET }}
DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }}
run: |
echo Authorizing B2 account
b2 account authorize "$APPID" "$APPKEY"
echo Uploading artifcates to B2
chmod +x ./ci/upload_create_nightly.sh
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/dist.zip
./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/dist.zip
./ci/upload_create_nightly.sh "$BUCKET" macos-arm64 macos_arm_artifacts/dist.zip
echo Deleting old artifacts in B2
python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP"
echo Creating nightly.json
python3 ci/create_nightly_json.py "$BUCKET" > nightly.json
echo Uploading nightly.json
b2 upload-file "$BUCKET" nightly.json nightly.json
echo Clear B2 account info
b2 clear-account
python3 ci/nightly.py artifact windows-amd64 windows_artifacts/
python3 ci/nightly.py artifact ubuntu-amd64 ubuntu_artifacts/dist.zip
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
+13 -2
View File
@@ -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()
}
+1 -1
View File
@@ -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 {
+25 -11
View File
@@ -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
View File
@@ -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()
}
+29
View File
@@ -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)
}
+1 -1
View File
@@ -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")
}
}
+2 -2
View File
@@ -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},
+43
View File
@@ -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
}
+1 -1
View File
@@ -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)
-51
View File
@@ -1,51 +0,0 @@
import subprocess
import sys
import json
import datetime
import urllib.parse
import sys
def main():
files_by_date = {}
bucket = sys.argv[1]
files_lines = execute_cli(f"b2 ls --long b2://{bucket}/nightly/").split("\n")
for x in files_lines:
parts = x.split(" ", 1)
if parts[0]:
print(f"Parts[0]: {parts[0]}", flush=True)
json_str = execute_cli(f"b2 file info b2://{bucket}/{parts[0]}")
data = json.loads(json_str)
name = remove_prefix(data['fileName'], "nightly/")
url = f"https://f001.backblazeb2.com/file/{bucket}/nightly/{urllib.parse.quote_plus(name)}"
sha1 = data['contentSha1']
size = int(data['size'])
ts = int(data['fileInfo']['src_last_modified_millis'])
date = datetime.datetime.fromtimestamp(ts/1000).strftime('%Y-%m-%d')
if date not in files_by_date.keys():
files_by_date[date] = []
files_by_date[date].append({
'name': name,
'url': url,
'sha1': sha1,
'sizeInBytes': size,
})
now = datetime.datetime.utcnow().isoformat()
print(json.dumps({
'last_updated' : now,
'files': files_by_date
}, sort_keys=True, indent=4))
def remove_prefix(text, prefix):
return text[text.startswith(prefix) and len(prefix):]
def execute_cli(command):
sb = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
return sb.stdout.read().decode("utf-8");
if __name__ == '__main__':
sys.exit(main())
-33
View File
@@ -1,33 +0,0 @@
import subprocess
import sys
import json
import datetime
import urllib.parse
import sys
def main():
files_by_date = {}
bucket = sys.argv[1]
days_to_keep = int(sys.argv[2])
print(f"Looking for binaries to delete older than {days_to_keep} days")
files_lines = execute_cli(f"b2 ls --long --versions b2://{bucket}/nightly/").split("\n")
for x in files_lines:
parts = [y for y in x.split(' ') if y]
if parts and parts[0]:
date = datetime.datetime.strptime(parts[2], '%Y-%m-%d').replace(hour=0, minute=0, second=0, microsecond=0)
now = datetime.datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
delta = now - date
if delta.days > days_to_keep:
print(f'Deleting b2://{bucket}/{parts[5]}')
execute_cli(f'b2 rm b2://{bucket}/{parts[5]}')
def execute_cli(command):
sb = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
return sb.stdout.read().decode("utf-8");
if __name__ == '__main__':
sys.exit(main())
+140
View File
@@ -0,0 +1,140 @@
import os
import sys
from zipfile import ZipFile, ZIP_DEFLATED
from b2sdk.v2 import InMemoryAccountInfo, B2Api
from datetime import datetime
import json
UPLOAD_FOLDER = "nightly/"
info = InMemoryAccountInfo()
b2_api = B2Api(info)
application_key_id = os.environ['APPID']
application_key = os.environ['APPKEY']
bucket_name = os.environ['BUCKET']
days_to_keep = os.environ['DAYS_TO_KEEP']
def auth() -> bool:
try:
realm = b2_api.account_info.get_realm()
return True # Already authenticated
except:
pass # Not yet authenticated
err = b2_api.authorize_account("production", application_key_id, application_key)
return err == None
def get_bucket():
if not auth(): sys.exit(1)
return b2_api.get_bucket_by_name(bucket_name)
def remove_prefix(text: str, prefix: str) -> str:
return text[text.startswith(prefix) and len(prefix):]
def create_and_upload_artifact_zip(platform: str, artifact: str) -> int:
now = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
destination_zip_name = "odin-{}-nightly+{}.zip".format(platform, now.strftime("%Y-%m-%d"))
source_zip_name = artifact
if not artifact.endswith(".zip"):
print(f"Creating archive {destination_zip_name} from {artifact} and uploading to {bucket_name}")
source_zip_name = destination_zip_name
with ZipFile(source_zip_name, mode='w', compression=ZIP_DEFLATED, compresslevel=9) as z:
for root, directory, filenames in os.walk(artifact):
for file in filenames:
file_path = os.path.join(root, file)
zip_path = os.path.join("dist", os.path.relpath(file_path, artifact))
z.write(file_path, zip_path)
if not os.path.exists(source_zip_name):
print(f"Error: Newly created ZIP archive {source_zip_name} not found.")
return 1
print("Uploading {} to {}".format(source_zip_name, UPLOAD_FOLDER + destination_zip_name))
bucket = get_bucket()
res = bucket.upload_local_file(
source_zip_name, # Local file to upload
"nightly/" + destination_zip_name, # B2 destination path
)
return 0
def prune_artifacts():
print(f"Looking for binaries to delete older than {days_to_keep} days")
bucket = get_bucket()
for file, _ in bucket.ls(UPLOAD_FOLDER, latest_only=False):
# Timestamp is in milliseconds
date = datetime.fromtimestamp(file.upload_timestamp / 1_000.0).replace(hour=0, minute=0, second=0, microsecond=0)
now = datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
delta = now - date
if delta.days > int(days_to_keep):
print("Deleting {}".format(file.file_name))
file.delete()
return 0
def update_nightly_json():
print(f"Updating nightly.json with files {days_to_keep} days or newer")
files_by_date = {}
bucket = get_bucket()
for file, _ in bucket.ls(UPLOAD_FOLDER, latest_only=True):
# Timestamp is in milliseconds
date = datetime.fromtimestamp(file.upload_timestamp / 1_000.0).replace(hour=0, minute=0, second=0, microsecond=0).strftime('%Y-%m-%d')
name = remove_prefix(file.file_name, UPLOAD_FOLDER)
sha1 = file.content_sha1
size = file.size
url = bucket.get_download_url(file.file_name)
if date not in files_by_date.keys():
files_by_date[date] = []
files_by_date[date].append({
'name': name,
'url': url,
'sha1': sha1,
'sizeInBytes': size,
})
now = datetime.utcnow().isoformat()
nightly = json.dumps({
'last_updated' : now,
'files': files_by_date
}, sort_keys=True, indent=4, ensure_ascii=False).encode('utf-8')
res = bucket.upload_bytes(
nightly, # JSON bytes
"nightly.json", # B2 destination path
)
return 0
if __name__ == "__main__":
if len(sys.argv) == 1:
print("Usage: {} <verb> [arguments]".format(sys.argv[0]))
print("\tartifact <platform prefix> <artifact path>\n\t\tCreates and uploads a platform artifact zip.")
print("\tprune\n\t\tDeletes old artifacts from bucket")
print("\tjson\n\t\tUpdate and upload nightly.json")
sys.exit(1)
else:
command = sys.argv[1].lower()
if command == "artifact":
if len(sys.argv) != 4:
print("Usage: {} artifact <platform prefix> <artifact path>".format(sys.argv[0]))
print("Error: Expected artifact command to be given platform prefix and artifact path.\n")
sys.exit(1)
res = create_and_upload_artifact_zip(sys.argv[2], sys.argv[3])
sys.exit(res)
elif command == "prune":
res = prune_artifacts()
sys.exit(res)
elif command == "json":
res = update_nightly_json()
sys.exit(res)
-25
View File
@@ -1,25 +0,0 @@
#!/bin/bash
set -e
bucket=$1
platform=$2
artifact=$3
now=$(date +'%Y-%m-%d')
filename="odin-$platform-nightly+$now.zip"
echo "Creating archive $filename from $artifact and uploading to $bucket"
# If this is already zipped up (done before artifact upload to keep permissions in tact), just move it.
if [ "${artifact: -4}" == ".zip" ]
then
echo "Artifact already a zip"
mkdir -p "output"
mv "$artifact" "output/$filename"
else
echo "Artifact needs to be zipped"
7z a -bd "output/$filename" -r "$artifact"
fi
b2 file upload "$bucket" "output/$filename" "nightly/$filename"
+1 -1
View File
@@ -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
View File
@@ -1,5 +1,6 @@
//+build !freestanding
//+build !js
//+build !orca
package fmt
import "base:runtime"
+746
View File
@@ -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)
}
+4
View File
@@ -0,0 +1,4 @@
//+build js
package core_image_bmp
load :: proc{load_from_bytes, load_from_context}
+34
View File
@@ -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
}
+126
View File
@@ -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
*/
+1 -1
View File
@@ -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
View File
@@ -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,
+3 -3
View File
@@ -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,
+7 -7
View File
@@ -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,
+1 -1
View File
@@ -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,
+3 -3
View File
@@ -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,
+3 -3
View File
@@ -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,
+5 -1
View File
@@ -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 != "" {
+1 -1
View File
@@ -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)
+6 -2
View File
@@ -5,7 +5,6 @@ foreign import libc "system:c"
import "base:runtime"
import "core:strings"
import "core:sys/unix"
import "core:c"
Handle :: distinct i32
@@ -328,6 +327,11 @@ foreign dl {
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
}
@(private)
foreign libc {
_lwp_self :: proc() -> i32 ---
}
// NOTE(phix): Perhaps share the following functions with FreeBSD if they turn out to be the same in the end.
is_path_separator :: proc(r: rune) -> bool {
@@ -721,7 +725,7 @@ exit :: proc "contextless" (code: int) -> ! {
}
current_thread_id :: proc "contextless" () -> int {
return cast(int) unix.pthread_self()
return int(_lwp_self())
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
+1 -1
View File
@@ -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 {
+10 -7
View File
@@ -882,13 +882,16 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
s = s[1:]
fallthrough
case 'i', 'I':
n = common_prefix_len_ignore_case(s, "infinity")
if 3 < n && n < 8 { // "inf" or "infinity"
n = 3
}
if n == 3 || n == 8 {
m := common_prefix_len_ignore_case(s, "infinity")
if 3 <= m && m < 9 { // "inf" to "infinity"
f = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
n = nsign + 3
if m == 8 {
// We only count the entire prefix if it is precisely "infinity".
n = nsign + m
} else {
// The string was either only "inf" or incomplete.
n = nsign + 3
}
ok = true
return
}
@@ -1092,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,
+1 -1
View File
@@ -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
+6 -2
View File
@@ -1,8 +1,12 @@
//+private
package sync
import "core:sys/unix"
foreign import libc "system:c"
foreign libc {
_lwp_self :: proc "c" () -> i32 ---
}
_current_thread_id :: proc "contextless" () -> int {
return cast(int) unix.pthread_self()
return int(_lwp_self())
}
+1
View File
@@ -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)
+3
View File
@@ -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\".")
}
}
}
+12 -11
View File
@@ -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,
+1
View File
@@ -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,
+24
View File
@@ -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" () {
}
+10
View File
@@ -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,
+1
View File
@@ -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}}},
};
+28 -8
View File
@@ -1039,6 +1039,13 @@ gb_global TargetMetrics target_netbsd_amd64 = {
str_lit("x86_64-unknown-netbsd-elf"),
};
gb_global TargetMetrics target_netbsd_arm64 = {
TargetOs_netbsd,
TargetArch_arm64,
8, 8, 16, 16,
str_lit("aarch64-unknown-netbsd-elf"),
};
gb_global TargetMetrics target_haiku_amd64 = {
TargetOs_haiku,
TargetArch_amd64,
@@ -1081,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"),
};
@@ -1154,13 +1160,16 @@ gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
{ str_lit("freebsd_arm64"), &target_freebsd_arm64 },
{ str_lit("openbsd_amd64"), &target_openbsd_amd64 },
{ str_lit("netbsd_amd64"), &target_netbsd_amd64 },
{ str_lit("netbsd_arm64"), &target_netbsd_arm64 },
{ str_lit("openbsd_amd64"), &target_openbsd_amd64 },
{ str_lit("haiku_amd64"), &target_haiku_amd64 },
{ 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 },
@@ -1916,7 +1925,11 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
#elif defined(GB_SYSTEM_OPENBSD)
metrics = &target_openbsd_amd64;
#elif defined(GB_SYSTEM_NETBSD)
metrics = &target_netbsd_amd64;
#if defined(GB_CPU_ARM)
metrics = &target_netbsd_arm64;
#else
metrics = &target_netbsd_amd64;
#endif
#elif defined(GB_SYSTEM_HAIKU)
metrics = &target_haiku_amd64;
#elif defined(GB_CPU_ARM)
@@ -2022,6 +2035,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 ");
@@ -2032,16 +2048,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
View File
@@ -2419,6 +2419,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) {
@@ -5912,15 +5915,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));
}
@@ -5952,20 +5949,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));
+6
View File
@@ -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");
}
+29 -11
View File
@@ -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, ", ");
@@ -8342,7 +8353,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" ||
@@ -8866,6 +8877,10 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
case Type_Array:
ft = bt->Array.elem;
break;
case Type_BitField:
is_constant = false;
ft = bt->BitField.fields[index]->type;
break;
default:
GB_PANIC("invalid type: %s", type_to_string(ft));
break;
@@ -8892,6 +8907,9 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slice<As
case Type_Array:
nested_ft = bt->Array.elem;
break;
case Type_BitField:
nested_ft = bt->BitField.fields[index]->type;
break;
default:
GB_PANIC("invalid type %s", type_to_string(nested_ft));
break;
+14 -2
View File
@@ -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
View File
@@ -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];
+6
View File
@@ -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) {
+1
View File
@@ -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;
+1
View File
@@ -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
View File
@@ -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;
}
}
+3
View File
@@ -256,6 +256,7 @@ extern "C" {
#if defined(GB_SYSTEM_NETBSD)
#include <stdio.h>
#include <lwp.h>
#define lseek64 lseek
#endif
@@ -3027,6 +3028,8 @@ gb_inline u32 gb_thread_current_id(void) {
thread_id = find_thread(NULL);
#elif defined(GB_SYSTEM_FREEBSD)
thread_id = pthread_getthreadid_np();
#elif defined(GB_SYSTEM_NETBSD)
thread_id = (u32)_lwp_self();
#else
#error Unsupported architecture for gb_thread_current_id()
#endif
+29 -15
View File
@@ -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
View File
@@ -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
View File
@@ -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);
+51 -4
View File
@@ -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;
}
@@ -4533,10 +4564,26 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
if (lb_is_nested_possibly_constant(type, sel, elem)) {
continue;
}
lbValue dst = lb_emit_deep_field_gep(p, comp_lit_ptr, sel);
field_expr = lb_build_expr(p, elem);
field_expr = lb_emit_conv(p, field_expr, sel.entity->type);
lb_emit_store(p, dst, field_expr);
if (sel.is_bit_field) {
Selection sub_sel = trim_selection(sel);
lbValue trimmed_dst = lb_emit_deep_field_gep(p, comp_lit_ptr, sub_sel);
Type *bf = base_type(type_deref(trimmed_dst.type));
if (is_type_pointer(bf)) {
trimmed_dst = lb_emit_load(p, trimmed_dst);
bf = base_type(type_deref(trimmed_dst.type));
}
GB_ASSERT(bf->kind == Type_BitField);
isize idx = sel.index[sel.index.count-1];
lbAddr dst = lb_addr_bit_field(trimmed_dst, bf->BitField.fields[idx]->type, bf->BitField.bit_offsets[idx], bf->BitField.bit_sizes[idx]);
lb_addr_store(p, dst, field_expr);
} else {
lbValue dst = lb_emit_deep_field_gep(p, comp_lit_ptr, sel);
lb_emit_store(p, dst, field_expr);
}
continue;
}
+2 -2
View File
@@ -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 {
+2 -3
View File
@@ -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) {
+3 -1
View File
@@ -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);
+32
View File
@@ -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();
+2
View File
@@ -494,6 +494,8 @@ gb_internal u32 thread_current_id(void) {
thread_id = find_thread(NULL);
#elif defined(GB_SYSTEM_FREEBSD)
thread_id = pthread_getthreadid_np();
#elif defined(GB_SYSTEM_NETBSD)
thread_id = (u32)_lwp_self();
#else
#error Unsupported architecture for thread_current_id()
#endif
+17 -2
View File
@@ -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;
@@ -457,6 +459,15 @@ gb_internal Selection sub_selection(Selection const &sel, isize offset) {
return res;
}
gb_internal Selection trim_selection(Selection const &sel) {
Selection res = {};
res.index.data = sel.index.data;
res.index.count = gb_max(sel.index.count - 1, 0);
res.index.capacity = res.index.count;
return res;
}
gb_global Type basic_types[] = {
{Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}},
@@ -1084,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;
}
@@ -1482,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;
@@ -2127,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;
}
-14
View File
@@ -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_*
+4
View File
@@ -0,0 +1,4 @@
package benchmarks
@(require) import "crypto"
@(require) import "hash"
-13
View File
@@ -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
View File
@@ -1,3 +1,4 @@
*.bmp
*.zip
*.png
math_big_test_library.*
-115
View File
@@ -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 \
mem_test \
noise_test \
odin_test \
os_exit_test \
reflect_test \
runtime_test \
slice_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
mem_test:
$(ODIN) test mem $(COMMON) -out:test_core_mem
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
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_*
-124
View File
@@ -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:mem tests
echo ---
%PATH_TO_ODIN% test mem %COMMON% -out:test_core_mem.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: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
+91 -2
View File
@@ -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
+40 -3
View File
@@ -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)
}
-2
View File
@@ -1,2 +0,0 @@
@echo off
odin test . -define:ODIN_TEST_TRACK_MEMORY=true -define:ODIN_TEST_PROGRESS_WIDTH=12 -vet -strict-style
+597 -72
View File
@@ -1,11 +1,11 @@
/*
Copyright 2021 Jeroen van Rijn <nom@duclavier.com>.
Copyright 2021-2024 Jeroen van Rijn <nom@duclavier.com>.
Made available under Odin's BSD-3 license.
List of contributors:
Jeroen van Rijn: Initial implementation.
A test suite for PNG + QOI.
A test suite for PNG, TGA, NetPBM, QOI and BMP.
*/
package test_core_image
@@ -13,6 +13,7 @@ import "core:testing"
import "core:compress"
import "core:image"
import "core:image/bmp"
import pbm "core:image/netpbm"
import "core:image/png"
import "core:image/qoi"
@@ -24,16 +25,17 @@ import "core:strings"
import "core:mem"
import "core:time"
TEST_SUITE_PATH :: ODIN_ROOT + "tests/core/assets/PNG/"
TEST_SUITE_PATH_PNG :: ODIN_ROOT + "tests/core/assets/PNG"
TEST_SUITE_PATH_BMP :: ODIN_ROOT + "tests/core/assets/BMP"
I_Error :: image.Error
PNG_Test :: struct {
Test :: struct {
file: string,
tests: []struct {
options: image.Options,
expected_error: image.Error,
dims: PNG_Dims,
dims: Dims,
hash: u32,
},
}
@@ -46,19 +48,18 @@ Blend_BG_Keep :: image.Options{.blend_background, .alpha_add_if_missing}
Return_Metadata :: image.Options{.return_metadata}
No_Channel_Expansion :: image.Options{.do_not_expand_channels, .return_metadata}
PNG_Dims :: struct {
Dims :: struct {
width: int,
height: int,
channels: int,
depth: int,
}
Basic_PNG_Tests := []PNG_Test{
Basic_PNG_Tests := []Test{
/*
Basic format tests:
http://www.schaik.com/pngsuite/pngsuite_bas_png.html
*/
{
"basn0g01", // Black and white.
{
@@ -166,7 +167,7 @@ Basic_PNG_Tests := []PNG_Test{
},
}
Interlaced_PNG_Tests := []PNG_Test{
Interlaced_PNG_Tests := []Test{
/*
Interlaced format tests:
http://www.schaik.com/pngsuite/pngsuite_int_png.html
@@ -284,9 +285,9 @@ Interlaced_PNG_Tests := []PNG_Test{
},
}
Odd_Sized_PNG_Tests := []PNG_Test{
Odd_Sized_PNG_Tests := []Test{
/*
" PngSuite", // Odd sizes / PNG-files:
"PngSuite", // Odd sizes / PNG-files:
http://www.schaik.com/pngsuite/pngsuite_siz_png.html
This tests curious sizes with and without interlacing.
@@ -510,7 +511,7 @@ Odd_Sized_PNG_Tests := []PNG_Test{
},
}
PNG_bKGD_Tests := []PNG_Test{
PNG_bKGD_Tests := []Test{
/*
" PngSuite", // Background colors / PNG-files:
http://www.schaik.com/pngsuite/pngsuite_bck_png.html
@@ -597,7 +598,7 @@ PNG_bKGD_Tests := []PNG_Test{
},
}
PNG_tRNS_Tests := []PNG_Test{
PNG_tRNS_Tests := []Test{
/*
PngSuite - Transparency:
http://www.schaik.com/pngsuite/pngsuite_trn_png.html
@@ -759,7 +760,7 @@ PNG_tRNS_Tests := []PNG_Test{
},
}
PNG_Filter_Tests := []PNG_Test{
PNG_Filter_Tests := []Test{
/*
PngSuite - Image filtering:
@@ -838,7 +839,7 @@ PNG_Filter_Tests := []PNG_Test{
},
}
PNG_Varied_IDAT_Tests := []PNG_Test{
PNG_Varied_IDAT_Tests := []Test{
/*
PngSuite - Chunk ordering:
@@ -897,7 +898,7 @@ PNG_Varied_IDAT_Tests := []PNG_Test{
},
}
PNG_ZLIB_Levels_Tests := []PNG_Test{
PNG_ZLIB_Levels_Tests := []Test{
/*
PngSuite - Zlib compression:
@@ -938,7 +939,7 @@ PNG_ZLIB_Levels_Tests := []PNG_Test{
},
}
PNG_sPAL_Tests := []PNG_Test{
PNG_sPAL_Tests := []Test{
/*
PngSuite - Additional palettes:
@@ -985,7 +986,7 @@ PNG_sPAL_Tests := []PNG_Test{
},
}
PNG_Ancillary_Tests := []PNG_Test{
PNG_Ancillary_Tests := []Test{
/*
PngSuite" - Ancillary chunks:
@@ -1153,7 +1154,7 @@ PNG_Ancillary_Tests := []PNG_Test{
}
Corrupt_PNG_Tests := []PNG_Test{
Corrupt_PNG_Tests := []Test{
/*
PngSuite - Corrupted files / PNG-files:
@@ -1249,7 +1250,7 @@ Corrupt_PNG_Tests := []PNG_Test{
}
No_Postprocesing_Tests := []PNG_Test{
No_Postprocesing_Tests := []Test{
/*
These are some custom tests where we skip expanding to RGB(A).
*/
@@ -1273,8 +1274,6 @@ No_Postprocesing_Tests := []PNG_Test{
},
}
Text_Title :: "PngSuite"
Text_Software :: "Created on a NeXTstation color using \"pnmtopng\"."
Text_Descrption :: "A compilation of a set of images created to test the\nvarious color-types of the PNG format. Included are\nblack&white, color, paletted, with alpha channel, with\ntransparency formats. All bit-depths allowed according\nto the spec are present."
@@ -1453,9 +1452,9 @@ png_test_no_postproc :: proc(t: ^testing.T) {
run_png_suite(t, No_Postprocesing_Tests)
}
run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
run_png_suite :: proc(t: ^testing.T, suite: []Test) {
for file in suite {
test_file := strings.concatenate({TEST_SUITE_PATH, file.file, ".png"})
test_file := strings.concatenate({TEST_SUITE_PATH_PNG, "/", file.file, ".png"}, context.allocator)
defer delete(test_file)
img: ^png.Image
@@ -1468,20 +1467,18 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
img, err = png.load(test_file, test.options)
passed := (test.expected_error == nil && err == nil) || (test.expected_error == err)
testing.expectf(t, passed, "%v failed with %v.", test_file, err)
testing.expectf(t, passed, "%q failed to load with error %v.", file.file, err)
if err == nil { // No point in running the other tests if it didn't load.
pixels := bytes.buffer_to_bytes(&img.pixels)
// This struct compare fails at -opt:2 if PNG_Dims is not #packed.
dims := PNG_Dims{img.width, img.height, img.channels, img.depth}
dims := Dims{img.width, img.height, img.channels, img.depth}
dims_pass := test.dims == dims
testing.expectf(t, dims_pass, "%v has %v, expected: %v.", file.file, dims, test.dims)
testing.expectf(t, dims_pass, "%v has %v, expected: %v", file.file, dims, test.dims)
passed &= dims_pass
png_hash := hash.crc32(pixels)
testing.expectf(t, test.hash == png_hash, "%v test %v hash is %08x, expected %08x with %v.", file.file, count, png_hash, test.hash, test.options)
testing.expectf(t, test.hash == png_hash, "%v test %v hash is %08x, expected %08x with %v", file.file, count, png_hash, test.hash, test.options)
passed &= test.hash == png_hash
@@ -1492,16 +1489,16 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
defer bytes.buffer_destroy(&qoi_buffer)
qoi_save_err := qoi.save(&qoi_buffer, img)
testing.expectf(t, qoi_save_err == nil, "%v test %v QOI save failed with %v.", file.file, count, qoi_save_err)
testing.expectf(t, qoi_save_err == nil, "%v test %v QOI save failed with %v", file.file, count, qoi_save_err)
if qoi_save_err == nil {
qoi_img, qoi_load_err := qoi.load(qoi_buffer.buf[:])
defer qoi.destroy(qoi_img)
testing.expectf(t, qoi_load_err == nil, "%v test %v QOI load failed with %v.", file.file, count, qoi_load_err)
testing.expectf(t, qoi_load_err == nil, "%v test %v QOI load failed with %v", file.file, count, qoi_load_err)
qoi_hash := hash.crc32(qoi_img.pixels.buf[:])
testing.expectf(t, qoi_hash == png_hash, "%v test %v QOI load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, qoi_hash, png_hash, test.options)
testing.expectf(t, qoi_hash == png_hash, "%v test %v QOI load hash is %08x, expected it match PNG's %08x with %v", file.file, count, qoi_hash, png_hash, test.options)
}
}
@@ -1511,15 +1508,15 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
defer bytes.buffer_destroy(&tga_buffer)
tga_save_err := tga.save(&tga_buffer, img)
testing.expectf(t, tga_save_err == nil, "%v test %v TGA save failed with %v.", file.file, count, tga_save_err)
testing.expectf(t, tga_save_err == nil, "%v test %v TGA save failed with %v", file.file, count, tga_save_err)
if tga_save_err == nil {
tga_img, tga_load_err := tga.load(tga_buffer.buf[:])
defer tga.destroy(tga_img)
testing.expectf(t, tga_load_err == nil, "%v test %v TGA load failed with %v.", file.file, count, tga_load_err)
testing.expectf(t, tga_load_err == nil, "%v test %v TGA load failed with %v", file.file, count, tga_load_err)
tga_hash := hash.crc32(tga_img.pixels.buf[:])
testing.expectf(t, tga_hash == png_hash, "%v test %v TGA load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, tga_hash, png_hash, test.options)
testing.expectf(t, tga_hash == png_hash, "%v test %v TGA load hash is %08x, expected it match PNG's %08x with %v", file.file, count, tga_hash, png_hash, test.options)
}
}
@@ -1528,18 +1525,18 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
pbm_buf, pbm_save_err := pbm.save_to_buffer(img)
defer delete(pbm_buf)
testing.expectf(t, pbm_save_err == nil, "%v test %v PBM save failed with %v.", file.file, count, pbm_save_err)
testing.expectf(t, pbm_save_err == nil, "%v test %v PBM save failed with %v", file.file, count, pbm_save_err)
if pbm_save_err == nil {
// Try to load it again.
pbm_img, pbm_load_err := pbm.load(pbm_buf)
defer pbm.destroy(pbm_img)
testing.expectf(t, pbm_load_err == nil, "%v test %v PBM load failed with %v.", file.file, count, pbm_load_err)
testing.expectf(t, pbm_load_err == nil, "%v test %v PBM load failed with %v", file.file, count, pbm_load_err)
if pbm_load_err == nil {
pbm_hash := hash.crc32(pbm_img.pixels.buf[:])
testing.expectf(t, pbm_hash == png_hash, "%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, pbm_hash, png_hash, test.options)
testing.expectf(t, pbm_hash == png_hash, "%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v", file.file, count, pbm_hash, png_hash, test.options)
}
}
}
@@ -1553,18 +1550,18 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
pbm_buf, pbm_save_err := pbm.save_to_buffer(img, pbm_info)
defer delete(pbm_buf)
testing.expectf(t, pbm_save_err == nil, "%v test %v PBM save failed with %v.", file.file, count, pbm_save_err)
testing.expectf(t, pbm_save_err == nil, "%v test %v PBM save failed with %v", file.file, count, pbm_save_err)
if pbm_save_err == nil {
// Try to load it again.
pbm_img, pbm_load_err := pbm.load(pbm_buf)
defer pbm.destroy(pbm_img)
testing.expectf(t, pbm_load_err == nil, "%v test %v PBM load failed with %v.", file.file, count, pbm_load_err)
testing.expectf(t, pbm_load_err == nil, "%v test %v PBM load failed with %v", file.file, count, pbm_load_err)
if pbm_load_err == nil {
pbm_hash := hash.crc32(pbm_img.pixels.buf[:])
testing.expectf(t, pbm_hash == png_hash, "%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v.", file.file, count, pbm_hash, png_hash, test.options)
testing.expectf(t, pbm_hash == png_hash, "%v test %v PBM load hash is %08x, expected it match PNG's %08x with %v", file.file, count, pbm_hash, png_hash, test.options)
}
}
}
@@ -1653,7 +1650,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
case "pp0n2c16", "pp0n6a08":
gamma, gamma_ok := png.gamma(c)
expected_gamma := f32(1.0)
testing.expectf(t, gamma == expected_gamma && gamma_ok, "%v test %v gAMA is %v, expected %v.", file.file, count, gamma, expected_gamma)
testing.expectf(t, gamma == expected_gamma && gamma_ok, "%v test %v gAMA is %v, expected %v", file.file, count, gamma, expected_gamma)
}
case .PLTE:
switch(file.file) {
@@ -1661,7 +1658,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
plte, plte_ok := png.plte(c)
expected_plte_len := u16(216)
testing.expectf(t, expected_plte_len == plte.used && plte_ok, "%v test %v PLTE length is %v, expected %v.", file.file, count, plte.used, expected_plte_len)
testing.expectf(t, expected_plte_len == plte.used && plte_ok, "%v test %v PLTE length is %v, expected %v", file.file, count, plte.used, expected_plte_len)
}
case .sPLT:
switch(file.file) {
@@ -1669,10 +1666,10 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
splt, splt_ok := png.splt(c)
expected_splt_len := u16(216)
testing.expectf(t, expected_splt_len == splt.used && splt_ok, "%v test %v sPLT length is %v, expected %v.", file.file, count, splt.used, expected_splt_len)
testing.expectf(t, expected_splt_len == splt.used && splt_ok, "%v test %v sPLT length is %v, expected %v", file.file, count, splt.used, expected_splt_len)
expected_splt_name := "six-cube"
testing.expectf(t, expected_splt_name == splt.name && splt_ok, "%v test %v sPLT name is %v, expected %v.", file.file, count, splt.name, expected_splt_name)
testing.expectf(t, expected_splt_name == splt.name && splt_ok, "%v test %v sPLT name is %v, expected %v", file.file, count, splt.name, expected_splt_name)
png.splt_destroy(splt)
}
@@ -1686,31 +1683,31 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
g = png.CIE_1931{x = 0.3000, y = 0.6000},
b = png.CIE_1931{x = 0.1500, y = 0.0600},
}
testing.expectf(t, expected_chrm == chrm && chrm_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, chrm, expected_chrm)
testing.expectf(t, expected_chrm == chrm && chrm_ok, "%v test %v cHRM is %v, expected %v", file.file, count, chrm, expected_chrm)
}
case .pHYs:
phys, phys_ok := png.phys(c)
switch (file.file) {
case "cdfn2c08":
expected_phys := png.pHYs{ppu_x = 1, ppu_y = 4, unit = .Unknown}
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys)
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v", file.file, count, phys, expected_phys)
case "cdhn2c08":
expected_phys := png.pHYs{ppu_x = 4, ppu_y = 1, unit = .Unknown}
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys)
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v", file.file, count, phys, expected_phys)
case "cdsn2c08":
expected_phys := png.pHYs{ppu_x = 1, ppu_y = 1, unit = .Unknown}
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys)
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v", file.file, count, phys, expected_phys)
case "cdun2c08":
expected_phys := png.pHYs{ppu_x = 1000, ppu_y = 1000, unit = .Meter}
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v.", file.file, count, phys, expected_phys)
testing.expectf(t, expected_phys == phys && phys_ok, "%v test %v cHRM is %v, expected %v", file.file, count, phys, expected_phys)
}
case .hIST:
hist, hist_ok := png.hist(c)
switch (file.file) {
case "ch1n3p04":
testing.expectf(t, hist.used == 15 && hist_ok, "%v test %v hIST has %v entries, expected %v.", file.file, count, hist.used, 15)
testing.expectf(t, hist.used == 15 && hist_ok, "%v test %v hIST has %v entries, expected %v", file.file, count, hist.used, 15)
case "ch2n3p08":
testing.expectf(t, hist.used == 256 && hist_ok, "%v test %v hIST has %v entries, expected %v.", file.file, count, hist.used, 256)
testing.expectf(t, hist.used == 256 && hist_ok, "%v test %v hIST has %v entries, expected %v", file.file, count, hist.used, 256)
}
case .tIME:
png_time, png_time_ok := png.time(c)
@@ -1731,8 +1728,8 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
expected_core = time.Time{_nsec = 946684799000000000}
}
testing.expectf(t, png_time == expected_time && png_time_ok, "%v test %v tIME was %v, expected %v.", file.file, count, png_time, expected_time)
testing.expectf(t, core_time == expected_core && core_time_ok, "%v test %v tIME->core:time is %v, expected %v.", file.file, count, core_time, expected_core)
testing.expectf(t, png_time == expected_time && png_time_ok, "%v test %v tIME was %v, expected %v", file.file, count, png_time, expected_time)
testing.expectf(t, core_time == expected_core && core_time_ok, "%v test %v tIME->core:time is %v, expected %v", file.file, count, core_time, expected_core)
case .sBIT:
sbit, sbit_ok := png.sbit(c)
expected_sbit: [4]u8
@@ -1753,7 +1750,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
case "cdfn2c08", "cdhn2c08", "cdsn2c08", "cdun2c08", "ch1n3p04", "basn3p04":
expected_sbit = [4]u8{ 4, 4, 4, 0}
}
testing.expectf(t, sbit == expected_sbit && sbit_ok, "%v test %v sBIT was %v, expected %v.", file.file, count, sbit, expected_sbit)
testing.expectf(t, sbit == expected_sbit && sbit_ok, "%v test %v sBIT was %v, expected %v", file.file, count, sbit, expected_sbit)
case .tEXt, .zTXt:
text, text_ok := png.text(c)
defer png.text_destroy(text)
@@ -1765,7 +1762,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] {
test_text := Expected_Text[file.file][text.keyword].text
testing.expectf(t, text.text == test_text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text.text, test_text)
testing.expectf(t, text.text == test_text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text.text, test_text)
}
}
}
@@ -1778,44 +1775,44 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword]
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
}
}
case "ctfn0g04": // international UTF-8, finnish
if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword]
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
}
}
case "ctgn0g04": // international UTF-8, greek
if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword]
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
}
}
case "cthn0g04": // international UTF-8, hindi
if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword]
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
}
}
case "ctjn0g04": // international UTF-8, japanese
if file.file in Expected_Text {
if text.keyword in Expected_Text[file.file] {
test := Expected_Text[file.file][text.keyword]
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'.", file.file, count, text.keyword, text, test)
testing.expectf(t, text.text == test.text && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.language == test.language && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
testing.expectf(t, text.keyword_localized == test.keyword_localized && text_ok, "%v test %v text keyword {{%v}}:'%v', expected '%v'", file.file, count, text.keyword, text, test)
}
}
}
@@ -1823,7 +1820,7 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
if file.file == "exif2c08" { // chunk with jpeg exif data
exif, exif_ok := png.exif(c)
testing.expectf(t, exif.byte_order == .big_endian && exif_ok, "%v test %v eXIf byte order '%v', expected 'big_endian'.", file.file, count, exif.byte_order)
testing.expectf(t, len(exif.data) == 978 && exif_ok, "%v test %v eXIf data length '%v', expected '%v'.", file.file, len(exif.data), 978)
testing.expectf(t, len(exif.data) == 978 && exif_ok, "%v test %v eXIf data length '%v', expected '%v'", file.file, len(exif.data), 978)
}
}
}
@@ -1833,4 +1830,532 @@ run_png_suite :: proc(t: ^testing.T, suite: []PNG_Test) {
png.destroy(img)
}
}
return
}
/*
Basic format tests:
https://entropymine.com/jason/bmpsuite/bmpsuite/html/bmpsuite.html - Version 2.8; 2023-11-28
The BMP Suite image generator itself is GPL, and isn't included, nor did it have its code referenced.
We do thank the author for the well-researched test suite, which we are free to include:
"Image files generated by this program are not covered by this license, and are
in the public domain (except for the embedded ICC profiles)."
The files with embedded ICC profiles aren't part of Odin's test assets. We don't support BMP metadata.
We don't support all "possibly correct" images, and thus only ship a subset of these from the BMP Suite.
*/
Basic_BMP_Tests := []Test{
{
"pal1", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"pal1wb", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"pal1bg", {
{Default, nil, {127, 64, 3, 8}, 0x_9e91_174a},
},
},
{
"pal4", {
{Default, nil, {127, 64, 3, 8}, 0x_288e_4371},
},
},
{
"pal4gs", {
{Default, nil, {127, 64, 3, 8}, 0x_452d_a01a},
},
},
{
"pal4rle", {
{Default, nil, {127, 64, 3, 8}, 0x_288e_4371},
},
},
{
"pal8", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8-0", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8gs", {
{Default, nil, {127, 64, 3, 8}, 0x_09c2_7834},
},
},
{
"pal8rle", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8w126", {
{Default, nil, {126, 63, 3, 8}, 0x_bb66_4cda},
},
},
{
"pal8w125", {
{Default, nil, {125, 62, 3, 8}, 0x_3ab8_f7c5},
},
},
{
"pal8w124", {
{Default, nil, {124, 61, 3, 8}, 0x_b53e_e6c8},
},
},
{
"pal8topdown", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8nonsquare", {
{Default, nil, {127, 32, 3, 8}, 0x_8409_c689},
},
},
{
"pal8v4", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8v5", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"rgb16", {
{Default, nil, {127, 64, 3, 8}, 0x_8b6f_81a2},
},
},
{
"rgb16bfdef", {
{Default, nil, {127, 64, 3, 8}, 0x_8b6f_81a2},
},
},
{
"rgb16-565", {
{Default, nil, {127, 64, 3, 8}, 0x_8c73_a2ff},
},
},
{
"rgb16-565pal", {
{Default, nil, {127, 64, 3, 8}, 0x_8c73_a2ff},
},
},
{
"rgb24", {
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgb24pal", {
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgb32", {
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgb32bf", {
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgb32bfdef", {
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
}
OS2_Tests := []Test{
{
"pal8os2", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2-sz", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2-hs", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2sp", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2v2", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2v2-16", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2v2-sz", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8os2v2-40sz", { // An OS/2-style bitmap. This format can be called OS/2 BMPv1, or Windows BMPv2.
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
}
// BMP files that aren't 100% to spec. Some we support, some we don't.
Questionable_BMP_Tests := []Test{
{
"pal1p1", { // Spec says 1-bit image has 2 palette entries. This one has 1.
{Default, nil, {127, 64, 3, 8}, 0x_2b54_2560},
},
},
{
"pal2", { // 2-bit. Allowed on Windows CE. Irfanview doesn't support it.
{Default, nil, {127, 64, 3, 8}, 0x_0da2_7594},
},
},
{
"pal2color", { // 2-bit, with color palette.
{Default, nil, {127, 64, 3, 8}, 0x_f0d8_c5d6},
},
},
{
"pal8offs", { // 300 palette entries (yes, only 256 can be used)
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal8oversizepal", { // Some padding between palette and image data
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"pal4rletrns", { // Using palette tricks to skip pixels
{Default, nil, {127, 64, 3, 8}, 0x_eed4_e744},
},
},
{
"pal4rlecut", { // Using palette tricks to skip pixels
{Default, nil, {127, 64, 3, 8}, 0x_473fbc7d},
},
},
{
"pal8rletrns", { // Using palette tricks to skip pixels
{Default, nil, {127, 64, 3, 8}, 0x_fe1f_e560},
},
},
{
"pal8rlecut", { // Using palette tricks to skip pixels
{Default, nil, {127, 64, 3, 8}, 0x_bd04_3619},
},
},
{
"rgb16faketrns", { // Using palette tricks to skip pixels
{Default, nil, {127, 64, 3, 8}, 0x_8b6f_81a2},
},
},
{
"rgb16-231", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_7393_a163},
},
},
{
"rgb16-3103", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_3b66_2189},
},
},
{
"rgba16-4444", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_b785_1f9f},
},
},
{
"rgba16-5551", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_8b6f_81a2},
},
},
{
"rgba16-1924", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_f038_2bed},
},
},
{
"rgb32-xbgr", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgb32fakealpha", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgb32-111110", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_b2c7_a8ff},
},
},
{
"rgb32-7187", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_b93a_4291},
},
},
{
"rgba32-1", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_7b67_823d},
},
},
{
"rgba32-2", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_7b67_823d},
},
},
{
"rgba32-1010102", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_aa42_0b16},
},
},
{
"rgba32-81284", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_28a2_4c16},
},
},
{
"rgba32-61754", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_4aae_26ed},
},
},
{
"rgba32abf", { // Custom bit fields
{Default, nil, {127, 64, 3, 8}, 0x_7b67_823d},
},
},
{
"rgb32h52", { // Truncated header (RGB bit fields included)
{Default, nil, {127, 64, 3, 8}, 0x_025b_ba0a},
},
},
{
"rgba32h56", { // Truncated header (RGBA bit fields included)
{Default, nil, {127, 64, 3, 8}, 0x_7b67_823d},
},
},
}
// Unsupported BMP features, or malformed images.
Unsupported_BMP_Tests := []Test{
{
"ba-bm", { // An OS/2 Bitmap array. We don't support this BA format.
{Default, .Unsupported_OS2_File, {127, 32, 3, 8}, 0x_0000_0000},
},
},
{
"pal1huffmsb", { // An OS/2 file with Huffman 1D compression
{Default, .Unsupported_Compression, {127, 32, 3, 8}, 0x_0000_0000},
},
},
{
"rgb24rle24", { // An OS/2 file with RLE24 compression
{Default, .Unsupported_Compression, {127, 64, 3, 8}, 0x_0000_0000},
},
},
{
"rgba64", { // An OS/2 file with RLE24 compression
{Default, .Unsupported_BPP, {127, 64, 3, 8}, 0x_0000_0000},
},
},
}
// Malformed / malicious files
Known_Bad_BMP_Tests := []Test{
{
"badbitcount", {
{Default, .Unsupported_BPP, {127, 64, 3, 8}, 0x_3ce81fae},
},
},
{
"badbitssize", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"baddens1", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"baddens2", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"badfilesize", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"badheadersize", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"badpalettesize", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"badplanes", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"badrle", {
{Default, nil, {127, 64, 3, 8}, 0x_1457_aae4},
},
},
{
"badrle4", {
{Default, nil, {127, 64, 3, 8}, 0x_6764_d2ac},
},
},
{
"badrle4bis", {
{Default, nil, {127, 64, 3, 8}, 0x_935d_bb37},
},
},
{
"badrle4ter", {
{Default, nil, {127, 64, 3, 8}, 0x_f2ba_5b08},
},
},
{
"badrlebis", {
{Default, nil, {127, 64, 3, 8}, 0x_07e2_d730},
},
},
{
"badrleter", {
{Default, nil, {127, 64, 3, 8}, 0x_a874_2742},
},
},
{
"badwidth", {
{Default, nil, {127, 64, 3, 8}, 0x_3ce8_1fae},
},
},
{
"pal8badindex", {
{Default, nil, {127, 64, 3, 8}, 0x_0450_0d02},
},
},
{
"reallybig", {
{Default, .Image_Dimensions_Too_Large, {3000000, 2000000, 1, 24}, 0x_0000_0000},
},
},
{
"rgb16-880", {
{Default, nil, {127, 64, 3, 8}, 0x_f1c2_0c73},
},
},
{
"rletopdown", {
{Default, nil, {127, 64, 3, 8}, 0x_3845_4155},
},
},
{
"shortfile", {
{Default, .Short_Buffer, {127, 64, 1, 1}, 0x_0000_0000},
},
},
}
@test
bmp_test_basic :: proc(t: ^testing.T) {
run_bmp_suite(t, Basic_BMP_Tests)
}
@test
bmp_test_os2 :: proc(t: ^testing.T) {
run_bmp_suite(t, OS2_Tests)
}
@test
bmp_test_questionable :: proc(t: ^testing.T) {
run_bmp_suite(t, Questionable_BMP_Tests)
}
@test
bmp_test_unsupported :: proc(t: ^testing.T) {
run_bmp_suite(t, Unsupported_BMP_Tests)
}
@test
bmp_test_known_bad :: proc(t: ^testing.T) {
run_bmp_suite(t, Known_Bad_BMP_Tests)
}
run_bmp_suite :: proc(t: ^testing.T, suite: []Test) {
for file in suite {
test_file := strings.concatenate({TEST_SUITE_PATH_BMP, "/", file.file, ".bmp"}, context.allocator)
defer delete(test_file)
for test in file.tests {
img, err := bmp.load(test_file, test.options)
passed := (test.expected_error == nil && err == nil) || (test.expected_error == err)
testing.expectf(t, passed, "%q failed to load with error %v.", file.file, err)
if err == nil { // No point in running the other tests if it didn't load.
pixels := bytes.buffer_to_bytes(&img.pixels)
dims := Dims{img.width, img.height, img.channels, img.depth}
testing.expectf(t, test.dims == dims, "%v has %v, expected: %v.", file.file, dims, test.dims)
img_hash := hash.crc32(pixels)
testing.expectf(t, test.hash == img_hash, "%v test #1's hash is %08x, expected %08x with %v.", file.file, img_hash, test.hash, test.options)
// Save to BMP file in memory
buf: bytes.Buffer
save_err := bmp.save(&buf, img)
testing.expectf(t, save_err == nil, "expected saving to BMP in memory not to raise error, got %v", save_err)
// Reload BMP from memory
reload_img, reload_err := bmp.load(buf.buf[:])
testing.expectf(t, reload_err == nil, "expected reloading BMP from memory not to raise error, got %v", reload_err)
testing.expect(t, img.width == reload_img.width && img.height == reload_img.height, "expected saved BMP to have the same dimensions")
testing.expect(t, img.channels == reload_img.channels && img.depth == reload_img.depth, "expected saved BMP to have the same dimensions")
reload_pixels := bytes.buffer_to_bytes(&reload_img.pixels)
reload_hash := hash.crc32(reload_pixels)
testing.expectf(t, img_hash == reload_hash, "expected saved BMP to have the same pixel hash (%08x), got %08x", img_hash, reload_hash)
bytes.buffer_destroy(&buf)
bmp.destroy(reload_img)
}
bmp.destroy(img)
}
}
return
}
+2 -1
View File
@@ -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")
}
}
+40
View File
@@ -0,0 +1,40 @@
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 "image"
@(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"
-10
View File
@@ -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)
}
+5
View File
@@ -0,0 +1,5 @@
// Tests intended to be ran with optimizations on
package tests_core
@(require) import "crypto"
@(require) import "hash"
+145
View File
@@ -0,0 +1,145 @@
package test_core_strconv
import "core:math"
import "core:strconv"
import "core:testing"
@(test)
test_float :: proc(t: ^testing.T) {
n: int
f: f64
ok: bool
f, ok = strconv.parse_f64("1.2", &n)
testing.expect_value(t, f, 1.2)
testing.expect_value(t, n, 3)
testing.expect_value(t, ok, true)
f, ok = strconv.parse_f64("1.2a", &n)
testing.expect_value(t, f, 1.2)
testing.expect_value(t, n, 3)
testing.expect_value(t, ok, false)
f, ok = strconv.parse_f64("+", &n)
testing.expect_value(t, f, 0)
testing.expect_value(t, n, 0)
testing.expect_value(t, ok, false)
f, ok = strconv.parse_f64("-", &n)
testing.expect_value(t, f, 0)
testing.expect_value(t, n, 0)
testing.expect_value(t, ok, false)
}
@(test)
test_nan :: proc(t: ^testing.T) {
n: int
f: f64
ok: bool
f, ok = strconv.parse_f64("nan", &n)
testing.expect_value(t, math.classify(f), math.Float_Class.NaN)
testing.expect_value(t, n, 3)
testing.expect_value(t, ok, true)
f, ok = strconv.parse_f64("nAN", &n)
testing.expect_value(t, math.classify(f), math.Float_Class.NaN)
testing.expect_value(t, n, 3)
testing.expect_value(t, ok, true)
f, ok = strconv.parse_f64("Nani", &n)
testing.expect_value(t, math.classify(f), math.Float_Class.NaN)
testing.expect_value(t, n, 3)
testing.expect_value(t, ok, false)
}
@(test)
test_infinity :: proc(t: ^testing.T) {
pos_inf := math.inf_f64(+1)
neg_inf := math.inf_f64(-1)
n: int
s := "infinity"
for i in 0 ..< len(s) + 1 {
ss := s[:i]
f, ok := strconv.parse_f64(ss, &n)
if i >= 3 { // "inf" .. "infinity"
expected_n := 8 if i == 8 else 3
expected_ok := i == 3 || i == 8
testing.expect_value(t, f, pos_inf)
testing.expect_value(t, n, expected_n)
testing.expect_value(t, ok, expected_ok)
testing.expect_value(t, math.classify(f), math.Float_Class.Inf)
} else { // invalid substring
testing.expect_value(t, f, 0)
testing.expect_value(t, n, 0)
testing.expect_value(t, ok, false)
testing.expect_value(t, math.classify(f), math.Float_Class.Zero)
}
}
s = "+infinity"
for i in 0 ..< len(s) + 1 {
ss := s[:i]
f, ok := strconv.parse_f64(ss, &n)
if i >= 4 { // "+inf" .. "+infinity"
expected_n := 9 if i == 9 else 4
expected_ok := i == 4 || i == 9
testing.expect_value(t, f, pos_inf)
testing.expect_value(t, n, expected_n)
testing.expect_value(t, ok, expected_ok)
testing.expect_value(t, math.classify(f), math.Float_Class.Inf)
} else { // invalid substring
testing.expect_value(t, f, 0)
testing.expect_value(t, n, 0)
testing.expect_value(t, ok, false)
testing.expect_value(t, math.classify(f), math.Float_Class.Zero)
}
}
s = "-infinity"
for i in 0 ..< len(s) + 1 {
ss := s[:i]
f, ok := strconv.parse_f64(ss, &n)
if i >= 4 { // "-inf" .. "infinity"
expected_n := 9 if i == 9 else 4
expected_ok := i == 4 || i == 9
testing.expect_value(t, f, neg_inf)
testing.expect_value(t, n, expected_n)
testing.expect_value(t, ok, expected_ok)
testing.expect_value(t, math.classify(f), math.Float_Class.Neg_Inf)
} else { // invalid substring
testing.expect_value(t, f, 0)
testing.expect_value(t, n, 0)
testing.expect_value(t, ok, false)
testing.expect_value(t, math.classify(f), math.Float_Class.Zero)
}
}
// Make sure odd casing works.
batch := [?]string {"INFiniTY", "iNfInItY", "InFiNiTy"}
for ss in batch {
f, ok := strconv.parse_f64(ss, &n)
testing.expect_value(t, f, pos_inf)
testing.expect_value(t, n, 8)
testing.expect_value(t, ok, true)
testing.expect_value(t, math.classify(f), math.Float_Class.Inf)
}
// Explicitly check how trailing characters are handled.
s = "infinityyyy"
f, ok := strconv.parse_f64(s, &n)
testing.expect_value(t, f, pos_inf)
testing.expect_value(t, n, 8)
testing.expect_value(t, ok, false)
testing.expect_value(t, math.classify(f), math.Float_Class.Inf)
s = "inflippity"
f, ok = strconv.parse_f64(s, &n)
testing.expect_value(t, f, pos_inf)
testing.expect_value(t, n, 3)
testing.expect_value(t, ok, false)
testing.expect_value(t, math.classify(f), math.Float_Class.Inf)
}
-22
View File
@@ -1,22 +0,0 @@
ODIN=../../odin
COMMON=-file -vet -strict-style -o:minimal
all: asan_test 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)
asan_test:
$(ODIN) test test_asan.odin $(COMMON) -sanitize:address -debug
string_compare_test:
$(ODIN) test test_string_compare.odin $(COMMON)
-9
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
package test_128
package test_internal
import "core:testing"
+1 -1
View File
@@ -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"
+2 -2
View File
@@ -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
}
}
}
+2 -2
View File
@@ -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)
}
}
}
}
+2 -2
View File
@@ -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)
}
}
+2 -2
View File
@@ -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) {
}
}
}
}
}
-1
View File
@@ -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
+3 -5
View File
@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -eu
mkdir -p build
@@ -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
-62
View File
@@ -1,62 +0,0 @@
// Tests issue #2087 https://github.com/odin-lang/Odin/issues/2087
package test_issues
import "core:math"
import "core:strconv"
import "core:testing"
@(test)
test_parse_float :: proc(t: ^testing.T) {
{
f, ok := strconv.parse_f64("1.2")
testing.expect(t, ok && f == 1.2, "expected f64(1.2), fully consumed")
f, ok = strconv.parse_f64("1.2a")
testing.expect(t, !ok && f == 1.2, "expected f64(1.2), partially consumed")
f, ok = strconv.parse_f64("+")
testing.expect(t, !ok && f == 0.0, "expected f64(0.0), with ok=false")
f, ok = strconv.parse_f64("-")
testing.expect(t, !ok && f == 0.0, "expected f64(0.0), with ok=false")
f, ok = strconv.parse_f64("inf")
testing.expect(t, ok && math.classify(f) == math.Float_Class.Inf, "expected f64(+inf), fully consumed")
f, ok = strconv.parse_f64("+inf")
testing.expect(t, ok && math.classify(f) == math.Float_Class.Inf, "expected f64(+inf), fully consumed")
f, ok = strconv.parse_f64("-inf")
testing.expect(t, ok && math.classify(f) == math.Float_Class.Neg_Inf, "expected f64(-inf), fully consumed")
f, ok = strconv.parse_f64("inFinity")
testing.expect(t, !ok && math.classify(f) == math.Float_Class.Inf, "expected f64(+inf), partially consumed")
f, ok = strconv.parse_f64("+InFinity")
testing.expect(t, !ok && math.classify(f) == math.Float_Class.Inf, "expected f64(+inf), partially consumed")
f, ok = strconv.parse_f64("-InfiniTy")
testing.expect(t, !ok && math.classify(f) == math.Float_Class.Neg_Inf, "expected f64(-inf), partially consumed")
f, ok = strconv.parse_f64("nan")
testing.expect(t, ok && math.classify(f) == math.Float_Class.NaN, "expected f64(nan), fully consumed")
f, ok = strconv.parse_f64("nAN")
testing.expect(t, ok && math.classify(f) == math.Float_Class.NaN, "expected f64(nan), fully consumed")
}
{
f, ok := strconv.parse_f32("1.2")
testing.expect(t, ok && f == 1.2, "expected f32(1.2), fully consumed")
f, ok = strconv.parse_f32("1.2a")
testing.expect(t, !ok && f == 1.2, "expected f32(1.2), partially consumed")
f, ok = strconv.parse_f32("inf")
testing.expect(t, ok && math.classify(f) == math.Float_Class.Inf, "expected f32(+inf), fully consumed")
f, ok = strconv.parse_f32("+inf")
testing.expect(t, ok && math.classify(f) == math.Float_Class.Inf, "expected f32(+inf), fully consumed")
f, ok = strconv.parse_f32("-inf")
testing.expect(t, ok && math.classify(f) == math.Float_Class.Neg_Inf, "expected f32(-inf), fully consumed")
f, ok = strconv.parse_f32("inFinity")
testing.expect(t, !ok && math.classify(f) == math.Float_Class.Inf, "expected f32(+inf), partially consumed")
f, ok = strconv.parse_f32("+InFinity")
testing.expect(t, !ok && math.classify(f) == math.Float_Class.Inf, "expected f32(+inf), partially consumed")
f, ok = strconv.parse_f32("-InfiniTy")
testing.expect(t, !ok && math.classify(f) == math.Float_Class.Neg_Inf, "expected f32(-inf), partially consumed")
f, ok = strconv.parse_f32("nan")
testing.expect(t, ok && math.classify(f) == math.Float_Class.NaN, "expected f32(nan), fully consumed")
f, ok = strconv.parse_f32("nAN")
testing.expect(t, ok && math.classify(f) == math.Float_Class.NaN, "expected f32(nan), fully consumed")
}
}
-2
View File
@@ -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,
-10
View File
@@ -1,10 +0,0 @@
ODIN=../../odin
ODINFLAGS=
OS=$(shell uname)
ifeq ($(OS), OpenBSD)
ODINFLAGS:=$(ODINFLAGS) -extra-linker-flags:-L/usr/local/lib
endif
all:
+3
View File
@@ -0,0 +1,3 @@
package tests_vendor
@(require) import "glfw"
-8
View File
@@ -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
View File
@@ -1,3 +1,4 @@
//+build darwin, windows
package test_vendor_glfw
import "core:testing"
+1 -1
View File
@@ -3374,7 +3374,7 @@ CREATE_DEVICE_FLAG :: enum u32 {
DEBUGGABLE = 6,
PREVENT_ALTERING_LAYER_SETTINGS_FROM_REGISTRY = 7,
DISABLE_GPU_TIMEOUT = 8,
VIDEO_SUPPORT = 12,
VIDEO_SUPPORT = 11,
}
PFN_CREATE_DEVICE :: #type proc "c" (a0: ^dxgi.IAdapter, a1: DRIVER_TYPE, a2: HMODULE, a3: u32, a4: ^FEATURE_LEVEL, a5: u32, a6: u32, a7: ^^IDevice, a8: ^FEATURE_LEVEL, a9: ^^IDeviceContext) -> HRESULT
+3 -3
View File
@@ -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
}
+2 -2
View File
@@ -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",
+1 -1
View File
@@ -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 ---
+5
View File
@@ -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)
+24 -6
View File
@@ -6,6 +6,16 @@ foreign xlib {
@(link_name="_Xdebug") _Xdebug: i32
}
foreign import xcursor "system:Xcursor"
@(default_calling_convention="c", link_prefix="X")
foreign xcursor {
cursorGetTheme :: proc(display: ^Display) -> cstring ---
cursorGetDefaultSize :: proc(display: ^Display) -> i32 ---
cursorLibraryLoadImage :: proc(name: cstring, theme: cstring, size: i32) -> rawptr ---
cursorImageLoadCursor :: proc(display: ^Display, img: rawptr) -> Cursor ---
cursorImageDestroy :: proc(img: rawptr) ---
}
/* ---- X11/Xlib.h ---------------------------------------------------------*/
@(default_calling_convention="c", link_prefix="X")
@@ -20,11 +30,9 @@ foreign xlib {
NoOp :: proc(display: ^Display) ---
// Display macros (connection)
ConnectionNumber :: proc(display: ^Display) -> i32 ---
ExtendedMaxRequestSize ::
proc(display: ^Display) -> int ---
ExtendedMaxRequestSize :: proc(display: ^Display) -> int ---
MaxRequestSize :: proc(display: ^Display) -> int ---
LastKnownRequestProcessed ::
proc(display: ^Display) -> uint ---
LastKnownRequestProcessed :: proc(display: ^Display) -> uint ---
NextRequest :: proc(display: ^Display) -> uint ---
ProtocolVersion :: proc(display: ^Display) -> i32 ---
ProtocolRevision :: proc(display: ^Display) -> i32 ---
@@ -46,8 +54,7 @@ foreign xlib {
DefaultRootWindow :: proc(display: ^Display) -> Window ---
DefaultScreen :: proc(display: ^Display) -> i32 ---
DefaultVisual :: proc(display: ^Display, screen_no: i32) -> ^Visual ---
DefaultScreenOfDisplay ::
proc(display: ^Display) -> ^Screen ---
DefaultScreenOfDisplay :: proc(display: ^Display) -> ^Screen ---
// Display macros (other)
RootWindow :: proc(display: ^Display, screen_no: i32) -> Window ---
ScreenCount :: proc(display: ^Display) -> i32 ---
@@ -1619,6 +1626,17 @@ foreign xlib {
) -> b32 ---
DestroyImage :: proc(image: ^XImage) ---
ResourceManagerString :: proc(display: ^Display) -> cstring ---
utf8SetWMProperties :: proc(
display: ^Display,
window: Window,
window_name: cstring,
icon_name: cstring,
argv: ^cstring,
argc: i32,
normal_hints: ^XSizeHints,
wm_hints: ^XWMHints,
class_hints: ^XClassHint,
) ---
}
@(default_calling_convention="c")