From d33668fa91b0f64cb6f69f315a2b2e8609fdbee0 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:24:46 -0400 Subject: [PATCH 01/58] Fix partial parsing of "infinity" in `parse_f64_prefix` It was previously reporting an invalid number of characters parsed for any string other than "inf", "+inf", or "-inf". --- core/strconv/strconv.odin | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 94842617e..990b2be2f 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -878,13 +878,10 @@ 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 m == 3 || m == 8 { // "inf" or "infinity" f = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000 - n = nsign + 3 + n = nsign + m ok = true return } From 7d670f65624c7c1f0d9a93808a99e259b95bd4b6 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 4 Jun 2024 13:39:31 -0400 Subject: [PATCH 02/58] Add initial test suite for `core:strconv` --- tests/core/Makefile | 4 ++ tests/core/build.bat | 5 ++ tests/core/strconv/test_core_strconv.odin | 79 +++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 tests/core/strconv/test_core_strconv.odin diff --git a/tests/core/Makefile b/tests/core/Makefile index 85f3783b4..79af9c3c7 100644 --- a/tests/core/Makefile +++ b/tests/core/Makefile @@ -25,6 +25,7 @@ all_bsd: download_test_assets \ reflect_test \ runtime_test \ slice_test \ + strconv_test \ strings_test \ thread_test \ time_test @@ -98,6 +99,9 @@ runtime_test: slice_test: $(ODIN) test slice $(COMMON) -out:test_core_slice +strconv_test: + $(ODIN) test strconv $(COMMON) -out:test_core_strconv + strings_test: $(ODIN) test strings $(COMMON) -out:test_core_strings diff --git a/tests/core/build.bat b/tests/core/build.bat index 67ac10f86..18506408a 100644 --- a/tests/core/build.bat +++ b/tests/core/build.bat @@ -103,6 +103,11 @@ echo Running core:slice tests echo --- %PATH_TO_ODIN% test slice %COMMON% -out:test_core_slice.exe || exit /b +echo --- +echo Running core:strconv tests +echo --- +%PATH_TO_ODIN% test strconv %COMMON% -out:test_core_strconv.exe || exit /b + echo --- echo Running core:strings tests echo --- diff --git a/tests/core/strconv/test_core_strconv.odin b/tests/core/strconv/test_core_strconv.odin new file mode 100644 index 000000000..9d713a356 --- /dev/null +++ b/tests/core/strconv/test_core_strconv.odin @@ -0,0 +1,79 @@ +package test_core_strconv + +import "core:math" +import "core:strconv" +import "core:testing" + +@(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 1 ..< len(s) + 1 { + ss := s[:i] + f, ok := strconv.parse_f64(ss, &n) + if i == 3 { // "inf" + testing.expect_value(t, f, pos_inf) + testing.expect_value(t, n, 3) + testing.expect_value(t, ok, true) + } else if i == 8 { // "infinity" + testing.expect_value(t, f, pos_inf) + testing.expect_value(t, n, 8) + testing.expect_value(t, ok, true) + } else { // invalid substring + testing.expect_value(t, f, 0) + testing.expect_value(t, n, 0) + testing.expect_value(t, ok, false) + } + } + + s = "+infinity" + for i in 1 ..< len(s) + 1 { + ss := s[:i] + f, ok := strconv.parse_f64(ss, &n) + if i == 4 { // "+inf" + testing.expect_value(t, f, pos_inf) + testing.expect_value(t, n, 4) + testing.expect_value(t, ok, true) + } else if i == 9 { // "+infinity" + testing.expect_value(t, f, pos_inf) + testing.expect_value(t, n, 9) + testing.expect_value(t, ok, true) + } else { // invalid substring + testing.expect_value(t, f, 0) + testing.expect_value(t, n, 0) + testing.expect_value(t, ok, false) + } + } + + s = "-infinity" + for i in 1 ..< len(s) + 1 { + ss := s[:i] + f, ok := strconv.parse_f64(ss, &n) + if i == 4 { // "-inf" + testing.expect_value(t, f, neg_inf) + testing.expect_value(t, n, 4) + testing.expect_value(t, ok, true) + } else if i == 9 { // "-infinity" + testing.expect_value(t, f, neg_inf) + testing.expect_value(t, n, 9) + testing.expect_value(t, ok, true) + } else { // invalid substring + testing.expect_value(t, f, 0) + testing.expect_value(t, n, 0) + testing.expect_value(t, ok, false) + } + } + + // 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) + } +} From c656a9e4cdd566b1aa473227d9d9a35b3c6812d3 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 4 Jun 2024 14:08:19 -0400 Subject: [PATCH 03/58] Fix and subsume `test_issue_2087` into `strconv` test suite The full "infinity" strings were expected to be partial consumes, but this is not the case. That has been fixed and the relevant extra tests from that file have been added to this test suite. Fixes #2670 --- tests/core/strconv/test_core_strconv.odin | 60 ++++++++++++++++++++++ tests/issues/test_issue_2087.odin | 62 ----------------------- 2 files changed, 60 insertions(+), 62 deletions(-) delete mode 100644 tests/issues/test_issue_2087.odin diff --git a/tests/core/strconv/test_core_strconv.odin b/tests/core/strconv/test_core_strconv.odin index 9d713a356..d33ac06b6 100644 --- a/tests/core/strconv/test_core_strconv.odin +++ b/tests/core/strconv/test_core_strconv.odin @@ -4,6 +4,56 @@ 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) @@ -19,14 +69,17 @@ test_infinity :: proc(t: ^testing.T) { testing.expect_value(t, f, pos_inf) testing.expect_value(t, n, 3) testing.expect_value(t, ok, true) + testing.expect_value(t, math.classify(f), math.Float_Class.Inf) } else if i == 8 { // "infinity" 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) } 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) } } @@ -38,14 +91,17 @@ test_infinity :: proc(t: ^testing.T) { testing.expect_value(t, f, pos_inf) testing.expect_value(t, n, 4) testing.expect_value(t, ok, true) + testing.expect_value(t, math.classify(f), math.Float_Class.Inf) } else if i == 9 { // "+infinity" testing.expect_value(t, f, pos_inf) testing.expect_value(t, n, 9) testing.expect_value(t, ok, true) + 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) } } @@ -57,14 +113,17 @@ test_infinity :: proc(t: ^testing.T) { testing.expect_value(t, f, neg_inf) testing.expect_value(t, n, 4) testing.expect_value(t, ok, true) + testing.expect_value(t, math.classify(f), math.Float_Class.Neg_Inf) } else if i == 9 { // "-infinity" testing.expect_value(t, f, neg_inf) testing.expect_value(t, n, 9) testing.expect_value(t, ok, true) + 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) } } @@ -75,5 +134,6 @@ test_infinity :: proc(t: ^testing.T) { 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) } } diff --git a/tests/issues/test_issue_2087.odin b/tests/issues/test_issue_2087.odin deleted file mode 100644 index 26b6d487d..000000000 --- a/tests/issues/test_issue_2087.odin +++ /dev/null @@ -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") - } -} \ No newline at end of file From 1fc6ff91b205836fad0fb30b36bfd042e381aa19 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 4 Jun 2024 15:17:57 -0400 Subject: [PATCH 04/58] Add test for `infinity` with trailing characters --- tests/core/strconv/test_core_strconv.odin | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/core/strconv/test_core_strconv.odin b/tests/core/strconv/test_core_strconv.odin index d33ac06b6..ed4adaf01 100644 --- a/tests/core/strconv/test_core_strconv.odin +++ b/tests/core/strconv/test_core_strconv.odin @@ -136,4 +136,19 @@ test_infinity :: proc(t: ^testing.T) { 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) } From 25feff3eb4c3845e6da4e8e56010301e316b2dd9 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Tue, 4 Jun 2024 18:50:08 -0400 Subject: [PATCH 05/58] Permit parsing of incomplete `infinity` but do not return true To clarify, `parse_f64` will indeed take `infi` to mean `+Inf` and return that as the value, but it will not return `ok = true`. It treats it as `inf` followed by any other trailing character. `parse_f64_prefix` is the lenient one which will return true so long as it finds some meaningful value. --- core/strconv/strconv.odin | 10 ++++- tests/core/strconv/test_core_strconv.odin | 45 +++++++++-------------- 2 files changed, 26 insertions(+), 29 deletions(-) diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 990b2be2f..60b4b4e2e 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -879,9 +879,15 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) { fallthrough case 'i', 'I': m := common_prefix_len_ignore_case(s, "infinity") - if m == 3 || m == 8 { // "inf" or "infinity" + if 3 <= m && m < 9 { // "inf" to "infinity" f = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000 - n = nsign + m + 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 } diff --git a/tests/core/strconv/test_core_strconv.odin b/tests/core/strconv/test_core_strconv.odin index ed4adaf01..6b70654cc 100644 --- a/tests/core/strconv/test_core_strconv.odin +++ b/tests/core/strconv/test_core_strconv.odin @@ -62,18 +62,15 @@ test_infinity :: proc(t: ^testing.T) { n: int s := "infinity" - for i in 1 ..< len(s) + 1 { + for i in 0 ..< len(s) + 1 { ss := s[:i] f, ok := strconv.parse_f64(ss, &n) - if i == 3 { // "inf" + 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, 3) - testing.expect_value(t, ok, true) - testing.expect_value(t, math.classify(f), math.Float_Class.Inf) - } else if i == 8 { // "infinity" - testing.expect_value(t, f, pos_inf) - testing.expect_value(t, n, 8) - testing.expect_value(t, ok, true) + 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) @@ -84,18 +81,15 @@ test_infinity :: proc(t: ^testing.T) { } s = "+infinity" - for i in 1 ..< len(s) + 1 { + for i in 0 ..< len(s) + 1 { ss := s[:i] f, ok := strconv.parse_f64(ss, &n) - if i == 4 { // "+inf" + 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, 4) - testing.expect_value(t, ok, true) - testing.expect_value(t, math.classify(f), math.Float_Class.Inf) - } else if i == 9 { // "+infinity" - testing.expect_value(t, f, pos_inf) - testing.expect_value(t, n, 9) - testing.expect_value(t, ok, true) + 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) @@ -106,18 +100,15 @@ test_infinity :: proc(t: ^testing.T) { } s = "-infinity" - for i in 1 ..< len(s) + 1 { + for i in 0 ..< len(s) + 1 { ss := s[:i] f, ok := strconv.parse_f64(ss, &n) - if i == 4 { // "-inf" + 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, 4) - testing.expect_value(t, ok, true) - testing.expect_value(t, math.classify(f), math.Float_Class.Neg_Inf) - } else if i == 9 { // "-infinity" - testing.expect_value(t, f, neg_inf) - testing.expect_value(t, n, 9) - testing.expect_value(t, ok, true) + 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) From 58f07698e849ca7542c53f9687b6c3d4a7f478e9 Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Wed, 5 Jun 2024 10:18:47 +0200 Subject: [PATCH 06/58] Added arm64 support for NetBSD --- .github/workflows/ci.yml | 3 ++- src/build_settings.cpp | 17 +++++++++++++++-- src/gb/gb.h | 3 +++ src/threading.cpp | 2 ++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aefc8add5..33bc303f0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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" @@ -31,6 +31,7 @@ jobs: ./odin version ./odin report ./odin check examples/all -vet -strict-style -target:netbsd_amd64 + ./odin check examples/all -vet -strict-style -target:netbsd_arm64 (cd tests/core; gmake all_bsd) (cd tests/internal; gmake all_bsd) (cd tests/issues; ./run.sh) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 3c7ff3f1e..a7e455818 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -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, @@ -1154,8 +1161,10 @@ 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 }, @@ -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) diff --git a/src/gb/gb.h b/src/gb/gb.h index 17d5e97d1..22a30a04b 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -256,6 +256,7 @@ extern "C" { #if defined(GB_SYSTEM_NETBSD) #include + #include #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 diff --git a/src/threading.cpp b/src/threading.cpp index 48c58e8f4..717dcb874 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -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 From ed6667ebf2fd5d85bcafc789c8e5e5adbd7524cc Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Wed, 5 Jun 2024 11:06:14 +0200 Subject: [PATCH 07/58] Propper thread identification on NetBSD --- core/os/os_netbsd.odin | 7 ++++++- core/sync/primitives_netbsd.odin | 8 ++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index e8e551340..ab4f877bb 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -328,6 +328,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 +726,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 { diff --git a/core/sync/primitives_netbsd.odin b/core/sync/primitives_netbsd.odin index 042e744e8..594f2ff5c 100644 --- a/core/sync/primitives_netbsd.odin +++ b/core/sync/primitives_netbsd.odin @@ -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()) } From 929437c7bc6c5e9efaff988930e2cdc7d07602df Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Wed, 5 Jun 2024 11:17:13 +0200 Subject: [PATCH 08/58] Fixed broken import --- core/os/os_netbsd.odin | 1 - 1 file changed, 1 deletion(-) diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index ab4f877bb..c0f237bf5 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -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 From f0e98372fb361cfd63e390eb2593b1457bc2e9c6 Mon Sep 17 00:00:00 2001 From: Andreas T Jonsson Date: Wed, 5 Jun 2024 15:37:02 +0200 Subject: [PATCH 09/58] Minor cleanup of makefiles and scripts --- .github/workflows/ci.yml | 1 - tests/internal/Makefile | 10 ++++++---- tests/issues/run.sh | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33bc303f0..2884fb301 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,6 @@ 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 diff --git a/tests/internal/Makefile b/tests/internal/Makefile index fb22767d2..307badb41 100644 --- a/tests/internal/Makefile +++ b/tests/internal/Makefile @@ -1,7 +1,9 @@ ODIN=../../odin COMMON=-file -vet -strict-style -o:minimal -all: asan_test rtti_test map_test pow_test 128_test string_compare_test +all: all_bsd asan_test + +all_bsd: rtti_test map_test pow_test 128_test string_compare_test rtti_test: $(ODIN) test test_rtti.odin $(COMMON) @@ -15,8 +17,8 @@ pow_test: 128_test: $(ODIN) test test_128.odin $(COMMON) +string_compare_test: + $(ODIN) test test_string_compare.odin $(COMMON) + asan_test: $(ODIN) test test_asan.odin $(COMMON) -sanitize:address -debug - -string_compare_test: - $(ODIN) test test_string_compare.odin $(COMMON) \ No newline at end of file diff --git a/tests/issues/run.sh b/tests/issues/run.sh index 24b388b07..a5280485e 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu mkdir -p build From cd4375061ce005ae66826a3082c7780528455d5d Mon Sep 17 00:00:00 2001 From: Mark Sniffen Date: Wed, 5 Jun 2024 10:11:18 -0400 Subject: [PATCH 10/58] D3D11 - fixed VIDEO_SUPPORT flag --- vendor/directx/d3d11/d3d11.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/directx/d3d11/d3d11.odin b/vendor/directx/d3d11/d3d11.odin index a1e3cf039..3af0f2965 100644 --- a/vendor/directx/d3d11/d3d11.odin +++ b/vendor/directx/d3d11/d3d11.odin @@ -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 From cbabcb0907e6430571c14d21fcb09c150275d99b Mon Sep 17 00:00:00 2001 From: gingerBill Date: Wed, 5 Jun 2024 15:43:15 +0100 Subject: [PATCH 11/58] Fix #3682 --- src/check_expr.cpp | 7 +++++++ src/llvm_backend_expr.cpp | 20 ++++++++++++++++++-- src/types.cpp | 9 +++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 0830f65bd..d2d01deda 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8866,6 +8866,10 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, SliceArray.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 +8896,9 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, SliceArray.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; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index c12489598..7772ba930 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -4533,10 +4533,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; } diff --git a/src/types.cpp b/src/types.cpp index e568d2af2..618e5bd8a 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -457,6 +457,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")}}, From 8455e159f5f6c6b1a3553d82ea891232fd56d336 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Wed, 5 Jun 2024 20:57:39 +0200 Subject: [PATCH 12/58] improve orca target --- base/runtime/core.odin | 15 ++++++- base/runtime/default_allocators_general.odin | 2 +- base/runtime/entry_wasm.odin | 36 +++++++++++----- base/runtime/error_checks.odin | 4 ++ base/runtime/heap_allocator_orca.odin | 29 +++++++++++++ base/runtime/heap_allocator_other.odin | 2 +- base/runtime/os_specific_orca.odin | 43 ++++++++++++++++++++ base/runtime/procs.odin | 2 +- core/fmt/fmt_os.odin | 1 + core/time/time_orca.odin | 24 +++++++++++ src/build_settings.cpp | 9 ++-- src/linker.cpp | 36 +++++++++++----- src/main.cpp | 28 +++++++++++++ 13 files changed, 200 insertions(+), 31 deletions(-) create mode 100644 base/runtime/heap_allocator_orca.odin create mode 100644 base/runtime/os_specific_orca.odin create mode 100644 core/time/time_orca.odin diff --git a/base/runtime/core.odin b/base/runtime/core.odin index 47b9a690c..3e24060af 100644 --- a/base/runtime/core.odin +++ b/base/runtime/core.odin @@ -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() } diff --git a/base/runtime/default_allocators_general.odin b/base/runtime/default_allocators_general.odin index ab4dd1db8..64af6c904 100644 --- a/base/runtime/default_allocators_general.odin +++ b/base/runtime/default_allocators_general.odin @@ -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 { diff --git a/base/runtime/entry_wasm.odin b/base/runtime/entry_wasm.odin index c608942ba..a24c6f4b7 100644 --- a/base/runtime/entry_wasm.odin +++ b/base/runtime/entry_wasm.odin @@ -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() - } -} \ No newline at end of file +} diff --git a/base/runtime/error_checks.odin b/base/runtime/error_checks.odin index 742e06a71..32a895c3f 100644 --- a/base/runtime/error_checks.odin +++ b/base/runtime/error_checks.odin @@ -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() } diff --git a/base/runtime/heap_allocator_orca.odin b/base/runtime/heap_allocator_orca.odin new file mode 100644 index 000000000..c22a67ca1 --- /dev/null +++ b/base/runtime/heap_allocator_orca.odin @@ -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) +} diff --git a/base/runtime/heap_allocator_other.odin b/base/runtime/heap_allocator_other.odin index 45049c7e9..74536ada9 100644 --- a/base/runtime/heap_allocator_other.odin +++ b/base/runtime/heap_allocator_other.odin @@ -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") -} \ No newline at end of file +} diff --git a/base/runtime/os_specific_orca.odin b/base/runtime/os_specific_orca.odin new file mode 100644 index 000000000..b6f5930ab --- /dev/null +++ b/base/runtime/os_specific_orca.odin @@ -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 +} diff --git a/base/runtime/procs.odin b/base/runtime/procs.odin index c9347463b..002a6501f 100644 --- a/base/runtime/procs.odin +++ b/base/runtime/procs.odin @@ -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) diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin index a403dcd65..9de0d43be 100644 --- a/core/fmt/fmt_os.odin +++ b/core/fmt/fmt_os.odin @@ -1,5 +1,6 @@ //+build !freestanding //+build !js +//+build !orca package fmt import "base:runtime" diff --git a/core/time/time_orca.odin b/core/time/time_orca.odin new file mode 100644 index 000000000..d222c8247 --- /dev/null +++ b/core/time/time_orca.odin @@ -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" () { +} diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 3c7ff3f1e..8a08c2b34 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1080,8 +1080,7 @@ gb_global TargetMetrics target_orca_wasm32 = { TargetOs_orca, TargetArch_wasm32, 4, 4, 8, 16, - str_lit("wasm32-wasi-js"), - // str_lit("e-m:e-p:32:32-i64:64-n32:64-S128"), + str_lit("wasm32-freestanding-js"), }; @@ -1161,6 +1160,7 @@ gb_global NamedTargetMetrics named_targets[] = { { str_lit("freestanding_wasm32"), &target_freestanding_wasm32 }, { str_lit("wasi_wasm32"), &target_wasi_wasm32 }, { str_lit("js_wasm32"), &target_js_wasm32 }, + { str_lit("orca_wasm32"), &target_orca_wasm32 }, { str_lit("freestanding_wasm64p32"), &target_freestanding_wasm64p32 }, { str_lit("js_wasm64p32"), &target_js_wasm64p32 }, @@ -2032,11 +2032,10 @@ 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 { diff --git a/src/linker.cpp b/src/linker.cpp index b699c0dfb..91055a604 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -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,42 @@ 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"); + // TODO: Orca windows. + + 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); } + + #if defined(GB_SYSTEM_WINDOWS) 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"); - } - 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)); + "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; } diff --git a/src/main.cpp b/src/main.cpp index 3ca024ed9..a30cad059 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -155,6 +155,34 @@ gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, return exit_code; } +// TODO: windows. +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 setup_args(int argc, char const **argv) { gbAllocator a = heap_allocator(); From 08382cb05dc23816f36c0a52b23c4d501431f88c Mon Sep 17 00:00:00 2001 From: laytan Date: Wed, 5 Jun 2024 19:26:23 +0200 Subject: [PATCH 13/58] orca windows --- src/build_settings.cpp | 2 +- src/linker.cpp | 10 ++++------ src/main.cpp | 6 +++++- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 8a08c2b34..05117a9b2 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1080,7 +1080,7 @@ gb_global TargetMetrics target_orca_wasm32 = { TargetOs_orca, TargetArch_wasm32, 4, 4, 8, 16, - str_lit("wasm32-freestanding-js"), + str_lit("wasm32-wasi-js"), }; diff --git a/src/linker.cpp b/src/linker.cpp index 91055a604..25c54a6ab 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -72,12 +72,10 @@ gb_internal i32 linker_stage(LinkerData *gen) { gbString extra_orca_flags = gb_string_make(temporary_allocator(), ""); - gbString inputs = gb_string_make(temporary_allocator(), ""); + gbString inputs = gb_string_make(temporary_allocator(), ""); inputs = gb_string_append_fmt(inputs, "\"%.*s.o\"", LIT(output_filename)); if (build_context.metrics.os == TargetOs_orca) { - // TODO: Orca windows. - 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"); @@ -95,10 +93,10 @@ gb_internal i32 linker_stage(LinkerData *gen) { #if defined(GB_SYSTEM_WINDOWS) result = system_exec_command_line_app("wasm-ld", - "\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s %.*s", + "\"%.*s\\bin\\wasm-ld\" %s -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)); + 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", diff --git a/src/main.cpp b/src/main.cpp index a30cad059..70def5802 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -155,7 +155,11 @@ gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, return exit_code; } -// TODO: windows. +#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); From 460ffe1aee6688fa9887b861ad4ab9a2fd431598 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 5 Jun 2024 21:04:35 +0200 Subject: [PATCH 14/58] Rewrite upload_b2 nightly action against B2 SDK --- .github/workflows/nightly.yml | 31 ++------ ci/create_nightly_json.py | 51 ------------- ci/delete_old_binaries.py | 33 -------- ci/nightly.py | 140 ++++++++++++++++++++++++++++++++++ ci/upload_create_nightly.sh | 25 ------ 5 files changed, 148 insertions(+), 132 deletions(-) delete mode 100644 ci/create_nightly_json.py delete mode 100644 ci/delete_old_binaries.py create mode 100644 ci/nightly.py delete mode 100755 ci/upload_create_nightly.sh diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 221ab1cdb..8d8392f95 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -151,11 +151,11 @@ jobs: 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 \ No newline at end of file diff --git a/ci/create_nightly_json.py b/ci/create_nightly_json.py deleted file mode 100644 index 3a1fcd1ef..000000000 --- a/ci/create_nightly_json.py +++ /dev/null @@ -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()) \ No newline at end of file diff --git a/ci/delete_old_binaries.py b/ci/delete_old_binaries.py deleted file mode 100644 index f7d5d02af..000000000 --- a/ci/delete_old_binaries.py +++ /dev/null @@ -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()) \ No newline at end of file diff --git a/ci/nightly.py b/ci/nightly.py new file mode 100644 index 000000000..7a23fd2c2 --- /dev/null +++ b/ci/nightly.py @@ -0,0 +1,140 @@ +import os +import sys +from zipfile import ZipFile +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, 'w') 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: {} [arguments]".format(sys.argv[0])) + print("\tartifact \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 ".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) \ No newline at end of file diff --git a/ci/upload_create_nightly.sh b/ci/upload_create_nightly.sh deleted file mode 100755 index 8404b33ff..000000000 --- a/ci/upload_create_nightly.sh +++ /dev/null @@ -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" From fcfc1cb97fedd2f25bed451c4093c64d25b3314d Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Wed, 5 Jun 2024 22:26:03 +0200 Subject: [PATCH 15/58] Nightly ZIP level 9 --- ci/nightly.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci/nightly.py b/ci/nightly.py index 7a23fd2c2..7bd32899d 100644 --- a/ci/nightly.py +++ b/ci/nightly.py @@ -1,6 +1,6 @@ import os import sys -from zipfile import ZipFile +from zipfile import ZipFile, ZIP_DEFLATED from b2sdk.v2 import InMemoryAccountInfo, B2Api from datetime import datetime import json @@ -40,7 +40,7 @@ def create_and_upload_artifact_zip(platform: str, artifact: str) -> int: 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, 'w') as z: + 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) From 70592630a48509b69a0c19df6bbff02e594d2969 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Thu, 6 Jun 2024 02:39:48 -0700 Subject: [PATCH 16/58] add support for title changes, cursor config, and dnd --- vendor/x11/xlib/xlib_const.odin | 5 +++++ vendor/x11/xlib/xlib_procs.odin | 30 ++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/vendor/x11/xlib/xlib_const.odin b/vendor/x11/xlib/xlib_const.odin index 910940dec..0b87ab9f7 100644 --- a/vendor/x11/xlib/xlib_const.odin +++ b/vendor/x11/xlib/xlib_const.odin @@ -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) diff --git a/vendor/x11/xlib/xlib_procs.odin b/vendor/x11/xlib/xlib_procs.odin index 735ddc9c4..20ec5bb39 100644 --- a/vendor/x11/xlib/xlib_procs.odin +++ b/vendor/x11/xlib/xlib_procs.odin @@ -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") From 483015fe578be20eeeb731c12510bedd21a29f32 Mon Sep 17 00:00:00 2001 From: Hector Date: Thu, 6 Jun 2024 11:47:36 +0100 Subject: [PATCH 17/58] Updated SDL_CreateTexture to take `PixelFormatEnum` instead of `u32` --- vendor/sdl2/sdl_render.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/sdl2/sdl_render.odin b/vendor/sdl2/sdl_render.odin index f948b39b0..cceebf3ac 100644 --- a/vendor/sdl2/sdl_render.odin +++ b/vendor/sdl2/sdl_render.odin @@ -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 --- From 155516b897450ec265cfb53b6e32db90f15544f5 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 13:02:08 +0100 Subject: [PATCH 18/58] Fix `-ignore-warnings` --- src/error.cpp | 63 +++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/src/error.cpp b/src/error.cpp index da444e998..2556a8de4 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -407,29 +407,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_coloured("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("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); + 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 +546,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(); From 971229fe66cdc93135e0f7e41cdf2a0bce28cd67 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 13:27:55 +0100 Subject: [PATCH 19/58] Fix #3686 --- src/llvm_backend_expr.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 7772ba930..36af60e46 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -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) { From 9ef43fc782159893b7af139f9d9be3aec3108ecd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 15:16:34 +0100 Subject: [PATCH 20/58] Add `@(rodata)` --- src/check_decl.cpp | 6 ++++++ src/check_stmt.cpp | 6 ++++++ src/checker.cpp | 6 ++++++ src/checker.hpp | 1 + src/entity.cpp | 1 + src/llvm_backend.cpp | 20 ++++++++++++++++++-- 6 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/check_decl.cpp b/src/check_decl.cpp index f2afce59c..43947836b 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -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"); } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2c37bced0..fc443a7b5 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -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: { @@ -2055,6 +2058,9 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f } } } + if (ac.rodata) { + error(e->token, "Only global variables can have @(rodata) applied"); + } if (ac.thread_local_model != "") { String name = e->token.string; if (name == "_") { diff --git a/src/checker.cpp b/src/checker.cpp index 97e685d33..8a58bb425 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -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) { diff --git a/src/checker.hpp b/src/checker.hpp index e793540e3..2ac4c8e7a 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -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; diff --git a/src/entity.cpp b/src/entity.cpp index e4fc66dac..7f484e308 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -230,6 +230,7 @@ struct Entity { bool is_foreign; bool is_export; bool is_global; + bool is_rodata; } Variable; struct { Type * type_parameter_specialization; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 03c17a8bb..5dc6d94d5 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -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); From bea47db4953559dbbcdce1da5dbaf38d0bb8d943 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 15:20:47 +0100 Subject: [PATCH 21/58] Allow `@(rodata)` on `@(static)` variables --- src/check_stmt.cpp | 6 +++++- src/llvm_backend_stmt.cpp | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index fc443a7b5..a1698bbfe 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2059,7 +2059,11 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f } } if (ac.rodata) { - error(e->token, "Only global variables can have @(rodata) applied"); + 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; diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index b18db4e45..9f28e45e0 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -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); From 3a9b86628a484aa03b594599653c1cab4b916c8e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 15:23:52 +0100 Subject: [PATCH 22/58] Add `@(rodata)` and `@(static, rodata)` where appropriate --- base/runtime/internal.odin | 4 ++-- core/math/cmplx/cmplx_trig.odin | 2 +- core/math/math.odin | 18 +++++++++--------- core/math/math_gamma.odin | 6 +++--- core/math/math_lgamma.odin | 14 +++++++------- core/math/math_sincos.odin | 2 +- core/math/rand/exp.odin | 6 +++--- core/math/rand/normal.odin | 6 +++--- core/os/os2/internal_util.odin | 2 +- core/strconv/generic_float.odin | 2 +- core/strconv/strconv.odin | 2 +- core/time/time.odin | 1 + core/unicode/tables.odin | 10 ++++++++++ 13 files changed, 43 insertions(+), 32 deletions(-) diff --git a/base/runtime/internal.odin b/base/runtime/internal.odin index 8e1b3d633..378eea256 100644 --- a/base/runtime/internal.odin +++ b/base/runtime/internal.odin @@ -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}, diff --git a/core/math/cmplx/cmplx_trig.odin b/core/math/cmplx/cmplx_trig.odin index 7ca404fab..15e757506 100644 --- a/core/math/cmplx/cmplx_trig.odin +++ b/core/math/cmplx/cmplx_trig.odin @@ -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, diff --git a/core/math/math.odin b/core/math/math.odin index 8d85c2381..3d0ab3c4e 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -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, diff --git a/core/math/math_gamma.odin b/core/math/math_gamma.odin index 00d4b7316..9f5a364d3 100644 --- a/core/math/math_gamma.odin +++ b/core/math/math_gamma.odin @@ -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, diff --git a/core/math/math_lgamma.odin b/core/math/math_lgamma.odin index 0705d8564..828f17178 100644 --- a/core/math/math_lgamma.odin +++ b/core/math/math_lgamma.odin @@ -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, diff --git a/core/math/math_sincos.odin b/core/math/math_sincos.odin index 578876ac5..b616f410d 100644 --- a/core/math/math_sincos.odin +++ b/core/math/math_sincos.odin @@ -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, diff --git a/core/math/rand/exp.odin b/core/math/rand/exp.odin index 719debe75..ebc849b2f 100644 --- a/core/math/rand/exp.odin +++ b/core/math/rand/exp.odin @@ -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, diff --git a/core/math/rand/normal.odin b/core/math/rand/normal.odin index f96163fe9..c8681db80 100644 --- a/core/math/rand/normal.odin +++ b/core/math/rand/normal.odin @@ -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, diff --git a/core/os/os2/internal_util.odin b/core/os/os2/internal_util.odin index 59d845350..e26cf7439 100644 --- a/core/os/os2/internal_util.odin +++ b/core/os/os2/internal_util.odin @@ -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) diff --git a/core/strconv/generic_float.odin b/core/strconv/generic_float.odin index 6dc11c0be..b049f0fe1 100644 --- a/core/strconv/generic_float.odin +++ b/core/strconv/generic_float.odin @@ -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 { diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 11590fa1b..902f1cdc5 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -1095,7 +1095,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) { } trunc_block: if !trunc { - @static pow10 := [?]f64{ + @(static, rodata) pow10 := [?]f64{ 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, diff --git a/core/time/time.odin b/core/time/time.odin index 4807af840..4575b36f7 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -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, diff --git a/core/unicode/tables.odin b/core/unicode/tables.odin index f43827413..dfa5caaa2 100644 --- a/core/unicode/tables.odin +++ b/core/unicode/tables.odin @@ -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, From 678fdae966e0e4b959a917f11d276b60fb78772f Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 6 Jun 2024 16:32:18 +0200 Subject: [PATCH 23/58] Rebased. --- core/image/bmp/bmp.odin | 652 ++++++++++++++++++++++++++ core/image/bmp/bmp_js.odin | 4 + core/image/bmp/bmp_os.odin | 19 + core/image/common.odin | 126 +++++ tests/core/image/test_core_image.odin | 652 +++++++++++++++++++++++--- 5 files changed, 1382 insertions(+), 71 deletions(-) create mode 100644 core/image/bmp/bmp.odin create mode 100644 core/image/bmp/bmp_js.odin create mode 100644 core/image/bmp/bmp_os.odin diff --git a/core/image/bmp/bmp.odin b/core/image/bmp/bmp.odin new file mode 100644 index 000000000..d9ecb55e5 --- /dev/null +++ b/core/image/bmp/bmp.odin @@ -0,0 +1,652 @@ +// 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" +@(require) import "core:fmt" + +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) + +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.. (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..> 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..> 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..> 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.. (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.. 0 { + for count in 0..> 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..> 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.. 0 { + for _ in 0..= len(data) { + return .Corrupt + } + for count in 0.. (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.. (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 + } +} \ No newline at end of file diff --git a/core/image/common.odin b/core/image/common.odin index b576a9521..fe75371b3 100644 --- a/core/image/common.odin +++ b/core/image/common.odin @@ -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 */ diff --git a/tests/core/image/test_core_image.odin b/tests/core/image/test_core_image.odin index 7fc04ce6b..0ce15715c 100644 --- a/tests/core/image/test_core_image.odin +++ b/tests/core/image/test_core_image.odin @@ -1,11 +1,11 @@ /* - Copyright 2021 Jeroen van Rijn . + Copyright 2021-2024 Jeroen van Rijn . 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,19 @@ 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 +1490,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 +1509,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 +1526,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 +1551,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 +1651,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 +1659,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 +1667,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 +1684,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 +1729,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 +1751,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 +1763,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 +1776,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 +1821,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 +1831,516 @@ 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. + qoi_file := strings.concatenate({TEST_SUITE_PATH_BMP, "/", file.file, ".qoi"}, context.allocator) + defer delete(qoi_file) + + qoi.save(qoi_file, img) + 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) + } + bmp.destroy(img) + } + } + return } \ No newline at end of file From 4a290f47ad771d2f992e14162b5fce27336424fe Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 6 Jun 2024 16:55:03 +0200 Subject: [PATCH 24/58] Re-add BMP test suite download. --- tests/core/download_assets.py | 93 ++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 2 deletions(-) diff --git a/tests/core/download_assets.py b/tests/core/download_assets.py index 7874b7e91..8e124954f 100644 --- a/tests/core/download_assets.py +++ b/tests/core/download_assets.py @@ -7,7 +7,7 @@ import zipfile import hashlib import hmac -TEST_SUITES = ['PNG', 'XML'] +TEST_SUITES = ['PNG', 'XML', 'BMP'] DOWNLOAD_BASE_PATH = "assets/{}" ASSETS_BASE_URL = "https://raw.githubusercontent.com/odin-lang/test-assets/master/{}/{}" HMAC_KEY = "https://odin-lang.org" @@ -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 @@ -263,4 +352,4 @@ def main(): return 0 if __name__ == '__main__': - sys.exit(main()) + sys.exit(main()) \ No newline at end of file From 039bb8794aae3b88cc0c14ac1b3f17c28bac0184 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 17:59:12 +0100 Subject: [PATCH 25/58] Improve `matrix_align_of` logic when it has invalid inputs. --- src/types.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types.cpp b/src/types.cpp index 618e5bd8a..45aa26894 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1491,10 +1491,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; From e2eb3cdd8afb8e105414ba5459d0b6382b609d89 Mon Sep 17 00:00:00 2001 From: Laytan Date: Thu, 6 Jun 2024 19:15:02 +0200 Subject: [PATCH 26/58] fix linking on weird linuxes --- src/build_settings.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 52e6fd7ff..251dd06dd 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -2035,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 "); @@ -2052,8 +2055,13 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta // 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 From ff37a7435cbb794bdf4244f94e0abe820c8a8288 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 21:03:35 +0100 Subject: [PATCH 27/58] Add parentheses around the foreign import paths --- vendor/raylib/raylib.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/raylib/raylib.odin b/vendor/raylib/raylib.odin index 6204f0bde..ebb1cdd27 100644 --- a/vendor/raylib/raylib.odin +++ b/vendor/raylib/raylib.odin @@ -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", From 566119ff83b72a473f9dc8630876e414f2d44b8a Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 6 Jun 2024 23:52:32 +0200 Subject: [PATCH 28/58] Add saving of 24 and 32-bit images to BMP format. --- core/image/bmp/bmp.odin | 96 +++++++++++++++++++++++++++++++++++++- core/image/bmp/bmp_os.odin | 15 ++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/core/image/bmp/bmp.odin b/core/image/bmp/bmp.odin index d9ecb55e5..64fc1d5a8 100644 --- a/core/image/bmp/bmp.odin +++ b/core/image/bmp/bmp.odin @@ -7,7 +7,6 @@ import "core:compress" import "core:mem" import "base:intrinsics" import "base:runtime" -@(require) import "core:fmt" Error :: image.Error Image :: image.Image @@ -19,6 +18,101 @@ 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: ^Image, err: Error) { ctx := &compress.Context_Memory_Input{ input_data = data, diff --git a/core/image/bmp/bmp_os.odin b/core/image/bmp/bmp_os.odin index 2245db4fd..d20abc685 100644 --- a/core/image/bmp/bmp_os.odin +++ b/core/image/bmp/bmp_os.odin @@ -2,6 +2,7 @@ package core_image_bmp import "core:os" +import "core:bytes" load :: proc{load_from_file, load_from_bytes, load_from_context} @@ -16,4 +17,18 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont } 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 } \ No newline at end of file From ed060819f34bd6b5b9f1f30136a7929faa2b36b4 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 7 Jun 2024 00:14:15 +0200 Subject: [PATCH 29/58] Test roundtripping BMP --- tests/core/image/test_core_image.odin | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/core/image/test_core_image.odin b/tests/core/image/test_core_image.odin index 0ce15715c..495950c80 100644 --- a/tests/core/image/test_core_image.odin +++ b/tests/core/image/test_core_image.odin @@ -2327,10 +2327,6 @@ run_bmp_suite :: proc(t: ^testing.T, suite: []Test) { 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. - qoi_file := strings.concatenate({TEST_SUITE_PATH_BMP, "/", file.file, ".qoi"}, context.allocator) - defer delete(qoi_file) - - qoi.save(qoi_file, img) pixels := bytes.buffer_to_bytes(&img.pixels) dims := Dims{img.width, img.height, img.channels, img.depth} @@ -2338,6 +2334,26 @@ run_bmp_suite :: proc(t: ^testing.T, suite: []Test) { 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) } From 7044a7d77650e922a66f3bfe99711f3ed370e1ba Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 6 Jun 2024 23:55:48 +0100 Subject: [PATCH 30/58] Try to fix a possible race condition with polymorphic record parameters --- src/check_builtin.cpp | 31 ++++++++----------------------- src/check_expr.cpp | 11 +++-------- src/check_type.cpp | 30 +++++++++++++----------------- src/checker.cpp | 4 ++++ src/types.cpp | 18 ++++++++++++++++++ 5 files changed, 46 insertions(+), 48 deletions(-) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 7e3bcb7ee..eef925d94 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -5912,15 +5912,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 +5946,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)); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d2d01deda..ad546858c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7331,14 +7331,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, ", "); diff --git a/src/check_type.cpp b/src/check_type.cpp index 7ed657bee..e0dea19cb 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -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 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); @@ -1453,12 +1445,14 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special return true; } + wait_for_record_polymorphic_params(s); + wait_for_record_polymorphic_params(t); if (t->Struct.polymorphic_parent == s->Struct.polymorphic_parent && 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]; @@ -1506,12 +1500,14 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special return true; } + wait_for_record_polymorphic_params(s); + wait_for_record_polymorphic_params(t); if (t->Union.polymorphic_parent == s->Union.polymorphic_parent && 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]; diff --git a/src/checker.cpp b/src/checker.cpp index 8a58bb425..e90509c1f 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2031,6 +2031,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { } else { add_type_info_type_internal(c, t_type_info_ptr); } + wait_for_record_polymorphic_params(bt); add_type_info_type_internal(c, bt->Union.polymorphic_params); for_array(i, bt->Union.variants) { add_type_info_type_internal(c, bt->Union.variants[i]); @@ -2063,6 +2064,7 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { } } } + wait_for_record_polymorphic_params(bt); add_type_info_type_internal(c, bt->Struct.polymorphic_params); for_array(i, bt->Struct.fields) { Entity *f = bt->Struct.fields[i]; @@ -2292,6 +2294,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { } else { add_min_dep_type_info(c, t_type_info_ptr); } + wait_for_record_polymorphic_params(bt); add_min_dep_type_info(c, bt->Union.polymorphic_params); for_array(i, bt->Union.variants) { add_min_dep_type_info(c, bt->Union.variants[i]); @@ -2321,6 +2324,7 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { } } } + wait_for_record_polymorphic_params(bt); add_min_dep_type_info(c, bt->Struct.polymorphic_params); for_array(i, bt->Struct.fields) { Entity *f = bt->Struct.fields[i]; diff --git a/src/types.cpp b/src/types.cpp index 45aa26894..4ceba5244 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -140,6 +140,7 @@ struct TypeStruct { i64 custom_field_align; Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; + Wait_Signal polymorphic_wait_signal; Type * soa_elem; i32 soa_count; @@ -167,6 +168,7 @@ struct TypeUnion { i64 custom_align; Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; + Wait_Signal polymorphic_wait_signal; i16 tag_size; bool is_polymorphic; @@ -1093,6 +1095,7 @@ gb_internal Type *alloc_type_struct() { gb_internal Type *alloc_type_struct_complete() { Type *t = alloc_type(Type_Struct); wait_signal_set(&t->Struct.fields_wait_signal); + wait_signal_set(&t->Struct.polymorphic_wait_signal); return t; } @@ -2136,15 +2139,30 @@ gb_internal bool is_type_polymorphic_record_unspecialized(Type *t) { return false; } +gb_internal void wait_for_record_polymorphic_params(Type *t) { + t = base_type(t); + switch (t->kind) { + case Type_Struct: + wait_signal_until_available(&t->Struct.polymorphic_wait_signal); + break; + case Type_Union: + wait_signal_until_available(&t->Union.polymorphic_wait_signal); + break; + } +} + + 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; } From 68781f8dd365692aee55099d3e14cb83a115764c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 7 Jun 2024 00:11:00 +0100 Subject: [PATCH 31/58] Remove unnecessary Wait_Signal checks --- src/check_type.cpp | 4 ---- src/checker.cpp | 4 ---- src/types.cpp | 12 ------------ 3 files changed, 20 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index e0dea19cb..17f7813d5 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -1445,8 +1445,6 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special return true; } - wait_for_record_polymorphic_params(s); - wait_for_record_polymorphic_params(t); if (t->Struct.polymorphic_parent == s->Struct.polymorphic_parent && s->Struct.polymorphic_params != nullptr && t->Struct.polymorphic_params != nullptr) { @@ -1500,8 +1498,6 @@ gb_internal bool check_type_specialization_to(CheckerContext *ctx, Type *special return true; } - wait_for_record_polymorphic_params(s); - wait_for_record_polymorphic_params(t); if (t->Union.polymorphic_parent == s->Union.polymorphic_parent && s->Union.polymorphic_params != nullptr && t->Union.polymorphic_params != nullptr) { diff --git a/src/checker.cpp b/src/checker.cpp index e90509c1f..8a58bb425 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2031,7 +2031,6 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { } else { add_type_info_type_internal(c, t_type_info_ptr); } - wait_for_record_polymorphic_params(bt); add_type_info_type_internal(c, bt->Union.polymorphic_params); for_array(i, bt->Union.variants) { add_type_info_type_internal(c, bt->Union.variants[i]); @@ -2064,7 +2063,6 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { } } } - wait_for_record_polymorphic_params(bt); add_type_info_type_internal(c, bt->Struct.polymorphic_params); for_array(i, bt->Struct.fields) { Entity *f = bt->Struct.fields[i]; @@ -2294,7 +2292,6 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { } else { add_min_dep_type_info(c, t_type_info_ptr); } - wait_for_record_polymorphic_params(bt); add_min_dep_type_info(c, bt->Union.polymorphic_params); for_array(i, bt->Union.variants) { add_min_dep_type_info(c, bt->Union.variants[i]); @@ -2324,7 +2321,6 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) { } } } - wait_for_record_polymorphic_params(bt); add_min_dep_type_info(c, bt->Struct.polymorphic_params); for_array(i, bt->Struct.fields) { Entity *f = bt->Struct.fields[i]; diff --git a/src/types.cpp b/src/types.cpp index 4ceba5244..97e8267a3 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2139,18 +2139,6 @@ gb_internal bool is_type_polymorphic_record_unspecialized(Type *t) { return false; } -gb_internal void wait_for_record_polymorphic_params(Type *t) { - t = base_type(t); - switch (t->kind) { - case Type_Struct: - wait_signal_until_available(&t->Struct.polymorphic_wait_signal); - break; - case Type_Union: - wait_signal_until_available(&t->Union.polymorphic_wait_signal); - break; - } -} - gb_internal TypeTuple *get_record_polymorphic_params(Type *t) { t = base_type(t); From ee93d7c05e563512ea41b00cc1347eb59427c500 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 7 Jun 2024 15:13:09 +0200 Subject: [PATCH 32/58] add error message for unknown test log level It would previously just be a compilation error about a missing return statement. --- core/testing/runner.odin | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/testing/runner.odin b/core/testing/runner.odin index c82aa1fda..328186c35 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -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\".") + } } } From 0a528777e87b228d0e0db9f9bde33b365d9ef90b Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 7 Jun 2024 15:31:01 +0200 Subject: [PATCH 33/58] utilize `odin test -all-packages` instead of (make/bat) scripts for running tests --- .github/workflows/ci.yml | 225 +++++++----------------- tests/benchmark/Makefile | 14 -- tests/benchmark/all.odin | 4 + tests/benchmark/build.bat | 13 -- tests/core/Makefile | 115 ------------ tests/core/build.bat | 124 ------------- tests/core/download_assets.py | 4 +- tests/core/image/build.bat | 2 - tests/core/net/test_core_net.odin | 3 +- tests/core/normal.odin | 39 ++++ tests/core/os/test_core_os_exit.odin | 10 -- tests/core/speed.odin | 5 + tests/internal/Makefile | 24 --- tests/internal/build.bat | 9 - tests/internal/test_128.odin | 2 +- tests/internal/test_asan.odin | 2 +- tests/internal/test_map.odin | 4 +- tests/internal/test_pow.odin | 4 +- tests/internal/test_rtti.odin | 4 +- tests/internal/test_string_compare.odin | 4 +- tests/issues/run.bat | 1 - tests/issues/run.sh | 1 - tests/vendor/Makefile | 10 -- tests/vendor/all.odin | 3 + tests/vendor/build.bat | 8 - tests/vendor/glfw/test_vendor_glfw.odin | 1 + 26 files changed, 130 insertions(+), 505 deletions(-) delete mode 100644 tests/benchmark/Makefile create mode 100644 tests/benchmark/all.odin delete mode 100644 tests/benchmark/build.bat delete mode 100644 tests/core/Makefile delete mode 100644 tests/core/build.bat delete mode 100644 tests/core/image/build.bat create mode 100644 tests/core/normal.odin delete mode 100644 tests/core/os/test_core_os_exit.odin create mode 100644 tests/core/speed.odin delete mode 100644 tests/internal/Makefile delete mode 100644 tests/internal/build.bat delete mode 100644 tests/vendor/Makefile create mode 100644 tests/vendor/all.odin delete mode 100644 tests/vendor/build.bat diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2884fb301..0aa6a7277 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,184 +29,99 @@ jobs: gmake release ./odin version ./odin report + gmake -C vendor/stb/src + gmake -C vendor/cgltf/src + gmake -C vendor/miniaudio/src ./odin check examples/all -vet -strict-style -target:netbsd_amd64 ./odin check examples/all -vet -strict-style -target:netbsd_arm64 - (cd tests/core; gmake all_bsd) - (cd tests/internal; gmake all_bsd) + ./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false + ./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false + ./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false + ./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false (cd tests/issues; ./run.sh) - (cd tests/benchmark; gmake all) - build_linux: - name: Ubuntu Build, Check, and Test - runs-on: ubuntu-latest + ci: + strategy: + fail-fast: false + matrix: + # MacOS 13 runs on Intel, 14 runs on ARM + os: [ubuntu-latest, macos-13, macos-14] + runs-on: ${{ matrix.os }} + name: ${{ matrix.os == 'macos-14' && 'MacOS ARM' || (matrix.os == 'macos-13' && 'MacOS Intel' || 'Ubuntu') }} Build, Check, and Test + timeout-minutes: 15 steps: - uses: actions/checkout@v1 - - name: Download LLVM + + - 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 - 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 - name: build Odin @@ -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 diff --git a/tests/benchmark/Makefile b/tests/benchmark/Makefile deleted file mode 100644 index 840174dbb..000000000 --- a/tests/benchmark/Makefile +++ /dev/null @@ -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_* \ No newline at end of file diff --git a/tests/benchmark/all.odin b/tests/benchmark/all.odin new file mode 100644 index 000000000..d1b7662e2 --- /dev/null +++ b/tests/benchmark/all.odin @@ -0,0 +1,4 @@ +package benchmarks + +@(require) import "crypto" +@(require) import "hash" diff --git a/tests/benchmark/build.bat b/tests/benchmark/build.bat deleted file mode 100644 index 30b030066..000000000 --- a/tests/benchmark/build.bat +++ /dev/null @@ -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 diff --git a/tests/core/Makefile b/tests/core/Makefile deleted file mode 100644 index 79af9c3c7..000000000 --- a/tests/core/Makefile +++ /dev/null @@ -1,115 +0,0 @@ -ODIN=../../odin -PYTHON=$(shell which python3) -COMMON=-no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false - -all: all_bsd \ - net_test - -all_bsd: download_test_assets \ - c_libc_test \ - compress_test \ - container_test \ - crypto_test \ - encoding_test \ - filepath_test \ - fmt_test \ - hash_test \ - i18n_test \ - image_test \ - linalg_glsl_math_test \ - match_test \ - math_test \ - noise_test \ - odin_test \ - os_exit_test \ - reflect_test \ - runtime_test \ - slice_test \ - strconv_test \ - strings_test \ - thread_test \ - time_test - -download_test_assets: - $(PYTHON) download_assets.py - -c_libc_test: - $(ODIN) test c/libc $(COMMON) -out:test_core_libc - -compress_test: - $(ODIN) test compress $(COMMON) -out:test_core_compress - -container_test: - $(ODIN) test container $(COMMON) -out:test_core_container - -crypto_test: - $(ODIN) test crypto $(COMMON) -o:speed -out:test_crypto - -encoding_test: - $(ODIN) test encoding/base64 $(COMMON) -out:test_base64 - $(ODIN) test encoding/cbor $(COMMON) -out:test_cbor - $(ODIN) test encoding/hex $(COMMON) -out:test_hex - $(ODIN) test encoding/hxa $(COMMON) -out:test_hxa - $(ODIN) test encoding/json $(COMMON) -out:test_json - $(ODIN) test encoding/varint $(COMMON) -out:test_varint - $(ODIN) test encoding/xml $(COMMON) -out:test_xml - -filepath_test: - $(ODIN) test path/filepath $(COMMON) -out:test_core_filepath - -fmt_test: - $(ODIN) test fmt $(COMMON) -out:test_core_fmt - -hash_test: - $(ODIN) test hash $(COMMON) -o:speed -out:test_hash - -image_test: - $(ODIN) test image $(COMMON) -out:test_core_image - -i18n_test: - $(ODIN) test text/i18n $(COMMON) -out:test_core_i18n - -match_test: - $(ODIN) test text/match $(COMMON) -out:test_core_match - -math_test: - $(ODIN) test math $(COMMON) -out:test_core_math - -linalg_glsl_math_test: - $(ODIN) test math/linalg/glsl $(COMMON) -out:test_linalg_glsl_math - -noise_test: - $(ODIN) test math/noise $(COMMON) -out:test_noise - -net_test: - $(ODIN) test net $(COMMON) -out:test_core_net - -os_exit_test: - $(ODIN) run os/test_core_os_exit.odin -file -out:test_core_os_exit && exit 1 || exit 0 - -odin_test: - $(ODIN) test odin $(COMMON) -out:test_core_odin - -reflect_test: - $(ODIN) test reflect $(COMMON) -out:test_core_reflect - -runtime_test: - $(ODIN) test runtime $(COMMON) -out:test_core_runtime - -slice_test: - $(ODIN) test slice $(COMMON) -out:test_core_slice - -strconv_test: - $(ODIN) test strconv $(COMMON) -out:test_core_strconv - -strings_test: - $(ODIN) test strings $(COMMON) -out:test_core_strings - -thread_test: - $(ODIN) test thread $(COMMON) -out:test_core_thread - -time_test: - $(ODIN) test time $(COMMON) -out:test_core_time - -clean: - rm test_* \ No newline at end of file diff --git a/tests/core/build.bat b/tests/core/build.bat deleted file mode 100644 index 18506408a..000000000 --- a/tests/core/build.bat +++ /dev/null @@ -1,124 +0,0 @@ -@echo off -set COMMON=-no-bounds-check -vet -strict-style -define:ODIN_TEST_FANCY=false -set PATH_TO_ODIN==..\..\odin -python3 download_assets.py -echo --- -echo Running core:c/libc tests -echo --- -%PATH_TO_ODIN% test c\libc %COMMON% -out:test_libc.exe || exit /b - -echo --- -echo Running core:compress tests -echo --- -%PATH_TO_ODIN% test compress %COMMON% -out:test_core_compress.exe || exit /b - -echo --- -echo Running core:container tests -echo --- -%PATH_TO_ODIN% test container %COMMON% -out:test_core_container.exe || exit /b - -echo --- -echo Running core:crypto tests -echo --- -%PATH_TO_ODIN% test crypto %COMMON% -o:speed -out:test_crypto.exe || exit /b - -echo --- -echo Running core:encoding tests -echo --- -%PATH_TO_ODIN% test encoding/base64 %COMMON% -out:test_base64.exe || exit /b -%PATH_TO_ODIN% test encoding/cbor %COMMON% -out:test_cbor.exe || exit /b -%PATH_TO_ODIN% test encoding/hex %COMMON% -out:test_hex.exe || exit /b -%PATH_TO_ODIN% test encoding/hxa %COMMON% -out:test_hxa.exe || exit /b -%PATH_TO_ODIN% test encoding/json %COMMON% -out:test_json.exe || exit /b -%PATH_TO_ODIN% test encoding/varint %COMMON% -out:test_varint.exe || exit /b -%PATH_TO_ODIN% test encoding/xml %COMMON% -out:test_xml.exe || exit /b - -echo --- -echo Running core:path/filepath tests -echo --- -%PATH_TO_ODIN% test path/filepath %COMMON% -out:test_core_filepath.exe || exit /b - -echo --- -echo Running core:fmt tests -echo --- -%PATH_TO_ODIN% test fmt %COMMON% -out:test_core_fmt.exe || exit /b - -echo --- -echo Running core:hash tests -echo --- -%PATH_TO_ODIN% test hash %COMMON% -o:speed -out:test_core_hash.exe || exit /b - -echo --- -echo Running core:image tests -echo --- -%PATH_TO_ODIN% test image %COMMON% -out:test_core_image.exe || exit /b - -echo --- -echo Running core:text/i18n tests -echo --- -%PATH_TO_ODIN% test text\i18n %COMMON% -out:test_core_i18n.exe || exit /b - -echo --- -echo Running text:match tests -echo --- -%PATH_TO_ODIN% test text/match %COMMON% -out:test_core_match.exe || exit /b - -echo --- -echo Running core:math tests -echo --- -%PATH_TO_ODIN% test math %COMMON% -out:test_core_math.exe || exit /b - -echo --- -echo Running core:math/linalg/glsl tests -echo --- -%PATH_TO_ODIN% test math/linalg/glsl %COMMON% -out:test_linalg_glsl.exe || exit /b - -echo --- -echo Running core:math/noise tests -echo --- -%PATH_TO_ODIN% test math/noise %COMMON% -out:test_noise.exe || exit /b - -echo --- -echo Running core:net -echo --- -%PATH_TO_ODIN% test net %COMMON% -out:test_core_net.exe || exit /b - -echo --- -echo Running core:odin tests -echo --- -%PATH_TO_ODIN% test odin %COMMON% -o:size -out:test_core_odin.exe || exit /b - -echo --- -echo Running core:reflect tests -echo --- -%PATH_TO_ODIN% test reflect %COMMON% -out:test_core_reflect.exe || exit /b - -echo --- -echo Running core:runtime tests -echo --- -%PATH_TO_ODIN% test runtime %COMMON% -out:test_core_runtime.exe || exit /b - -echo --- -echo Running core:slice tests -echo --- -%PATH_TO_ODIN% test slice %COMMON% -out:test_core_slice.exe || exit /b - -echo --- -echo Running core:strconv tests -echo --- -%PATH_TO_ODIN% test strconv %COMMON% -out:test_core_strconv.exe || exit /b - -echo --- -echo Running core:strings tests -echo --- -%PATH_TO_ODIN% test strings %COMMON% -out:test_core_strings.exe || exit /b - -echo --- -echo Running core:thread tests -echo --- -%PATH_TO_ODIN% test thread %COMMON% -out:test_core_thread.exe || exit /b - -echo --- -echo Running core:time tests -echo --- -%PATH_TO_ODIN% test time %COMMON% -out:test_core_time.exe || exit /b \ No newline at end of file diff --git a/tests/core/download_assets.py b/tests/core/download_assets.py index 8e124954f..fc4a71cdc 100644 --- a/tests/core/download_assets.py +++ b/tests/core/download_assets.py @@ -8,7 +8,7 @@ import hashlib import hmac TEST_SUITES = ['PNG', 'XML', 'BMP'] -DOWNLOAD_BASE_PATH = "assets/{}" +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 @@ -352,4 +352,4 @@ def main(): return 0 if __name__ == '__main__': - sys.exit(main()) \ No newline at end of file + sys.exit(main()) diff --git a/tests/core/image/build.bat b/tests/core/image/build.bat deleted file mode 100644 index 5a07971b8..000000000 --- a/tests/core/image/build.bat +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -odin test . -define:ODIN_TEST_TRACK_MEMORY=true -define:ODIN_TEST_PROGRESS_WIDTH=12 -vet -strict-style diff --git a/tests/core/net/test_core_net.odin b/tests/core/net/test_core_net.odin index 3cbb7fa34..6aada88a6 100644 --- a/tests/core/net/test_core_net.odin +++ b/tests/core/net/test_core_net.odin @@ -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") -} \ No newline at end of file +} diff --git a/tests/core/normal.odin b/tests/core/normal.odin new file mode 100644 index 000000000..f23763a59 --- /dev/null +++ b/tests/core/normal.odin @@ -0,0 +1,39 @@ +package tests_core + +import rlibc "core:c/libc" + +@(init) +download_assets :: proc() { + if rlibc.system("python3 " + ODIN_ROOT + "tests/core/download_assets.py " + ODIN_ROOT + "tests/core/assets") != 0 { + panic("downloading test assets failed!") + } +} + +@(require) import "c/libc" +@(require) import "compress" +@(require) import "container" +@(require) import "encoding/base64" +@(require) import "encoding/cbor" +@(require) import "encoding/hex" +@(require) import "encoding/hxa" +@(require) import "encoding/json" +@(require) import "encoding/varint" +@(require) import "encoding/xml" +@(require) import "fmt" +@(require) import "image" +@(require) import "math" +@(require) import "math/big" +@(require) import "math/linalg/glsl" +@(require) import "math/noise" +@(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" diff --git a/tests/core/os/test_core_os_exit.odin b/tests/core/os/test_core_os_exit.odin deleted file mode 100644 index 2ab274f5e..000000000 --- a/tests/core/os/test_core_os_exit.odin +++ /dev/null @@ -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) -} diff --git a/tests/core/speed.odin b/tests/core/speed.odin new file mode 100644 index 000000000..555d30f5e --- /dev/null +++ b/tests/core/speed.odin @@ -0,0 +1,5 @@ +// Tests intended to be ran with optimizations on +package tests_core + +@(require) import "crypto" +@(require) import "hash" diff --git a/tests/internal/Makefile b/tests/internal/Makefile deleted file mode 100644 index 307badb41..000000000 --- a/tests/internal/Makefile +++ /dev/null @@ -1,24 +0,0 @@ -ODIN=../../odin -COMMON=-file -vet -strict-style -o:minimal - -all: all_bsd asan_test - -all_bsd: rtti_test map_test pow_test 128_test string_compare_test - -rtti_test: - $(ODIN) test test_rtti.odin $(COMMON) - -map_test: - $(ODIN) test test_map.odin $(COMMON) - -pow_test: - $(ODIN) test test_pow.odin $(COMMON) - -128_test: - $(ODIN) test test_128.odin $(COMMON) - -string_compare_test: - $(ODIN) test test_string_compare.odin $(COMMON) - -asan_test: - $(ODIN) test test_asan.odin $(COMMON) -sanitize:address -debug diff --git a/tests/internal/build.bat b/tests/internal/build.bat deleted file mode 100644 index edaa2684b..000000000 --- a/tests/internal/build.bat +++ /dev/null @@ -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 \ No newline at end of file diff --git a/tests/internal/test_128.odin b/tests/internal/test_128.odin index 1aa7487b6..7b7d655e8 100644 --- a/tests/internal/test_128.odin +++ b/tests/internal/test_128.odin @@ -1,4 +1,4 @@ -package test_128 +package test_internal import "core:testing" diff --git a/tests/internal/test_asan.odin b/tests/internal/test_asan.odin index 66f3c6547..1ac599acf 100644 --- a/tests/internal/test_asan.odin +++ b/tests/internal/test_asan.odin @@ -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" diff --git a/tests/internal/test_map.odin b/tests/internal/test_map.odin index fa13244ac..a9a8cf5d4 100644 --- a/tests/internal/test_map.odin +++ b/tests/internal/test_map.odin @@ -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 } -} \ No newline at end of file +} diff --git a/tests/internal/test_pow.odin b/tests/internal/test_pow.odin index 609087fb2..d1939ace5 100644 --- a/tests/internal/test_pow.odin +++ b/tests/internal/test_pow.odin @@ -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) } } -} \ No newline at end of file +} diff --git a/tests/internal/test_rtti.odin b/tests/internal/test_rtti.odin index ec5508208..e72c107b2 100644 --- a/tests/internal/test_rtti.odin +++ b/tests/internal/test_rtti.odin @@ -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) -} \ No newline at end of file +} diff --git a/tests/internal/test_string_compare.odin b/tests/internal/test_string_compare.odin index 63b62cc96..32a93ddf5 100644 --- a/tests/internal/test_string_compare.odin +++ b/tests/internal/test_string_compare.odin @@ -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) { } } } -} \ No newline at end of file +} diff --git a/tests/issues/run.bat b/tests/issues/run.bat index f3e3daba9..299e08791 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -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 diff --git a/tests/issues/run.sh b/tests/issues/run.sh index a5280485e..e17cb4c73 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -13,7 +13,6 @@ 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 diff --git a/tests/vendor/Makefile b/tests/vendor/Makefile deleted file mode 100644 index 7d6b84978..000000000 --- a/tests/vendor/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -ODIN=../../odin -ODINFLAGS= - -OS=$(shell uname) - -ifeq ($(OS), OpenBSD) - ODINFLAGS:=$(ODINFLAGS) -extra-linker-flags:-L/usr/local/lib -endif - -all: diff --git a/tests/vendor/all.odin b/tests/vendor/all.odin new file mode 100644 index 000000000..1ce56e786 --- /dev/null +++ b/tests/vendor/all.odin @@ -0,0 +1,3 @@ +package tests_vendor + +@(require) import "glfw" diff --git a/tests/vendor/build.bat b/tests/vendor/build.bat deleted file mode 100644 index 84ab2f1ef..000000000 --- a/tests/vendor/build.bat +++ /dev/null @@ -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 \ No newline at end of file diff --git a/tests/vendor/glfw/test_vendor_glfw.odin b/tests/vendor/glfw/test_vendor_glfw.odin index 4254ebb09..8a7fb0d0a 100644 --- a/tests/vendor/glfw/test_vendor_glfw.odin +++ b/tests/vendor/glfw/test_vendor_glfw.odin @@ -1,3 +1,4 @@ +//+build darwin, windows package test_vendor_glfw import "core:testing" From 9122c20d4b4e1d9fbd8833d47ceec164170a73f5 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 7 Jun 2024 15:57:09 +0200 Subject: [PATCH 34/58] update actions/checkout --- .github/workflows/ci.yml | 10 +++++----- .github/workflows/nightly.yml | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0aa6a7277..66c848a8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -49,7 +49,7 @@ jobs: 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 + - uses: actions/checkout@v4 - name: Download LLVM (Linux) if: matrix.os == 'ubuntu-latest' @@ -79,9 +79,9 @@ jobs: run: ./odin report - 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 + make -C vendor/stb/src + make -C vendor/cgltf/src + make -C vendor/miniaudio/src - name: Odin check run: ./odin check examples/demo -vet - name: Odin run @@ -123,7 +123,7 @@ jobs: runs-on: windows-2022 timeout-minutes: 15 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: build Odin shell: cmd run: | diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 8d8392f95..0857e99ad 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -11,7 +11,7 @@ jobs: if: github.repository == 'odin-lang/Odin' runs-on: windows-2022 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: build Odin shell: cmd run: | @@ -45,7 +45,7 @@ jobs: if: github.repository == 'odin-lang/Odin' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: (Linux) Download LLVM run: | wget https://apt.llvm.org/llvm.sh @@ -79,7 +79,7 @@ jobs: if: github.repository == 'odin-lang/Odin' runs-on: macos-13 steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Download LLVM and setup PATH run: | brew install llvm@17 dylibbundler @@ -113,7 +113,7 @@ jobs: if: github.repository == 'odin-lang/Odin' runs-on: macos-14 # ARM machine steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - name: Download LLVM and setup PATH run: | brew install llvm@17 dylibbundler @@ -146,7 +146,7 @@ jobs: runs-on: [ubuntu-latest] needs: [build_windows, build_macos, build_macos_arm, build_ubuntu] steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v4 - uses: actions/setup-python@v2 with: python-version: '3.8.x' @@ -193,4 +193,4 @@ jobs: python3 ci/nightly.py artifact macos-amd64 macos_artifacts/dist.zip python3 ci/nightly.py artifact macos-arm64 macos_arm_artifacts/dist.zip python3 ci/nightly.py prune - python3 ci/nightly.py json \ No newline at end of file + python3 ci/nightly.py json From 29250f2657f644e766dba09dcffdf594f54554f5 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 7 Jun 2024 16:33:38 +0200 Subject: [PATCH 35/58] fix regression in test_issue_2395 --- src/check_type.cpp | 2 +- tests/issues/run.sh | 5 ++--- tests/issues/test_issue_2395.odin | 2 -- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 17f7813d5..c56c8a739 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -776,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; } diff --git a/tests/issues/run.sh b/tests/issues/run.sh index e17cb4c73..20259ac63 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -6,8 +6,6 @@ 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 @@ -18,10 +16,11 @@ $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 "must have at least 2 variants") -eq 2 ]] ; then echo "SUCCESSFUL 1/1" else echo "SUCCESSFUL 0/1" + exit 1 fi set +x diff --git a/tests/issues/test_issue_2395.odin b/tests/issues/test_issue_2395.odin index 48e1ee516..bbbcb3aea 100644 --- a/tests/issues/test_issue_2395.odin +++ b/tests/issues/test_issue_2395.odin @@ -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, From e627fcb0e66421d52526d844c01065ba0e043c5e Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 7 Jun 2024 16:41:26 +0200 Subject: [PATCH 36/58] fix not printing `Error:` when terminal has no color support --- src/error.cpp | 6 ++---- tests/issues/run.sh | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/error.cpp b/src/error.cpp index 2556a8de4..eed69b779 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -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); @@ -427,8 +425,8 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, error_out_empty(); } else { error_out_pos(pos); + error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); } - error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); error_out_va(fmt, va); error_out("\n"); show_error_on_line(pos, end); @@ -841,4 +839,4 @@ gb_internal void print_all_errors(void) { gb_file_write(f, res, gb_string_length(res)); errors_already_printed = true; -} \ No newline at end of file +} diff --git a/tests/issues/run.sh b/tests/issues/run.sh index 20259ac63..8b4c1e7f2 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -16,7 +16,7 @@ $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 "must have at least 2 variants") -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" From 072825ac5a1652de349eae20ccae0e67c23a843b Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 7 Jun 2024 17:40:34 +0200 Subject: [PATCH 37/58] add MacOS 14.5 to 'core:sys/info' and 'odin report' --- core/sys/info/platform_darwin.odin | 1 + src/bug_report.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/core/sys/info/platform_darwin.odin b/core/sys/info/platform_darwin.odin index 122dd42ee..0cae0aa98 100644 --- a/core/sys/info/platform_darwin.odin +++ b/core/sys/info/platform_darwin.odin @@ -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) diff --git a/src/bug_report.cpp b/src/bug_report.cpp index 1f754ce7c..dab8c4391 100644 --- a/src/bug_report.cpp +++ b/src/bug_report.cpp @@ -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}}}, }; From b35e72c82b53be5ae16b1a0923ae948183a32a8d Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 7 Jun 2024 20:26:31 +0200 Subject: [PATCH 38/58] core/sync: fix wrong timeout calculation, `time.Duration` is ns already --- core/sync/futex_darwin.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/sync/futex_darwin.odin b/core/sync/futex_darwin.odin index 6ea177d1b..fca9aadfe 100644 --- a/core/sync/futex_darwin.odin +++ b/core/sync/futex_darwin.odin @@ -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 From 00dfff7ee06d8a691ba754232243689e4bfd8381 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Fri, 7 Jun 2024 20:28:09 +0200 Subject: [PATCH 39/58] core/thread: fix a deadlock situation on unix --- core/thread/thread_unix.odin | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index acc0e05cb..5291917da 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -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, From 61c481bd8187925f3ceaf2269d8a479a91e8b763 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Fri, 7 Jun 2024 15:27:43 -0400 Subject: [PATCH 40/58] Fix `%p` pointers not printing leading `0x` --- core/fmt/fmt.odin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index f43683d11..f9113a7a7 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -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) From b65589d036da3c45e5ebbdafc007f308ccb09e40 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:01:03 -0400 Subject: [PATCH 41/58] Add `%p` tests for `fmt` --- tests/core/fmt/test_core_fmt.odin | 39 ++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/tests/core/fmt/test_core_fmt.odin b/tests/core/fmt/test_core_fmt.odin index 5053e788f..80ab6fb6e 100644 --- a/tests/core/fmt/test_core_fmt.odin +++ b/tests/core/fmt/test_core_fmt.odin @@ -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, "", "%v", c) + + check(t, "0x0", "%#v", a) + check(t, "0x0", "%#v", b) + check(t, "", "%#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) { got := fmt.tprintf(format, ..args) testing.expectf(t, got == exp, "(%q, %v): %q != %q", format, args, got, exp) -} \ No newline at end of file +} From affd48c791251dc1c57939fff0168e7e601e7e38 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Fri, 7 Jun 2024 16:08:37 -0400 Subject: [PATCH 42/58] Add `#caller_location` to `check` in `fmt` tests This makes it much easier to track down which line failed. --- tests/core/fmt/test_core_fmt.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/core/fmt/test_core_fmt.odin b/tests/core/fmt/test_core_fmt.odin index 80ab6fb6e..507e0f433 100644 --- a/tests/core/fmt/test_core_fmt.odin +++ b/tests/core/fmt/test_core_fmt.odin @@ -259,7 +259,7 @@ test_pointers :: proc(t: ^testing.T) { } @(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) } From 323a6e0728b019d5e0aca30a24ed317bdc9354d2 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 7 Jun 2024 22:19:01 +0200 Subject: [PATCH 43/58] Nuke ancient comment. --- tests/core/image/test_core_image.odin | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/core/image/test_core_image.odin b/tests/core/image/test_core_image.odin index 495950c80..857022b81 100644 --- a/tests/core/image/test_core_image.odin +++ b/tests/core/image/test_core_image.odin @@ -1472,7 +1472,6 @@ run_png_suite :: proc(t: ^testing.T, suite: []Test) { 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 := 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) From 371749d474ac510c8dcee210af79a00162bda212 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 8 Jun 2024 14:48:04 +0100 Subject: [PATCH 44/58] Make certain procedures `"contextless"` for microui --- vendor/microui/microui.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/microui/microui.odin b/vendor/microui/microui.odin index e545b742b..08a96acf2 100644 --- a/vendor/microui/microui.odin +++ b/vendor/microui/microui.odin @@ -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 } From 35e57fdef8826685a2aeaebe9b2532cf0a44df8c Mon Sep 17 00:00:00 2001 From: John Leidegren Date: Sat, 8 Jun 2024 15:58:34 +0200 Subject: [PATCH 45/58] Fixed error handling in read_dir on Windows --- core/os/dir_windows.odin | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/os/dir_windows.odin b/core/os/dir_windows.odin index 491507313..df02f54e7 100644 --- a/core/os/dir_windows.odin +++ b/core/os/dir_windows.odin @@ -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 { + e = Errno(win32.GetLastError()) + return + } 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 != "" { From 7e994b6d216b091b90c47eba1b834bc7c690535c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 8 Jun 2024 15:42:19 +0100 Subject: [PATCH 46/58] Remove empty line preventing a suggestion from happening --- src/check_stmt.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a1698bbfe..65bac36be 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1255,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"); } } From de7d3e248741b584ab2afb54322a8bb3e612a3f0 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sat, 8 Jun 2024 17:00:38 +0200 Subject: [PATCH 47/58] Update dir_windows.odin --- core/os/dir_windows.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/os/dir_windows.odin b/core/os/dir_windows.odin index df02f54e7..9ca78948e 100644 --- a/core/os/dir_windows.odin +++ b/core/os/dir_windows.odin @@ -88,8 +88,8 @@ 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 { - e = Errno(win32.GetLastError()) - return + err = Errno(win32.GetLastError()) + return dfi[:], err } defer win32.FindClose(find_handle) for n != 0 { From be0774acc8a67ac2f9e003764284bbda21d4ebcd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 8 Jun 2024 16:07:28 +0100 Subject: [PATCH 48/58] Add error message on return a constant slice value from a procedure --- src/check_stmt.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 65bac36be..c37c58cd6 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2501,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"); } } From 5c8f78a25aaf517b6a4670551f3668a45b068327 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 8 Jun 2024 16:40:59 -0400 Subject: [PATCH 49/58] Add `*.bmp` to core tests `.gitignore` --- tests/core/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/core/.gitignore b/tests/core/.gitignore index cd013e188..2a4e21679 100644 --- a/tests/core/.gitignore +++ b/tests/core/.gitignore @@ -1,3 +1,4 @@ +*.bmp *.zip *.png math_big_test_library.* \ No newline at end of file From 49f147cc86d06ee60dd6936404b18f952196d9a8 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sat, 8 Jun 2024 17:05:00 -0400 Subject: [PATCH 50/58] Prevent panic when `swizzle` called with < 2 indices The requirement for at least 2 indices has been sourced from `lb_addr_swizzle` in `llvm_backend_general.cpp`, where there is an assert to ensure the swizzle_count is `1 < n <= 4`. --- src/check_builtin.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index eef925d94..98c695a2c 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -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) { From 9ad9236c3bc2d9ea538bd1e5050141c85e0732ab Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sun, 9 Jun 2024 02:47:05 +0200 Subject: [PATCH 51/58] fix large ints amd64 sysv abi Fixes #3707 --- src/llvm_abi.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index 85a16d321..1f7a39447 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -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); From 3628154849ff8908d5acc765d887a399ce4324c2 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sun, 9 Jun 2024 03:33:23 +0200 Subject: [PATCH 52/58] fix swizzle crash due to wrong alignment Fixes #3691 --- src/llvm_backend_general.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index e8183027f..ea98fc60a 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -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 { From 6d862cc4e529b71e65356c73d44fc39c61cbbb77 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Sun, 9 Jun 2024 04:43:19 +0200 Subject: [PATCH 53/58] fix unreachable hit when param and/or return have complex inits Fixes #3630 --- src/llvm_backend_proc.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 6cb1efab2..87f75fb1d 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -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) { From e0d0dc704ce69e079d70ad2e98c3ae21cce56379 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 9 Jun 2024 13:20:48 +0100 Subject: [PATCH 54/58] Make `f32(u8)` etc do an immediate cast to `f32(u32(u8))` in code generation --- src/llvm_backend_expr.cpp | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 36af60e46..98e773ddb 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1873,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), false, ""); + 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), false, ""); + break; + default: + GB_PANIC("Unhandled float type"); + break; + } } return res; } From 8fcfd8c506752d3e0d65e4d9ef7856486a65ca5f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 9 Jun 2024 13:21:22 +0100 Subject: [PATCH 55/58] Fix sign flag --- src/llvm_backend_expr.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 98e773ddb..a23f8cfbe 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -1897,11 +1897,11 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { 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), false, ""); + 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), false, ""); + res.value = LLVMBuildIntCast2(p->builder, res.value, lb_type(m, t), true, ""); break; default: GB_PANIC("Unhandled float type"); From 4b52f7fe2bd68d74efffa55ef8b4582164c1e268 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 9 Jun 2024 13:35:22 +0100 Subject: [PATCH 56/58] Fix #3713 --- src/check_expr.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ad546858c..4115cd1e9 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3336,11 +3336,12 @@ 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)) { - final_type = default_type(x->type); - } - update_untyped_expr_type(c, x->expr, final_type, true); + convert_to_typed(c, x, type); + // Type *final_type = type; + // if (is_const_expr && !is_type_constant_type(type)) { + // final_type = default_type(x->type); + // } + // update_untyped_expr_type(c, x->expr, final_type, true); } else { Type *src = core_type(x->type); Type *dst = core_type(type); @@ -4286,7 +4287,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; @@ -7527,9 +7529,6 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } operand->type = t; operand->expr = call; - if (operand->mode != Addressing_Invalid) { - update_untyped_expr_type(c, arg, t, false); - } break; } } From c17981ac38ace6cac12463029273d16068bf23c3 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 9 Jun 2024 14:02:01 +0100 Subject: [PATCH 57/58] Add edge case to `error_operand_no_value` --- src/check_expr.cpp | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4115cd1e9..1e6d6e2ab 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -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 { @@ -3336,12 +3348,11 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) { } if (is_type_untyped(x->type)) { - convert_to_typed(c, x, type); - // Type *final_type = type; - // if (is_const_expr && !is_type_constant_type(type)) { - // final_type = default_type(x->type); - // } - // update_untyped_expr_type(c, x->expr, final_type, true); + Type *final_type = type; + if (is_const_expr && !is_type_constant_type(type)) { + final_type = default_type(x->type); + } + update_untyped_expr_type(c, x->expr, final_type, true); } else { Type *src = core_type(x->type); Type *dst = core_type(type); @@ -4287,8 +4298,7 @@ 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) && - !is_type_integer(target_type)) { + if (!is_type_boolean(target_type)) { operand->mode = Addressing_Invalid; convert_untyped_error(c, operand, target_type); return; @@ -7529,6 +7539,9 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c } operand->type = t; operand->expr = call; + if (operand->mode != Addressing_Invalid) { + update_untyped_expr_type(c, arg, t, false); + } break; } } @@ -8336,7 +8349,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" || From ef7c6b98951aacc72bd25750e1c4a3a8c30c7d06 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 9 Jun 2024 14:07:07 +0100 Subject: [PATCH 58/58] Re Fix #3713 --- src/check_expr.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 1e6d6e2ab..13d0e277d 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3350,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); @@ -4298,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;