diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2bd3ca9ad..3cc4283b0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -38,6 +38,9 @@ jobs:
cd tests/vendor
make
timeout-minutes: 10
+ - name: Odin issues tests
+ run: tests/issues/run.sh
+ 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
@@ -87,6 +90,9 @@ jobs:
cd tests/vendor
make
timeout-minutes: 10
+ - name: Odin issues tests
+ run: tests/issues/run.sh
+ timeout-minutes: 10
- name: Odin check examples/all for Darwin arm64
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
timeout-minutes: 10
@@ -153,6 +159,12 @@ jobs:
cd tests\core\math\big
call build.bat
timeout-minutes: 10
+ - name: Odin issues tests
+ shell: cmd
+ run: |
+ call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ call tests\issues\run.bat
+ timeout-minutes: 10
- name: Odin check examples/all for Windows 32bits
shell: cmd
run: |
diff --git a/Makefile b/Makefile
index bea0569a9..82150c6a2 100644
--- a/Makefile
+++ b/Makefile
@@ -1,74 +1,3 @@
-GIT_SHA=$(shell git rev-parse --short HEAD)
-DISABLED_WARNINGS=-Wno-switch -Wno-macro-redefined -Wno-unused-value
-LDFLAGS=-pthread -lm -lstdc++
-CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
-CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
-CC=clang
-
-OS=$(shell uname)
-
-ifeq ($(OS), Darwin)
-
- ARCH=$(shell uname -m)
- LLVM_CONFIG=llvm-config
-
- # allow for arm only llvm's with version 13
- ifeq ($(ARCH), arm64)
- LLVM_VERSIONS = "13.%.%"
- else
- # allow for x86 / amd64 all llvm versions begining from 11
- LLVM_VERSIONS = "13.%.%" "12.0.1" "11.1.0"
- endif
-
- LLVM_VERSION_PATTERN_SEPERATOR = )|(
- LLVM_VERSION_PATTERNS_ESCAPED_DOT = $(subst .,\.,$(LLVM_VERSIONS))
- LLVM_VERSION_PATTERNS_REPLACE_PERCENT = $(subst %,.*,$(LLVM_VERSION_PATTERNS_ESCAPED_DOT))
- LLVM_VERSION_PATTERN_REMOVE_ELEMENTS = $(subst " ",$(LLVM_VERSION_PATTERN_SEPERATOR),$(LLVM_VERSION_PATTERNS_REPLACE_PERCENT))
- LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR = $(subst ",,$(LLVM_VERSION_PATTERN_REMOVE_ELEMENTS))
- LLVM_VERSION_PATTERN = "^(($(LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR)))"
-
- ifeq ($(shell $(LLVM_CONFIG) --version | grep -E $(LLVM_VERSION_PATTERN)),)
- ifeq ($(ARCH), arm64)
- $(error "Requirement: llvm-config must be base version 13 for arm64")
- else
- $(error "Requirement: llvm-config must be base version greater than 11 for amd64/x86")
- endif
- endif
-
- LDFLAGS:=$(LDFLAGS) -liconv -ldl
- CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
- LDFLAGS:=$(LDFLAGS) -lLLVM-C
-endif
-ifeq ($(OS), Linux)
- LLVM_CONFIG=llvm-config-11
- ifneq ($(shell which llvm-config-11 2>/dev/null),)
- LLVM_CONFIG=llvm-config-11
- else ifneq ($(shell which llvm-config-11-64 2>/dev/null),)
- LLVM_CONFIG=llvm-config-11-64
- else
- ifeq ($(shell $(LLVM_CONFIG) --version | grep '^11\.'),)
- $(error "Requirement: llvm-config must be version 11")
- endif
- endif
-
- LDFLAGS:=$(LDFLAGS) -ldl
- CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
- LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
-endif
-ifeq ($(OS), OpenBSD)
- LLVM_CONFIG=/usr/local/bin/llvm-config
-
- LDFLAGS:=$(LDFLAGS) -liconv
- CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
- LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
-endif
-ifeq ($(OS), FreeBSD)
- LLVM_CONFIG=/usr/local/bin/llvm-config11
-
- CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
- LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
-endif
-
all: debug demo
demo:
@@ -78,13 +7,13 @@ report:
./odin report
debug:
- $(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
+ ./build_odin.sh debug
release:
- $(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 $(LDFLAGS) -o odin
+ ./build_odin.sh release
release_native:
- $(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
+ ./build_odin.sh release-native
nightly:
- $(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
+ ./build_odin.sh nightly
diff --git a/build_odin.sh b/build_odin.sh
index a323782a1..dd2725f5d 100755
--- a/build_odin.sh
+++ b/build_odin.sh
@@ -41,6 +41,13 @@ config_darwin() {
LDFLAGS="$LDFLAGS -lLLVM-C"
}
+config_freebsd() {
+ LLVM_CONFIG=/usr/local/bin/llvm-config11
+
+ CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
+ LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
+}
+
config_openbsd() {
LLVM_CONFIG=/usr/local/bin/llvm-config
@@ -108,6 +115,9 @@ Darwin)
OpenBSD)
config_openbsd
;;
+FreeBSD)
+ config_freebsd
+ ;;
*)
panic "Platform unsupported!"
esac
diff --git a/core/bufio/scanner.odin b/core/bufio/scanner.odin
index cc3b4533a..86e6d8eb0 100644
--- a/core/bufio/scanner.odin
+++ b/core/bufio/scanner.odin
@@ -8,6 +8,7 @@ import "core:intrinsics"
// Extra errors returns by scanning procedures
Scanner_Extra_Error :: enum i32 {
+ None,
Negative_Advance,
Advanced_Too_Far,
Bad_Read_Count,
@@ -15,7 +16,7 @@ Scanner_Extra_Error :: enum i32 {
Too_Short,
}
-Scanner_Error :: union {
+Scanner_Error :: union #shared_nil {
io.Error,
Scanner_Extra_Error,
}
@@ -68,7 +69,7 @@ scanner_destroy :: proc(s: ^Scanner) {
// Returns the first non-EOF error that was encounted by the scanner
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
switch s._err {
- case .EOF, .None:
+ case .EOF, nil:
return nil
}
return s._err
@@ -93,10 +94,6 @@ scanner_text :: proc(s: ^Scanner) -> string {
// scanner_scan advances the scanner
scanner_scan :: proc(s: ^Scanner) -> bool {
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
- err := err
- if err == .None {
- err = nil
- }
switch s._err {
case nil, .EOF:
s._err = err
diff --git a/core/c/libc/stdatomic.odin b/core/c/libc/stdatomic.odin
index fbe1ef7ea..9de3c4678 100644
--- a/core/c/libc/stdatomic.odin
+++ b/core/c/libc/stdatomic.odin
@@ -47,24 +47,25 @@ kill_dependency :: #force_inline proc(value: $T) -> T {
// 7.17.4 Fences
atomic_thread_fence :: #force_inline proc(order: memory_order) {
- switch (order) {
- case .relaxed:
- return
- case .consume:
- intrinsics.atomic_fence_acq()
- case .acquire:
- intrinsics.atomic_fence_acq()
- case .release:
- intrinsics.atomic_fence_rel()
- case .acq_rel:
- intrinsics.atomic_fence_acqrel()
- case .seq_cst:
- intrinsics.atomic_fence_acqrel()
+ assert(order != .relaxed)
+ assert(order != .consume)
+ #partial switch order {
+ case .acquire: intrinsics.atomic_thread_fence(.Acquire)
+ case .release: intrinsics.atomic_thread_fence(.Release)
+ case .acq_rel: intrinsics.atomic_thread_fence(.Acq_Rel)
+ case .seq_cst: intrinsics.atomic_thread_fence(.Seq_Cst)
}
}
atomic_signal_fence :: #force_inline proc(order: memory_order) {
- atomic_thread_fence(order)
+ assert(order != .relaxed)
+ assert(order != .consume)
+ #partial switch order {
+ case .acquire: intrinsics.atomic_signal_fence(.Acquire)
+ case .release: intrinsics.atomic_signal_fence(.Release)
+ case .acq_rel: intrinsics.atomic_signal_fence(.Acq_Rel)
+ case .seq_cst: intrinsics.atomic_signal_fence(.Seq_Cst)
+ }
}
// 7.17.5 Lock-free property
@@ -121,13 +122,10 @@ atomic_store_explicit :: #force_inline proc(object: ^$T, desired: T, order: memo
assert(order != .acquire)
assert(order != .acq_rel)
- #partial switch (order) {
- case .relaxed:
- intrinsics.atomic_store_relaxed(object, desired)
- case .release:
- intrinsics.atomic_store_rel(object, desired)
- case .seq_cst:
- intrinsics.atomic_store(object, desired)
+ #partial switch order {
+ case .relaxed: intrinsics.atomic_store_explicit(object, desired, .Relaxed)
+ case .release: intrinsics.atomic_store_explicit(object, desired, .Release)
+ case .seq_cst: intrinsics.atomic_store_explicit(object, desired, .Seq_Cst)
}
}
@@ -139,36 +137,26 @@ atomic_load_explicit :: #force_inline proc(object: ^$T, order: memory_order) {
assert(order != .release)
assert(order != .acq_rel)
- #partial switch (order) {
- case .relaxed:
- return intrinsics.atomic_load_relaxed(object)
- case .consume:
- return intrinsics.atomic_load_acq(object)
- case .acquire:
- return intrinsics.atomic_load_acq(object)
- case .seq_cst:
- return intrinsics.atomic_load(object)
+ #partial switch order {
+ case .relaxed: return intrinsics.atomic_load_explicit(object, .Relaxed)
+ case .consume: return intrinsics.atomic_load_explicit(object, .Consume)
+ case .acquire: return intrinsics.atomic_load_explicit(object, .Acquire)
+ case .seq_cst: return intrinsics.atomic_load_explicit(object, .Seq_Cst)
}
}
atomic_exchange :: #force_inline proc(object: ^$T, desired: T) -> T {
- return intrinsics.atomic_xchg(object, desired)
+ return intrinsics.atomic_exchange(object, desired)
}
atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: memory_order) -> T {
- switch (order) {
- case .relaxed:
- return intrinsics.atomic_xchg_relaxed(object, desired)
- case .consume:
- return intrinsics.atomic_xchg_acq(object, desired)
- case .acquire:
- return intrinsics.atomic_xchg_acq(object, desired)
- case .release:
- return intrinsics.atomic_xchg_rel(object, desired)
- case .acq_rel:
- return intrinsics.atomic_xchg_acqrel(object, desired)
- case .seq_cst:
- return intrinsics.atomic_xchg(object, desired)
+ switch order {
+ case .relaxed: return intrinsics.atomic_exchange_explicit(object, desired, .Relaxed)
+ case .consume: return intrinsics.atomic_exchange_explicit(object, desired, .Consume)
+ case .acquire: return intrinsics.atomic_exchange_explicit(object, desired, .Acquire)
+ case .release: return intrinsics.atomic_exchange_explicit(object, desired, .Release)
+ case .acq_rel: return intrinsics.atomic_exchange_explicit(object, desired, .Acq_Rel)
+ case .seq_cst: return intrinsics.atomic_exchange_explicit(object, desired, .Seq_Cst)
}
return false
}
@@ -189,102 +177,104 @@ atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: m
// [success = seq_cst, failure = acquire] => failacq
// [success = acquire, failure = relaxed] => acq_failrelaxed
// [success = acq_rel, failure = relaxed] => acqrel_failrelaxed
-atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) {
- value, ok := intrinsics.atomic_cxchg(object, expected^, desired)
+atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) -> bool {
+ value, ok := intrinsics.atomic_compare_exchange_strong(object, expected^, desired)
if !ok { expected^ = value }
return ok
}
-atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) {
+atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) -> bool {
assert(failure != .release)
assert(failure != .acq_rel)
value: T; ok: bool
- #partial switch (failure) {
+ #partial switch failure {
case .seq_cst:
assert(success != .relaxed)
- #partial switch (success) {
+ #partial switch success {
case .seq_cst:
- value, ok := intrinsics.atomic_cxchg(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst)
case .acquire:
- value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Seq_Cst)
case .consume:
- value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Seq_Cst)
case .release:
- value, ok := intrinsics.atomic_cxchg_rel(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Release, .Seq_Cst)
case .acq_rel:
- value, ok := intrinsics.atomic_cxchg_acqrel(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst)
}
case .relaxed:
assert(success != .release)
- #partial switch (success) {
+ #partial switch success {
case .relaxed:
- value, ok := intrinsics.atomic_cxchg_relaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Relaxed, .Relaxed)
case .seq_cst:
- value, ok := intrinsics.atomic_cxchg_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Relaxed)
case .acquire:
- value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Relaxed)
case .consume:
- value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Relaxed)
case .acq_rel:
- value, ok := intrinsics.atomic_cxchg_acqrel_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Relaxed)
}
case .consume:
- fallthrough
+ assert(success == .seq_cst)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Consume)
case .acquire:
assert(success == .seq_cst)
- value, ok := intrinsics.atomic_cxchg_failacq(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Acquire)
}
if !ok { expected^ = value }
return ok
}
-atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) {
- value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired)
+atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) -> bool {
+ value, ok := intrinsics.atomic_compare_exchange_weak(object, expected^, desired)
if !ok { expected^ = value }
return ok
}
-atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) {
+atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) -> bool {
assert(failure != .release)
assert(failure != .acq_rel)
value: T; ok: bool
- #partial switch (failure) {
+ #partial switch failure {
case .seq_cst:
assert(success != .relaxed)
- #partial switch (success) {
+ #partial switch success {
case .seq_cst:
- value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst)
case .acquire:
- value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Seq_Cst)
case .consume:
- value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Seq_Cst)
case .release:
- value, ok := intrinsics.atomic_cxchgweak_rel(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Release, .Seq_Cst)
case .acq_rel:
- value, ok := intrinsics.atomic_cxchgweak_acqrel(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst)
}
case .relaxed:
assert(success != .release)
- #partial switch (success) {
+ #partial switch success {
case .relaxed:
- value, ok := intrinsics.atomic_cxchgweak_relaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Relaxed, .Relaxed)
case .seq_cst:
- value, ok := intrinsics.atomic_cxchgweak_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Relaxed)
case .acquire:
- value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Relaxed)
case .consume:
- value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Relaxed)
case .acq_rel:
- value, ok := intrinsics.atomic_cxchgweak_acqrel_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Relaxed)
}
case .consume:
- fallthrough
+ assert(success == .seq_cst)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Consume)
case .acquire:
assert(success == .seq_cst)
- value, ok := intrinsics.atomic_cxchgweak_failacq(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Acquire)
}
if !ok { expected^ = value }
@@ -297,19 +287,14 @@ atomic_fetch_add :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_add_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
- switch (order) {
- case .relaxed:
- return intrinsics.atomic_add_relaxed(object, operand)
- case .consume:
- return intrinsics.atomic_add_acq(object, operand)
- case .acquire:
- return intrinsics.atomic_add_acq(object, operand)
- case .release:
- return intrinsics.atomic_add_rel(object, operand)
- case .acq_rel:
- return intrinsics.atomic_add_acqrel(object, operand)
- case .seq_cst:
- return intrinsics.atomic_add(object, operand)
+ switch order {
+ case .relaxed: return intrinsics.atomic_add_explicit(object, operand, .Relaxed)
+ case .consume: return intrinsics.atomic_add_explicit(object, operand, .Consume)
+ case .acquire: return intrinsics.atomic_add_explicit(object, operand, .Acquire)
+ case .release: return intrinsics.atomic_add_explicit(object, operand, .Release)
+ case .acq_rel: return intrinsics.atomic_add_explicit(object, operand, .Acq_Rel)
+ case: fallthrough
+ case .seq_cst: return intrinsics.atomic_add_explicit(object, operand, .Seq_Cst)
}
}
@@ -318,19 +303,14 @@ atomic_fetch_sub :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_sub_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
- switch (order) {
- case .relaxed:
- return intrinsics.atomic_sub_relaxed(object, operand)
- case .consume:
- return intrinsics.atomic_sub_acq(object, operand)
- case .acquire:
- return intrinsics.atomic_sub_acq(object, operand)
- case .release:
- return intrinsics.atomic_sub_rel(object, operand)
- case .acq_rel:
- return intrinsics.atomic_sub_acqrel(object, operand)
- case .seq_cst:
- return intrinsics.atomic_sub(object, operand)
+ switch order {
+ case .relaxed: return intrinsics.atomic_sub_explicit(object, operand, .Relaxed)
+ case .consume: return intrinsics.atomic_sub_explicit(object, operand, .Consume)
+ case .acquire: return intrinsics.atomic_sub_explicit(object, operand, .Acquire)
+ case .release: return intrinsics.atomic_sub_explicit(object, operand, .Release)
+ case .acq_rel: return intrinsics.atomic_sub_explicit(object, operand, .Acq_Rel)
+ case: fallthrough
+ case .seq_cst: return intrinsics.atomic_sub_explicit(object, operand, .Seq_Cst)
}
}
@@ -339,19 +319,14 @@ atomic_fetch_or :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_or_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
- switch (order) {
- case .relaxed:
- return intrinsics.atomic_or_relaxed(object, operand)
- case .consume:
- return intrinsics.atomic_or_acq(object, operand)
- case .acquire:
- return intrinsics.atomic_or_acq(object, operand)
- case .release:
- return intrinsics.atomic_or_rel(object, operand)
- case .acq_rel:
- return intrinsics.atomic_or_acqrel(object, operand)
- case .seq_cst:
- return intrinsics.atomic_or(object, operand)
+ switch order {
+ case .relaxed: return intrinsics.atomic_or_explicit(object, operand, .Relaxed)
+ case .consume: return intrinsics.atomic_or_explicit(object, operand, .Consume)
+ case .acquire: return intrinsics.atomic_or_explicit(object, operand, .Acquire)
+ case .release: return intrinsics.atomic_or_explicit(object, operand, .Release)
+ case .acq_rel: return intrinsics.atomic_or_explicit(object, operand, .Acq_Rel)
+ case: fallthrough
+ case .seq_cst: return intrinsics.atomic_or_explicit(object, operand, .Seq_Cst)
}
}
@@ -360,19 +335,14 @@ atomic_fetch_xor :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_xor_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
- switch (order) {
- case .relaxed:
- return intrinsics.atomic_xor_relaxed(object, operand)
- case .consume:
- return intrinsics.atomic_xor_acq(object, operand)
- case .acquire:
- return intrinsics.atomic_xor_acq(object, operand)
- case .release:
- return intrinsics.atomic_xor_rel(object, operand)
- case .acq_rel:
- return intrinsics.atomic_xor_acqrel(object, operand)
- case .seq_cst:
- return intrinsics.atomic_xor(object, operand)
+ switch order {
+ case .relaxed: return intrinsics.atomic_xor_explicit(object, operand, .Relaxed)
+ case .consume: return intrinsics.atomic_xor_explicit(object, operand, .Consume)
+ case .acquire: return intrinsics.atomic_xor_explicit(object, operand, .Acquire)
+ case .release: return intrinsics.atomic_xor_explicit(object, operand, .Release)
+ case .acq_rel: return intrinsics.atomic_xor_explicit(object, operand, .Acq_Rel)
+ case: fallthrough
+ case .seq_cst: return intrinsics.atomic_xor_explicit(object, operand, .Seq_Cst)
}
}
@@ -380,19 +350,14 @@ atomic_fetch_and :: #force_inline proc(object: ^$T, operand: T) -> T {
return intrinsics.atomic_and(object, operand)
}
atomic_fetch_and_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
- switch (order) {
- case .relaxed:
- return intrinsics.atomic_and_relaxed(object, operand)
- case .consume:
- return intrinsics.atomic_and_acq(object, operand)
- case .acquire:
- return intrinsics.atomic_and_acq(object, operand)
- case .release:
- return intrinsics.atomic_and_rel(object, operand)
- case .acq_rel:
- return intrinsics.atomic_and_acqrel(object, operand)
- case .seq_cst:
- return intrinsics.atomic_and(object, operand)
+ switch order {
+ case .relaxed: return intrinsics.atomic_and_explicit(object, operand, .Relaxed)
+ case .consume: return intrinsics.atomic_and_explicit(object, operand, .Consume)
+ case .acquire: return intrinsics.atomic_and_explicit(object, operand, .Acquire)
+ case .release: return intrinsics.atomic_and_explicit(object, operand, .Release)
+ case .acq_rel: return intrinsics.atomic_and_explicit(object, operand, .Acq_Rel)
+ case: fallthrough
+ case .seq_cst: return intrinsics.atomic_and_explicit(object, operand, .Seq_Cst)
}
}
diff --git a/core/compress/common.odin b/core/compress/common.odin
index fb0f74e95..58aeac25b 100644
--- a/core/compress/common.odin
+++ b/core/compress/common.odin
@@ -47,7 +47,7 @@ when size_of(uintptr) == 8 {
}
-Error :: union {
+Error :: union #shared_nil {
General_Error,
Deflate_Error,
ZLIB_Error,
@@ -58,6 +58,7 @@ Error :: union {
}
General_Error :: enum {
+ None = 0,
File_Not_Found,
Cannot_Open_File,
File_Too_Short,
@@ -76,6 +77,7 @@ General_Error :: enum {
}
GZIP_Error :: enum {
+ None = 0,
Invalid_GZIP_Signature,
Reserved_Flag_Set,
Invalid_Extra_Data,
@@ -100,6 +102,7 @@ GZIP_Error :: enum {
}
ZIP_Error :: enum {
+ None = 0,
Invalid_ZIP_File_Signature,
Unexpected_Signature,
Insert_Next_Disk,
@@ -107,6 +110,7 @@ ZIP_Error :: enum {
}
ZLIB_Error :: enum {
+ None = 0,
Unsupported_Window_Size,
FDICT_Unsupported,
Unsupported_Compression_Level,
@@ -114,6 +118,7 @@ ZLIB_Error :: enum {
}
Deflate_Error :: enum {
+ None = 0,
Huffman_Bad_Sizes,
Huffman_Bad_Code_Lengths,
Inflate_Error,
diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin
index d4b770929..855eef7a8 100644
--- a/core/compress/zlib/zlib.odin
+++ b/core/compress/zlib/zlib.odin
@@ -47,10 +47,10 @@ Options :: struct {
level: u8,
}
-Error :: compress.Error
-E_General :: compress.General_Error
-E_ZLIB :: compress.ZLIB_Error
-E_Deflate :: compress.Deflate_Error
+Error :: compress.Error
+General_Error :: compress.General_Error
+ZLIB_Error :: compress.ZLIB_Error
+Deflate_Error :: compress.Deflate_Error
DEFLATE_MAX_CHUNK_SIZE :: 65535
DEFLATE_MAX_LITERAL_SIZE :: 65535
@@ -258,7 +258,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
for i in 1 ..< HUFFMAN_MAX_BITS {
if sizes[i] > (1 << uint(i)) {
- return E_Deflate.Huffman_Bad_Sizes
+ return .Huffman_Bad_Sizes
}
}
code := int(0)
@@ -270,7 +270,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
code = code + sizes[i]
if sizes[i] != 0 {
if code - 1 >= (1 << u16(i)) {
- return E_Deflate.Huffman_Bad_Code_Lengths
+ return .Huffman_Bad_Code_Lengths
}
}
z.maxcode[i] = code << (HUFFMAN_MAX_BITS - uint(i))
@@ -314,15 +314,15 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
s += 1
}
if s >= 16 {
- return 0, E_Deflate.Bad_Huffman_Code
+ return 0, .Bad_Huffman_Code
}
// code size is s, so:
b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s])
if b >= size_of(t.size) {
- return 0, E_Deflate.Bad_Huffman_Code
+ return 0, .Bad_Huffman_Code
}
if t.size[b] != s {
- return 0, E_Deflate.Bad_Huffman_Code
+ return 0, .Bad_Huffman_Code
}
compress.consume_bits_lsb(z, s)
@@ -335,11 +335,11 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
if z.num_bits < 16 {
if z.num_bits > 63 {
- return 0, E_ZLIB.Code_Buffer_Malformed
+ return 0, .Code_Buffer_Malformed
}
compress.refill_lsb(z)
if z.num_bits > 63 {
- return 0, E_General.Stream_Too_Short
+ return 0, .Stream_Too_Short
}
}
#no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK]
@@ -361,7 +361,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
if value < 256 {
e := write_byte(z, u8(value))
if e != .None {
- return E_General.Output_Too_Short
+ return .Output_Too_Short
}
} else {
if value == 256 {
@@ -377,7 +377,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
value, e = decode_huffman(z, z_offset)
if e != nil {
- return E_Deflate.Bad_Huffman_Code
+ return .Bad_Huffman_Code
}
distance := Z_DIST_BASE[value]
@@ -387,7 +387,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
if z.bytes_written < i64(distance) {
// Distance is longer than we've decoded so far.
- return E_Deflate.Bad_Distance
+ return .Bad_Distance
}
/*
@@ -405,14 +405,14 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
c := z.output.buf[z.bytes_written - i64(distance)]
e := repl_byte(z, length, c)
if e != .None {
- return E_General.Output_Too_Short
+ return .Output_Too_Short
}
}
} else {
if length > 0 {
e := repl_bytes(z, length, distance)
if e != .None {
- return E_General.Output_Too_Short
+ return .Output_Too_Short
}
}
}
@@ -432,25 +432,25 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
if !raw {
size, size_err := compress.input_size(ctx)
if size < 6 || size_err != nil {
- return E_General.Stream_Too_Short
+ return .Stream_Too_Short
}
cmf, _ := compress.read_u8(ctx)
method := Compression_Method(cmf & 0xf)
if method != .DEFLATE {
- return E_General.Unknown_Compression_Method
+ return .Unknown_Compression_Method
}
if cinfo := (cmf >> 4) & 0xf; cinfo > 7 {
- return E_ZLIB.Unsupported_Window_Size
+ return .Unsupported_Window_Size
}
flg, _ := compress.read_u8(ctx)
fcheck := flg & 0x1f
fcheck_computed := (cmf << 8 | flg) & 0x1f
if fcheck != fcheck_computed {
- return E_General.Checksum_Failed
+ return .Checksum_Failed
}
/*
@@ -458,7 +458,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
They're application specific and PNG doesn't use them.
*/
if fdict := (flg >> 5) & 1; fdict != 0 {
- return E_ZLIB.FDICT_Unsupported
+ return .FDICT_Unsupported
}
// flevel := Compression_Level((flg >> 6) & 3);
@@ -485,7 +485,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
output_hash := hash.adler32(ctx.output.buf[:])
if output_hash != u32(adler) {
- return E_General.Checksum_Failed
+ return .Checksum_Failed
}
}
return nil
@@ -555,7 +555,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
if ~uncompressed_len != length_check {
- return E_Deflate.Len_Nlen_Mismatch
+ return .Len_Nlen_Mismatch
}
/*
@@ -571,7 +571,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
assert(uncompressed_len == 0)
case 3:
- return E_Deflate.BType_3
+ return .BType_3
case:
// fmt.printf("Err: %v | Final: %v | Type: %v\n", err, final, type)
if type == 1 {
@@ -604,7 +604,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
c = decode_huffman(z, codelength_ht) or_return
if c < 0 || c >= 19 {
- return E_Deflate.Huffman_Bad_Code_Lengths
+ return .Huffman_Bad_Code_Lengths
}
if c < 16 {
lencodes[n] = u8(c)
@@ -616,7 +616,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
case 16:
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3)
if n == 0 {
- return E_Deflate.Huffman_Bad_Code_Lengths
+ return .Huffman_Bad_Code_Lengths
}
fill = lencodes[n - 1]
case 17:
@@ -624,11 +624,11 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
case 18:
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11)
case:
- return E_Deflate.Huffman_Bad_Code_Lengths
+ return .Huffman_Bad_Code_Lengths
}
if ntot - n < u32(c) {
- return E_Deflate.Huffman_Bad_Code_Lengths
+ return .Huffman_Bad_Code_Lengths
}
nc := n + u32(c)
@@ -639,7 +639,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
}
if n != ntot {
- return E_Deflate.Huffman_Bad_Code_Lengths
+ return .Huffman_Bad_Code_Lengths
}
build_huffman(z_repeat, lencodes[:hlit]) or_return
diff --git a/core/encoding/hxa/read.odin b/core/encoding/hxa/read.odin
index ef7edc8b7..abe295530 100644
--- a/core/encoding/hxa/read.odin
+++ b/core/encoding/hxa/read.odin
@@ -39,6 +39,9 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
read_value :: proc(r: ^Reader, $T: typeid) -> (value: T, err: Read_Error) {
remaining := len(r.data) - r.offset
if remaining < size_of(T) {
+ if r.print_error {
+ fmt.eprintf("file '%s' failed to read value at offset %v\n", r.filename, r.offset)
+ }
err = .Short_Read
return
}
@@ -51,6 +54,10 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
read_array :: proc(r: ^Reader, $T: typeid, count: int) -> (value: []T, err: Read_Error) {
remaining := len(r.data) - r.offset
if remaining < size_of(T)*count {
+ if r.print_error {
+ fmt.eprintf("file '%s' failed to read array of %d elements at offset %v\n",
+ r.filename, count, r.offset)
+ }
err = .Short_Read
return
}
@@ -82,7 +89,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
type := read_value(r, Meta_Value_Type) or_return
if type > max(Meta_Value_Type) {
if r.print_error {
- fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is ", r.filename, u8(type), u8(max(Meta_Value_Type)))
+ fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is %d\n",
+ r.filename, u8(type), u8(max(Meta_Value_Type)))
}
err = .Invalid_Data
return
@@ -114,7 +122,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
type := read_value(r, Layer_Data_Type) or_return
if type > max(type) {
if r.print_error {
- fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is ", r.filename, u8(type), u8(max(Layer_Data_Type)))
+ fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is %d\n",
+ r.filename, u8(type), u8(max(Layer_Data_Type)))
}
err = .Invalid_Data
return
@@ -134,13 +143,23 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
}
if len(data) < size_of(Header) {
+ if print_error {
+ fmt.eprintf("HxA Error: file '%s' has no header\n", filename)
+ }
+ err = .Short_Read
return
}
context.allocator = allocator
header := cast(^Header)raw_data(data)
- assert(header.magic_number == MAGIC_NUMBER)
+ if (header.magic_number != MAGIC_NUMBER) {
+ if print_error {
+ fmt.eprintf("HxA Error: file '%s' has invalid magic number 0x%x\n", filename, header.magic_number)
+ }
+ err = .Invalid_Data
+ return
+ }
r := &Reader{
filename = filename,
@@ -150,6 +169,7 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
}
node_count := 0
+ file.header = header^
file.nodes = make([]Node, header.internal_node_count)
defer if err != nil {
nodes_destroy(file.nodes)
@@ -162,7 +182,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
type := read_value(r, Node_Type) or_return
if type > max(Node_Type) {
if r.print_error {
- fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is ", r.filename, u8(type), u8(max(Node_Type)))
+ fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is %d\n",
+ r.filename, u8(type), u8(max(Node_Type)))
}
err = .Invalid_Data
return
diff --git a/core/encoding/hxa/write.odin b/core/encoding/hxa/write.odin
index e774018b2..5bb950e81 100644
--- a/core/encoding/hxa/write.odin
+++ b/core/encoding/hxa/write.odin
@@ -84,7 +84,7 @@ write_internal :: proc(w: ^Writer, file: File) {
write_metadata :: proc(w: ^Writer, meta_data: []Meta) {
for m in meta_data {
- name_len := max(len(m.name), 255)
+ name_len := min(len(m.name), 255)
write_value(w, u8(name_len))
write_string(w, m.name[:name_len])
@@ -127,7 +127,7 @@ write_internal :: proc(w: ^Writer, file: File) {
write_layer_stack :: proc(w: ^Writer, layers: Layer_Stack) {
write_value(w, u32(len(layers)))
for layer in layers {
- name_len := max(len(layer.name), 255)
+ name_len := min(len(layer.name), 255)
write_value(w, u8(name_len))
write_string(w, layer .name[:name_len])
@@ -152,7 +152,7 @@ write_internal :: proc(w: ^Writer, file: File) {
return
}
- write_value(w, &Header{
+ write_value(w, Header{
magic_number = MAGIC_NUMBER,
version = LATEST_VERSION,
internal_node_count = u32le(len(file.nodes)),
diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin
index aa1c1559c..9c54f35f0 100644
--- a/core/encoding/json/marshal.odin
+++ b/core/encoding/json/marshal.odin
@@ -8,17 +8,18 @@ import "core:strings"
import "core:io"
Marshal_Data_Error :: enum {
+ None,
Unsupported_Type,
}
-Marshal_Error :: union {
+Marshal_Error :: union #shared_nil {
Marshal_Data_Error,
io.Error,
}
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
b := strings.make_builder(allocator)
- defer if err != .None {
+ defer if err != nil {
strings.destroy_builder(&b)
}
@@ -27,7 +28,7 @@ marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: M
if len(b.buf) != 0 {
data = b.buf[:]
}
- return data, .None
+ return data, nil
}
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
diff --git a/core/image/common.odin b/core/image/common.odin
index d72b770d5..adaa094d8 100644
--- a/core/image/common.odin
+++ b/core/image/common.odin
@@ -118,7 +118,7 @@ Option :: enum {
}
Options :: distinct bit_set[Option]
-Error :: union {
+Error :: union #shared_nil {
General_Image_Error,
PNG_Error,
@@ -137,6 +137,7 @@ General_Image_Error :: enum {
}
PNG_Error :: enum {
+ None = 0,
Invalid_PNG_Signature,
IHDR_Not_First_Chunk,
IHDR_Corrupt,
diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin
index 6ec674544..c64e6f471 100644
--- a/core/image/png/helpers.odin
+++ b/core/image/png/helpers.odin
@@ -439,7 +439,7 @@ when false {
flags: int = O_WRONLY|O_CREATE|O_TRUNC
if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) {
- return E_PNG.Invalid_Image_Dimensions
+ return .Invalid_Image_Dimensions
}
mode: int = 0
@@ -450,7 +450,7 @@ when false {
fd, fderr := open(filename, flags, mode)
if fderr != 0 {
- return E_General.Cannot_Open_File
+ return .Cannot_Open_File
}
defer close(fd)
@@ -473,7 +473,7 @@ when false {
case 3: ihdr.color_type = Color_Type{.Color}
case 4: ihdr.color_type = Color_Type{.Color, .Alpha}
case:// Unhandled
- return E_PNG.Unknown_Color_Type
+ return .Unknown_Color_Type
}
h := make_chunk(ihdr, .IHDR)
write_chunk(fd, h)
diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin
index c3ef787bc..ce8fd96ea 100644
--- a/core/intrinsics/intrinsics.odin
+++ b/core/intrinsics/intrinsics.odin
@@ -62,77 +62,44 @@ syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
// Atomics
-atomic_fence :: proc() ---
-atomic_fence_acq :: proc() ---
-atomic_fence_rel :: proc() ---
-atomic_fence_acqrel :: proc() ---
+Atomic_Memory_Order :: enum {
+ Relaxed = 0, // Unordered
+ Consume = 1, // Monotonic
+ Acquire = 2,
+ Release = 3,
+ Acq_Rel = 4,
+ Seq_Cst = 5,
+}
-atomic_store :: proc(dst: ^$T, val: T) ---
-atomic_store_rel :: proc(dst: ^$T, val: T) ---
-atomic_store_relaxed :: proc(dst: ^$T, val: T) ---
-atomic_store_unordered :: proc(dst: ^$T, val: T) ---
+atomic_thread_fence :: proc(order: Atomic_Memory_Order) ---
+atomic_signal_fence :: proc(order: Atomic_Memory_Order) ---
+
+atomic_store :: proc(dst: ^$T, val: T) ---
+atomic_store_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) ---
atomic_load :: proc(dst: ^$T) -> T ---
-atomic_load_acq :: proc(dst: ^$T) -> T ---
-atomic_load_relaxed :: proc(dst: ^$T) -> T ---
-atomic_load_unordered :: proc(dst: ^$T) -> T ---
+atomic_load_explicit :: proc(dst: ^$T, order: Atomic_Memory_Order) -> T ---
-atomic_add :: proc(dst; ^$T, val: T) -> T ---
-atomic_add_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_add_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_add_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_add_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_and :: proc(dst; ^$T, val: T) -> T ---
-atomic_and_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_and_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_and_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_and_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_or :: proc(dst; ^$T, val: T) -> T ---
-atomic_or_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_or_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_or_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_or_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor_relaxed :: proc(dst; ^$T, val: T) -> T ---
+atomic_add :: proc(dst; ^$T, val: T) -> T ---
+atomic_add_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_sub :: proc(dst; ^$T, val: T) -> T ---
+atomic_sub_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_and :: proc(dst; ^$T, val: T) -> T ---
+atomic_and_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_nand :: proc(dst; ^$T, val: T) -> T ---
+atomic_nand_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_or :: proc(dst; ^$T, val: T) -> T ---
+atomic_or_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_xor :: proc(dst; ^$T, val: T) -> T ---
+atomic_xor_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_exchange :: proc(dst; ^$T, val: T) -> T ---
+atomic_exchange_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
-atomic_xchg :: proc(dst; ^$T, val: T) -> T ---
-atomic_xchg_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_xchg_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_xchg_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_xchg_relaxed :: proc(dst; ^$T, val: T) -> T ---
+atomic_compare_exchange_strong :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
+atomic_compare_exchange_strong_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok ---
+atomic_compare_exchange_weak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
+atomic_compare_exchange_weak_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok ---
-atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-
-atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
// Constant type tests
@@ -201,6 +168,10 @@ type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, raw
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
+// WASM targets only
+wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
+wasm_memory_size :: proc(index: uintptr) -> int ---
+
// Internal compiler use only
__entry_point :: proc() ---
\ No newline at end of file
diff --git a/core/math/ease/ease.odin b/core/math/ease/ease.odin
new file mode 100644
index 000000000..5a767b5a9
--- /dev/null
+++ b/core/math/ease/ease.odin
@@ -0,0 +1,483 @@
+// easing procedures and flux easing used for animations
+package ease
+
+import "core:math"
+import "core:intrinsics"
+import "core:time"
+
+@(private) PI_2 :: math.PI / 2
+
+// converted to odin from https://github.com/warrenm/AHEasing
+// with additional enum based call
+
+// Modeled after the parabola y = x^2
+quadratic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p * p
+}
+
+// Modeled after the parabola y = -x^2 + 2x
+quadratic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return -(p * (p - 2))
+}
+
+// Modeled after the piecewise quadratic
+// y = (1/2)((2x)^2) ; [0, 0.5)
+// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1]
+quadratic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 2 * p * p
+ } else {
+ return (-2 * p * p) + (4 * p) - 1
+ }
+}
+
+// Modeled after the cubic y = x^3
+cubic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p * p * p
+}
+
+// Modeled after the cubic y = (x - 1)^3 + 1
+cubic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ f := p - 1
+ return f * f * f + 1
+}
+
+// Modeled after the piecewise cubic
+// y = (1/2)((2x)^3) ; [0, 0.5)
+// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1]
+cubic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 4 * p * p * p
+ } else {
+ f := (2 * p) - 2
+ return 0.5 * f * f * f + 1
+ }
+}
+
+// Modeled after the quartic x^4
+quartic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p * p * p * p
+}
+
+// Modeled after the quartic y = 1 - (x - 1)^4
+quartic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ f := p - 1
+ return f * f * f * (1 - p) + 1
+}
+
+// Modeled after the piecewise quartic
+// y = (1/2)((2x)^4) ; [0, 0.5)
+// y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1]
+quartic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 8 * p * p * p * p
+ } else {
+ f := p - 1
+ return -8 * f * f * f * f + 1
+ }
+}
+
+// Modeled after the quintic y = x^5
+quintic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p * p * p * p * p
+}
+
+// Modeled after the quintic y = (x - 1)^5 + 1
+quintic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ f := p - 1
+ return f * f * f * f * f + 1
+}
+
+// Modeled after the piecewise quintic
+// y = (1/2)((2x)^5) ; [0, 0.5)
+// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1]
+quintic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 16 * p * p * p * p * p
+ } else {
+ f := (2 * p) - 2
+ return 0.5 * f * f * f * f * f + 1
+ }
+}
+
+// Modeled after quarter-cycle of sine wave
+sine_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return math.sin((p - 1) * PI_2) + 1
+}
+
+// Modeled after quarter-cycle of sine wave (different phase)
+sine_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return math.sin(p * PI_2)
+}
+
+// Modeled after half sine wave
+sine_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return 0.5 * (1 - math.cos(p * math.PI))
+}
+
+// Modeled after shifted quadrant IV of unit circle
+circular_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return 1 - math.sqrt(1 - (p * p))
+}
+
+// Modeled after shifted quadrant II of unit circle
+circular_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return math.sqrt((2 - p) * p)
+}
+
+// Modeled after the piecewise circular function
+// y = (1/2)(1 - sqrt(1 - 4x^2)) ; [0, 0.5)
+// y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1]
+circular_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 0.5 * (1 - math.sqrt(1 - 4 * (p * p)))
+ } else {
+ return 0.5 * (math.sqrt(-((2 * p) - 3) * ((2 * p) - 1)) + 1)
+ }
+}
+
+// Modeled after the exponential function y = 2^(10(x - 1))
+exponential_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p == 0.0 ? p : math.pow(2, 10 * (p - 1))
+}
+
+// Modeled after the exponential function y = -2^(-10x) + 1
+exponential_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p == 1.0 ? p : 1 - math.pow(2, -10 * p)
+}
+
+// Modeled after the piecewise exponential
+// y = (1/2)2^(10(2x - 1)) ; [0,0.5)
+// y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1]
+exponential_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p == 0.0 || p == 1.0 {
+ return p
+ }
+
+ if p < 0.5 {
+ return 0.5 * math.pow(2, (20 * p) - 10)
+ } else {
+ return -0.5 * math.pow(2, (-20 * p) + 10) + 1
+ }
+}
+
+// Modeled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1))
+elastic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return math.sin(13 * PI_2 * p) * math.pow(2, 10 * (p - 1))
+}
+
+// Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) + 1
+elastic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return math.sin(-13 * PI_2 * (p + 1)) * math.pow(2, -10 * p) + 1
+}
+
+// Modeled after the piecewise exponentially-damped sine wave:
+// y = (1/2)*sin(13pi/2*(2*x))*pow(2, 10 * ((2*x) - 1)) ; [0,0.5)
+// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1]
+elastic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 0.5 * math.sin(13 * PI_2 * (2 * p)) * math.pow(2, 10 * ((2 * p) - 1))
+ } else {
+ return 0.5 * (math.sin(-13 * PI_2 * ((2 * p - 1) + 1)) * math.pow(2, -10 * (2 * p - 1)) + 2)
+ }
+}
+
+// Modeled after the overshooting cubic y = x^3-x*sin(x*pi)
+back_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p * p * p - p * math.sin(p * math.PI)
+}
+
+// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi))
+back_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ f := 1 - p
+ return 1 - (f * f * f - f * math.sin(f * math.PI))
+}
+
+// Modeled after the piecewise overshooting cubic function:
+// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5)
+// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1]
+back_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ f := 2 * p
+ return 0.5 * (f * f * f - f * math.sin(f * math.PI))
+ } else {
+ f := (1 - (2*p - 1))
+ return 0.5 * (1 - (f * f * f - f * math.sin(f * math.PI))) + 0.5
+ }
+}
+
+bounce_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return 1 - bounce_out(1 - p)
+}
+
+bounce_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 4/11.0 {
+ return (121 * p * p)/16.0
+ } else if p < 8/11.0 {
+ return (363/40.0 * p * p) - (99/10.0 * p) + 17/5.0
+ } else if p < 9/10.0 {
+ return (4356/361.0 * p * p) - (35442/1805.0 * p) + 16061/1805.0
+ } else {
+ return (54/5.0 * p * p) - (513/25.0 * p) + 268/25.0
+ }
+}
+
+bounce_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 0.5 * bounce_in(p*2)
+ } else {
+ return 0.5 * bounce_out(p * 2 - 1) + 0.5
+ }
+}
+
+// additional enum variant
+
+Ease :: enum {
+ Linear,
+
+ Quadratic_In,
+ Quadratic_Out,
+ Quadratic_In_Out,
+
+ Cubic_In,
+ Cubic_Out,
+ Cubic_In_Out,
+
+ Quartic_In,
+ Quartic_Out,
+ Quartic_In_Out,
+
+ Quintic_In,
+ Quintic_Out,
+ Quintic_In_Out,
+
+ Sine_In,
+ Sine_Out,
+ Sine_In_Out,
+
+ Circular_In,
+ Circular_Out,
+ Circular_In_Out,
+
+ Exponential_In,
+ Exponential_Out,
+ Exponential_In_Out,
+
+ Elastic_In,
+ Elastic_Out,
+ Elastic_In_Out,
+
+ Back_In,
+ Back_Out,
+ Back_In_Out,
+
+ Bounce_In,
+ Bounce_Out,
+ Bounce_In_Out,
+}
+
+ease :: proc "contextless" (type: Ease, p: $T) -> T
+ where intrinsics.type_is_float(T) {
+ switch type {
+ case .Linear: return p
+
+ case .Quadratic_In: return quadratic_in(p)
+ case .Quadratic_Out: return quadratic_out(p)
+ case .Quadratic_In_Out: return quadratic_in_out(p)
+
+ case .Cubic_In: return cubic_in(p)
+ case .Cubic_Out: return cubic_out(p)
+ case .Cubic_In_Out: return cubic_in_out(p)
+
+ case .Quartic_In: return quartic_in(p)
+ case .Quartic_Out: return quartic_out(p)
+ case .Quartic_In_Out: return quartic_in_out(p)
+
+ case .Quintic_In: return quintic_in(p)
+ case .Quintic_Out: return quintic_out(p)
+ case .Quintic_In_Out: return quintic_in_out(p)
+
+ case .Sine_In: return sine_in(p)
+ case .Sine_Out: return sine_out(p)
+ case .Sine_In_Out: return sine_in_out(p)
+
+ case .Circular_In: return circular_in(p)
+ case .Circular_Out: return circular_out(p)
+ case .Circular_In_Out: return circular_in_out(p)
+
+ case .Exponential_In: return exponential_in(p)
+ case .Exponential_Out: return exponential_out(p)
+ case .Exponential_In_Out: return exponential_in_out(p)
+
+ case .Elastic_In: return elastic_in(p)
+ case .Elastic_Out: return elastic_out(p)
+ case .Elastic_In_Out: return elastic_in_out(p)
+
+ case .Back_In: return back_in(p)
+ case .Back_Out: return back_out(p)
+ case .Back_In_Out: return back_in_out(p)
+
+ case .Bounce_In: return bounce_in(p)
+ case .Bounce_Out: return bounce_out(p)
+ case .Bounce_In_Out: return bounce_in_out(p)
+ }
+
+ // in case type was invalid
+ return 0
+}
+
+Flux_Map :: struct($T: typeid) {
+ values: map[^T]Flux_Tween(T),
+}
+
+Flux_Tween :: struct($T: typeid) {
+ value: ^T,
+ start: T,
+ diff: T,
+ goal: T,
+
+ delay: f64, // in seconds
+ duration: time.Duration,
+
+ progress: f64,
+ rate: f64,
+ type: Ease,
+
+ inited: bool,
+
+ // callbacks, data can be set, will be pushed to callback
+ data: rawptr, // by default gets set to value input
+ on_start: proc(flux: ^Flux_Map(T), data: rawptr),
+ on_update: proc(flux: ^Flux_Map(T), data: rawptr),
+ on_complete: proc(flux: ^Flux_Map(T), data: rawptr),
+}
+
+// init flux map to a float type and a wanted cap
+flux_init :: proc($T: typeid, cap := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
+ return {
+ make(map[^T]Flux_Tween(T), cap),
+ }
+}
+
+// delete map content
+flux_destroy :: proc(flux: Flux_Map($T)) where intrinsics.type_is_float(T) {
+ delete(flux.values)
+}
+
+// clear map content, stops all animations
+flux_clear :: proc(flux: ^Flux_Map($T)) where intrinsics.type_is_float(T) {
+ clear(&flux.values)
+}
+
+// append / overwrite existing tween value to parameters
+// rest is initialized in flux_tween_init, inside update
+// return value can be used to set callbacks
+flux_to :: proc(
+ flux: ^Flux_Map($T),
+ value: ^f32,
+ goal: f32,
+ type: Ease = .Quadratic_Out,
+ duration: time.Duration = time.Second,
+ delay: f64 = 0,
+) -> (tween: ^Flux_Tween(T)) where intrinsics.type_is_float(T) {
+ if res, ok := &flux.values[value]; ok {
+ tween = res
+ } else {
+ flux.values[value] = {}
+ tween = &flux.values[value]
+ }
+
+ tween^ = {
+ value = value,
+ goal = goal,
+ duration = duration,
+ delay = delay,
+ type = type,
+ data = value,
+ }
+
+ return
+}
+
+// init internal properties
+flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where intrinsics.type_is_float(T) {
+ tween.inited = true
+ tween.start = tween.value^
+ tween.diff = tween.goal - tween.value^
+ s := time.duration_seconds(duration)
+ tween.rate = duration > 0 ? 1.0 / s : 0
+ tween.progress = duration > 0 ? 0 : 1
+}
+
+// update all tweens, wait for their delay if one exists
+// calls callbacks in all stages, when they're filled
+// deletes tween from the map after completion
+flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) {
+ for key, tween in &flux.values {
+ delay_remainder := f64(0)
+
+ // Update delay if necessary.
+ if tween.delay > 0 {
+ tween.delay -= dt
+
+ if tween.delay < 0 {
+ // We finished the delay, but in doing so consumed part of this frame's `dt` budget.
+ // Keep track of it so we can apply it to this tween without affecting others.
+ delay_remainder = tween.delay
+ // We're done with this delay.
+ tween.delay = 0
+ }
+ }
+
+ // We either had no delay, or the delay has been consumed.
+ if tween.delay <= 0 {
+ if !tween.inited {
+ flux_tween_init(&tween, tween.duration)
+
+ if tween.on_start != nil {
+ tween.on_start(flux, tween.data)
+ }
+ }
+
+ // If part of the `dt` budget was consumed this frame, then `delay_remainder` will be
+ // that remainder, a negative value. Adding it to `dt` applies what's left of the `dt`
+ // to the tween so it advances properly, instead of too much or little.
+ tween.progress += tween.rate * (dt + delay_remainder)
+ x := tween.progress >= 1 ? 1 : ease(tween.type, tween.progress)
+ tween.value^ = tween.start + tween.diff * T(x)
+
+ if tween.on_update != nil {
+ tween.on_update(flux, tween.data)
+ }
+
+ if tween.progress >= 1 {
+ delete_key(&flux.values, key)
+
+ if tween.on_complete != nil {
+ tween.on_complete(flux, tween.data)
+ }
+ }
+ }
+ }
+}
+
+// stop a specific key inside the map
+// returns true when it successfully removed the key
+flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) {
+ if key in flux.values {
+ delete_key(&flux.values, key)
+ return true
+ }
+
+ return false
+}
+
+// returns the amount of time left for the tween animation, if the key exists in the map
+// returns 0 if the tween doesnt exist on the map
+flux_tween_time_left :: proc(flux: Flux_Map($T), key: ^T) -> f64 {
+ if tween, ok := flux.values[key]; ok {
+ return ((1 - tween.progress) * tween.rate) + tween.delay
+ } else {
+ return 0
+ }
+}
diff --git a/core/math/math.odin b/core/math/math.odin
index afbda051d..b711c160f 100644
--- a/core/math/math.odin
+++ b/core/math/math.odin
@@ -1206,7 +1206,7 @@ prod :: proc "contextless" (x: $T/[]$E) -> (res: E)
return
}
-cumsum_inplace :: proc "contextless" (x: $T/[]$E) -> T
+cumsum_inplace :: proc "contextless" (x: $T/[]$E)
where intrinsics.type_is_numeric(E) {
for i in 1.. ([]byte, Allocator_Error) {
- return nil, nil
+ switch mode {
+ case .Alloc:
+ return nil, .Out_Of_Memory
+ case .Free:
+ return nil, .None
+ case .Free_All:
+ return nil, .Mode_Not_Implemented
+ case .Resize:
+ if size == 0 {
+ return nil, .None
+ }
+ return nil, .Out_Of_Memory
+ case .Query_Features:
+ return nil, .Mode_Not_Implemented
+ case .Query_Info:
+ return nil, .Mode_Not_Implemented
+ }
+ return nil, .None
}
nil_allocator :: proc() -> Allocator {
diff --git a/core/mem/mem.odin b/core/mem/mem.odin
index 8eb877e75..817c0ea5f 100644
--- a/core/mem/mem.odin
+++ b/core/mem/mem.odin
@@ -16,7 +16,7 @@ zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr {
// equivalent semantics to those provided by the C11 Annex K 3.7.4.1
// memset_s call.
intrinsics.mem_zero_volatile(data, len) // Use the volatile mem_zero
- intrinsics.atomic_fence() // Prevent reordering
+ intrinsics.atomic_thread_fence(.Seq_Cst) // Prevent reordering
return data
}
zero_item :: proc "contextless" (item: $P/^$T) {
diff --git a/core/mem/virtual/virtual_platform.odin b/core/mem/virtual/virtual_platform.odin
index d707bc427..367346f63 100644
--- a/core/mem/virtual/virtual_platform.odin
+++ b/core/mem/virtual/virtual_platform.odin
@@ -1,7 +1,7 @@
//+private
package mem_virtual
-import sync "core:sync/sync2"
+import "core:sync"
Platform_Memory_Block :: struct {
block: Memory_Block,
diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin
index 740299539..f4aa67446 100644
--- a/core/odin/ast/ast.odin
+++ b/core/odin/ast/ast.odin
@@ -708,13 +708,19 @@ Struct_Type :: struct {
name_count: int,
}
+Union_Type_Kind :: enum u8 {
+ Normal,
+ maybe,
+ no_nil,
+ shared_nil,
+}
+
Union_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
poly_params: ^Field_List,
align: ^Expr,
- is_maybe: bool,
- is_no_nil: bool,
+ kind: Union_Type_Kind,
where_token: tokenizer.Token,
where_clauses: []^Expr,
variants: []^Expr,
diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin
index 1546dce2d..25eda6bed 100644
--- a/core/odin/parser/parser.odin
+++ b/core/odin/parser/parser.odin
@@ -2630,8 +2630,9 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
tok := expect_token(p, .Union)
poly_params: ^ast.Field_List
align: ^ast.Expr
- is_maybe: bool
- is_no_nil: bool
+ is_maybe: bool
+ is_no_nil: bool
+ is_shared_nil: bool
if allow_token(p, .Open_Paren) {
param_count: int
@@ -2663,12 +2664,34 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
}
is_no_nil = true
+ case "shared_nil":
+ if is_shared_nil {
+ error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
+ }
+ is_shared_nil = true
case:
error(p, tag.pos, "invalid union tag '#%s", tag.text)
}
}
p.expr_level = prev_level
+ if is_no_nil && is_maybe {
+ error(p, p.curr_tok.pos, "#maybe and #no_nil cannot be applied together")
+ }
+ if is_no_nil && is_shared_nil {
+ error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together")
+ }
+ if is_shared_nil && is_maybe {
+ error(p, p.curr_tok.pos, "#maybe and #shared_nil cannot be applied together")
+ }
+
+ union_kind := ast.Union_Type_Kind.Normal
+ switch {
+ case is_maybe: union_kind = .maybe
+ case is_no_nil: union_kind = .no_nil
+ case is_shared_nil: union_kind = .shared_nil
+ }
+
where_token: tokenizer.Token
where_clauses: []^ast.Expr
@@ -2699,14 +2722,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
close := expect_closing_brace_of_field_list(p)
+
+
ut := ast.new(ast.Union_Type, tok.pos, end_pos(close))
ut.poly_params = poly_params
ut.variants = variants[:]
ut.align = align
ut.where_token = where_token
ut.where_clauses = where_clauses
- ut.is_maybe = is_maybe
- ut.is_no_nil = is_no_nil
+ ut.kind = union_kind
return ut
diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin
index 9eba29987..70140f180 100644
--- a/core/odin/printer/visit.odin
+++ b/core/odin/printer/visit.odin
@@ -1046,8 +1046,11 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_poly_params(p, v.poly_params)
- if v.is_maybe {
- push_ident_token(p, "#maybe", 1)
+ switch v.kind {
+ case .Normal:
+ case .maybe: push_ident_token(p, "#maybe", 1)
+ case .no_nil: push_ident_token(p, "#no_nil", 1)
+ case .shared_nil: push_ident_token(p, "#shared_nil", 1)
}
push_where_clauses(p, v.where_clauses)
diff --git a/core/os/dir_windows.odin b/core/os/dir_windows.odin
index ff7e53293..3261b8cb3 100644
--- a/core/os/dir_windows.odin
+++ b/core/os/dir_windows.odin
@@ -82,6 +82,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
wpath_search[len(wpath)+2] = 0
path := cleanpath_from_buf(wpath)
+ defer delete(path)
find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin
index 028951fe3..7fc7a4ac0 100644
--- a/core/os/os2/process.odin
+++ b/core/os/os2/process.odin
@@ -1,6 +1,6 @@
package os2
-import sync "core:sync/sync2"
+import "core:sync"
import "core:time"
import "core:runtime"
diff --git a/core/os/stat.odin b/core/os/stat.odin
index 6f4c8b0ef..1b64ad33b 100644
--- a/core/os/stat.odin
+++ b/core/os/stat.odin
@@ -2,7 +2,6 @@ package os
import "core:time"
-
File_Info :: struct {
fullpath: string,
name: string,
diff --git a/core/os/stat_windows.odin b/core/os/stat_windows.odin
index 964625315..5da925560 100644
--- a/core/os/stat_windows.odin
+++ b/core/os/stat_windows.odin
@@ -80,7 +80,7 @@ stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno)
return _stat(name, attrs, allocator)
}
-fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno) {
+fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, errno: Errno) {
if fd == 0 {
return {}, ERROR_INVALID_HANDLE
}
@@ -94,14 +94,14 @@ fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno)
h := win32.HANDLE(fd)
switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
- fi: File_Info
- fi.fullpath = path
fi.name = basename(path)
fi.mode |= file_type_mode(h)
- return fi, ERROR_NONE
+ errno = ERROR_NONE
+ case:
+ fi, errno = file_info_from_get_file_information_by_handle(path, h)
}
-
- return file_info_from_get_file_information_by_handle(path, h)
+ fi.fullpath = path
+ return
}
diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin
index 6399f86a2..252912710 100644
--- a/core/path/filepath/match.odin
+++ b/core/path/filepath/match.odin
@@ -220,9 +220,11 @@ get_escape :: proc(chunk: string) -> (r: rune, next_chunk: string, err: Match_Er
//
glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []string, err: Match_Error) {
+ context.allocator = allocator
+
if !has_meta(pattern) {
// TODO(bill): os.lstat on here to check for error
- m := make([]string, 1, allocator)
+ m := make([]string, 1)
m[0] = pattern
return m[:], .None
}
@@ -232,6 +234,7 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
when ODIN_OS == .Windows {
temp_buf: [8]byte
volume_len, dir = clean_glob_path_windows(dir, temp_buf[:])
+
} else {
dir = clean_glob_path(dir)
}
@@ -246,7 +249,7 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
if err != .None {
return
}
- dmatches := make([dynamic]string, 0, 0, allocator)
+ dmatches := make([dynamic]string, 0, 0)
for d in m {
dmatches, err = _glob(d, file, &dmatches)
if err != .None {
@@ -258,11 +261,13 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
}
return
}
-_glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]string, e: Match_Error) {
+_glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := context.allocator) -> (m: [dynamic]string, e: Match_Error) {
+ context.allocator = allocator
+
if matches != nil {
m = matches^
} else {
- m = make([dynamic]string, 0, 0, context.allocator)
+ m = make([dynamic]string, 0, 0)
}
@@ -275,6 +280,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s
{
file_info, ferr := os.fstat(d)
defer os.file_info_delete(file_info)
+
if ferr != 0 {
return
}
diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin
index ba6e11044..42714d736 100644
--- a/core/path/filepath/path.odin
+++ b/core/path/filepath/path.odin
@@ -1,5 +1,5 @@
// The path/filepath package uses either forward slashes or backslashes depending on the operating system
-// To process paths usch as URLs that depend on forward slashes regardless of the OS, use the path package
+// To process paths such as URLs that depend on forward slashes regardless of the OS, use the path package
package filepath
import "core:strings"
@@ -122,6 +122,7 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
vol_and_path = original_path,
vol_len = vol_len,
}
+ defer lazy_buffer_destroy(out)
r, dot_dot := 0, 0
if rooted {
@@ -170,7 +171,6 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
cleaned, new_allocation := from_slash(s)
if new_allocation {
delete(s)
- lazy_buffer_destroy(out)
}
return cleaned
}
@@ -300,6 +300,11 @@ dir :: proc(path: string, allocator := context.allocator) -> string {
+// Splits the PATH-like `path` string, returning an array of its separated components (delete after use).
+// For Windows the separator is `;`, for Unix it's `:`.
+// An empty string returns nil. A non-empty string with no separators returns a 1-element array.
+// Any empty components will be included, e.g. `a::b` will return a 3-element array, as will `::`.
+// Separators within pairs of double-quotes will be ignored and stripped, e.g. `"a:b"c:d` will return []{`a:bc`, `d`}.
split_list :: proc(path: string, allocator := context.allocator) -> []string {
if path == "" {
return nil
@@ -322,7 +327,7 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string {
}
start, quote = 0, false
- list := make([]string, count, allocator)
+ list := make([]string, count + 1, allocator)
index := 0
for i := 0; i < len(path); i += 1 {
c := path[i]
@@ -336,6 +341,7 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string {
}
}
assert(index == count)
+ list[index] = path[start:]
for s0, i in list {
s, new := strings.replace_all(s0, `"`, ``, allocator)
diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin
index d05026532..49d7ef9b5 100644
--- a/core/reflect/reflect.odin
+++ b/core/reflect/reflect.odin
@@ -1054,6 +1054,7 @@ as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
case Type_Info_Float:
valid = true
switch v in a {
+ case f16: value = u64(v)
case f32: value = u64(v)
case f64: value = u64(v)
case f32le: value = u64(v)
@@ -1159,6 +1160,7 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
case Type_Info_Float:
valid = true
switch v in a {
+ case f16: value = f64(v)
case f32: value = f64(v)
case f64: value = (v)
case f32le: value = f64(v)
diff --git a/core/runtime/core.odin b/core/runtime/core.odin
index 8c95a234f..a5a190a9c 100644
--- a/core/runtime/core.odin
+++ b/core/runtime/core.odin
@@ -136,6 +136,7 @@ Type_Info_Union :: struct {
custom_align: bool,
no_nil: bool,
maybe: bool,
+ shared_nil: bool,
}
Type_Info_Enum :: struct {
base: ^Type_Info,
diff --git a/core/runtime/entry_unix.odin b/core/runtime/entry_unix.odin
index 1a3def200..9f7d219c3 100644
--- a/core/runtime/entry_unix.odin
+++ b/core/runtime/entry_unix.odin
@@ -30,4 +30,4 @@ when ODIN_BUILD_MODE == .Dynamic {
#force_no_inline _cleanup_runtime()
return 0
}
-}
\ No newline at end of file
+}
diff --git a/core/strings/strings.odin b/core/strings/strings.odin
index 670da166b..d01be7989 100644
--- a/core/strings/strings.odin
+++ b/core/strings/strings.odin
@@ -1,16 +1,21 @@
+// simple procedures to manipulate UTF-8 encoded strings
package strings
import "core:io"
import "core:mem"
+import "core:slice"
import "core:unicode"
import "core:unicode/utf8"
+// returns a clone of the string `s` allocated using the `allocator`
clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> string {
c := make([]byte, len(s), allocator, loc)
copy(c, s)
return string(c[:len(s)])
}
+// returns a clone of the string `s` allocated using the `allocator` as a cstring
+// a nul byte is appended to the clone, to make the cstring safe
clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> cstring {
c := make([]byte, len(s)+1, allocator, loc)
copy(c, s)
@@ -18,27 +23,35 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #call
return cstring(&c[0])
}
+// returns a string from a byte pointer `ptr` and byte length `len`
+// the string is valid as long as the parameters stay alive
string_from_ptr :: proc(ptr: ^byte, len: int) -> string {
return transmute(string)mem.Raw_String{ptr, len}
}
+// returns a string from a byte pointer `ptr and byte length `len`
+// searches for a nul byte from 0.. string {
s := transmute(string)mem.Raw_String{ptr, len}
s = truncate_to_byte(s, 0)
return s
}
-
+// returns the raw ^byte start of the string `str`
ptr_from_string :: proc(str: string) -> ^byte {
d := transmute(mem.Raw_String)str
return d.data
}
+// returns the transmute of string `str` to a cstring
+// not safe since the origin string may not contain a nul byte
unsafe_string_to_cstring :: proc(str: string) -> cstring {
d := transmute(mem.Raw_String)str
return cstring(d.data)
}
+// returns a string truncated to the first time it finds the byte `b`
+// uses the `len` of the string `str` when it couldn't find the input
truncate_to_byte :: proc(str: string, b: byte) -> string {
n := index_byte(str, b)
if n < 0 {
@@ -46,6 +59,9 @@ truncate_to_byte :: proc(str: string, b: byte) -> string {
}
return str[:n]
}
+
+// returns a string truncated to the first time it finds the rune `r`
+// uses the `len` of the string `str` when it couldn't find the input
truncate_to_rune :: proc(str: string, r: rune) -> string {
n := index_rune(str, r)
if n < 0 {
@@ -54,20 +70,28 @@ truncate_to_rune :: proc(str: string, r: rune) -> string {
return str[:n]
}
+// returns a cloned string of the byte array `s` using the `allocator`
+// appends a leading nul byte
clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> string {
c := make([]byte, len(s)+1, allocator, loc)
copy(c, s)
c[len(s)] = 0
return string(c[:len(s)])
}
+
+// returns a clone of the cstring `s` using the `allocator` as a string
clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> string {
return clone(string(s), allocator, loc)
}
+
+// returns a cloned string from the pointer `ptr` and a byte length `len` using the `allocator`
+// same to `string_from_ptr` but allocates
clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc := #caller_location) -> string {
s := string_from_ptr(ptr, len)
return clone(s, allocator, loc)
}
+// overload to clone from a `string`, `[]byte`, `cstring` or a `^byte + length` to a string
clone_from :: proc{
clone,
clone_from_bytes,
@@ -75,6 +99,8 @@ clone_from :: proc{
clone_from_ptr,
}
+// returns a cloned string from the cstring `ptr` and a byte length `len` using the `allocator`
+// truncates till the first nul byte it finds or the byte len
clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.allocator, loc := #caller_location) -> string {
s := string_from_ptr((^u8)(ptr), len)
s = truncate_to_byte(s, 0)
@@ -82,11 +108,12 @@ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.
}
// Compares two strings, returning a value representing which one comes first lexiographically.
-// -1 for `a`; 1 for `b`, or 0 if they are equal.
+// -1 for `lhs`; 1 for `rhs`, or 0 if they are equal.
compare :: proc(lhs, rhs: string) -> int {
return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs)
}
+// returns the byte offset of the rune `r` in the string `s`, -1 when not found
contains_rune :: proc(s: string, r: rune) -> int {
for c, offset in s {
if c == r {
@@ -96,20 +123,48 @@ contains_rune :: proc(s: string, r: rune) -> int {
return -1
}
+/*
+ returns true when the string `substr` is contained inside the string `s`
+
+ strings.contains("testing", "test") -> true
+ strings.contains("testing", "ing") -> true
+ strings.contains("testing", "text") -> false
+*/
contains :: proc(s, substr: string) -> bool {
return index(s, substr) >= 0
}
+/*
+ returns true when the string `s` contains any of the characters inside the string `chars`
+
+ strings.contains_any("test", "test") -> true
+ strings.contains_any("test", "ts") -> true
+ strings.contains_any("test", "et") -> true
+ strings.contains_any("test", "a") -> false
+*/
contains_any :: proc(s, chars: string) -> bool {
return index_any(s, chars) >= 0
}
+/*
+ returns the utf8 rune count of the string `s`
+ strings.rune_count("test") -> 4
+ strings.rune_count("testö") -> 5, where len("testö") -> 6
+*/
rune_count :: proc(s: string) -> int {
return utf8.rune_count_in_string(s)
}
+/*
+ returns wether the strings `u` and `v` are the same alpha characters
+ works with utf8 string content and ignores different casings
+ strings.equal_fold("test", "test") -> true
+ strings.equal_fold("Test", "test") -> true
+ strings.equal_fold("Test", "tEsT") -> true
+ strings.equal_fold("test", "tes") -> false
+*/
equal_fold :: proc(u, v: string) -> bool {
s, t := u, v
loop: for s != "" && t != "" {
@@ -153,15 +208,39 @@ equal_fold :: proc(u, v: string) -> bool {
return s == t
}
+/*
+ return true when the string `prefix` is contained at the start of the string `s`
+
+ strings.has_prefix("testing", "test") -> true
+ strings.has_prefix("testing", "te") -> true
+ strings.has_prefix("telephone", "te") -> true
+ strings.has_prefix("testing", "est") -> false
+*/
has_prefix :: proc(s, prefix: string) -> bool {
return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}
+/*
+ returns true when the string `suffix` is contained at the end of the string `s`
+ good example to use this is for file extensions
+
+ strings.has_suffix("todo.txt", ".txt") -> true
+ strings.has_suffix("todo.doc", ".txt") -> false
+ strings.has_suffix("todo.doc.txt", ".txt") -> true
+*/
has_suffix :: proc(s, suffix: string) -> bool {
return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
}
+/*
+ returns a combined string from the slice of strings `a` seperated with the `sep` string
+ allocates the string using the `allocator`
+ a := [?]string { "a", "b", "c" }
+ b := strings.join(a[:], " ") -> "a b c"
+ c := strings.join(a[:], "-") -> "a-b-c"
+ d := strings.join(a[:], "...") -> "a...b...c"
+*/
join :: proc(a: []string, sep: string, allocator := context.allocator) -> string {
if len(a) == 0 {
return ""
@@ -181,6 +260,14 @@ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string
return string(b)
}
+/*
+ returns a combined string from the slice of strings `a` without a seperator
+ allocates the string using the `allocator`
+
+
+ a := [?]string { "a", "b", "c" }
+ b := strings.concatenate(a[:]) -> "abc"
+*/
concatenate :: proc(a: []string, allocator := context.allocator) -> string {
if len(a) == 0 {
return ""
@@ -200,30 +287,56 @@ concatenate :: proc(a: []string, allocator := context.allocator) -> string {
/*
`rune_offset` and `rune_length` are in runes, not bytes.
- If `rune_length` <= 0, then it'll return the remainder of the string starting with `rune_offset`.
+ If `rune_length` <= 0, then it'll return the remainder of the string starting at `rune_offset`.
+
+ strings.cut("some example text", 0, 4) -> "some"
+ strings.cut("some example text", 2, 2) -> "me"
+ strings.cut("some example text", 5, 7) -> "example"
*/
cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator := context.allocator) -> (res: string) {
s := s; rune_length := rune_length
- l := utf8.rune_count_in_string(s)
+ context.allocator = allocator
- if rune_offset >= l { return "" }
+ // If we signal that we want the entire remainder (length <= 0) *and*
+ // the offset is zero, then we can early out by cloning the input
if rune_offset == 0 && rune_length <= 0 {
- return clone(s, allocator)
+ return clone(s)
}
- if rune_length == 0 { rune_length = l }
+ // We need to know if we have enough runes to cover offset + length.
+ rune_count := utf8.rune_count_in_string(s)
+
+ // We're asking for a substring starting after the end of the input string.
+ // That's just an empty string.
+ if rune_offset >= rune_count {
+ return ""
+ }
+
+ // If we don't specify the length of the substring, use the remainder.
+ if rune_length <= 0 {
+ rune_length = rune_count - rune_offset
+ }
+
+ // We don't yet know how many bytes we need exactly.
+ // But we do know it's bounded by the number of runes * 4 bytes,
+ // and can be no more than the size of the input string.
bytes_needed := min(rune_length * 4, len(s))
- buf := make([]u8, bytes_needed, allocator)
+ buf := make([]u8, bytes_needed)
byte_offset := 0
- for i := 0; i < l; i += 1 {
+ for i := 0; i < rune_count; i += 1 {
_, w := utf8.decode_rune_in_string(s)
+
+ // If the rune is part of the substring, copy it to the output buffer.
if i >= rune_offset {
for j := 0; j < w; j += 1 {
buf[byte_offset+j] = s[j]
}
byte_offset += w
}
+
+ // We're done if we reach the end of the input string, *or*
+ // if we've reached a specified length in runes.
if rune_length > 0 {
if i == rune_offset + rune_length - 1 { break }
}
@@ -307,17 +420,37 @@ split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []str
return _split(s, sep, 0, n, allocator)
}
+/*
+ splits the string `s` after the seperator string `sep` appears
+ returns the slice of split strings allocated using `allocator`
+
+ a := "aaa.bbb.ccc.ddd.eee"
+ aa := strings.split_after(a, ".")
+ fmt.eprintln(aa) // [aaa., bbb., ccc., ddd., eee]
+*/
split_after :: proc(s, sep: string, allocator := context.allocator) -> []string {
return _split(s, sep, len(sep), -1, allocator)
}
+/*
+ splits the string `s` after the seperator string `sep` appears into a total of `n` parts
+ returns the slice of split strings allocated using `allocator`
+
+ a := "aaa.bbb.ccc.ddd.eee"
+ aa := strings.split_after(a, ".")
+ fmt.eprintln(aa) // [aaa., bbb., ccc., ddd., eee]
+*/
split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string {
return _split(s, sep, len(sep), n, allocator)
}
-
@private
_split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) {
+ // stop once the string is empty or nil
+ if s == nil || len(s^) == 0 {
+ return
+ }
+
if sep == "" {
res = s[:]
ok = true
@@ -339,11 +472,52 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string,
return
}
+/*
+ split the ^string `s` by the byte seperator `sep` in an iterator fashion
+ consumes the original string till the end, leaving the string `s` with len == 0
+ text := "a.b.c.d.e"
+ for str in strings.split_by_byte_iterator(&text, '.') {
+ fmt.eprintln(str) // every loop -> a b c d e
+ }
+*/
+split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) {
+ m := index_byte(s^, sep)
+ if m < 0 {
+ // not found
+ res = s[:]
+ ok = res != ""
+ s^ = {}
+ } else {
+ res = s[:m]
+ ok = true
+ s^ = s[m+1:]
+ }
+ return
+}
+
+/*
+ split the ^string `s` by the seperator string `sep` in an iterator fashion
+ consumes the original string till the end
+
+ text := "a.b.c.d.e"
+ for str in strings.split_iterator(&text, ".") {
+ fmt.eprintln(str) // every loop -> a b c d e
+ }
+*/
split_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
return _split_iterator(s, sep, 0)
}
+/*
+ split the ^string `s` after every seperator string `sep` in an iterator fashion
+ consumes the original string till the end
+
+ text := "a.b.c.d.e"
+ for str in strings.split_after_iterator(&text, ".") {
+ fmt.eprintln(str) // every loop -> a. b. c. d. e
+ }
+*/
split_after_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
return _split_iterator(s, sep, len(sep))
}
@@ -360,6 +534,14 @@ _trim_cr :: proc(s: string) -> string {
return s
}
+/*
+ split the string `s` at every line break '\n'
+ return an allocated slice of strings
+
+ a := "a\nb\nc\nd\ne"
+ b := strings.split_lines(a)
+ fmt.eprintln(b) // [a, b, c, d, e]
+*/
split_lines :: proc(s: string, allocator := context.allocator) -> []string {
sep :: "\n"
lines := _split(s, sep, 0, -1, allocator)
@@ -369,6 +551,14 @@ split_lines :: proc(s: string, allocator := context.allocator) -> []string {
return lines
}
+/*
+ split the string `s` at every line break '\n' for `n` parts
+ return an allocated slice of strings
+
+ a := "a\nb\nc\nd\ne"
+ b := strings.split_lines_n(a, 3)
+ fmt.eprintln(b) // [a, b, c, d\ne\n]
+*/
split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []string {
sep :: "\n"
lines := _split(s, sep, 0, n, allocator)
@@ -378,6 +568,14 @@ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []st
return lines
}
+/*
+ split the string `s` at every line break '\n' leaving the '\n' in the resulting strings
+ return an allocated slice of strings
+
+ a := "a\nb\nc\nd\ne"
+ b := strings.split_lines_after(a)
+ fmt.eprintln(b) // [a\n, b\n, c\n, d\n, e\n]
+*/
split_lines_after :: proc(s: string, allocator := context.allocator) -> []string {
sep :: "\n"
lines := _split(s, sep, len(sep), -1, allocator)
@@ -387,6 +585,15 @@ split_lines_after :: proc(s: string, allocator := context.allocator) -> []string
return lines
}
+/*
+ split the string `s` at every line break '\n' leaving the '\n' in the resulting strings
+ only runs for `n` parts
+ return an allocated slice of strings
+
+ a := "a\nb\nc\nd\ne"
+ b := strings.split_lines_after_n(a, 3)
+ fmt.eprintln(b) // [a\n, b\n, c\n, d\ne\n]
+*/
split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -> []string {
sep :: "\n"
lines := _split(s, sep, len(sep), n, allocator)
@@ -396,21 +603,45 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -
return lines
}
+/*
+ split the string `s` at every line break '\n'
+ returns the current split string every iteration till the string is consumed
+
+ text := "a\nb\nc\nd\ne"
+ for str in strings.split_lines_iterator(&text) {
+ fmt.eprintln(text) // every loop -> a b c d e
+ }
+*/
split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
sep :: "\n"
line = _split_iterator(s, sep, 0) or_return
return _trim_cr(line), true
}
+/*
+ split the string `s` at every line break '\n'
+ returns the current split string every iteration till the string is consumed
+
+ text := "a\nb\nc\nd\ne"
+ for str in strings.split_lines_after_iterator(&text) {
+ fmt.eprintln(text) // every loop -> a\n b\n c\n d\n e\n
+ }
+*/
split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
sep :: "\n"
line = _split_iterator(s, sep, len(sep)) or_return
return _trim_cr(line), true
}
+/*
+ returns the byte offset of the first byte `c` in the string `s` it finds, -1 when not found
+ can't find utf8 based runes
-
-
+ strings.index_byte("test", 't') -> 0
+ strings.index_byte("test", 'e') -> 1
+ strings.index_byte("test", 'x') -> -1
+ strings.index_byte("teäst", 'ä') -> -1
+*/
index_byte :: proc(s: string, c: byte) -> int {
for i := 0; i < len(s); i += 1 {
if s[i] == c {
@@ -420,7 +651,15 @@ index_byte :: proc(s: string, c: byte) -> int {
return -1
}
-// Returns -1 if c is not present
+/*
+ returns the byte offset of the last byte `c` in the string `s` it finds, -1 when not found
+ can't find utf8 based runes
+
+ strings.index_byte("test", 't') -> 3
+ strings.index_byte("test", 'e') -> 1
+ strings.index_byte("test", 'x') -> -1
+ strings.index_byte("teäst", 'ä') -> -1
+*/
last_index_byte :: proc(s: string, c: byte) -> int {
for i := len(s)-1; i >= 0; i -= 1 {
if s[i] == c {
@@ -431,9 +670,50 @@ last_index_byte :: proc(s: string, c: byte) -> int {
}
+/*
+ returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found
+ avoids invalid runes
+
+ strings.index_rune("abcädef", 'x') -> -1
+ strings.index_rune("abcädef", 'a') -> 0
+ strings.index_rune("abcädef", 'b') -> 1
+ strings.index_rune("abcädef", 'c') -> 2
+ strings.index_rune("abcädef", 'ä') -> 3
+ strings.index_rune("abcädef", 'd') -> 5
+ strings.index_rune("abcädef", 'e') -> 6
+ strings.index_rune("abcädef", 'f') -> 7
+*/
+index_rune :: proc(s: string, r: rune) -> int {
+ switch {
+ case 0 <= r && r < utf8.RUNE_SELF:
+ return index_byte(s, byte(r))
+
+ case r == utf8.RUNE_ERROR:
+ for c, i in s {
+ if c == utf8.RUNE_ERROR {
+ return i
+ }
+ }
+ return -1
+
+ case !utf8.valid_rune(r):
+ return -1
+ }
+
+ b, w := utf8.encode_rune(r)
+ return index(s, string(b[:w]))
+}
@private PRIME_RABIN_KARP :: 16777619
+/*
+ returns the byte offset of the string `substr` in the string `s`, -1 when not found
+
+ strings.index("test", "t") -> 0
+ strings.index("test", "te") -> 0
+ strings.index("test", "st") -> 2
+ strings.index("test", "tt") -> -1
+*/
index :: proc(s, substr: string) -> int {
hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
for i := 0; i < len(s); i += 1 {
@@ -484,6 +764,14 @@ index :: proc(s, substr: string) -> int {
return -1
}
+/*
+ returns the last byte offset of the string `substr` in the string `s`, -1 when not found
+
+ strings.index("test", "t") -> 3
+ strings.index("test", "te") -> 0
+ strings.index("test", "st") -> 2
+ strings.index("test", "tt") -> -1
+*/
last_index :: proc(s, substr: string) -> int {
hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
for i := len(s) - 1; i >= 0; i -= 1 {
@@ -532,7 +820,15 @@ last_index :: proc(s, substr: string) -> int {
return -1
}
-// index_any returns the index of the first char of `chars` found in `s`. -1 if not found.
+/*
+ returns the index of any first char of `chars` found in `s`, -1 if not found
+
+ strings.index_any("test", "s") -> 2
+ strings.index_any("test", "se") -> 1
+ strings.index_any("test", "et") -> 0
+ strings.index_any("test", "set") -> 0
+ strings.index_any("test", "x") -> -1
+*/
index_any :: proc(s, chars: string) -> int {
if chars == "" {
return -1
@@ -565,6 +861,16 @@ index_any :: proc(s, chars: string) -> int {
return -1
}
+/*
+ returns the index of any first char of `chars` found in `s`, -1 if not found
+ iterates the string in reverse
+
+ strings.index_any("test", "s") -> 2
+ strings.index_any("test", "se") -> 2
+ strings.index_any("test", "et") -> 1
+ strings.index_any("test", "set") -> 3
+ strings.index_any("test", "x") -> -1
+*/
last_index_any :: proc(s, chars: string) -> int {
if chars == "" {
return -1
@@ -614,6 +920,16 @@ last_index_any :: proc(s, chars: string) -> int {
return -1
}
+/*
+ returns the count of the string `substr` found in the string `s`
+ returns the rune_count + 1 of the string `s` on empty `substr`
+
+ strings.count("abbccc", "a") -> 1
+ strings.count("abbccc", "b") -> 2
+ strings.count("abbccc", "c") -> 3
+ strings.count("abbccc", "ab") -> 1
+ strings.count("abbccc", " ") -> 0
+*/
count :: proc(s, substr: string) -> int {
if len(substr) == 0 { // special case
return rune_count(s) + 1
@@ -649,7 +965,12 @@ count :: proc(s, substr: string) -> int {
return n
}
+/*
+ repeats the string `s` multiple `count` times and returns the allocated string
+ panics when `count` is below 0
+ strings.repeat("abc", 2) -> "abcabc"
+*/
repeat :: proc(s: string, count: int, allocator := context.allocator) -> string {
if count < 0 {
panic("strings: negative repeat count")
@@ -666,11 +987,28 @@ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string
return string(b)
}
+/*
+ replaces all instances of `old` in the string `s` with the `new` string
+ returns the `output` string and true when an a allocation through a replace happened
+
+ strings.replace_all("xyzxyz", "xyz", "abc") -> "abcabc", true
+ strings.replace_all("xyzxyz", "abc", "xyz") -> "xyzxyz", false
+ strings.replace_all("xyzxyz", "xy", "z") -> "zzzz", true
+*/
replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return replace(s, old, new, -1, allocator)
}
-// if n < 0, no limit on the number of replacements
+/*
+ replaces `n` instances of `old` in the string `s` with the `new` string
+ if n < 0, no limit on the number of replacements
+ returns the `output` string and true when an a allocation through a replace happened
+
+ strings.replace("xyzxyz", "xyz", "abc", 2) -> "abcabc", true
+ strings.replace("xyzxyz", "xyz", "abc", 1) -> "abcxyz", true
+ strings.replace("xyzxyz", "abc", "xyz", -1) -> "xyzxyz", false
+ strings.replace("xyzxyz", "xy", "z", -1) -> "zzzz", true
+*/
replace :: proc(s, old, new: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) {
if old == new || n == 0 {
was_allocation = false
@@ -711,17 +1049,35 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
return
}
+/*
+ removes the `key` string `n` times from the `s` string
+ if n < 0, no limit on the number of removes
+ returns the `output` string and true when an a allocation through a remove happened
+
+ strings.remove("abcabc", "abc", 1) -> "abc", true
+ strings.remove("abcabc", "abc", -1) -> "", true
+ strings.remove("abcabc", "a", -1) -> "bcbc", true
+ strings.remove("abcabc", "x", -1) -> "abcabc", false
+*/
remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return replace(s, key, "", n, allocator)
}
+/*
+ removes all the `key` string instanes from the `s` string
+ returns the `output` string and true when an a allocation through a remove happened
+
+ strings.remove("abcabc", "abc") -> "", true
+ strings.remove("abcabc", "a") -> "bcbc", true
+ strings.remove("abcabc", "x") -> "abcabc", false
+*/
remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return remove(s, key, -1, allocator)
}
@(private) _ascii_space := [256]bool{'\t' = true, '\n' = true, '\v' = true, '\f' = true, '\r' = true, ' ' = true}
-
+// return true when the `r` rune is '\t', '\n', '\v', '\f', '\r' or ' '
is_ascii_space :: proc(r: rune) -> bool {
if r < utf8.RUNE_SELF {
return _ascii_space[u8(r)]
@@ -729,6 +1085,7 @@ is_ascii_space :: proc(r: rune) -> bool {
return false
}
+// returns true when the `r` rune is any asci or utf8 based whitespace
is_space :: proc(r: rune) -> bool {
if r < 0x2000 {
switch r {
@@ -747,10 +1104,24 @@ is_space :: proc(r: rune) -> bool {
return false
}
+// returns true when the `r` rune is a nul byte
is_null :: proc(r: rune) -> bool {
return r == 0x0000
}
+/*
+ runs trough the `s` string linearly and watches wether the `p` procedure matches the `truth` bool
+ returns the rune offset or -1 when no match was found
+
+ call :: proc(r: rune) -> bool {
+ return r == 'a'
+ }
+ strings.index_proc("abcabc", call) -> 0
+ strings.index_proc("cbacba", call) -> 2
+ strings.index_proc("cbacba", call, false) -> 0
+ strings.index_proc("abcabc", call, false) -> 1
+ strings.index_proc("xyz", call) -> -1
+*/
index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int {
for r, i in s {
if p(r) == truth {
@@ -760,6 +1131,7 @@ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int {
return -1
}
+// same as `index_proc` but with a `p` procedure taking a rawptr for state
index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int {
for r, i in s {
if p(state, r) == truth {
@@ -769,6 +1141,7 @@ index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: r
return -1
}
+// same as `index_proc` but runs through the string in reverse
last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int {
// TODO(bill): Probably use Rabin-Karp Search
for i := len(s); i > 0; {
@@ -781,6 +1154,7 @@ last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int
return -1
}
+// same as `index_proc_with_state` but runs through the string in reverse
last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int {
// TODO(bill): Probably use Rabin-Karp Search
for i := len(s); i > 0; {
@@ -792,7 +1166,17 @@ last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta
}
return -1
}
+
+/*
+ trims the input string `s` until the procedure `p` returns false
+ does not allocate - only returns a cut variant of the input string
+ returns an empty string when no match was found at all
+ find :: proc(r: rune) -> bool {
+ return r != 'i'
+ }
+ strings.trim_left_proc("testing", find) -> "ing"
+*/
trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
i := index_proc(s, p, false)
if i == -1 {
@@ -801,29 +1185,10 @@ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
return s[i:]
}
-
-index_rune :: proc(s: string, r: rune) -> int {
- switch {
- case 0 <= r && r < utf8.RUNE_SELF:
- return index_byte(s, byte(r))
-
- case r == utf8.RUNE_ERROR:
- for c, i in s {
- if c == utf8.RUNE_ERROR {
- return i
- }
- }
- return -1
-
- case !utf8.valid_rune(r):
- return -1
- }
-
- b, w := utf8.encode_rune(r)
- return index(s, string(b[:w]))
-}
-
-
+/*
+ trims the input string `s` until the procedure `p` with state returns false
+ returns an empty string when no match was found at all
+*/
trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string {
i := index_proc_with_state(s, p, state, false)
if i == -1 {
@@ -832,6 +1197,16 @@ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, stat
return s[i:]
}
+/*
+ trims the input string `s` from the right until the procedure `p` returns false
+ does not allocate - only returns a cut variant of the input string
+ returns an empty string when no match was found at all
+
+ find :: proc(r: rune) -> bool {
+ return r != 't'
+ }
+ strings.trim_left_proc("testing", find) -> "test"
+*/
trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
i := last_index_proc(s, p, false)
if i >= 0 && s[i] >= utf8.RUNE_SELF {
@@ -843,6 +1218,10 @@ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
return s[0:i]
}
+/*
+ trims the input string `s` from the right until the procedure `p` with state returns false
+ returns an empty string when no match was found at all
+*/
trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string {
i := last_index_proc_with_state(s, p, state, false)
if i >= 0 && s[i] >= utf8.RUNE_SELF {
@@ -854,7 +1233,7 @@ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta
return s[0:i]
}
-
+// procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison
is_in_cutset :: proc(state: rawptr, r: rune) -> bool {
if state == nil {
return false
@@ -868,7 +1247,7 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> bool {
return false
}
-
+// trims the `cutset` string from the `s` string
trim_left :: proc(s: string, cutset: string) -> string {
if s == "" || cutset == "" {
return s
@@ -877,6 +1256,7 @@ trim_left :: proc(s: string, cutset: string) -> string {
return trim_left_proc_with_state(s, is_in_cutset, &state)
}
+// trims the `cutset` string from the `s` string from the right
trim_right :: proc(s: string, cutset: string) -> string {
if s == "" || cutset == "" {
return s
@@ -885,35 +1265,48 @@ trim_right :: proc(s: string, cutset: string) -> string {
return trim_right_proc_with_state(s, is_in_cutset, &state)
}
+// trims the `cutset` string from the `s` string, both from left and right
trim :: proc(s: string, cutset: string) -> string {
return trim_right(trim_left(s, cutset), cutset)
}
+// trims until a valid non space rune: "\t\txyz\t\t" -> "xyz\t\t"
trim_left_space :: proc(s: string) -> string {
return trim_left_proc(s, is_space)
}
+// trims from the right until a valid non space rune: "\t\txyz\t\t" -> "\t\txyz"
trim_right_space :: proc(s: string) -> string {
return trim_right_proc(s, is_space)
}
+// trims from both sides until a valid non space rune: "\t\txyz\t\t" -> "xyz"
trim_space :: proc(s: string) -> string {
return trim_right_space(trim_left_space(s))
}
-
+// trims nul runes from the left: "\x00\x00testing\x00\x00" -> "testing\x00\x00"
trim_left_null :: proc(s: string) -> string {
return trim_left_proc(s, is_null)
}
+// trims nul runes from the right: "\x00\x00testing\x00\x00" -> "\x00\x00testing"
trim_right_null :: proc(s: string) -> string {
return trim_right_proc(s, is_null)
}
+// trims nul runes from both sides: "\x00\x00testing\x00\x00" -> "testing"
trim_null :: proc(s: string) -> string {
return trim_right_null(trim_left_null(s))
}
+/*
+ trims a `prefix` string from the start of the `s` string and returns the trimmed string
+ returns the input string `s` when no prefix was found
+
+ strings.trim_prefix("testing", "test") -> "ing"
+ strings.trim_prefix("testing", "abc") -> "testing"
+*/
trim_prefix :: proc(s, prefix: string) -> string {
if has_prefix(s, prefix) {
return s[len(prefix):]
@@ -921,6 +1314,13 @@ trim_prefix :: proc(s, prefix: string) -> string {
return s
}
+/*
+ trims a `suffix` string from the end of the `s` string and returns the trimmed string
+ returns the input string `s` when no suffix was found
+
+ strings.trim_suffix("todo.txt", ".txt") -> "todo"
+ strings.trim_suffix("todo.doc", ".txt") -> "todo.doc"
+*/
trim_suffix :: proc(s, suffix: string) -> string {
if has_suffix(s, suffix) {
return s[:len(s)-len(suffix)]
@@ -928,142 +1328,151 @@ trim_suffix :: proc(s, suffix: string) -> string {
return s
}
-split_multi :: proc(s: string, substrs: []string, skip_empty := false, allocator := context.allocator) -> []string #no_bounds_check {
+/*
+ splits the input string `s` by all possible `substrs` []string
+ returns the allocated []string, nil on any empty substring or no matches
+
+ splits := [?]string { "---", "~~~", ".", "_", "," }
+ res := strings.split_multi("testing,this.out_nice---done~~~last", splits[:])
+ fmt.eprintln(res) // -> [testing, this, out, nice, done, last]
+*/
+split_multi :: proc(s: string, substrs: []string, allocator := context.allocator) -> (buf: []string) #no_bounds_check {
if s == "" || len(substrs) <= 0 {
- return nil
+ return
}
- sublen := len(substrs[0])
-
- for substr in substrs[1:] {
- sublen = min(sublen, len(substr))
+ // disallow "" substr
+ for substr in substrs {
+ if len(substr) == 0 {
+ return
+ }
}
- shared := len(s) - sublen
+ // TODO maybe remove duplicate substrs
+ // sort substrings by string size, largest to smallest
+ temp_substrs := slice.clone(substrs, context.temp_allocator)
+ slice.sort_by(temp_substrs, proc(a, b: string) -> bool {
+ return len(a) > len(b)
+ })
- if shared <= 0 {
- return nil
- }
+ substrings_found: int
+ temp := s
- // number, index, last
- n, i, l := 0, 0, 0
-
- // count results
- first_pass: for i <= shared {
- for substr in substrs {
- if s[i:i+sublen] == substr {
- if !skip_empty || i - l > 0 {
- n += 1
- }
-
- i += sublen
- l = i
+ // count substr results found in string
+ first_pass: for len(temp) > 0 {
+ for substr in temp_substrs {
+ size := len(substr)
+ // check range and compare string to substr
+ if size <= len(temp) && temp[:size] == substr {
+ substrings_found += 1
+ temp = temp[size:]
continue first_pass
}
}
- _, skip := utf8.decode_rune_in_string(s[i:])
- i += skip
+ // step through string
+ _, skip := utf8.decode_rune_in_string(temp[:])
+ temp = temp[skip:]
}
- if !skip_empty || len(s) - l > 0 {
- n += 1
+ // skip when no results
+ if substrings_found < 1 {
+ return
}
- if n < 1 {
- // no results
- return nil
- }
+ buf = make([]string, substrings_found + 1, allocator)
+ buf_index: int
+ temp = s
+ temp_old := temp
- buf := make([]string, n, allocator)
-
- n, i, l = 0, 0, 0
-
- // slice results
- second_pass: for i <= shared {
- for substr in substrs {
- if s[i:i+sublen] == substr {
- if !skip_empty || i - l > 0 {
- buf[n] = s[l:i]
- n += 1
- }
-
- i += sublen
- l = i
+ // gather results in the same fashion
+ second_pass: for len(temp) > 0 {
+ for substr in temp_substrs {
+ size := len(substr)
+ // check range and compare string to substr
+ if size <= len(temp) && temp[:size] == substr {
+ buf[buf_index] = temp_old[:len(temp_old) - len(temp)]
+ buf_index += 1
+ temp = temp[size:]
+ temp_old = temp
continue second_pass
}
}
- _, skip := utf8.decode_rune_in_string(s[i:])
- i += skip
+ // step through string
+ _, skip := utf8.decode_rune_in_string(temp[:])
+ temp = temp[skip:]
}
- if !skip_empty || len(s) - l > 0 {
- buf[n] = s[l:]
- }
+ buf[buf_index] = temp_old[:]
return buf
}
+// state for the split multi iterator
+Split_Multi :: struct {
+ temp: string,
+ temp_old: string,
+ substrs: []string,
+}
+// returns split multi state with sorted `substrs`
+split_multi_init :: proc(s: string, substrs: []string) -> Split_Multi {
+ // sort substrings, largest to smallest
+ temp_substrs := slice.clone(substrs, context.temp_allocator)
+ slice.sort_by(temp_substrs, proc(a, b: string) -> bool {
+ return len(a) > len(b)
+ })
-
-split_multi_iterator :: proc(s: ^string, substrs: []string, skip_empty := false) -> (string, bool) #no_bounds_check {
- if s == nil || s^ == "" || len(substrs) <= 0 {
- return "", false
+ return {
+ temp = s,
+ temp_old = s,
+ substrs = temp_substrs,
}
+}
- sublen := len(substrs[0])
+/*
+ splits the input string `s` by all possible `substrs` []string in an iterator fashion
+ returns the split string every iteration, the full string on no match
- for substr in substrs[1:] {
- sublen = min(sublen, len(substr))
+ splits := [?]string { "---", "~~~", ".", "_", "," }
+ state := strings.split_multi_init("testing,this.out_nice---done~~~last", splits[:])
+ for str in strings.split_multi_iterate(&state) {
+ fmt.eprintln(str) // every iteration -> [testing, this, out, nice, done, last]
}
-
- shared := len(s) - sublen
-
- if shared <= 0 {
- return "", false
- }
-
- // index, last
- i, l := 0, 0
-
- loop: for i <= shared {
+*/
+split_multi_iterate :: proc(using sm: ^Split_Multi) -> (res: string, ok: bool) #no_bounds_check {
+ pass: for len(temp) > 0 {
for substr in substrs {
- if s[i:i+sublen] == substr {
- if !skip_empty || i - l > 0 {
- res := s[l:i]
- s^ = s[i:]
- return res, true
- }
+ size := len(substr)
- i += sublen
- l = i
-
- continue loop
+ // check range and compare string to substr
+ if size <= len(temp) && temp[:size] == substr {
+ res = temp_old[:len(temp_old) - len(temp)]
+ temp = temp[size:]
+ temp_old = temp
+ ok = true
+ return
}
}
- _, skip := utf8.decode_rune_in_string(s[i:])
- i += skip
+ // step through string
+ _, skip := utf8.decode_rune_in_string(temp[:])
+ temp = temp[skip:]
}
- if !skip_empty || len(s) - l > 0 {
- res := s[l:]
- s^ = s[len(s):]
- return res, true
+ // allow last iteration
+ if temp_old != "" {
+ res = temp_old[:]
+ ok = true
+ temp_old = ""
}
- return "", false
+ return
}
-
-
-
-
-
// scrub scruvs invalid utf-8 characters and replaces them with the replacement string
// Adjacent invalid bytes are only replaced once
scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string {
@@ -1098,7 +1507,13 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) ->
return to_string(b)
}
+/*
+ returns a reversed version of the `s` string
+ a := "abcxyz"
+ b := strings.reverse(a)
+ fmt.eprintln(a, b) // abcxyz zyxcba
+*/
reverse :: proc(s: string, allocator := context.allocator) -> string {
str := s
n := len(str)
@@ -1114,12 +1529,19 @@ reverse :: proc(s: string, allocator := context.allocator) -> string {
return string(buf)
}
+/*
+ expands the string to a grid spaced by `tab_size` whenever a `\t` character appears
+ returns the tabbed string, panics on tab_size <= 0
+
+ strings.expand_tabs("abc1\tabc2\tabc3", 4) -> abc1 abc2 abc3
+ strings.expand_tabs("abc1\tabc2\tabc3", 5) -> abc1 abc2 abc3
+ strings.expand_tabs("abc1\tabc2\tabc3", 6) -> abc1 abc2 abc3
+*/
expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string {
if tab_size <= 0 {
panic("tab size must be positive")
}
-
if s == "" {
return ""
}
@@ -1157,7 +1579,16 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
return to_string(b)
}
+/*
+ splits the `str` string by the seperator `sep` string and returns 3 parts
+ `head`: before the split, `match`: the seperator, `tail`: the end of the split
+ returns the input string when the `sep` was not found
+ text := "testing this out"
+ strings.partition(text, " this ") -> head: "testing", match: " this ", tail: "out"
+ strings.partition(text, "hi") -> head: "testing t", match: "hi", tail: "s out"
+ strings.partition(text, "xyz") -> head: "testing this out", match: "", tail: ""
+*/
partition :: proc(str, sep: string) -> (head, match, tail: string) {
i := index(str, sep)
if i == -1 {
@@ -1346,3 +1777,35 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc
return substrings[:]
}
+
+
+// `fields_iterator` returns the first run of characters in `s` that does not contain white space, defined by `unicode.is_space`
+// `s` will then start from any space after the substring, or be an empty string if the substring was the remaining characters
+fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) {
+ start, end := -1, -1
+ for r, offset in s {
+ end = offset
+ if unicode.is_space(r) {
+ if start >= 0 {
+ field = s[start : end]
+ ok = true
+ s^ = s[end:]
+ return
+ }
+ } else {
+ if start < 0 {
+ start = end
+ }
+ }
+ }
+
+ // if either of these are true, the string did not contain any characters
+ if end < 0 || start < 0 {
+ return "", false
+ }
+
+ field = s[:len(s)]
+ ok = true
+ s^ = s[len(s):]
+ return
+}
diff --git a/core/sync/atomic.odin b/core/sync/atomic.odin
index 21dcea178..f537764c4 100644
--- a/core/sync/atomic.odin
+++ b/core/sync/atomic.odin
@@ -2,167 +2,44 @@ package sync
import "core:intrinsics"
-Ordering :: enum {
- Relaxed, // Monotonic
- Release,
- Acquire,
- Acquire_Release,
- Sequentially_Consistent,
-}
-
-strongest_failure_ordering_table := [Ordering]Ordering{
- .Relaxed = .Relaxed,
- .Release = .Relaxed,
- .Acquire = .Acquire,
- .Acquire_Release = .Acquire,
- .Sequentially_Consistent = .Sequentially_Consistent,
-}
-
-strongest_failure_ordering :: #force_inline proc(order: Ordering) -> Ordering {
- return strongest_failure_ordering_table[order]
-}
-
-fence :: #force_inline proc($order: Ordering) {
- when order == .Relaxed { #panic("there is no such thing as a relaxed fence") }
- else when order == .Release { intrinsics.atomic_fence_rel() }
- else when order == .Acquire { intrinsics.atomic_fence_acq() }
- else when order == .Acquire_Release { intrinsics.atomic_fence_acqrel() }
- else when order == .Sequentially_Consistent { intrinsics.atomic_fence() }
- else { #panic("unknown order") }
+cpu_relax :: intrinsics.cpu_relax
+
+/*
+Atomic_Memory_Order :: enum {
+ Relaxed = 0,
+ Consume = 1,
+ Acquire = 2,
+ Release = 3,
+ Acq_Rel = 4,
+ Seq_Cst = 5,
}
+*/
+Atomic_Memory_Order :: intrinsics.Atomic_Memory_Order
-atomic_store :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) {
- when order == .Relaxed { intrinsics.atomic_store_relaxed(dst, val) }
- else when order == .Release { intrinsics.atomic_store_rel(dst, val) }
- else when order == .Sequentially_Consistent { intrinsics.atomic_store(dst, val) }
- else when order == .Acquire { #panic("there is not such thing as an acquire store") }
- else when order == .Acquire_Release { #panic("there is not such thing as an acquire/release store") }
- else { #panic("unknown order") }
-}
-
-atomic_load :: #force_inline proc(dst: ^$T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_load_relaxed(dst) }
- else when order == .Acquire { return intrinsics.atomic_load_acq(dst) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_load(dst) }
- else when order == .Release { #panic("there is no such thing as a release load") }
- else when order == .Acquire_Release { #panic("there is no such thing as an acquire/release load") }
- else { #panic("unknown order") }
-}
-
-atomic_swap :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_xchg_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_xchg_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_xchg_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_xchg_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_xchg(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_compare_exchange :: #force_inline proc(dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) {
- when failure == .Relaxed {
- when success == .Relaxed { return intrinsics.atomic_cxchg_relaxed(dst, old, new) }
- else when success == .Acquire { return intrinsics.atomic_cxchg_acq_failrelaxed(dst, old, new) }
- else when success == .Acquire_Release { return intrinsics.atomic_cxchg_acqrel_failrelaxed(dst, old, new) }
- else when success == .Sequentially_Consistent { return intrinsics.atomic_cxchg_failrelaxed(dst, old, new) }
- else when success == .Release { return intrinsics.atomic_cxchg_rel(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Acquire {
- when success == .Release { return intrinsics.atomic_cxchg_acqrel(dst, old, new) }
- else when success == .Acquire { return intrinsics.atomic_cxchg_acq(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Sequentially_Consistent {
- when success == .Sequentially_Consistent { return intrinsics.atomic_cxchg(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Acquire_Release {
- #panic("there is not such thing as an acquire/release failure ordering")
- } else when failure == .Release {
- when success == .Acquire { return instrinsics.atomic_cxchg_failacq(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else {
- return T{}, false
- }
-
-}
-
-atomic_compare_exchange_weak :: #force_inline proc(dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) {
- when failure == .Relaxed {
- when success == .Relaxed { return intrinsics.atomic_cxchgweak_relaxed(dst, old, new) }
- else when success == .Acquire { return intrinsics.atomic_cxchgweak_acq_failrelaxed(dst, old, new) }
- else when success == .Acquire_Release { return intrinsics.atomic_cxchgweak_acqrel_failrelaxed(dst, old, new) }
- else when success == .Sequentially_Consistent { return intrinsics.atomic_cxchgweak_failrelaxed(dst, old, new) }
- else when success == .Release { return intrinsics.atomic_cxchgweak_rel(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Acquire {
- when success == .Release { return intrinsics.atomic_cxchgweak_acqrel(dst, old, new) }
- else when success == .Acquire { return intrinsics.atomic_cxchgweak_acq(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Sequentially_Consistent {
- when success == .Sequentially_Consistent { return intrinsics.atomic_cxchgweak(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Acquire_Release {
- #panic("there is not such thing as an acquire/release failure ordering")
- } else when failure == .Release {
- when success == .Acquire { return intrinsics.atomic_cxchgweak_failacq(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else {
- return T{}, false
- }
-
-}
-
-
-atomic_add :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_add_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_add_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_add_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_add_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_add(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_sub :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_sub_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_sub_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_sub_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_sub_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_sub(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_and :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_and_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_and_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_and_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_and_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_and(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_nand :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_nand_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_nand_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_nand_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_nand_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_nand(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_or :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_or_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_or_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_or_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_or_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_or(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_xor :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_xor_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_xor_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_xor_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_xor_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_xor(dst, val) }
- else { #panic("unknown order") }
-}
+atomic_thread_fence :: intrinsics.atomic_thread_fence
+atomic_signal_fence :: intrinsics.atomic_signal_fence
+atomic_store :: intrinsics.atomic_store
+atomic_store_explicit :: intrinsics.atomic_store_explicit
+atomic_load :: intrinsics.atomic_load
+atomic_load_explicit :: intrinsics.atomic_load_explicit
+atomic_add :: intrinsics.atomic_add
+atomic_add_explicit :: intrinsics.atomic_add_explicit
+atomic_sub :: intrinsics.atomic_sub
+atomic_sub_explicit :: intrinsics.atomic_sub_explicit
+atomic_and :: intrinsics.atomic_and
+atomic_and_explicit :: intrinsics.atomic_and_explicit
+atomic_nand :: intrinsics.atomic_nand
+atomic_nand_explicit :: intrinsics.atomic_nand_explicit
+atomic_or :: intrinsics.atomic_or
+atomic_or_explicit :: intrinsics.atomic_or_explicit
+atomic_xor :: intrinsics.atomic_xor
+atomic_xor_explicit :: intrinsics.atomic_xor_explicit
+atomic_exchange :: intrinsics.atomic_exchange
+atomic_exchange_explicit :: intrinsics.atomic_exchange_explicit
+// Returns value and optional ok boolean
+atomic_compare_exchange_strong :: intrinsics.atomic_compare_exchange_strong
+atomic_compare_exchange_strong_explicit :: intrinsics.atomic_compare_exchange_strong_explicit
+atomic_compare_exchange_weak :: intrinsics.atomic_compare_exchange_weak
+atomic_compare_exchange_weak_explicit :: intrinsics.atomic_compare_exchange_weak_explicit
\ No newline at end of file
diff --git a/core/sync/barrier.odin b/core/sync/barrier.odin
deleted file mode 100644
index 13c870a3b..000000000
--- a/core/sync/barrier.odin
+++ /dev/null
@@ -1,80 +0,0 @@
-package sync
-
-
-/*
-A barrier enabling multiple threads to synchronize the beginning of some computation
-Example:
-
- package example
-
- import "core:fmt"
- import "core:sync"
- import "core:thread"
-
- barrier := &sync.Barrier{};
-
- main :: proc() {
- fmt.println("Start");
-
- THREAD_COUNT :: 4;
- threads: [THREAD_COUNT]^thread.Thread;
-
- sync.barrier_init(barrier, THREAD_COUNT);
- defer sync.barrier_destroy(barrier);
-
-
- for _, i in threads {
- threads[i] = thread.create_and_start(proc(t: ^thread.Thread) {
- // Same messages will be printed together but without any interleaving
- fmt.println("Getting ready!");
- sync.barrier_wait(barrier);
- fmt.println("Off their marks they go!");
- });
- }
-
- for t in threads {
- thread.destroy(t); // join and free thread
- }
- fmt.println("Finished");
- }
-*/
-Barrier :: struct {
- mutex: Blocking_Mutex,
- cond: Condition,
- index: int,
- generation_id: int,
- thread_count: int,
-}
-
-barrier_init :: proc(b: ^Barrier, thread_count: int) {
- blocking_mutex_init(&b.mutex)
- condition_init(&b.cond, &b.mutex)
- b.index = 0
- b.generation_id = 0
- b.thread_count = thread_count
-}
-
-barrier_destroy :: proc(b: ^Barrier) {
- blocking_mutex_destroy(&b.mutex)
- condition_destroy(&b.cond)
-}
-
-// Block the current thread until all threads have rendezvoused
-// Barrier can be reused after all threads rendezvoused once, and can be used continuously
-barrier_wait :: proc(b: ^Barrier) -> (is_leader: bool) {
- blocking_mutex_lock(&b.mutex)
- defer blocking_mutex_unlock(&b.mutex)
- local_gen := b.generation_id
- b.index += 1
- if b.index < b.thread_count {
- for local_gen == b.generation_id && b.index < b.thread_count {
- condition_wait_for(&b.cond)
- }
- return false
- }
-
- b.index = 0
- b.generation_id += 1
- condition_broadcast(&b.cond)
- return true
-}
diff --git a/core/sync/channel.odin b/core/sync/channel.odin
deleted file mode 100644
index 82b9504f4..000000000
--- a/core/sync/channel.odin
+++ /dev/null
@@ -1,889 +0,0 @@
-package sync
-
-import "core:mem"
-import "core:time"
-import "core:intrinsics"
-import "core:math/rand"
-
-_, _ :: time, rand
-
-Channel_Direction :: enum i8 {
- Both = 0,
- Send = +1,
- Recv = -1,
-}
-
-Channel :: struct($T: typeid, $Direction := Channel_Direction.Both) {
- using _internal: ^Raw_Channel,
-}
-
-channel_init :: proc(ch: ^$C/Channel($T, $D), cap := 0, allocator := context.allocator) {
- context.allocator = allocator
- ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
- return
-}
-
-channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Both)) {
- context.allocator = allocator
- ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
- return
-}
-
-channel_make_send :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Send)) {
- context.allocator = allocator
- ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
- return
-}
-channel_make_recv :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Recv)) {
- context.allocator = allocator
- ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
- return
-}
-
-channel_destroy :: proc(ch: $C/Channel($T, $D)) {
- raw_channel_destroy(ch._internal)
-}
-
-channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) {
- res._internal = ch._internal
- return
-}
-
-channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) {
- res._internal = ch._internal
- return
-}
-
-
-channel_len :: proc(ch: $C/Channel($T, $D)) -> int {
- return ch._internal.len if ch._internal != nil else 0
-}
-channel_cap :: proc(ch: $C/Channel($T, $D)) -> int {
- return ch._internal.cap if ch._internal != nil else 0
-}
-
-
-channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both {
- msg := msg
- _ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc)
-}
-channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both {
- msg := msg
- return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc)
-}
-
-channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both {
- c := ch._internal
- if c == nil {
- panic(message="cannot recv message; channel is nil", loc=loc)
- }
- mutex_lock(&c.mutex)
- raw_channel_recv_impl(c, &msg, loc)
- mutex_unlock(&c.mutex)
- return
-}
-channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both {
- c := ch._internal
- if c != nil && mutex_try_lock(&c.mutex) {
- if c.len > 0 {
- raw_channel_recv_impl(c, &msg, loc)
- ok = true
- }
- mutex_unlock(&c.mutex)
- }
- return
-}
-channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both {
- res: T
- res, ok = channel_try_recv(ch, loc)
- if ok && msg != nil {
- msg^ = res
- }
- return
-}
-
-
-channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool {
- return ch._internal == nil
-}
-channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool {
- c := ch._internal
- return c != nil && !c.closed
-}
-
-
-channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool {
- return a._internal == b._internal
-}
-channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool {
- return a._internal != b._internal
-}
-
-
-channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both {
- return raw_channel_can_send(ch._internal)
-}
-channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both {
- return raw_channel_can_recv(ch._internal)
-}
-
-
-channel_peek :: proc(ch: $C/Channel($T, $D)) -> int {
- c := ch._internal
- if c == nil {
- return -1
- }
- if intrinsics.atomic_load(&c.closed) {
- return -1
- }
- return intrinsics.atomic_load(&c.len)
-}
-
-
-channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) {
- raw_channel_close(ch._internal, loc)
-}
-
-
-channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both {
- c := ch._internal
- if c == nil {
- return
- }
-
- if !c.closed || c.len > 0 {
- msg, ok = channel_recv(ch), true
- }
- return
-}
-channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both {
- raw_channel_drain(ch._internal)
-}
-
-
-channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both {
- for msg in channel_iterator(src) {
- channel_send(dst, msg)
- }
-}
-
-
-Raw_Channel_Wait_Queue :: struct {
- next: ^Raw_Channel_Wait_Queue,
- state: ^uintptr,
-}
-
-
-Raw_Channel :: struct {
- closed: bool,
- ready: bool, // ready to recv
- data_offset: u16, // data is stored at the end of this data structure
- elem_size: u32,
- len, cap: int,
- read, write: int,
- mutex: Mutex,
- cond: Condition,
- allocator: mem.Allocator,
-
- sendq: ^Raw_Channel_Wait_Queue,
- recvq: ^Raw_Channel_Wait_Queue,
-}
-
-raw_channel_wait_queue_insert :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) {
- val.next = head^
- head^ = val
-}
-raw_channel_wait_queue_remove :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) {
- p := head
- for p^ != nil && p^ != val {
- p = &p^.next
- }
- if p != nil {
- p^ = p^.next
- }
-}
-
-
-raw_channel_create :: proc(elem_size, elem_align: int, cap := 0) -> ^Raw_Channel {
- assert(int(u32(elem_size)) == elem_size)
-
- s := size_of(Raw_Channel)
- s = mem.align_forward_int(s, elem_align)
- data_offset := uintptr(s)
- s += elem_size * max(cap, 1)
-
- a := max(elem_align, align_of(Raw_Channel))
-
- c := (^Raw_Channel)(mem.alloc(s, a))
- if c == nil {
- return nil
- }
-
- c.data_offset = u16(data_offset)
- c.elem_size = u32(elem_size)
- c.len, c.cap = 0, max(cap, 0)
- c.read, c.write = 0, 0
- mutex_init(&c.mutex)
- condition_init(&c.cond, &c.mutex)
- c.allocator = context.allocator
- c.closed = false
-
- return c
-}
-
-
-raw_channel_destroy :: proc(c: ^Raw_Channel) {
- if c == nil {
- return
- }
- context.allocator = c.allocator
- intrinsics.atomic_store(&c.closed, true)
-
- condition_destroy(&c.cond)
- mutex_destroy(&c.mutex)
- free(c)
-}
-
-raw_channel_close :: proc(c: ^Raw_Channel, loc := #caller_location) {
- if c == nil {
- panic(message="cannot close nil channel", loc=loc)
- }
- mutex_lock(&c.mutex)
- defer mutex_unlock(&c.mutex)
- intrinsics.atomic_store(&c.closed, true)
-
- // Release readers and writers
- raw_channel_wait_queue_broadcast(c.recvq)
- raw_channel_wait_queue_broadcast(c.sendq)
- condition_broadcast(&c.cond)
-}
-
-
-
-raw_channel_send_impl :: proc(c: ^Raw_Channel, msg: rawptr, block: bool, loc := #caller_location) -> bool {
- send :: proc(c: ^Raw_Channel, src: rawptr) {
- data := uintptr(c) + uintptr(c.data_offset)
- dst := data + uintptr(c.write * int(c.elem_size))
- mem.copy(rawptr(dst), src, int(c.elem_size))
- c.len += 1
- c.write = (c.write + 1) % max(c.cap, 1)
- }
-
- switch {
- case c == nil:
- panic(message="cannot send message; channel is nil", loc=loc)
- case c.closed:
- panic(message="cannot send message; channel is closed", loc=loc)
- }
-
- mutex_lock(&c.mutex)
- defer mutex_unlock(&c.mutex)
-
- if c.cap > 0 {
- if !block && c.len >= c.cap {
- return false
- }
-
- for c.len >= c.cap {
- condition_wait_for(&c.cond)
- }
- } else if c.len > 0 { // TODO(bill): determine correct behaviour
- if !block {
- return false
- }
- condition_wait_for(&c.cond)
- } else if c.len == 0 && !block {
- return false
- }
-
- send(c, msg)
- condition_signal(&c.cond)
- raw_channel_wait_queue_signal(c.recvq)
-
- return true
-}
-
-raw_channel_recv_impl :: proc(c: ^Raw_Channel, res: rawptr, loc := #caller_location) {
- recv :: proc(c: ^Raw_Channel, dst: rawptr, loc := #caller_location) {
- if c.len < 1 {
- panic(message="cannot recv message; channel is empty", loc=loc)
- }
- c.len -= 1
-
- data := uintptr(c) + uintptr(c.data_offset)
- src := data + uintptr(c.read * int(c.elem_size))
- mem.copy(dst, rawptr(src), int(c.elem_size))
- c.read = (c.read + 1) % max(c.cap, 1)
- }
-
- if c == nil {
- panic(message="cannot recv message; channel is nil", loc=loc)
- }
- intrinsics.atomic_store(&c.ready, true)
- for c.len < 1 {
- raw_channel_wait_queue_signal(c.sendq)
- condition_wait_for(&c.cond)
- }
- intrinsics.atomic_store(&c.ready, false)
- recv(c, res, loc)
- if c.cap > 0 {
- if c.len == c.cap - 1 {
- // NOTE(bill): Only signal on the last one
- condition_signal(&c.cond)
- }
- } else {
- condition_signal(&c.cond)
- }
-}
-
-
-raw_channel_can_send :: proc(c: ^Raw_Channel) -> (ok: bool) {
- if c == nil {
- return false
- }
- mutex_lock(&c.mutex)
- switch {
- case c.closed:
- ok = false
- case c.cap > 0:
- ok = c.ready && c.len < c.cap
- case:
- ok = c.ready && c.len == 0
- }
- mutex_unlock(&c.mutex)
- return
-}
-raw_channel_can_recv :: proc(c: ^Raw_Channel) -> (ok: bool) {
- if c == nil {
- return false
- }
- mutex_lock(&c.mutex)
- ok = c.len > 0
- mutex_unlock(&c.mutex)
- return
-}
-
-
-raw_channel_drain :: proc(c: ^Raw_Channel) {
- if c == nil {
- return
- }
- mutex_lock(&c.mutex)
- c.len = 0
- c.read = 0
- c.write = 0
- mutex_unlock(&c.mutex)
-}
-
-
-
-MAX_SELECT_CHANNELS :: 64
-SELECT_MAX_TIMEOUT :: max(time.Duration)
-
-Select_Command :: enum {
- Recv,
- Send,
-}
-
-Select_Channel :: struct {
- channel: ^Raw_Channel,
- command: Select_Command,
-}
-
-
-
-select :: proc(channels: ..Select_Channel) -> (index: int) {
- return select_timeout(SELECT_MAX_TIMEOUT, ..channels)
-}
-select_timeout :: proc(timeout: time.Duration, channels: ..Select_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- backing: [MAX_SELECT_CHANNELS]int
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
- candidates := backing[:]
- cap := len(channels)
- candidates = candidates[:cap]
-
- count := u32(0)
- for c, i in channels {
- if c.channel == nil {
- continue
- }
- switch c.command {
- case .Recv:
- if raw_channel_can_recv(c.channel) {
- candidates[count] = i
- count += 1
- }
- case .Send:
- if raw_channel_can_send(c.channel) {
- candidates[count] = i
- count += 1
- }
- }
- }
-
- if count == 0 {
- wait_state: uintptr = 0
- for _, i in channels {
- q := &queues[i]
- q.state = &wait_state
- }
-
- for c, i in channels {
- if c.channel == nil {
- continue
- }
- q := &queues[i]
- switch c.command {
- case .Recv: raw_channel_wait_queue_insert(&c.channel.recvq, q)
- case .Send: raw_channel_wait_queue_insert(&c.channel.sendq, q)
- }
- }
- raw_channel_wait_queue_wait_on(&wait_state, timeout)
- for c, i in channels {
- if c.channel == nil {
- continue
- }
- q := &queues[i]
- switch c.command {
- case .Recv: raw_channel_wait_queue_remove(&c.channel.recvq, q)
- case .Send: raw_channel_wait_queue_remove(&c.channel.sendq, q)
- }
- }
-
- for c, i in channels {
- switch c.command {
- case .Recv:
- if raw_channel_can_recv(c.channel) {
- candidates[count] = i
- count += 1
- }
- case .Send:
- if raw_channel_can_send(c.channel) {
- candidates[count] = i
- count += 1
- }
- }
- }
- if count == 0 && timeout == SELECT_MAX_TIMEOUT {
- index = -1
- return
- }
-
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-select_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- backing: [MAX_SELECT_CHANNELS]int
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
- candidates := backing[:]
- cap := len(channels)
- candidates = candidates[:cap]
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- state: uintptr
- for c, i in channels {
- q := &queues[i]
- q.state = &state
- raw_channel_wait_queue_insert(&c.recvq, q)
- }
- raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
- for c, i in channels {
- q := &queues[i]
- raw_channel_wait_queue_remove(&c.recvq, q)
- }
-
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- state: uintptr
- for c, i in channels {
- q := &queues[i]
- q.state = &state
- raw_channel_wait_queue_insert(&c.recvq, q)
- }
- raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
- for c, i in channels {
- q := &queues[i]
- raw_channel_wait_queue_remove(&c.recvq, q)
- }
-
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- msg = channel_recv(channels[index])
-
- return
-}
-
-select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- backing: [MAX_SELECT_CHANNELS]int
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
- candidates := backing[:]
- cap := len(channels)
- candidates = candidates[:cap]
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- state: uintptr
- for c, i in channels {
- q := &queues[i]
- q.state = &state
- raw_channel_wait_queue_insert(&c.recvq, q)
- }
- raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
- for c, i in channels {
- q := &queues[i]
- raw_channel_wait_queue_remove(&c.recvq, q)
- }
-
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
-
- if msg != nil {
- channel_send(channels[index], msg)
- }
-
- return
-}
-
-select_send :: proc(channels: ..^Raw_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_send(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- state: uintptr
- for c, i in channels {
- q := &queues[i]
- q.state = &state
- raw_channel_wait_queue_insert(&c.sendq, q)
- }
- raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
- for c, i in channels {
- q := &queues[i]
- raw_channel_wait_queue_remove(&c.sendq, q)
- }
-
- for c, i in channels {
- if raw_channel_can_send(c) {
- candidates[count] = i
- count += 1
- }
- }
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-select_try :: proc(channels: ..Select_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- backing: [MAX_SELECT_CHANNELS]int
- candidates := backing[:]
- cap := len(channels)
- candidates = candidates[:cap]
-
- count := u32(0)
- for c, i in channels {
- switch c.command {
- case .Recv:
- if raw_channel_can_recv(c.channel) {
- candidates[count] = i
- count += 1
- }
- case .Send:
- if raw_channel_can_send(c.channel) {
- candidates[count] = i
- count += 1
- }
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-
-select_try_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- index = -1
- return
- case 1:
- index = -1
- if raw_channel_can_recv(channels[0]) {
- index = 0
- }
- return
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-
-select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_check {
- switch len(channels) {
- case 0:
- return -1
- case 1:
- if raw_channel_can_send(channels[0]) {
- return 0
- }
- return -1
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_send(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
- switch len(channels) {
- case 0:
- index = -1
- return
- case 1:
- ok: bool
- if msg, ok = channel_try_recv(channels[0]); ok {
- index = 0
- }
- return
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- msg = channel_recv(channels[index])
- return
-}
-
-select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
- index = -1
- switch len(channels) {
- case 0:
- return
- case 1:
- if channel_try_send(channels[0], msg) {
- index = 0
- }
- return
- }
-
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_send(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- channel_send(channels[index], msg)
- return
-}
-
diff --git a/core/sync/channel_unix.odin b/core/sync/channel_unix.odin
deleted file mode 100644
index 47aa46004..000000000
--- a/core/sync/channel_unix.odin
+++ /dev/null
@@ -1,16 +0,0 @@
-// +build linux, darwin, freebsd, openbsd
-package sync
-
-import "core:time"
-
-raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) {
- // stub
-}
-
-raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) {
- // stub
-}
-
-raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) {
- // stub
-}
diff --git a/core/sync/channel_windows.odin b/core/sync/channel_windows.odin
deleted file mode 100644
index 5d469ffff..000000000
--- a/core/sync/channel_windows.odin
+++ /dev/null
@@ -1,33 +0,0 @@
-package sync
-
-import "core:intrinsics"
-import win32 "core:sys/windows"
-import "core:time"
-
-raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) {
- ms: win32.DWORD = win32.INFINITE
- if max(time.Duration) != SELECT_MAX_TIMEOUT {
- ms = win32.DWORD((max(time.duration_nanoseconds(timeout), 0) + 999999)/1000000)
- }
-
- v := intrinsics.atomic_load(state)
- for v == 0 {
- win32.WaitOnAddress(state, &v, size_of(state^), ms)
- v = intrinsics.atomic_load(state)
- }
- intrinsics.atomic_store(state, 0)
-}
-
-raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) {
- for x := q; x != nil; x = x.next {
- intrinsics.atomic_add(x.state, 1)
- win32.WakeByAddressSingle(x.state)
- }
-}
-
-raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) {
- for x := q; x != nil; x = x.next {
- intrinsics.atomic_add(x.state, 1)
- win32.WakeByAddressAll(x.state)
- }
-}
diff --git a/core/sync/sync2/extended.odin b/core/sync/extended.odin
similarity index 85%
rename from core/sync/sync2/extended.odin
rename to core/sync/extended.odin
index deb48a22d..24f7c096c 100644
--- a/core/sync/sync2/extended.odin
+++ b/core/sync/extended.odin
@@ -1,4 +1,4 @@
-package sync2
+package sync
import "core:time"
@@ -146,10 +146,10 @@ Auto_Reset_Event :: struct {
}
auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
- old_status := atomic_load_relaxed(&e.status)
+ old_status := atomic_load_explicit(&e.status, .Relaxed)
for {
new_status := old_status + 1 if old_status < 1 else 1
- if _, ok := atomic_compare_exchange_weak_release(&e.status, old_status, new_status); ok {
+ if _, ok := atomic_compare_exchange_weak_explicit(&e.status, old_status, new_status, .Release, .Relaxed); ok {
break
}
@@ -160,7 +160,7 @@ auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
}
auto_reset_event_wait :: proc(e: ^Auto_Reset_Event) {
- old_status := atomic_sub_acquire(&e.status, 1)
+ old_status := atomic_sub_explicit(&e.status, 1, .Acquire)
if old_status < 1 {
sema_wait(&e.sema)
}
@@ -174,14 +174,14 @@ Ticket_Mutex :: struct {
}
ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) {
- ticket := atomic_add_relaxed(&m.ticket, 1)
- for ticket != atomic_load_acquire(&m.serving) {
+ ticket := atomic_add_explicit(&m.ticket, 1, .Relaxed)
+ for ticket != atomic_load_explicit(&m.serving, .Acquire) {
cpu_relax()
}
}
ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) {
- atomic_add_relaxed(&m.serving, 1)
+ atomic_add_explicit(&m.serving, 1, .Relaxed)
}
@(deferred_in=ticket_mutex_unlock)
ticket_mutex_guard :: proc(m: ^Ticket_Mutex) -> bool {
@@ -196,18 +196,18 @@ Benaphore :: struct {
}
benaphore_lock :: proc(b: ^Benaphore) {
- if atomic_add_acquire(&b.counter, 1) > 1 {
+ if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
sema_wait(&b.sema)
}
}
benaphore_try_lock :: proc(b: ^Benaphore) -> bool {
- v, _ := atomic_compare_exchange_strong_acquire(&b.counter, 1, 0)
+ v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 1, 0, .Acquire, .Acquire)
return v == 0
}
benaphore_unlock :: proc(b: ^Benaphore) {
- if atomic_sub_release(&b.counter, 1) > 0 {
+ if atomic_sub_explicit(&b.counter, 1, .Release) > 0 {
sema_post(&b.sema)
}
}
@@ -227,7 +227,7 @@ Recursive_Benaphore :: struct {
recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
tid := current_thread_id()
- if atomic_add_acquire(&b.counter, 1) > 1 {
+ if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
if tid != b.owner {
sema_wait(&b.sema)
}
@@ -240,10 +240,10 @@ recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
tid := current_thread_id()
if b.owner == tid {
- atomic_add_acquire(&b.counter, 1)
+ atomic_add_explicit(&b.counter, 1, .Acquire)
}
- if v, _ := atomic_compare_exchange_strong_acquire(&b.counter, 1, 0); v != 0 {
+ if v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 1, 0, .Acquire, .Acquire); v != 0 {
return false
}
// inside the lock
@@ -260,7 +260,7 @@ recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
if recursion == 0 {
b.owner = 0
}
- if atomic_sub_release(&b.counter, 1) > 0 {
+ if atomic_sub_explicit(&b.counter, 1, .Release) > 0 {
if recursion == 0 {
sema_post(&b.sema)
}
@@ -293,12 +293,12 @@ once_do :: proc(o: ^Once, fn: proc()) {
defer mutex_unlock(&o.m)
if !o.done {
fn()
- atomic_store_release(&o.done, true)
+ atomic_store_explicit(&o.done, true, .Release)
}
}
- if atomic_load_acquire(&o.done) == false {
+ if atomic_load_explicit(&o.done, .Acquire) == false {
do_slow(o, fn)
}
}
diff --git a/core/sync/sync2/futex_darwin.odin b/core/sync/futex_darwin.odin
similarity index 99%
rename from core/sync/sync2/futex_darwin.odin
rename to core/sync/futex_darwin.odin
index 9dad8d375..88e354827 100644
--- a/core/sync/sync2/futex_darwin.odin
+++ b/core/sync/futex_darwin.odin
@@ -1,6 +1,6 @@
//+private
//+build darwin
-package sync2
+package sync
import "core:c"
import "core:time"
diff --git a/core/sync/sync2/futex_freebsd.odin b/core/sync/futex_freebsd.odin
similarity index 98%
rename from core/sync/sync2/futex_freebsd.odin
rename to core/sync/futex_freebsd.odin
index 2cbdb4aaa..2e1d065bc 100644
--- a/core/sync/sync2/futex_freebsd.odin
+++ b/core/sync/futex_freebsd.odin
@@ -1,6 +1,6 @@
//+private
//+build freebsd
-package sync2
+package sync
import "core:c"
import "core:os"
diff --git a/core/sync/sync2/futex_linux.odin b/core/sync/futex_linux.odin
similarity index 94%
rename from core/sync/sync2/futex_linux.odin
rename to core/sync/futex_linux.odin
index fca28cace..c429a9d64 100644
--- a/core/sync/sync2/futex_linux.odin
+++ b/core/sync/futex_linux.odin
@@ -1,6 +1,6 @@
//+private
//+build linux
-package sync2
+package sync
import "core:c"
import "core:time"
@@ -14,12 +14,6 @@ FUTEX_PRIVATE_FLAG :: 128
FUTEX_WAIT_PRIVATE :: (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
FUTEX_WAKE_PRIVATE :: (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
-foreign import libc "system:c"
-
-foreign libc {
- __errno_location :: proc "c" () -> ^c.int ---
-}
-
ESUCCESS :: 0
EINTR :: -4
EAGAIN :: -11
diff --git a/core/sync/sync2/futex_openbsd.odin b/core/sync/futex_openbsd.odin
similarity index 99%
rename from core/sync/sync2/futex_openbsd.odin
rename to core/sync/futex_openbsd.odin
index dbc80747b..6ac9d3efb 100644
--- a/core/sync/sync2/futex_openbsd.odin
+++ b/core/sync/futex_openbsd.odin
@@ -1,6 +1,6 @@
//+private
//+build openbsd
-package sync2
+package sync
import "core:c"
import "core:os"
diff --git a/core/sync/sync2/futex_windows.odin b/core/sync/futex_windows.odin
similarity index 98%
rename from core/sync/sync2/futex_windows.odin
rename to core/sync/futex_windows.odin
index 200a119ff..1c9d8b845 100644
--- a/core/sync/sync2/futex_windows.odin
+++ b/core/sync/futex_windows.odin
@@ -1,6 +1,6 @@
//+private
//+build windows
-package sync2
+package sync
import "core:time"
diff --git a/core/sync/sync2/primitives.odin b/core/sync/primitives.odin
similarity index 99%
rename from core/sync/sync2/primitives.odin
rename to core/sync/primitives.odin
index 6d056d439..483f85343 100644
--- a/core/sync/sync2/primitives.odin
+++ b/core/sync/primitives.odin
@@ -1,4 +1,4 @@
-package sync2
+package sync
import "core:time"
diff --git a/core/sync/sync2/primitives_atomic.odin b/core/sync/primitives_atomic.odin
similarity index 93%
rename from core/sync/sync2/primitives_atomic.odin
rename to core/sync/primitives_atomic.odin
index 5fc6fba85..11fff4e60 100644
--- a/core/sync/sync2/primitives_atomic.odin
+++ b/core/sync/primitives_atomic.odin
@@ -1,4 +1,4 @@
-package sync2
+package sync
import "core:time"
@@ -24,7 +24,7 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
new_state := curr_state // Make a copy of it
spin_lock: for spin in 0.. bool {
- _, ok := atomic_compare_exchange_strong_acquire(&m.state, .Unlocked, .Locked)
+ _, ok := atomic_compare_exchange_strong_explicit(&m.state, .Unlocked, .Locked, .Acquire, .Consume)
return ok
}
@@ -291,7 +290,7 @@ Queue_Item :: struct {
@(private="file")
queue_item_wait :: proc(item: ^Queue_Item) {
- for atomic_load_acquire(&item.futex) == 0 {
+ for atomic_load_explicit(&item.futex, .Acquire) == 0 {
futex_wait(&item.futex, 0)
cpu_relax()
}
@@ -299,7 +298,7 @@ queue_item_wait :: proc(item: ^Queue_Item) {
@(private="file")
queue_item_wait_with_timeout :: proc(item: ^Queue_Item, duration: time.Duration) -> bool {
start := time.tick_now()
- for atomic_load_acquire(&item.futex) == 0 {
+ for atomic_load_explicit(&item.futex, .Acquire) == 0 {
remaining := duration - time.tick_since(start)
if remaining < 0 {
return false
@@ -313,7 +312,7 @@ queue_item_wait_with_timeout :: proc(item: ^Queue_Item, duration: time.Duration)
}
@(private="file")
queue_item_signal :: proc(item: ^Queue_Item) {
- atomic_store_release(&item.futex, 1)
+ atomic_store_explicit(&item.futex, 1, .Release)
futex_signal(&item.futex)
}
diff --git a/core/sync/sync2/primitives_darwin.odin b/core/sync/primitives_darwin.odin
similarity index 98%
rename from core/sync/sync2/primitives_darwin.odin
rename to core/sync/primitives_darwin.odin
index 66995bd01..514f66f3e 100644
--- a/core/sync/sync2/primitives_darwin.odin
+++ b/core/sync/primitives_darwin.odin
@@ -1,6 +1,6 @@
//+build darwin
//+private
-package sync2
+package sync
import "core:c"
import "core:time"
diff --git a/core/sync/primitives_freebsd.odin b/core/sync/primitives_freebsd.odin
new file mode 100644
index 000000000..b88fca181
--- /dev/null
+++ b/core/sync/primitives_freebsd.odin
@@ -0,0 +1,46 @@
+//+build freebsd
+//+private
+package sync
+
+import "core:os"
+import "core:time"
+
+_current_thread_id :: proc "contextless" () -> int {
+ return os.current_thread_id()
+}
+
+_Mutex :: struct {
+ mutex: Atomic_Mutex,
+}
+
+_mutex_lock :: proc(m: ^Mutex) {
+ atomic_mutex_lock(&m.impl.mutex)
+}
+
+_mutex_unlock :: proc(m: ^Mutex) {
+ atomic_mutex_unlock(&m.impl.mutex)
+}
+
+_mutex_try_lock :: proc(m: ^Mutex) -> bool {
+ return atomic_mutex_try_lock(&m.impl.mutex)
+}
+
+_Cond :: struct {
+ cond: Atomic_Cond,
+}
+
+_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
+ atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
+}
+
+_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
+ return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
+}
+
+_cond_signal :: proc(c: ^Cond) {
+ atomic_cond_signal(&c.impl.cond)
+}
+
+_cond_broadcast :: proc(c: ^Cond) {
+ atomic_cond_broadcast(&c.impl.cond)
+}
diff --git a/core/sync/primitives_internal.odin b/core/sync/primitives_internal.odin
new file mode 100644
index 000000000..15967d45e
--- /dev/null
+++ b/core/sync/primitives_internal.odin
@@ -0,0 +1,125 @@
+//+private
+package sync
+
+when #config(ODIN_SYNC_RECURSIVE_MUTEX_USE_FUTEX, true) {
+ _Recursive_Mutex :: struct {
+ owner: Futex,
+ recursion: i32,
+ }
+
+ _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
+ tid := Futex(current_thread_id())
+ for {
+ prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, tid, 0, .Acquire, .Acquire)
+ switch prev_owner {
+ case 0, tid:
+ m.impl.recursion += 1
+ // inside the lock
+ return
+ }
+
+ futex_wait(&m.impl.owner, u32(prev_owner))
+ }
+ }
+
+ _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
+ m.impl.recursion -= 1
+ if m.impl.recursion != 0 {
+ return
+ }
+ atomic_exchange_explicit(&m.impl.owner, 0, .Release)
+
+ futex_signal(&m.impl.owner)
+ // outside the lock
+
+ }
+
+ _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
+ tid := Futex(current_thread_id())
+ prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, tid, 0, .Acquire, .Acquire)
+ switch prev_owner {
+ case 0, tid:
+ m.impl.recursion += 1
+ // inside the lock
+ return true
+ }
+ return false
+ }
+} else {
+ _Recursive_Mutex :: struct {
+ owner: int,
+ recursion: int,
+ mutex: Mutex,
+ }
+
+ _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
+ tid := current_thread_id()
+ if tid != m.impl.owner {
+ mutex_lock(&m.impl.mutex)
+ }
+ // inside the lock
+ m.impl.owner = tid
+ m.impl.recursion += 1
+ }
+
+ _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
+ tid := current_thread_id()
+ assert(tid == m.impl.owner)
+ m.impl.recursion -= 1
+ recursion := m.impl.recursion
+ if recursion == 0 {
+ m.impl.owner = 0
+ }
+ if recursion == 0 {
+ mutex_unlock(&m.impl.mutex)
+ }
+ // outside the lock
+
+ }
+
+ _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
+ tid := current_thread_id()
+ if m.impl.owner == tid {
+ return mutex_try_lock(&m.impl.mutex)
+ }
+ if !mutex_try_lock(&m.impl.mutex) {
+ return false
+ }
+ // inside the lock
+ m.impl.owner = tid
+ m.impl.recursion += 1
+ return true
+ }
+}
+
+
+when ODIN_OS != .Windows {
+ _RW_Mutex :: struct {
+ mutex: Atomic_RW_Mutex,
+ }
+
+ _rw_mutex_lock :: proc(rw: ^RW_Mutex) {
+ atomic_rw_mutex_lock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
+ atomic_rw_mutex_unlock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
+ return atomic_rw_mutex_try_lock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
+ atomic_rw_mutex_shared_lock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
+ atomic_rw_mutex_shared_unlock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
+ return atomic_rw_mutex_try_shared_lock(&rw.impl.mutex)
+ }
+
+}
\ No newline at end of file
diff --git a/core/sync/primitives_linux.odin b/core/sync/primitives_linux.odin
new file mode 100644
index 000000000..0a9f0cc33
--- /dev/null
+++ b/core/sync/primitives_linux.odin
@@ -0,0 +1,47 @@
+//+build linux
+//+private
+package sync
+
+import "core:sys/unix"
+import "core:time"
+
+_current_thread_id :: proc "contextless" () -> int {
+ return unix.sys_gettid()
+}
+
+
+_Mutex :: struct {
+ mutex: Atomic_Mutex,
+}
+
+_mutex_lock :: proc(m: ^Mutex) {
+ atomic_mutex_lock(&m.impl.mutex)
+}
+
+_mutex_unlock :: proc(m: ^Mutex) {
+ atomic_mutex_unlock(&m.impl.mutex)
+}
+
+_mutex_try_lock :: proc(m: ^Mutex) -> bool {
+ return atomic_mutex_try_lock(&m.impl.mutex)
+}
+
+_Cond :: struct {
+ cond: Atomic_Cond,
+}
+
+_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
+ atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
+}
+
+_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
+ return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
+}
+
+_cond_signal :: proc(c: ^Cond) {
+ atomic_cond_signal(&c.impl.cond)
+}
+
+_cond_broadcast :: proc(c: ^Cond) {
+ atomic_cond_broadcast(&c.impl.cond)
+}
diff --git a/core/sync/primitives_openbsd.odin b/core/sync/primitives_openbsd.odin
new file mode 100644
index 000000000..7794016f8
--- /dev/null
+++ b/core/sync/primitives_openbsd.odin
@@ -0,0 +1,46 @@
+//+build openbsd
+//+private
+package sync
+
+import "core:os"
+import "core:time"
+
+_current_thread_id :: proc "contextless" () -> int {
+ return os.current_thread_id()
+}
+
+_Mutex :: struct {
+ mutex: Atomic_Mutex,
+}
+
+_mutex_lock :: proc(m: ^Mutex) {
+ atomic_mutex_lock(&m.impl.mutex)
+}
+
+_mutex_unlock :: proc(m: ^Mutex) {
+ atomic_mutex_unlock(&m.impl.mutex)
+}
+
+_mutex_try_lock :: proc(m: ^Mutex) -> bool {
+ return atomic_mutex_try_lock(&m.impl.mutex)
+}
+
+_Cond :: struct {
+ cond: Atomic_Cond,
+}
+
+_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
+ atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
+}
+
+_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
+ return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
+}
+
+_cond_signal :: proc(c: ^Cond) {
+ atomic_cond_signal(&c.impl.cond)
+}
+
+_cond_broadcast :: proc(c: ^Cond) {
+ atomic_cond_broadcast(&c.impl.cond)
+}
diff --git a/core/sync/sync2/primitives_windows.odin b/core/sync/primitives_windows.odin
similarity index 99%
rename from core/sync/sync2/primitives_windows.odin
rename to core/sync/primitives_windows.odin
index ca7a5c9ee..055167892 100644
--- a/core/sync/sync2/primitives_windows.odin
+++ b/core/sync/primitives_windows.odin
@@ -1,6 +1,6 @@
//+build windows
//+private
-package sync2
+package sync
import "core:time"
import win32 "core:sys/windows"
diff --git a/core/sync/sync2/sema_internal.odin b/core/sync/sema_internal.odin
similarity index 99%
rename from core/sync/sync2/sema_internal.odin
rename to core/sync/sema_internal.odin
index f4027e908..8cf157708 100644
--- a/core/sync/sync2/sema_internal.odin
+++ b/core/sync/sema_internal.odin
@@ -1,5 +1,5 @@
//+private
-package sync2
+package sync
import "core:time"
diff --git a/core/sync/sync.odin b/core/sync/sync.odin
deleted file mode 100644
index 05c86a868..000000000
--- a/core/sync/sync.odin
+++ /dev/null
@@ -1,123 +0,0 @@
-package sync
-
-import "core:intrinsics"
-
-cpu_relax :: #force_inline proc "contextless" () {
- intrinsics.cpu_relax()
-}
-
-Condition_Mutex_Ptr :: union{^Mutex, ^Blocking_Mutex}
-
-
-Ticket_Mutex :: struct {
- ticket: u64,
- serving: u64,
-}
-
-ticket_mutex_init :: proc(m: ^Ticket_Mutex) {
- atomic_store(&m.ticket, 0, .Relaxed)
- atomic_store(&m.serving, 0, .Relaxed)
-}
-
-ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) {
- ticket := atomic_add(&m.ticket, 1, .Relaxed)
- for ticket != atomic_load(&m.serving, .Acquire) {
- intrinsics.cpu_relax()
- }
-}
-
-ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) {
- atomic_add(&m.serving, 1, .Relaxed)
-}
-
-
-Benaphore :: struct {
- counter: int,
- sema: Semaphore,
-}
-
-benaphore_init :: proc(b: ^Benaphore) {
- intrinsics.atomic_store(&b.counter, 0)
- semaphore_init(&b.sema)
-}
-
-benaphore_destroy :: proc(b: ^Benaphore) {
- semaphore_destroy(&b.sema)
-}
-
-benaphore_lock :: proc(b: ^Benaphore) {
- if intrinsics.atomic_add_acq(&b.counter, 1) > 1 {
- semaphore_wait_for(&b.sema)
- }
-}
-
-benaphore_try_lock :: proc(b: ^Benaphore) -> bool {
- v, _ := intrinsics.atomic_cxchg_acq(&b.counter, 1, 0)
- return v == 0
-}
-
-benaphore_unlock :: proc(b: ^Benaphore) {
- if intrinsics.atomic_sub_rel(&b.counter, 1) > 0 {
- semaphore_post(&b.sema)
- }
-}
-
-Recursive_Benaphore :: struct {
- counter: int,
- owner: int,
- recursion: int,
- sema: Semaphore,
-}
-
-recursive_benaphore_init :: proc(b: ^Recursive_Benaphore) {
- intrinsics.atomic_store(&b.counter, 0)
- semaphore_init(&b.sema)
-}
-
-recursive_benaphore_destroy :: proc(b: ^Recursive_Benaphore) {
- semaphore_destroy(&b.sema)
-}
-
-recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
- tid := current_thread_id()
- if intrinsics.atomic_add_acq(&b.counter, 1) > 1 {
- if tid != b.owner {
- semaphore_wait_for(&b.sema)
- }
- }
- // inside the lock
- b.owner = tid
- b.recursion += 1
-}
-
-recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
- tid := current_thread_id()
- if b.owner == tid {
- intrinsics.atomic_add_acq(&b.counter, 1)
- } else {
- v, _ := intrinsics.atomic_cxchg_acq(&b.counter, 1, 0)
- if v != 0 {
- return false
- }
- // inside the lock
- b.owner = tid
- }
- b.recursion += 1
- return true
-}
-
-recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
- tid := current_thread_id()
- assert(tid == b.owner)
- b.recursion -= 1
- recursion := b.recursion
- if recursion == 0 {
- b.owner = 0
- }
- if intrinsics.atomic_sub_rel(&b.counter, 1) > 0 {
- if recursion == 0 {
- semaphore_post(&b.sema)
- }
- }
- // outside the lock
-}
diff --git a/core/sync/sync2/atomic.odin b/core/sync/sync2/atomic.odin
deleted file mode 100644
index fe19f17c8..000000000
--- a/core/sync/sync2/atomic.odin
+++ /dev/null
@@ -1,79 +0,0 @@
-package sync2
-
-import "core:intrinsics"
-
-cpu_relax :: intrinsics.cpu_relax
-
-atomic_fence :: intrinsics.atomic_fence
-atomic_fence_acquire :: intrinsics.atomic_fence_acq
-atomic_fence_release :: intrinsics.atomic_fence_rel
-atomic_fence_acqrel :: intrinsics.atomic_fence_acqrel
-
-atomic_store :: intrinsics.atomic_store
-atomic_store_release :: intrinsics.atomic_store_rel
-atomic_store_relaxed :: intrinsics.atomic_store_relaxed
-atomic_store_unordered :: intrinsics.atomic_store_unordered
-
-atomic_load :: intrinsics.atomic_load
-atomic_load_acquire :: intrinsics.atomic_load_acq
-atomic_load_relaxed :: intrinsics.atomic_load_relaxed
-atomic_load_unordered :: intrinsics.atomic_load_unordered
-
-atomic_add :: intrinsics.atomic_add
-atomic_add_acquire :: intrinsics.atomic_add_acq
-atomic_add_release :: intrinsics.atomic_add_rel
-atomic_add_acqrel :: intrinsics.atomic_add_acqrel
-atomic_add_relaxed :: intrinsics.atomic_add_relaxed
-atomic_sub :: intrinsics.atomic_sub
-atomic_sub_acquire :: intrinsics.atomic_sub_acq
-atomic_sub_release :: intrinsics.atomic_sub_rel
-atomic_sub_acqrel :: intrinsics.atomic_sub_acqrel
-atomic_sub_relaxed :: intrinsics.atomic_sub_relaxed
-atomic_and :: intrinsics.atomic_and
-atomic_and_acquire :: intrinsics.atomic_and_acq
-atomic_and_release :: intrinsics.atomic_and_rel
-atomic_and_acqrel :: intrinsics.atomic_and_acqrel
-atomic_and_relaxed :: intrinsics.atomic_and_relaxed
-atomic_nand :: intrinsics.atomic_nand
-atomic_nand_acquire :: intrinsics.atomic_nand_acq
-atomic_nand_release :: intrinsics.atomic_nand_rel
-atomic_nand_acqrel :: intrinsics.atomic_nand_acqrel
-atomic_nand_relaxed :: intrinsics.atomic_nand_relaxed
-atomic_or :: intrinsics.atomic_or
-atomic_or_acquire :: intrinsics.atomic_or_acq
-atomic_or_release :: intrinsics.atomic_or_rel
-atomic_or_acqrel :: intrinsics.atomic_or_acqrel
-atomic_or_relaxed :: intrinsics.atomic_or_relaxed
-atomic_xor :: intrinsics.atomic_xor
-atomic_xor_acquire :: intrinsics.atomic_xor_acq
-atomic_xor_release :: intrinsics.atomic_xor_rel
-atomic_xor_acqrel :: intrinsics.atomic_xor_acqrel
-atomic_xor_relaxed :: intrinsics.atomic_xor_relaxed
-
-atomic_exchange :: intrinsics.atomic_xchg
-atomic_exchange_acquire :: intrinsics.atomic_xchg_acq
-atomic_exchange_release :: intrinsics.atomic_xchg_rel
-atomic_exchange_acqrel :: intrinsics.atomic_xchg_acqrel
-atomic_exchange_relaxed :: intrinsics.atomic_xchg_relaxed
-
-// Returns value and optional ok boolean
-atomic_compare_exchange_strong :: intrinsics.atomic_cxchg
-atomic_compare_exchange_strong_acquire :: intrinsics.atomic_cxchg_acq
-atomic_compare_exchange_strong_release :: intrinsics.atomic_cxchg_rel
-atomic_compare_exchange_strong_acqrel :: intrinsics.atomic_cxchg_acqrel
-atomic_compare_exchange_strong_relaxed :: intrinsics.atomic_cxchg_relaxed
-atomic_compare_exchange_strong_failrelaxed :: intrinsics.atomic_cxchg_failrelaxed
-atomic_compare_exchange_strong_failacquire :: intrinsics.atomic_cxchg_failacq
-atomic_compare_exchange_strong_acquire_failrelaxed :: intrinsics.atomic_cxchg_acq_failrelaxed
-atomic_compare_exchange_strong_acqrel_failrelaxed :: intrinsics.atomic_cxchg_acqrel_failrelaxed
-
-// Returns value and optional ok boolean
-atomic_compare_exchange_weak :: intrinsics.atomic_cxchgweak
-atomic_compare_exchange_weak_acquire :: intrinsics.atomic_cxchgweak_acq
-atomic_compare_exchange_weak_release :: intrinsics.atomic_cxchgweak_rel
-atomic_compare_exchange_weak_acqrel :: intrinsics.atomic_cxchgweak_acqrel
-atomic_compare_exchange_weak_relaxed :: intrinsics.atomic_cxchgweak_relaxed
-atomic_compare_exchange_weak_failrelaxed :: intrinsics.atomic_cxchgweak_failrelaxed
-atomic_compare_exchange_weak_failacquire :: intrinsics.atomic_cxchgweak_failacq
-atomic_compare_exchange_weak_acquire_failrelaxed :: intrinsics.atomic_cxchgweak_acq_failrelaxed
-atomic_compare_exchange_weak_acqrel_failrelaxed :: intrinsics.atomic_cxchgweak_acqrel_failrelaxed
diff --git a/core/sync/sync2/primitives_freebsd.odin b/core/sync/sync2/primitives_freebsd.odin
deleted file mode 100644
index 2a25a18a4..000000000
--- a/core/sync/sync2/primitives_freebsd.odin
+++ /dev/null
@@ -1,9 +0,0 @@
-//+build freebsd
-//+private
-package sync2
-
-import "core:os"
-
-_current_thread_id :: proc "contextless" () -> int {
- return os.current_thread_id()
-}
diff --git a/core/sync/sync2/primitives_internal.odin b/core/sync/sync2/primitives_internal.odin
deleted file mode 100644
index eb692c6ae..000000000
--- a/core/sync/sync2/primitives_internal.odin
+++ /dev/null
@@ -1,184 +0,0 @@
-//+private
-package sync2
-
-when #config(ODIN_SYNC_RECURSIVE_MUTEX_USE_FUTEX, true) {
- _Recursive_Mutex :: struct {
- owner: Futex,
- recursion: i32,
- }
-
- _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
- tid := Futex(current_thread_id())
- for {
- prev_owner := atomic_compare_exchange_strong_acquire(&m.impl.owner, tid, 0)
- switch prev_owner {
- case 0, tid:
- m.impl.recursion += 1
- // inside the lock
- return
- }
-
- futex_wait(&m.impl.owner, u32(prev_owner))
- }
- }
-
- _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
- m.impl.recursion -= 1
- if m.impl.recursion != 0 {
- return
- }
- atomic_exchange_release(&m.impl.owner, 0)
-
- futex_signal(&m.impl.owner)
- // outside the lock
-
- }
-
- _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
- tid := Futex(current_thread_id())
- prev_owner := atomic_compare_exchange_strong_acquire(&m.impl.owner, tid, 0)
- switch prev_owner {
- case 0, tid:
- m.impl.recursion += 1
- // inside the lock
- return true
- }
- return false
- }
-} else {
- _Recursive_Mutex :: struct {
- owner: int,
- recursion: int,
- mutex: Mutex,
- }
-
- _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
- tid := current_thread_id()
- if tid != m.impl.owner {
- mutex_lock(&m.impl.mutex)
- }
- // inside the lock
- m.impl.owner = tid
- m.impl.recursion += 1
- }
-
- _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
- tid := current_thread_id()
- assert(tid == m.impl.owner)
- m.impl.recursion -= 1
- recursion := m.impl.recursion
- if recursion == 0 {
- m.impl.owner = 0
- }
- if recursion == 0 {
- mutex_unlock(&m.impl.mutex)
- }
- // outside the lock
-
- }
-
- _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
- tid := current_thread_id()
- if m.impl.owner == tid {
- return mutex_try_lock(&m.impl.mutex)
- }
- if !mutex_try_lock(&m.impl.mutex) {
- return false
- }
- // inside the lock
- m.impl.owner = tid
- m.impl.recursion += 1
- return true
- }
-}
-
-
-when ODIN_OS != .Windows {
- RW_Mutex_State :: distinct uint
- RW_Mutex_State_Half_Width :: size_of(RW_Mutex_State)*8/2
- RW_Mutex_State_Is_Writing :: RW_Mutex_State(1)
- RW_Mutex_State_Writer :: RW_Mutex_State(1)<<1
- RW_Mutex_State_Reader :: RW_Mutex_State(1)< bool {
- if mutex_try_lock(&rw.impl.mutex) {
- state := atomic_load(&rw.impl.state)
- if state & RW_Mutex_State_Reader_Mask == 0 {
- _ = atomic_or(&rw.impl.state, RW_Mutex_State_Is_Writing)
- return true
- }
-
- mutex_unlock(&rw.impl.mutex)
- }
- return false
- }
-
- _rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
- state := atomic_load(&rw.impl.state)
- for state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 {
- ok: bool
- state, ok = atomic_compare_exchange_weak(&rw.impl.state, state, state + RW_Mutex_State_Reader)
- if ok {
- return
- }
- }
-
- mutex_lock(&rw.impl.mutex)
- _ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader)
- mutex_unlock(&rw.impl.mutex)
- }
-
- _rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
- state := atomic_sub(&rw.impl.state, RW_Mutex_State_Reader)
-
- if (state & RW_Mutex_State_Reader_Mask == RW_Mutex_State_Reader) &&
- (state & RW_Mutex_State_Is_Writing != 0) {
- sema_post(&rw.impl.sema)
- }
- }
-
- _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
- state := atomic_load(&rw.impl.state)
- if state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 {
- _, ok := atomic_compare_exchange_strong(&rw.impl.state, state, state + RW_Mutex_State_Reader)
- if ok {
- return true
- }
- }
- if mutex_try_lock(&rw.impl.mutex) {
- _ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader)
- mutex_unlock(&rw.impl.mutex)
- return true
- }
-
- return false
- }
-
-}
\ No newline at end of file
diff --git a/core/sync/sync2/primitives_linux.odin b/core/sync/sync2/primitives_linux.odin
deleted file mode 100644
index 89ed97985..000000000
--- a/core/sync/sync2/primitives_linux.odin
+++ /dev/null
@@ -1,9 +0,0 @@
-//+build linux
-//+private
-package sync2
-
-import "core:sys/unix"
-
-_current_thread_id :: proc "contextless" () -> int {
- return unix.sys_gettid()
-}
diff --git a/core/sync/sync2/primitives_openbsd.odin b/core/sync/sync2/primitives_openbsd.odin
deleted file mode 100644
index ef122b02e..000000000
--- a/core/sync/sync2/primitives_openbsd.odin
+++ /dev/null
@@ -1,9 +0,0 @@
-//+build openbsd
-//+private
-package sync2
-
-import "core:os"
-
-_current_thread_id :: proc "contextless" () -> int {
- return os.current_thread_id()
-}
diff --git a/core/sync/sync2/primitives_pthreads.odin b/core/sync/sync2/primitives_pthreads.odin
deleted file mode 100644
index 28053f9cc..000000000
--- a/core/sync/sync2/primitives_pthreads.odin
+++ /dev/null
@@ -1,58 +0,0 @@
-//+build linux, freebsd, openbsd
-//+private
-package sync2
-
-import "core:time"
-import "core:sys/unix"
-
-_Mutex_State :: enum i32 {
- Unlocked = 0,
- Locked = 1,
- Waiting = 2,
-}
-_Mutex :: struct {
- pthread_mutex: unix.pthread_mutex_t,
-}
-
-_mutex_lock :: proc(m: ^Mutex) {
- err := unix.pthread_mutex_lock(&m.impl.pthread_mutex)
- assert(err == 0)
-}
-
-_mutex_unlock :: proc(m: ^Mutex) {
- err := unix.pthread_mutex_unlock(&m.impl.pthread_mutex)
- assert(err == 0)
-}
-
-_mutex_try_lock :: proc(m: ^Mutex) -> bool {
- err := unix.pthread_mutex_trylock(&m.impl.pthread_mutex)
- return err == 0
-}
-
-_Cond :: struct {
- pthread_cond: unix.pthread_cond_t,
-}
-
-_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
- err := unix.pthread_cond_wait(&c.impl.pthread_cond, &m.impl.pthread_mutex)
- assert(err == 0)
-}
-
-
-_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
- tv_sec := i64(duration/1e9)
- tv_nsec := i64(duration%1e9)
- err := unix.pthread_cond_timedwait(&c.impl.pthread_cond, &m.impl.pthread_mutex, &{tv_sec, tv_nsec})
- return err == 0
-}
-
-
-_cond_signal :: proc(c: ^Cond) {
- err := unix.pthread_cond_signal(&c.impl.pthread_cond)
- assert(err == 0)
-}
-
-_cond_broadcast :: proc(c: ^Cond) {
- err := unix.pthread_cond_broadcast(&c.impl.pthread_cond)
- assert(err == 0)
-}
diff --git a/core/sync/sync_darwin.odin b/core/sync/sync_darwin.odin
deleted file mode 100644
index f3bb4d5a3..000000000
--- a/core/sync/sync_darwin.odin
+++ /dev/null
@@ -1,54 +0,0 @@
-package sync
-
-import "core:sys/darwin"
-
-import "core:c"
-
-foreign import pthread "System.framework"
-
-current_thread_id :: proc "contextless" () -> int {
- tid: u64
- // NOTE(Oskar): available from OSX 10.6 and iOS 3.2.
- // For older versions there is `syscall(SYS_thread_selfid)`, but not really
- // the same thing apparently.
- foreign pthread { pthread_threadid_np :: proc "c" (rawptr, ^u64) -> c.int --- }
- pthread_threadid_np(nil, &tid)
- return int(tid)
-}
-
-
-// The Darwin docs say it best:
-// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
-// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
-// but when there are none left, a thread must wait until another thread returns one.
-Semaphore :: struct #align 16 {
- handle: darwin.semaphore_t,
-}
-// TODO(tetra): Only marked with alignment because we cannot mark distinct integers with alignments.
-// See core/sys/unix/pthread_linux.odin/pthread_t.
-
-semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
- ct := darwin.mach_task_self()
- res := darwin.semaphore_create(ct, &s.handle, 0, c.int(initial_count))
- assert(res == 0)
-}
-
-semaphore_destroy :: proc(s: ^Semaphore) {
- ct := darwin.mach_task_self()
- res := darwin.semaphore_destroy(ct, s.handle)
- assert(res == 0)
- s.handle = {}
-}
-
-semaphore_post :: proc(s: ^Semaphore, count := 1) {
- // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
- for in 0.. int {
- SYS_GETTID :: 186
- return int(intrinsics.syscall(SYS_GETTID))
-}
-
-
-// The Darwin docs say it best:
-// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
-// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
-// but when there are none left, a thread must wait until another thread returns one.
-Semaphore :: struct #align 16 {
- handle: unix.sem_t,
-}
-
-semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
- assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0)
-}
-
-semaphore_destroy :: proc(s: ^Semaphore) {
- assert(unix.sem_destroy(&s.handle) == 0)
- s.handle = {}
-}
-
-semaphore_post :: proc(s: ^Semaphore, count := 1) {
- // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
- for in 0.. int {
- return unix.sys_gettid()
-}
-
-
-// The Darwin docs say it best:
-// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
-// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
-// but when there are none left, a thread must wait until another thread returns one.
-Semaphore :: struct #align 16 {
- handle: unix.sem_t,
-}
-
-semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
- assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0)
-}
-
-semaphore_destroy :: proc(s: ^Semaphore) {
- assert(unix.sem_destroy(&s.handle) == 0)
- s.handle = {}
-}
-
-semaphore_post :: proc(s: ^Semaphore, count := 1) {
- // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
- for in 0.. int {
- return os.current_thread_id()
-}
-
-// The Darwin docs say it best:
-// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
-// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
-// but when there are none left, a thread must wait until another thread returns one.
-Semaphore :: struct #align 16 {
- handle: unix.sem_t,
-}
-
-semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
- assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0)
-}
-
-semaphore_destroy :: proc(s: ^Semaphore) {
- assert(unix.sem_destroy(&s.handle) == 0)
- s.handle = {}
-}
-
-semaphore_post :: proc(s: ^Semaphore, count := 1) {
- // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
- for in 0.. bool {
- return unix.pthread_mutex_trylock(&m.handle) == 0
-}
-
-mutex_unlock :: proc(m: ^Mutex) {
- assert(unix.pthread_mutex_unlock(&m.handle) == 0)
-}
-
-
-Blocking_Mutex :: struct {
- handle: unix.pthread_mutex_t,
-}
-
-
-blocking_mutex_init :: proc(m: ^Blocking_Mutex) {
- // NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the mutex.
- attrs: unix.pthread_mutexattr_t
- assert(unix.pthread_mutexattr_init(&attrs) == 0)
- defer unix.pthread_mutexattr_destroy(&attrs) // ignores destruction error
-
- assert(unix.pthread_mutex_init(&m.handle, &attrs) == 0)
-}
-
-blocking_mutex_destroy :: proc(m: ^Blocking_Mutex) {
- assert(unix.pthread_mutex_destroy(&m.handle) == 0)
- m.handle = {}
-}
-
-blocking_mutex_lock :: proc(m: ^Blocking_Mutex) {
- assert(unix.pthread_mutex_lock(&m.handle) == 0)
-}
-
-// Returns false if someone else holds the lock.
-blocking_mutex_try_lock :: proc(m: ^Blocking_Mutex) -> bool {
- return unix.pthread_mutex_trylock(&m.handle) == 0
-}
-
-blocking_mutex_unlock :: proc(m: ^Blocking_Mutex) {
- assert(unix.pthread_mutex_unlock(&m.handle) == 0)
-}
-
-
-
-// Blocks until signalled, and then lets past exactly
-// one thread.
-Condition :: struct {
- handle: unix.pthread_cond_t,
- mutex: Condition_Mutex_Ptr,
-
- // NOTE(tetra, 2019-11-11): Used to mimic the more sane behavior of Windows' AutoResetEvent.
- // This means that you may signal the condition before anyone is waiting to cause the
- // next thread that tries to wait to just pass by uninterrupted, without sleeping.
- // Without this, signalling a condition will only wake up a thread which is already waiting,
- // but not one that is about to wait, which can cause your program to become out of sync in
- // ways that are hard to debug or fix.
- flag: bool, // atomically mutated
-}
-
-condition_init :: proc(c: ^Condition, mutex: Condition_Mutex_Ptr) -> bool {
- // NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the condition.
- attrs: unix.pthread_condattr_t
- if unix.pthread_condattr_init(&attrs) != 0 {
- return false
- }
- defer unix.pthread_condattr_destroy(&attrs) // ignores destruction error
-
- c.flag = false
- c.mutex = mutex
- return unix.pthread_cond_init(&c.handle, &attrs) == 0
-}
-
-condition_destroy :: proc(c: ^Condition) {
- assert(unix.pthread_cond_destroy(&c.handle) == 0)
- c.handle = {}
-}
-
-// Awaken exactly one thread who is waiting on the condition
-condition_signal :: proc(c: ^Condition) -> bool {
- switch m in c.mutex {
- case ^Mutex:
- mutex_lock(m)
- defer mutex_unlock(m)
- atomic_swap(&c.flag, true, .Sequentially_Consistent)
- return unix.pthread_cond_signal(&c.handle) == 0
- case ^Blocking_Mutex:
- blocking_mutex_lock(m)
- defer blocking_mutex_unlock(m)
- atomic_swap(&c.flag, true, .Sequentially_Consistent)
- return unix.pthread_cond_signal(&c.handle) == 0
- }
- return false
-}
-
-// Awaken all threads who are waiting on the condition
-condition_broadcast :: proc(c: ^Condition) -> bool {
- return unix.pthread_cond_broadcast(&c.handle) == 0
-}
-
-// Wait for the condition to be signalled.
-// Does not block if the condition has been signalled and no one
-// has waited on it yet.
-condition_wait_for :: proc(c: ^Condition) -> bool {
- switch m in c.mutex {
- case ^Mutex:
- mutex_lock(m)
- defer mutex_unlock(m)
- // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
- // the thread that gets signalled and wakes up, discovers that the flag was taken and goes
- // back to sleep.
- // Though this overall behavior is the most sane, there may be a better way to do this that means that
- // the first thread to wait, gets the flag first.
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- for {
- if unix.pthread_cond_wait(&c.handle, &m.handle) != 0 {
- return false
- }
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- }
- return false
-
- case ^Blocking_Mutex:
- blocking_mutex_lock(m)
- defer blocking_mutex_unlock(m)
- // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
- // the thread that gets signalled and wakes up, discovers that the flag was taken and goes
- // back to sleep.
- // Though this overall behavior is the most sane, there may be a better way to do this that means that
- // the first thread to wait, gets the flag first.
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- for {
- if unix.pthread_cond_wait(&c.handle, &m.handle) != 0 {
- return false
- }
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- }
- return false
- }
- return false
-}
-
-// Wait for the condition to be signalled.
-// Does not block if the condition has been signalled and no one
-// has waited on it yet.
-condition_wait_for_timeout :: proc(c: ^Condition, duration: time.Duration) -> bool {
- switch m in c.mutex {
- case ^Mutex:
- mutex_lock(m)
- defer mutex_unlock(m)
- // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
- // the thread that gets signalled and wakes up, discovers that the flag was taken and goes
- // back to sleep.
- // Though this overall behavior is the most sane, there may be a better way to do this that means that
- // the first thread to wait, gets the flag first.
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
-
- ns := time.duration_nanoseconds(duration)
- timeout: time.TimeSpec
- timeout.tv_sec = ns / 1e9
- timeout.tv_nsec = ns % 1e9
-
- for {
- if unix.pthread_cond_timedwait(&c.handle, &m.handle, &timeout) != 0 {
- return false
- }
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- }
- return false
-
- case ^Blocking_Mutex:
- blocking_mutex_lock(m)
- defer blocking_mutex_unlock(m)
- // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
- // the thread that gets signalled and wakes up, discovers that the flag was taken and goes
- // back to sleep.
- // Though this overall behavior is the most sane, there may be a better way to do this that means that
- // the first thread to wait, gets the flag first.
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
-
- ns := time.duration_nanoseconds(duration)
-
- timeout: time.TimeSpec
- timeout.tv_sec = ns / 1e9
- timeout.tv_nsec = ns % 1e9
-
- for {
- if unix.pthread_cond_timedwait(&c.handle, &m.handle, &timeout) != 0 {
- return false
- }
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- }
- return false
- }
- return false
-}
-
-
-
-thread_yield :: proc() {
- unix.sched_yield()
-}
diff --git a/core/sync/sync2/sync_util.odin b/core/sync/sync_util.odin
similarity index 99%
rename from core/sync/sync2/sync_util.odin
rename to core/sync/sync_util.odin
index 013bf511a..4add9064d 100644
--- a/core/sync/sync2/sync_util.odin
+++ b/core/sync/sync_util.odin
@@ -1,4 +1,4 @@
-package sync2
+package sync
/*
Example:
diff --git a/core/sync/sync_windows.odin b/core/sync/sync_windows.odin
deleted file mode 100644
index 0a7cf71b2..000000000
--- a/core/sync/sync_windows.odin
+++ /dev/null
@@ -1,180 +0,0 @@
-// +build windows
-package sync
-
-import win32 "core:sys/windows"
-import "core:time"
-
-current_thread_id :: proc "contextless" () -> int {
- return int(win32.GetCurrentThreadId())
-}
-
-
-// When waited upon, blocks until the internal count is greater than zero, then subtracts one.
-// Posting to the semaphore increases the count by one, or the provided amount.
-Semaphore :: struct {
- _handle: win32.HANDLE,
-}
-
-semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
- s._handle = win32.CreateSemaphoreW(nil, i32(initial_count), 1<<31-1, nil)
-}
-
-semaphore_destroy :: proc(s: ^Semaphore) {
- win32.CloseHandle(s._handle)
-}
-
-semaphore_post :: proc(s: ^Semaphore, count := 1) {
- win32.ReleaseSemaphore(s._handle, i32(count), nil)
-}
-
-semaphore_wait_for :: proc(s: ^Semaphore) {
- // NOTE(tetra, 2019-10-30): wait_for_single_object decrements the count before it returns.
- result := win32.WaitForSingleObject(s._handle, win32.INFINITE)
- assert(result != win32.WAIT_FAILED)
-}
-
-
-Mutex :: struct {
- _critical_section: win32.CRITICAL_SECTION,
-}
-
-
-mutex_init :: proc(m: ^Mutex, spin_count := 0) {
- win32.InitializeCriticalSectionAndSpinCount(&m._critical_section, u32(spin_count))
-}
-
-mutex_destroy :: proc(m: ^Mutex) {
- win32.DeleteCriticalSection(&m._critical_section)
-}
-
-mutex_lock :: proc(m: ^Mutex) {
- win32.EnterCriticalSection(&m._critical_section)
-}
-
-mutex_try_lock :: proc(m: ^Mutex) -> bool {
- return bool(win32.TryEnterCriticalSection(&m._critical_section))
-}
-
-mutex_unlock :: proc(m: ^Mutex) {
- win32.LeaveCriticalSection(&m._critical_section)
-}
-
-Blocking_Mutex :: struct {
- _handle: win32.SRWLOCK,
-}
-
-
-blocking_mutex_init :: proc(m: ^Blocking_Mutex) {
- win32.InitializeSRWLock(&m._handle)
-}
-
-blocking_mutex_destroy :: proc(m: ^Blocking_Mutex) {
- //
-}
-
-blocking_mutex_lock :: proc(m: ^Blocking_Mutex) {
- win32.AcquireSRWLockExclusive(&m._handle)
-}
-
-blocking_mutex_try_lock :: proc(m: ^Blocking_Mutex) -> bool {
- return bool(win32.TryAcquireSRWLockExclusive(&m._handle))
-}
-
-blocking_mutex_unlock :: proc(m: ^Blocking_Mutex) {
- win32.ReleaseSRWLockExclusive(&m._handle)
-}
-
-
-// Blocks until signalled.
-// When signalled, awakens exactly one waiting thread.
-Condition :: struct {
- _handle: win32.CONDITION_VARIABLE,
-
- mutex: Condition_Mutex_Ptr,
-}
-
-
-condition_init :: proc(c: ^Condition, mutex: Condition_Mutex_Ptr) -> bool {
- assert(mutex != nil)
- win32.InitializeConditionVariable(&c._handle)
- c.mutex = mutex
- return true
-}
-
-condition_destroy :: proc(c: ^Condition) {
- //
-}
-
-condition_signal :: proc(c: ^Condition) -> bool {
- if c._handle.ptr == nil {
- return false
- }
- win32.WakeConditionVariable(&c._handle)
- return true
-}
-
-condition_broadcast :: proc(c: ^Condition) -> bool {
- if c._handle.ptr == nil {
- return false
- }
- win32.WakeAllConditionVariable(&c._handle)
- return true
-}
-
-condition_wait_for :: proc(c: ^Condition) -> bool {
- switch m in &c.mutex {
- case ^Mutex:
- return cast(bool)win32.SleepConditionVariableCS(&c._handle, &m._critical_section, win32.INFINITE)
- case ^Blocking_Mutex:
- return cast(bool)win32.SleepConditionVariableSRW(&c._handle, &m._handle, win32.INFINITE, 0)
- }
- return false
-}
-condition_wait_for_timeout :: proc(c: ^Condition, duration: time.Duration) -> bool {
- ms := win32.DWORD((max(time.duration_nanoseconds(duration), 0) + 999999)/1000000)
- switch m in &c.mutex {
- case ^Mutex:
- return cast(bool)win32.SleepConditionVariableCS(&c._handle, &m._critical_section, ms)
- case ^Blocking_Mutex:
- return cast(bool)win32.SleepConditionVariableSRW(&c._handle, &m._handle, ms, 0)
- }
- return false
-}
-
-
-
-
-RW_Lock :: struct {
- _handle: win32.SRWLOCK,
-}
-
-rw_lock_init :: proc(l: ^RW_Lock) {
- l._handle = win32.SRWLOCK_INIT
-}
-rw_lock_destroy :: proc(l: ^RW_Lock) {
- //
-}
-rw_lock_read :: proc(l: ^RW_Lock) {
- win32.AcquireSRWLockShared(&l._handle)
-}
-rw_lock_try_read :: proc(l: ^RW_Lock) -> bool {
- return bool(win32.TryAcquireSRWLockShared(&l._handle))
-}
-rw_lock_write :: proc(l: ^RW_Lock) {
- win32.AcquireSRWLockExclusive(&l._handle)
-}
-rw_lock_try_write :: proc(l: ^RW_Lock) -> bool {
- return bool(win32.TryAcquireSRWLockExclusive(&l._handle))
-}
-rw_lock_read_unlock :: proc(l: ^RW_Lock) {
- win32.ReleaseSRWLockShared(&l._handle)
-}
-rw_lock_write_unlock :: proc(l: ^RW_Lock) {
- win32.ReleaseSRWLockExclusive(&l._handle)
-}
-
-
-thread_yield :: proc() {
- win32.SwitchToThread()
-}
-
diff --git a/core/sync/wait_group.odin b/core/sync/wait_group.odin
deleted file mode 100644
index 63d882ed1..000000000
--- a/core/sync/wait_group.odin
+++ /dev/null
@@ -1,58 +0,0 @@
-package sync
-
-import "core:intrinsics"
-
-Wait_Group :: struct {
- counter: int,
- mutex: Blocking_Mutex,
- cond: Condition,
-}
-
-wait_group_init :: proc(wg: ^Wait_Group) {
- wg.counter = 0
- blocking_mutex_init(&wg.mutex)
- condition_init(&wg.cond, &wg.mutex)
-}
-
-
-wait_group_destroy :: proc(wg: ^Wait_Group) {
- condition_destroy(&wg.cond)
- blocking_mutex_destroy(&wg.mutex)
-}
-
-wait_group_add :: proc(wg: ^Wait_Group, delta: int) {
- if delta == 0 {
- return
- }
-
- blocking_mutex_lock(&wg.mutex)
- defer blocking_mutex_unlock(&wg.mutex)
-
- intrinsics.atomic_add(&wg.counter, delta)
- if wg.counter < 0 {
- panic("sync.Wait_Group negative counter")
- }
- if wg.counter == 0 {
- condition_broadcast(&wg.cond)
- if wg.counter != 0 {
- panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
- }
- }
-}
-
-wait_group_done :: proc(wg: ^Wait_Group) {
- wait_group_add(wg, -1)
-}
-
-wait_group_wait :: proc(wg: ^Wait_Group) {
- blocking_mutex_lock(&wg.mutex)
- defer blocking_mutex_unlock(&wg.mutex)
-
- if wg.counter != 0 {
- condition_wait_for(&wg.cond)
- if wg.counter != 0 {
- panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
- }
- }
-}
-
diff --git a/core/sys/win32/user32.odin b/core/sys/win32/user32.odin
index fdf6a91f6..44d1f7004 100644
--- a/core/sys/win32/user32.odin
+++ b/core/sys/win32/user32.odin
@@ -111,6 +111,8 @@ foreign user32 {
@(link_name="SetWindowTextW") set_window_text_w :: proc(hwnd: Hwnd, c_string: Wstring) -> Bool ---
@(link_name="RegisterClassA") register_class_a :: proc(wc: ^Wnd_Class_A) -> i16 ---
@(link_name="RegisterClassW") register_class_w :: proc(wc: ^Wnd_Class_W) -> i16 ---
+ @(link_name="UnregisterClassA") unregister_class_a :: proc(class_name: cstring, instance: Hinstance) -> Bool ---
+ @(link_name="UnregisterClassW") unregister_class_w :: proc(class_name: Wstring, instance: Hinstance) -> Bool ---
@(link_name="RegisterClassExA") register_class_ex_a :: proc(wc: ^Wnd_Class_Ex_A) -> i16 ---
@(link_name="RegisterClassExW") register_class_ex_w :: proc(wc: ^Wnd_Class_Ex_W) -> i16 ---
diff --git a/core/sys/windows/gdi32.odin b/core/sys/windows/gdi32.odin
new file mode 100644
index 000000000..15d5567c7
--- /dev/null
+++ b/core/sys/windows/gdi32.odin
@@ -0,0 +1,66 @@
+// +build windows
+package sys_windows
+
+foreign import gdi32 "system:Gdi32.lib"
+
+@(default_calling_convention="stdcall")
+foreign gdi32 {
+ GetStockObject :: proc(i: c_int) -> HGDIOBJ ---
+ SelectObject :: proc(hdc: HDC, h: HGDIOBJ) -> HGDIOBJ ---
+
+ CreateDIBPatternBrush :: proc(h: HGLOBAL, iUsage: UINT) -> HBRUSH ---
+
+ CreateDIBitmap :: proc(
+ hdc: HDC,
+ pbmih: ^BITMAPINFOHEADER,
+ flInit: DWORD,
+ pjBits: VOID,
+ pbmi: ^BITMAPINFO,
+ iUsage: UINT,
+ ) -> HBITMAP ---
+
+ CreateDIBSection :: proc(
+ hdc: HDC,
+ pbmi: ^BITMAPINFO,
+ usage: UINT,
+ ppvBits: VOID,
+ hSection: HANDLE,
+ offset: DWORD,
+ ) -> HBITMAP ---
+
+ StretchDIBits :: proc(
+ hdc: HDC,
+ xDest: c_int,
+ yDest: c_int,
+ DestWidth: c_int,
+ DestHeight: c_int,
+ xSrc: c_int,
+ ySrc: c_int,
+ SrcWidth: c_int,
+ SrcHeight: c_int,
+ lpBits: VOID,
+ lpbmi: ^BITMAPINFO,
+ iUsage: UINT,
+ rop: DWORD,
+ ) -> c_int ---
+
+ StretchBlt :: proc(
+ hdcDest: HDC,
+ xDest: c_int,
+ yDest: c_int,
+ wDest: c_int,
+ hDest: c_int,
+ hdcSrc: HDC,
+ xSrc: c_int,
+ ySrc: c_int,
+ wSrc: c_int,
+ hSrc: c_int,
+ rop: DWORD,
+ ) -> BOOL ---
+
+ SetPixelFormat :: proc(hdc: HDC, format: c_int, ppfd: ^PIXELFORMATDESCRIPTOR) -> BOOL ---
+ ChoosePixelFormat :: proc(hdc: HDC, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int ---
+ SwapBuffers :: proc(HDC) -> BOOL ---
+
+ PatBlt :: proc(hdc: HDC, x, y, w, h: c_int, rop: DWORD) -> BOOL ---
+}
diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin
index 8c58fbd52..f81c51311 100644
--- a/core/sys/windows/kernel32.odin
+++ b/core/sys/windows/kernel32.odin
@@ -341,6 +341,7 @@ MEM_TOP_DOWN :: 0x100000
MEM_LARGE_PAGES :: 0x20000000
MEM_4MB_PAGES :: 0x80000000
+@(default_calling_convention="stdcall")
foreign kernel32 {
VirtualAlloc :: proc(
lpAddress: LPVOID,
@@ -483,6 +484,7 @@ LowMemoryResourceNotification :: MEMORY_RESOURCE_NOTIFICATION_TYPE.LowMemoryRes
HighMemoryResourceNotification :: MEMORY_RESOURCE_NOTIFICATION_TYPE.HighMemoryResourceNotification
+@(default_calling_convention="stdcall")
foreign kernel32 {
CreateMemoryResourceNotification :: proc(
NotificationType: MEMORY_RESOURCE_NOTIFICATION_TYPE,
@@ -498,6 +500,7 @@ FILE_CACHE_MAX_HARD_DISABLE :: DWORD(0x00000002)
FILE_CACHE_MIN_HARD_ENABLE :: DWORD(0x00000004)
FILE_CACHE_MIN_HARD_DISABLE :: DWORD(0x00000008)
+@(default_calling_convention="stdcall")
foreign kernel32 {
GetSystemFileCacheSize :: proc(
lpMinimumFileCacheSize: PSIZE_T,
@@ -527,6 +530,7 @@ WIN32_MEMORY_RANGE_ENTRY :: struct {
PWIN32_MEMORY_RANGE_ENTRY :: ^WIN32_MEMORY_RANGE_ENTRY
+@(default_calling_convention="stdcall")
foreign kernel32 {
PrefetchVirtualMemory :: proc(
hProcess: HANDLE,
@@ -584,6 +588,7 @@ foreign kernel32 {
MEHC_PATROL_SCRUBBER_PRESENT :: ULONG(0x1)
+@(default_calling_convention="stdcall")
foreign kernel32 {
GetMemoryErrorHandlingCapabilities :: proc(
Capabilities: PULONG,
@@ -592,6 +597,7 @@ foreign kernel32 {
PBAD_MEMORY_CALLBACK_ROUTINE :: #type proc "stdcall" ()
+@(default_calling_convention="stdcall")
foreign kernel32 {
RegisterBadMemoryNotification :: proc(
Callback: PBAD_MEMORY_CALLBACK_ROUTINE,
@@ -612,6 +618,7 @@ VmOfferPriorityLow :: OFFER_PRIORITY.VmOfferPriorityLow
VmOfferPriorityBelowNormal :: OFFER_PRIORITY.VmOfferPriorityBelowNormal
VmOfferPriorityNormal :: OFFER_PRIORITY.VmOfferPriorityNormal
+@(default_calling_convention="stdcall")
foreign kernel32 {
OfferVirtualMemory :: proc(
VirtualAddress: PVOID,
@@ -676,6 +683,7 @@ WIN32_MEMORY_REGION_INFORMATION_u_s_Bitfield :: distinct ULONG
Reserved : 32-6,
}*/
+@(default_calling_convention="stdcall")
foreign kernel32 {
QueryVirtualMemoryInformation :: proc(
Process: HANDLE,
@@ -700,7 +708,7 @@ foreign kernel32 {
NUMA_NO_PREFERRED_NODE :: 0xffffffff
-MapViewOfFile2 :: #force_inline proc(
+MapViewOfFile2 :: #force_inline proc "stdcall" (
FileMappingHandle: HANDLE,
ProcessHandle: HANDLE,
Offset: ULONG64,
@@ -721,6 +729,7 @@ MapViewOfFile2 :: #force_inline proc(
)
}
+@(default_calling_convention="stdcall")
foreign kernel32 {
UnmapViewOfFile2 :: proc(
ProcessHandle: HANDLE,
diff --git a/core/sys/windows/key_codes.odin b/core/sys/windows/key_codes.odin
new file mode 100644
index 000000000..2f6e01116
--- /dev/null
+++ b/core/sys/windows/key_codes.odin
@@ -0,0 +1,252 @@
+// +build windows
+package sys_windows
+
+// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
+// Virtual Keys, Standard Set
+VK_LBUTTON :: 0x01
+VK_RBUTTON :: 0x02
+VK_CANCEL :: 0x03
+VK_MBUTTON :: 0x04 // NOT contiguous with L & RBUTTON
+VK_XBUTTON1 :: 0x05 // NOT contiguous with L & RBUTTON
+VK_XBUTTON2 :: 0x06 // NOT contiguous with L & RBUTTON
+
+// 0x07 : reserved
+
+VK_BACK :: 0x08
+VK_TAB :: 0x09
+
+// 0x0A - 0x0B : reserved
+
+VK_CLEAR :: 0x0C
+VK_RETURN :: 0x0D
+
+// 0x0E - 0x0F : unassigned
+
+VK_SHIFT :: 0x10
+VK_CONTROL :: 0x11
+VK_MENU :: 0x12
+VK_PAUSE :: 0x13
+VK_CAPITAL :: 0x14
+VK_KANA :: 0x15
+VK_HANGEUL :: 0x15 // old name - should be here for compatibility
+VK_HANGUL :: 0x15
+VK_IME_ON :: 0x16
+VK_JUNJA :: 0x17
+VK_FINAL :: 0x18
+VK_HANJA :: 0x19
+VK_KANJI :: 0x19
+VK_IME_OFF :: 0x1A
+VK_ESCAPE :: 0x1B
+VK_CONVERT :: 0x1C
+VK_NONCONVERT :: 0x1D
+VK_ACCEPT :: 0x1E
+VK_MODECHANGE :: 0x1F
+VK_SPACE :: 0x20
+VK_PRIOR :: 0x21
+VK_NEXT :: 0x22
+VK_END :: 0x23
+VK_HOME :: 0x24
+VK_LEFT :: 0x25
+VK_UP :: 0x26
+VK_RIGHT :: 0x27
+VK_DOWN :: 0x28
+VK_SELECT :: 0x29
+VK_PRINT :: 0x2A
+VK_EXECUTE :: 0x2B
+VK_SNAPSHOT :: 0x2C
+VK_INSERT :: 0x2D
+VK_DELETE :: 0x2E
+VK_HELP :: 0x2F
+
+VK_0 :: '0'
+VK_1 :: '1'
+VK_2 :: '2'
+VK_3 :: '3'
+VK_4 :: '4'
+VK_5 :: '5'
+VK_6 :: '6'
+VK_7 :: '7'
+VK_8 :: '8'
+VK_9 :: '9'
+
+// 0x3A - 0x40 : unassigned
+
+VK_A :: 'A'
+VK_B :: 'B'
+VK_C :: 'C'
+VK_D :: 'D'
+VK_E :: 'E'
+VK_F :: 'F'
+VK_G :: 'G'
+VK_H :: 'H'
+VK_I :: 'I'
+VK_J :: 'J'
+VK_K :: 'K'
+VK_L :: 'L'
+VK_M :: 'M'
+VK_N :: 'N'
+VK_O :: 'O'
+VK_P :: 'P'
+VK_Q :: 'Q'
+VK_R :: 'R'
+VK_S :: 'S'
+VK_T :: 'T'
+VK_U :: 'U'
+VK_V :: 'V'
+VK_W :: 'W'
+VK_X :: 'X'
+VK_Y :: 'Y'
+VK_Z :: 'Z'
+
+VK_LWIN :: 0x5B
+VK_RWIN :: 0x5C
+VK_APPS :: 0x5D
+
+// 0x5E : reserved
+
+VK_SLEEP :: 0x5F
+VK_NUMPAD0 :: 0x60
+VK_NUMPAD1 :: 0x61
+VK_NUMPAD2 :: 0x62
+VK_NUMPAD3 :: 0x63
+VK_NUMPAD4 :: 0x64
+VK_NUMPAD5 :: 0x65
+VK_NUMPAD6 :: 0x66
+VK_NUMPAD7 :: 0x67
+VK_NUMPAD8 :: 0x68
+VK_NUMPAD9 :: 0x69
+VK_MULTIPLY :: 0x6A
+VK_ADD :: 0x6B
+VK_SEPARATOR :: 0x6C
+VK_SUBTRACT :: 0x6D
+VK_DECIMAL :: 0x6E
+VK_DIVIDE :: 0x6F
+VK_F1 :: 0x70
+VK_F2 :: 0x71
+VK_F3 :: 0x72
+VK_F4 :: 0x73
+VK_F5 :: 0x74
+VK_F6 :: 0x75
+VK_F7 :: 0x76
+VK_F8 :: 0x77
+VK_F9 :: 0x78
+VK_F10 :: 0x79
+VK_F11 :: 0x7A
+VK_F12 :: 0x7B
+VK_F13 :: 0x7C
+VK_F14 :: 0x7D
+VK_F15 :: 0x7E
+VK_F16 :: 0x7F
+VK_F17 :: 0x80
+VK_F18 :: 0x81
+VK_F19 :: 0x82
+VK_F20 :: 0x83
+VK_F21 :: 0x84
+VK_F22 :: 0x85
+VK_F23 :: 0x86
+VK_F24 :: 0x87
+
+// 0x88 - 0x8F : reserved
+
+VK_NUMLOCK :: 0x90
+VK_SCROLL :: 0x91
+
+// NEC PC-9800 kbd definitions
+VK_OEM_NEC_EQUAL :: 0x92 // '=' key on numpad
+
+// Fujitsu/OASYS kbd definitions
+VK_OEM_FJ_JISHO :: 0x92 // 'Dictionary' key
+VK_OEM_FJ_MASSHOU :: 0x93 // 'Unregister word' key
+VK_OEM_FJ_TOUROKU :: 0x94 // 'Register word' key
+VK_OEM_FJ_LOYA :: 0x95 // 'Left OYAYUBI' key
+VK_OEM_FJ_ROYA :: 0x96 // 'Right OYAYUBI' key
+
+// 0x97 - 0x9F : unassigned
+
+// VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys.
+// Used only as parameters to GetAsyncKeyState() and GetKeyState().
+// No other API or message will distinguish left and right keys in this way.
+VK_LSHIFT :: 0xA0
+VK_RSHIFT :: 0xA1
+VK_LCONTROL :: 0xA2
+VK_RCONTROL :: 0xA3
+VK_LMENU :: 0xA4
+VK_RMENU :: 0xA5
+
+VK_BROWSER_BACK :: 0xA6
+VK_BROWSER_FORWARD :: 0xA7
+VK_BROWSER_REFRESH :: 0xA8
+VK_BROWSER_STOP :: 0xA9
+VK_BROWSER_SEARCH :: 0xAA
+VK_BROWSER_FAVORITES :: 0xAB
+VK_BROWSER_HOME :: 0xAC
+VK_VOLUME_MUTE :: 0xAD
+VK_VOLUME_DOWN :: 0xAE
+VK_VOLUME_UP :: 0xAF
+VK_MEDIA_NEXT_TRACK :: 0xB0
+VK_MEDIA_PREV_TRACK :: 0xB1
+VK_MEDIA_STOP :: 0xB2
+VK_MEDIA_PLAY_PAUSE :: 0xB3
+VK_LAUNCH_MAIL :: 0xB4
+VK_LAUNCH_MEDIA_SELECT :: 0xB5
+VK_LAUNCH_APP1 :: 0xB6
+VK_LAUNCH_APP2 :: 0xB7
+
+// 0xB8 - 0xB9 : reserved
+
+VK_OEM_1 :: 0xBA // ';:' for US
+VK_OEM_PLUS :: 0xBB // '+' any country
+VK_OEM_COMMA :: 0xBC // ',' any country
+VK_OEM_MINUS :: 0xBD // '-' any country
+VK_OEM_PERIOD :: 0xBE // '.' any country
+VK_OEM_2 :: 0xBF // '/?' for US
+VK_OEM_3 :: 0xC0 // '`~' for US
+
+// 0xC1 - 0xDA : reserved
+
+VK_OEM_4 :: 0xDB // '[{' for US
+VK_OEM_5 :: 0xDC // '\|' for US
+VK_OEM_6 :: 0xDD // ']}' for US
+VK_OEM_7 :: 0xDE // ''"' for US
+VK_OEM_8 :: 0xDF
+
+// 0xE0 : reserved
+
+// Various extended or enhanced keyboards
+VK_OEM_AX :: 0xE1 // 'AX' key on Japanese AX kbd
+VK_OEM_102 :: 0xE2 // "<>" or "\|" on RT 102-key kbd.
+VK_ICO_HELP :: 0xE3 // Help key on ICO
+VK_ICO_00 :: 0xE4 // 00 key on ICO
+
+VK_PROCESSKEY :: 0xE5
+VK_ICO_CLEAR :: 0xE6
+VK_PACKET :: 0xE7
+
+// 0xE8 : unassigned
+
+// Nokia/Ericsson definitions
+VK_OEM_RESET :: 0xE9
+VK_OEM_JUMP :: 0xEA
+VK_OEM_PA1 :: 0xEB
+VK_OEM_PA2 :: 0xEC
+VK_OEM_PA3 :: 0xED
+VK_OEM_WSCTRL :: 0xEE
+VK_OEM_CUSEL :: 0xEF
+VK_OEM_ATTN :: 0xF0
+VK_OEM_FINISH :: 0xF1
+VK_OEM_COPY :: 0xF2
+VK_OEM_AUTO :: 0xF3
+VK_OEM_ENLW :: 0xF4
+VK_OEM_BACKTAB :: 0xF5
+
+VK_ATTN :: 0xF6
+VK_CRSEL :: 0xF7
+VK_EXSEL :: 0xF8
+VK_EREOF :: 0xF9
+VK_PLAY :: 0xFA
+VK_ZOOM :: 0xFB
+VK_NONAME :: 0xFC
+VK_PA1 :: 0xFD
+VK_OEM_CLEAR :: 0xFE
+
+// 0xFF : reserved
diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin
index 1407201d6..50098a59c 100644
--- a/core/sys/windows/types.odin
+++ b/core/sys/windows/types.odin
@@ -21,7 +21,15 @@ HINSTANCE :: HANDLE
HMODULE :: distinct HINSTANCE
HRESULT :: distinct LONG
HWND :: distinct HANDLE
+HDC :: distinct HANDLE
HMONITOR :: distinct HANDLE
+HICON :: distinct HANDLE
+HCURSOR :: distinct HANDLE
+HMENU :: distinct HANDLE
+HBRUSH :: distinct HANDLE
+HGDIOBJ :: distinct HANDLE
+HBITMAP :: distinct HANDLE
+HGLOBAL :: distinct HANDLE
BOOL :: distinct b32
BYTE :: distinct u8
BOOLEAN :: distinct b8
@@ -42,9 +50,15 @@ PULONG_PTR :: ^ULONG_PTR
LPULONG_PTR :: ^ULONG_PTR
DWORD_PTR :: ULONG_PTR
LONG_PTR :: int
+UINT_PTR :: uintptr
ULONG :: c_ulong
UCHAR :: BYTE
NTSTATUS :: c.long
+LPARAM :: LONG_PTR
+WPARAM :: UINT_PTR
+LRESULT :: LONG_PTR
+LPRECT :: ^RECT
+LPPOINT :: ^POINT
UINT8 :: u8
UINT16 :: u16
@@ -71,6 +85,7 @@ PBOOL :: ^BOOL
LPBOOL :: ^BOOL
LPCSTR :: cstring
LPCWSTR :: wstring
+LPCTSTR :: wstring
LPDWORD :: ^DWORD
PCSTR :: cstring
PCWSTR :: wstring
@@ -81,7 +96,8 @@ LPPROCESS_INFORMATION :: ^PROCESS_INFORMATION
PSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
LPSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
LPSTARTUPINFO :: ^STARTUPINFO
-PVOID :: rawptr
+VOID :: rawptr
+PVOID :: rawptr
LPVOID :: rawptr
PINT :: ^INT
LPINT :: ^INT
@@ -178,6 +194,84 @@ GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0
GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1
+TIMERPROC :: #type proc "stdcall" (HWND, UINT, UINT_PTR, DWORD)
+
+WNDPROC :: #type proc "stdcall" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
+
+WNDCLASSA :: struct {
+ style: UINT,
+ lpfnWndProc: WNDPROC,
+ cbClsExtra: c_int,
+ cbWndExtra: c_int,
+ hInstance: HINSTANCE,
+ hIcon: HICON,
+ hCursor: HCURSOR,
+ hbrBackground: HBRUSH,
+ lpszMenuName: LPCSTR,
+ lpszClassName: LPCSTR,
+}
+
+WNDCLASSW :: struct {
+ style: UINT,
+ lpfnWndProc: WNDPROC,
+ cbClsExtra: c_int,
+ cbWndExtra: c_int,
+ hInstance: HINSTANCE,
+ hIcon: HICON,
+ hCursor: HCURSOR,
+ hbrBackground: HBRUSH,
+ lpszMenuName: LPCWSTR,
+ lpszClassName: LPCWSTR,
+}
+
+WNDCLASSEXA :: struct {
+ cbSize: UINT,
+ style: UINT,
+ lpfnWndProc: WNDPROC,
+ cbClsExtra: c_int,
+ cbWndExtra: c_int,
+ hInstance: HINSTANCE,
+ hIcon: HICON,
+ hCursor: HCURSOR,
+ hbrBackground: HBRUSH,
+ lpszMenuName: LPCSTR,
+ lpszClassName: LPCSTR,
+ hIconSm: HICON,
+}
+
+WNDCLASSEXW :: struct {
+ cbSize: UINT,
+ style: UINT,
+ lpfnWndProc: WNDPROC,
+ cbClsExtra: c_int,
+ cbWndExtra: c_int,
+ hInstance: HINSTANCE,
+ hIcon: HICON,
+ hCursor: HCURSOR,
+ hbrBackground: HBRUSH,
+ lpszMenuName: LPCWSTR,
+ lpszClassName: LPCWSTR,
+ hIconSm: HICON,
+}
+
+MSG :: struct {
+ hwnd: HWND,
+ message: UINT,
+ wParam: WPARAM,
+ lParam: LPARAM,
+ time: DWORD,
+ pt: POINT,
+}
+
+PAINTSTRUCT :: struct {
+ hdc: HDC,
+ fErase: BOOL,
+ rcPaint: RECT,
+ fRestore: BOOL,
+ fIncUpdate: BOOL,
+ rgbReserved: [32]BYTE,
+}
+
WIN32_FIND_DATAW :: struct {
dwFileAttributes: DWORD,
ftCreationTime: FILETIME,
@@ -191,6 +285,602 @@ WIN32_FIND_DATAW :: struct {
cAlternateFileName: [14]wchar_t,
}
+CREATESTRUCTA :: struct {
+ lpCreateParams: LPVOID,
+ hInstance: HINSTANCE,
+ hMenu: HMENU,
+ hwndParent: HWND,
+ cy: c_int,
+ cx: c_int,
+ y: c_int,
+ x: c_int,
+ style: LONG,
+ lpszName: LPCSTR,
+ lpszClass: LPCSTR,
+ dwExStyle: DWORD,
+}
+
+CREATESTRUCTW:: struct {
+ lpCreateParams: LPVOID,
+ hInstance: HINSTANCE,
+ hMenu: HMENU,
+ hwndParent: HWND,
+ cy: c_int,
+ cx: c_int,
+ y: c_int,
+ x: c_int,
+ style: LONG,
+ lpszName: LPCWSTR,
+ lpszClass: LPCWSTR,
+ dwExStyle: DWORD,
+}
+
+// MessageBox() Flags
+MB_OK :: 0x00000000
+MB_OKCANCEL :: 0x00000001
+MB_ABORTRETRYIGNORE :: 0x00000002
+MB_YESNOCANCEL :: 0x00000003
+MB_YESNO :: 0x00000004
+MB_RETRYCANCEL :: 0x00000005
+MB_CANCELTRYCONTINUE :: 0x00000006
+
+MB_ICONHAND :: 0x00000010
+MB_ICONQUESTION :: 0x00000020
+MB_ICONEXCLAMATION :: 0x00000030
+MB_ICONASTERISK :: 0x00000040
+MB_USERICON :: 0x00000080
+MB_ICONWARNING :: MB_ICONEXCLAMATION
+MB_ICONERROR :: MB_ICONHAND
+MB_ICONINFORMATION :: MB_ICONASTERISK
+MB_ICONSTOP :: MB_ICONHAND
+
+MB_DEFBUTTON1 :: 0x00000000
+MB_DEFBUTTON2 :: 0x00000100
+MB_DEFBUTTON3 :: 0x00000200
+MB_DEFBUTTON4 :: 0x00000300
+
+MB_APPLMODAL :: 0x00000000
+MB_SYSTEMMODAL :: 0x00001000
+MB_TASKMODAL :: 0x00002000
+MB_HELP :: 0x00004000 // Help Button
+
+MB_NOFOCUS :: 0x00008000
+MB_SETFOREGROUND :: 0x00010000
+MB_DEFAULT_DESKTOP_ONLY :: 0x00020000
+MB_TOPMOST :: 0x00040000
+MB_RIGHT :: 0x00080000
+MB_RTLREADING :: 0x00100000
+
+MB_SERVICE_NOTIFICATION :: 0x00200000
+MB_SERVICE_NOTIFICATION_NT3X :: 0x00040000
+
+MB_TYPEMASK :: 0x0000000F
+MB_ICONMASK :: 0x000000F0
+MB_DEFMASK :: 0x00000F00
+MB_MODEMASK :: 0x00003000
+MB_MISCMASK :: 0x0000C000
+
+// Dialog Box Command IDs
+IDOK :: 1
+IDCANCEL :: 2
+IDABORT :: 3
+IDRETRY :: 4
+IDIGNORE :: 5
+IDYES :: 6
+IDNO :: 7
+IDCLOSE :: 8
+IDHELP :: 9
+IDTRYAGAIN :: 10
+IDCONTINUE :: 11
+IDTIMEOUT :: 32000
+
+CS_VREDRAW : UINT : 0x0001
+CS_HREDRAW : UINT : 0x0002
+CS_DBLCLKS : UINT : 0x0008
+CS_OWNDC : UINT : 0x0020
+CS_CLASSDC : UINT : 0x0040
+CS_PARENTDC : UINT : 0x0080
+CS_NOCLOSE : UINT : 0x0200
+CS_SAVEBITS : UINT : 0x0800
+CS_BYTEALIGNCLIENT : UINT : 0x1000
+CS_BYTEALIGNWINDOW : UINT : 0x2000
+CS_GLOBALCLASS : UINT : 0x4000
+CS_DROPSHADOW : UINT : 0x0002_0000
+
+GWL_EXSTYLE : c_int : -20
+GWLP_HINSTANCE : c_int : -6
+GWLP_ID : c_int : -12
+GWL_STYLE : c_int : -16
+GWLP_USERDATA : c_int : -21
+GWLP_WNDPROC : c_int : -4
+
+WS_BORDER : UINT : 0x0080_0000
+WS_CAPTION : UINT : 0x00C0_0000
+WS_CHILD : UINT : 0x4000_0000
+WS_CHILDWINDOW : UINT : WS_CHILD
+WS_CLIPCHILDREN : UINT : 0x0200_0000
+WS_CLIPSIBLINGS : UINT : 0x0400_0000
+WS_DISABLED : UINT : 0x0800_0000
+WS_DLGFRAME : UINT : 0x0040_0000
+WS_GROUP : UINT : 0x0002_0000
+WS_HSCROLL : UINT : 0x0010_0000
+WS_ICONIC : UINT : 0x2000_0000
+WS_MAXIMIZE : UINT : 0x0100_0000
+WS_MAXIMIZEBOX : UINT : 0x0001_0000
+WS_MINIMIZE : UINT : 0x2000_0000
+WS_MINIMIZEBOX : UINT : 0x0002_0000
+WS_OVERLAPPED : UINT : 0x0000_0000
+WS_OVERLAPPEDWINDOW : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
+WS_POPUP : UINT : 0x8000_0000
+WS_POPUPWINDOW : UINT : WS_POPUP | WS_BORDER | WS_SYSMENU
+WS_SIZEBOX : UINT : 0x0004_0000
+WS_SYSMENU : UINT : 0x0008_0000
+WS_TABSTOP : UINT : 0x0001_0000
+WS_THICKFRAME : UINT : 0x0004_0000
+WS_TILED : UINT : 0x0000_0000
+WS_TILEDWINDOW : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE
+WS_VISIBLE : UINT : 0x1000_0000
+WS_VSCROLL : UINT : 0x0020_0000
+
+QS_ALLEVENTS : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY
+QS_ALLINPUT : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE
+QS_ALLPOSTMESSAGE : UINT : 0x0100
+QS_HOTKEY : UINT : 0x0080
+QS_INPUT : UINT : QS_MOUSE | QS_KEY | QS_RAWINPUT
+QS_KEY : UINT : 0x0001
+QS_MOUSE : UINT : QS_MOUSEMOVE | QS_MOUSEBUTTON
+QS_MOUSEBUTTON : UINT : 0x0004
+QS_MOUSEMOVE : UINT : 0x0002
+QS_PAINT : UINT : 0x0020
+QS_POSTMESSAGE : UINT : 0x0008
+QS_RAWINPUT : UINT : 0x0400
+QS_SENDMESSAGE : UINT : 0x0040
+QS_TIMER : UINT : 0x0010
+
+PM_NOREMOVE : UINT : 0x0000
+PM_REMOVE : UINT : 0x0001
+PM_NOYIELD : UINT : 0x0002
+
+PM_QS_INPUT : UINT : QS_INPUT << 16
+PM_QS_PAINT : UINT : QS_PAINT << 16
+PM_QS_POSTMESSAGE : UINT : (QS_POSTMESSAGE | QS_HOTKEY | QS_TIMER) << 16
+PM_QS_SENDMESSAGE : UINT : QS_SENDMESSAGE << 16
+
+SW_HIDE : c_int : 0
+SW_SHOWNORMAL : c_int : SW_NORMAL
+SW_NORMAL : c_int : 1
+SW_SHOWMINIMIZED : c_int : 2
+SW_SHOWMAXIMIZED : c_int : SW_MAXIMIZE
+SW_MAXIMIZE : c_int : 3
+SW_SHOWNOACTIVATE : c_int : 4
+SW_SHOW : c_int : 5
+SW_MINIMIZE : c_int : 6
+SW_SHOWMINNOACTIVE : c_int : 7
+SW_SHOWNA : c_int : 8
+SW_RESTORE : c_int : 9
+SW_SHOWDEFAULT : c_int : 10
+SW_FORCEMINIMIZE : c_int : 11
+
+// SetWindowPos Flags
+SWP_NOSIZE :: 0x0001
+SWP_NOMOVE :: 0x0002
+SWP_NOZORDER :: 0x0004
+SWP_NOREDRAW :: 0x0008
+SWP_NOACTIVATE :: 0x0010
+SWP_FRAMECHANGED :: 0x0020 // The frame changed: send WM_NCCALCSIZE
+SWP_SHOWWINDOW :: 0x0040
+SWP_HIDEWINDOW :: 0x0080
+SWP_NOCOPYBITS :: 0x0100
+SWP_NOOWNERZORDER :: 0x0200 // Don't do owner Z ordering
+SWP_NOSENDCHANGING :: 0x0400 // Don't send WM_WINDOWPOSCHANGING
+
+SWP_DRAWFRAME :: SWP_FRAMECHANGED
+SWP_NOREPOSITION :: SWP_NOOWNERZORDER
+
+SWP_DEFERERASE :: 0x2000 // same as SWP_DEFERDRAWING
+SWP_ASYNCWINDOWPOS :: 0x4000 // same as SWP_CREATESPB
+
+HWND_TOP :: HWND( uintptr(0)) // 0
+HWND_BOTTOM :: HWND( uintptr(1)) // 1
+HWND_TOPMOST :: HWND(~uintptr(0)) // -1
+HWND_NOTOPMOST :: HWND(~uintptr(0) - 1) // -2
+
+// GetSystemMetrics() codes
+SM_CXSCREEN :: 0
+SM_CYSCREEN :: 1
+SM_CXVSCROLL :: 2
+SM_CYHSCROLL :: 3
+SM_CYCAPTION :: 4
+SM_CXBORDER :: 5
+SM_CYBORDER :: 6
+SM_CXDLGFRAME :: 7
+SM_CYDLGFRAME :: 8
+SM_CYVTHUMB :: 9
+SM_CXHTHUMB :: 10
+SM_CXICON :: 11
+SM_CYICON :: 12
+SM_CXCURSOR :: 13
+SM_CYCURSOR :: 14
+SM_CYMENU :: 15
+SM_CXFULLSCREEN :: 16
+SM_CYFULLSCREEN :: 17
+SM_CYKANJIWINDOW :: 18
+SM_MOUSEPRESENT :: 19
+SM_CYVSCROLL :: 20
+SM_CXHSCROLL :: 21
+SM_DEBUG :: 22
+SM_SWAPBUTTON :: 23
+SM_RESERVED1 :: 24
+SM_RESERVED2 :: 25
+SM_RESERVED3 :: 26
+SM_RESERVED4 :: 27
+SM_CXMIN :: 28
+SM_CYMIN :: 29
+SM_CXSIZE :: 30
+SM_CYSIZE :: 31
+SM_CXFRAME :: 32
+SM_CYFRAME :: 33
+SM_CXMINTRACK :: 34
+SM_CYMINTRACK :: 35
+SM_CXDOUBLECLK :: 36
+SM_CYDOUBLECLK :: 37
+SM_CXICONSPACING :: 38
+SM_CYICONSPACING :: 39
+SM_MENUDROPALIGNMENT :: 40
+SM_PENWINDOWS :: 41
+SM_DBCSENABLED :: 42
+SM_CMOUSEBUTTONS :: 43
+
+SM_CXFIXEDFRAME :: SM_CXDLGFRAME // ;win40 name change
+SM_CYFIXEDFRAME :: SM_CYDLGFRAME // ;win40 name change
+SM_CXSIZEFRAME :: SM_CXFRAME // ;win40 name change
+SM_CYSIZEFRAME :: SM_CYFRAME // ;win40 name change
+
+SM_SECURE :: 44
+SM_CXEDGE :: 45
+SM_CYEDGE :: 46
+SM_CXMINSPACING :: 47
+SM_CYMINSPACING :: 48
+SM_CXSMICON :: 49
+SM_CYSMICON :: 50
+SM_CYSMCAPTION :: 51
+SM_CXSMSIZE :: 52
+SM_CYSMSIZE :: 53
+SM_CXMENUSIZE :: 54
+SM_CYMENUSIZE :: 55
+SM_ARRANGE :: 56
+SM_CXMINIMIZED :: 57
+SM_CYMINIMIZED :: 58
+SM_CXMAXTRACK :: 59
+SM_CYMAXTRACK :: 60
+SM_CXMAXIMIZED :: 61
+SM_CYMAXIMIZED :: 62
+SM_NETWORK :: 63
+SM_CLEANBOOT :: 67
+SM_CXDRAG :: 68
+SM_CYDRAG :: 69
+
+SM_SHOWSOUNDS :: 70
+SM_CXMENUCHECK :: 71 // Use instead of GetMenuCheckMarkDimensions()!
+SM_CYMENUCHECK :: 72
+SM_SLOWMACHINE :: 73
+SM_MIDEASTENABLED :: 74
+SM_MOUSEWHEELPRESENT :: 75
+SM_XVIRTUALSCREEN :: 76
+SM_YVIRTUALSCREEN :: 77
+SM_CXVIRTUALSCREEN :: 78
+SM_CYVIRTUALSCREEN :: 79
+SM_CMONITORS :: 80
+SM_SAMEDISPLAYFORMAT :: 81
+SM_IMMENABLED :: 82
+SM_CXFOCUSBORDER :: 83
+SM_CYFOCUSBORDER :: 84
+SM_TABLETPC :: 86
+SM_MEDIACENTER :: 87
+SM_STARTER :: 88
+SM_SERVERR2 :: 89
+
+SM_MOUSEHORIZONTALWHEELPRESENT :: 91
+
+SM_CXPADDEDBORDER :: 92
+SM_DIGITIZER :: 94
+SM_MAXIMUMTOUCHES :: 95
+SM_CMETRICS :: 97
+
+SM_REMOTESESSION :: 0x1000
+SM_SHUTTINGDOWN :: 0x2000
+SM_REMOTECONTROL :: 0x2001
+SM_CARETBLINKINGENABLED :: 0x2002
+SM_CONVERTIBLESLATEMODE :: 0x2003
+SM_SYSTEMDOCKED :: 0x2004
+
+// System Menu Command Values
+SC_SIZE :: 0xF000
+SC_MOVE :: 0xF010
+SC_MINIMIZE :: 0xF020
+SC_MAXIMIZE :: 0xF030
+SC_NEXTWINDOW :: 0xF040
+SC_PREVWINDOW :: 0xF050
+SC_CLOSE :: 0xF060
+SC_VSCROLL :: 0xF070
+SC_HSCROLL :: 0xF080
+SC_MOUSEMENU :: 0xF090
+SC_KEYMENU :: 0xF100
+SC_ARRANGE :: 0xF110
+SC_RESTORE :: 0xF120
+SC_TASKLIST :: 0xF130
+SC_SCREENSAVE :: 0xF140
+SC_HOTKEY :: 0xF150
+SC_DEFAULT :: 0xF160
+SC_MONITORPOWER :: 0xF170
+SC_CONTEXTHELP :: 0xF180
+SC_SEPARATOR :: 0xF00F
+SCF_ISSECURE :: 0x00000001
+SC_ICON :: SC_MINIMIZE
+SC_ZOOM :: SC_MAXIMIZE
+
+CW_USEDEFAULT : c_int : -2147483648
+
+SIZE_RESTORED :: 0
+SIZE_MINIMIZED :: 1
+SIZE_MAXIMIZED :: 2
+SIZE_MAXSHOW :: 3
+SIZE_MAXHIDE :: 4
+
+WMSZ_LEFT :: 1
+WMSZ_RIGHT :: 2
+WMSZ_TOP :: 3
+WMSZ_TOPLEFT :: 4
+WMSZ_TOPRIGHT :: 5
+WMSZ_BOTTOM :: 6
+WMSZ_BOTTOMLEFT :: 7
+WMSZ_BOTTOMRIGHT :: 8
+
+// Key State Masks for Mouse Messages
+MK_LBUTTON :: 0x0001
+MK_RBUTTON :: 0x0002
+MK_SHIFT :: 0x0004
+MK_CONTROL :: 0x0008
+MK_MBUTTON :: 0x0010
+MK_XBUTTON1 :: 0x0020
+MK_XBUTTON2 :: 0x0040
+
+USER_TIMER_MAXIMUM :: 0x7FFFFFFF
+USER_TIMER_MINIMUM :: 0x0000000A
+
+_IDC_APPSTARTING := rawptr(uintptr(32650))
+_IDC_ARROW := rawptr(uintptr(32512))
+_IDC_CROSS := rawptr(uintptr(32515))
+_IDC_HAND := rawptr(uintptr(32649))
+_IDC_HELP := rawptr(uintptr(32651))
+_IDC_IBEAM := rawptr(uintptr(32513))
+_IDC_ICON := rawptr(uintptr(32641))
+_IDC_NO := rawptr(uintptr(32648))
+_IDC_SIZE := rawptr(uintptr(32640))
+_IDC_SIZEALL := rawptr(uintptr(32646))
+_IDC_SIZENESW := rawptr(uintptr(32643))
+_IDC_SIZENS := rawptr(uintptr(32645))
+_IDC_SIZENWSE := rawptr(uintptr(32642))
+_IDC_SIZEWE := rawptr(uintptr(32644))
+_IDC_UPARROW := rawptr(uintptr(32516))
+_IDC_WAIT := rawptr(uintptr(32514))
+
+IDC_APPSTARTING := cstring(_IDC_APPSTARTING)
+IDC_ARROW := cstring(_IDC_ARROW)
+IDC_CROSS := cstring(_IDC_CROSS)
+IDC_HAND := cstring(_IDC_HAND)
+IDC_HELP := cstring(_IDC_HELP)
+IDC_IBEAM := cstring(_IDC_IBEAM)
+IDC_ICON := cstring(_IDC_ICON)
+IDC_NO := cstring(_IDC_NO)
+IDC_SIZE := cstring(_IDC_SIZE)
+IDC_SIZEALL := cstring(_IDC_SIZEALL)
+IDC_SIZENESW := cstring(_IDC_SIZENESW)
+IDC_SIZENS := cstring(_IDC_SIZENS)
+IDC_SIZENWSE := cstring(_IDC_SIZENWSE)
+IDC_SIZEWE := cstring(_IDC_SIZEWE)
+IDC_UPARROW := cstring(_IDC_UPARROW)
+IDC_WAIT := cstring(_IDC_WAIT)
+
+
+_IDI_APPLICATION := rawptr(uintptr(32512))
+_IDI_HAND := rawptr(uintptr(32513))
+_IDI_QUESTION := rawptr(uintptr(32514))
+_IDI_EXCLAMATION := rawptr(uintptr(32515))
+_IDI_ASTERISK := rawptr(uintptr(32516))
+_IDI_WINLOGO := rawptr(uintptr(32517))
+_IDI_SHIELD := rawptr(uintptr(32518))
+IDI_APPLICATION := cstring(_IDI_APPLICATION)
+IDI_HAND := cstring(_IDI_HAND)
+IDI_QUESTION := cstring(_IDI_QUESTION)
+IDI_EXCLAMATION := cstring(_IDI_EXCLAMATION)
+IDI_ASTERISK := cstring(_IDI_ASTERISK)
+IDI_WINLOGO := cstring(_IDI_WINLOGO)
+IDI_SHIELD := cstring(_IDI_SHIELD)
+IDI_WARNING := IDI_EXCLAMATION
+IDI_ERROR := IDI_HAND
+IDI_INFORMATION := IDI_ASTERISK
+
+
+// DIB color table identifiers
+DIB_RGB_COLORS :: 0
+DIB_PAL_COLORS :: 1
+
+// constants for CreateDIBitmap
+CBM_INIT :: 0x04 // initialize bitmap
+
+// Region Flags
+ERROR :: 0
+NULLREGION :: 1
+SIMPLEREGION :: 2
+COMPLEXREGION :: 3
+RGN_ERROR :: ERROR
+
+// StretchBlt() Modes
+BLACKONWHITE :: 1
+WHITEONBLACK :: 2
+COLORONCOLOR :: 3
+HALFTONE :: 4
+MAXSTRETCHBLTMODE :: 4
+
+// Binary raster ops
+R2_BLACK :: 1 // 0
+R2_NOTMERGEPEN :: 2 // DPon
+R2_MASKNOTPEN :: 3 // DPna
+R2_NOTCOPYPEN :: 4 // PN
+R2_MASKPENNOT :: 5 // PDna
+R2_NOT :: 6 // Dn
+R2_XORPEN :: 7 // DPx
+R2_NOTMASKPEN :: 8 // DPan
+R2_MASKPEN :: 9 // DPa
+R2_NOTXORPEN :: 10 // DPxn
+R2_NOP :: 11 // D
+R2_MERGENOTPEN :: 12 // DPno
+R2_COPYPEN :: 13 // P
+R2_MERGEPENNOT :: 14 // PDno
+R2_MERGEPEN :: 15 // DPo
+R2_WHITE :: 16 // 1
+R2_LAST :: 16
+
+// Ternary raster operations
+SRCCOPY : DWORD : 0x00CC0020 // dest = source
+SRCPAINT : DWORD : 0x00EE0086 // dest = source OR dest
+SRCAND : DWORD : 0x008800C6 // dest = source AND dest
+SRCINVERT : DWORD : 0x00660046 // dest = source XOR dest
+SRCERASE : DWORD : 0x00440328 // dest = source AND (NOT dest)
+NOTSRCCOPY : DWORD : 0x00330008 // dest = (NOT source)
+NOTSRCERASE : DWORD : 0x001100A6 // dest = (NOT src) AND (NOT dest)
+MERGECOPY : DWORD : 0x00C000CA // dest = (source AND pattern
+MERGEPAINT : DWORD : 0x00BB0226 // dest = (NOT source) OR dest
+PATCOPY : DWORD : 0x00F00021 // dest = pattern
+PATPAINT : DWORD : 0x00FB0A09 // dest = DPSnoo
+PATINVERT : DWORD : 0x005A0049 // dest = pattern XOR dest
+DSTINVERT : DWORD : 0x00550009 // dest = (NOT dest)
+BLACKNESS : DWORD : 0x00000042 // dest = BLACK
+WHITENESS : DWORD : 0x00FF0062 // dest = WHITE
+NOMIRRORBITMAP : DWORD : 0x80000000 // Do not Mirror the bitmap in this call
+CAPTUREBLT : DWORD : 0x40000000 // Include layered windows
+
+// Stock Logical Objects
+WHITE_BRUSH :: 0
+LTGRAY_BRUSH :: 1
+GRAY_BRUSH :: 2
+DKGRAY_BRUSH :: 3
+BLACK_BRUSH :: 4
+NULL_BRUSH :: 5
+HOLLOW_BRUSH :: NULL_BRUSH
+WHITE_PEN :: 6
+BLACK_PEN :: 7
+NULL_PEN :: 8
+OEM_FIXED_FONT :: 10
+ANSI_FIXED_FONT :: 11
+ANSI_VAR_FONT :: 12
+SYSTEM_FONT :: 13
+DEVICE_DEFAULT_FONT :: 14
+DEFAULT_PALETTE :: 15
+SYSTEM_FIXED_FONT :: 16
+DEFAULT_GUI_FONT :: 17
+DC_BRUSH :: 18
+DC_PEN :: 19
+STOCK_LAST :: 19
+
+CLR_INVALID :: 0xFFFFFFFF
+
+RGBQUAD :: struct {
+ rgbBlue: BYTE,
+ rgbGreen: BYTE,
+ rgbRed: BYTE,
+ rgbReserved: BYTE,
+}
+
+PIXELFORMATDESCRIPTOR :: struct {
+ nSize: WORD,
+ nVersion: WORD,
+ dwFlags: DWORD,
+ iPixelType: BYTE,
+ cColorBits: BYTE,
+ cRedBits: BYTE,
+ cRedShift: BYTE,
+ cGreenBits: BYTE,
+ cGreenShift: BYTE,
+ cBlueBits: BYTE,
+ cBlueShift: BYTE,
+ cAlphaBits: BYTE,
+ cAlphaShift: BYTE,
+ cAccumBits: BYTE,
+ cAccumRedBits: BYTE,
+ cAccumGreenBits: BYTE,
+ cAccumBlueBits: BYTE,
+ cAccumAlphaBits: BYTE,
+ cDepthBits: BYTE,
+ cStencilBits: BYTE,
+ cAuxBuffers: BYTE,
+ iLayerType: BYTE,
+ bReserved: BYTE,
+ dwLayerMask: DWORD,
+ dwVisibleMask: DWORD,
+ dwDamageMask: DWORD,
+}
+
+BITMAPINFOHEADER :: struct {
+ biSize: DWORD,
+ biWidth: LONG,
+ biHeight: LONG,
+ biPlanes: WORD,
+ biBitCount: WORD,
+ biCompression: DWORD,
+ biSizeImage: DWORD,
+ biXPelsPerMeter: LONG,
+ biYPelsPerMeter: LONG,
+ biClrUsed: DWORD,
+ biClrImportant: DWORD,
+}
+
+BITMAPINFO :: struct {
+ bmiHeader: BITMAPINFOHEADER,
+ bmiColors: [1]RGBQUAD,
+}
+
+// pixel types
+PFD_TYPE_RGBA :: 0
+PFD_TYPE_COLORINDEX :: 1
+
+// layer types
+PFD_MAIN_PLANE :: 0
+PFD_OVERLAY_PLANE :: 1
+PFD_UNDERLAY_PLANE :: -1
+
+// PIXELFORMATDESCRIPTOR flags
+PFD_DOUBLEBUFFER :: 0x00000001
+PFD_STEREO :: 0x00000002
+PFD_DRAW_TO_WINDOW :: 0x00000004
+PFD_DRAW_TO_BITMAP :: 0x00000008
+PFD_SUPPORT_GDI :: 0x00000010
+PFD_SUPPORT_OPENGL :: 0x00000020
+PFD_GENERIC_FORMAT :: 0x00000040
+PFD_NEED_PALETTE :: 0x00000080
+PFD_NEED_SYSTEM_PALETTE :: 0x00000100
+PFD_SWAP_EXCHANGE :: 0x00000200
+PFD_SWAP_COPY :: 0x00000400
+PFD_SWAP_LAYER_BUFFERS :: 0x00000800
+PFD_GENERIC_ACCELERATED :: 0x00001000
+PFD_SUPPORT_DIRECTDRAW :: 0x00002000
+PFD_DIRECT3D_ACCELERATED :: 0x00004000
+PFD_SUPPORT_COMPOSITION :: 0x00008000
+
+// PIXELFORMATDESCRIPTOR flags for use in ChoosePixelFormat only
+PFD_DEPTH_DONTCARE :: 0x20000000
+PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000
+PFD_STEREO_DONTCARE :: 0x80000000
+
+// constants for the biCompression field
+BI_RGB :: 0
+BI_RLE8 :: 1
+BI_RLE4 :: 2
+BI_BITFIELDS :: 3
+BI_JPEG :: 4
+BI_PNG :: 5
+
WSA_FLAG_OVERLAPPED: DWORD : 0x01
WSA_FLAG_NO_HANDLE_INHERIT: DWORD : 0x80
@@ -379,6 +1069,15 @@ FILE_TYPE_PIPE :: 0x0003
RECT :: struct {left, top, right, bottom: LONG}
POINT :: struct {x, y: LONG}
+WINDOWPOS :: struct {
+ hwnd: HWND,
+ hwndInsertAfter: HWND,
+ x: c_int,
+ y: c_int,
+ cx: c_int,
+ cy: c_int,
+ flags: UINT,
+}
when size_of(uintptr) == 4 {
WSADATA :: struct {
@@ -578,7 +1277,7 @@ PROCESS_INFORMATION :: struct {
}
// FYI: This is STARTUPINFOW, not STARTUPINFOA
-STARTUPINFO :: struct #packed {
+STARTUPINFO :: struct {
cb: DWORD,
lpReserved: LPWSTR,
lpDesktop: LPWSTR,
@@ -784,17 +1483,17 @@ SYSTEM_INFO :: struct {
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
OSVERSIONINFOEXW :: struct {
- dwOSVersionInfoSize: ULONG,
- dwMajorVersion: ULONG,
- dwMinorVersion: ULONG,
- dwBuildNumber: ULONG,
- dwPlatformId: ULONG,
- szCSDVersion: [128]WCHAR,
- wServicePackMajor: USHORT,
- wServicePackMinor: USHORT,
- wSuiteMask: USHORT,
- wProductType: UCHAR,
- wReserved: UCHAR,
+ dwOSVersionInfoSize: ULONG,
+ dwMajorVersion: ULONG,
+ dwMinorVersion: ULONG,
+ dwBuildNumber: ULONG,
+ dwPlatformId: ULONG,
+ szCSDVersion: [128]WCHAR,
+ wServicePackMajor: USHORT,
+ wServicePackMinor: USHORT,
+ wSuiteMask: USHORT,
+ wProductType: UCHAR,
+ wReserved: UCHAR,
}
// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-quota_limits
@@ -837,24 +1536,24 @@ PROFILEINFOW :: struct {
lpDefaultPath: LPWSTR,
lpServerName: LPWSTR,
lpPolicyPath: LPWSTR,
- hProfile: HANDLE,
+ hProfile: HANDLE,
}
// Used in LookupAccountNameW
SID_NAME_USE :: distinct DWORD
SID_TYPE :: enum SID_NAME_USE {
- User = 1,
- Group,
- Domain,
- Alias,
- WellKnownGroup,
- DeletedAccount,
- Invalid,
- Unknown,
- Computer,
- Label,
- LogonSession,
+ User = 1,
+ Group,
+ Domain,
+ Alias,
+ WellKnownGroup,
+ DeletedAccount,
+ Invalid,
+ Unknown,
+ Computer,
+ Label,
+ LogonSession,
}
SECURITY_MAX_SID_SIZE :: 68
@@ -869,7 +1568,7 @@ SID :: struct #packed {
#assert(size_of(SID) == SECURITY_MAX_SID_SIZE)
SID_IDENTIFIER_AUTHORITY :: struct #packed {
- Value: [6]u8,
+ Value: [6]u8,
}
// For NetAPI32
@@ -901,11 +1600,11 @@ USER_INFO_FLAG :: enum DWORD {
Passwd_Cant_Change = 6, // 1 << 6: 0x0040,
Encrypted_Text_Password_Allowed = 7, // 1 << 7: 0x0080,
- Temp_Duplicate_Account = 8, // 1 << 8: 0x0100,
- Normal_Account = 9, // 1 << 9: 0x0200,
- InterDomain_Trust_Account = 11, // 1 << 11: 0x0800,
- Workstation_Trust_Account = 12, // 1 << 12: 0x1000,
- Server_Trust_Account = 13, // 1 << 13: 0x2000,
+ Temp_Duplicate_Account = 8, // 1 << 8: 0x0100,
+ Normal_Account = 9, // 1 << 9: 0x0200,
+ InterDomain_Trust_Account = 11, // 1 << 11: 0x0800,
+ Workstation_Trust_Account = 12, // 1 << 12: 0x1000,
+ Server_Trust_Account = 13, // 1 << 13: 0x2000,
}
USER_INFO_FLAGS :: distinct bit_set[USER_INFO_FLAG]
@@ -1252,4 +1951,4 @@ SYSTEMTIME :: struct {
minute: WORD,
second: WORD,
milliseconds: WORD,
-}
\ No newline at end of file
+}
diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin
new file mode 100644
index 000000000..bdab77e27
--- /dev/null
+++ b/core/sys/windows/user32.odin
@@ -0,0 +1,198 @@
+// +build windows
+package sys_windows
+
+foreign import user32 "system:User32.lib"
+
+@(default_calling_convention="stdcall")
+foreign user32 {
+ GetClassInfoA :: proc(hInstance: HINSTANCE, lpClassNAme: LPCSTR, lpWndClass: ^WNDCLASSA) -> BOOL ---
+ GetClassInfoW :: proc(hInstance: HINSTANCE, lpClassNAme: LPCWSTR, lpWndClass: ^WNDCLASSW) -> BOOL ---
+ GetClassInfoExA :: proc(hInsatnce: HINSTANCE, lpszClass: LPCSTR, lpwcx: ^WNDCLASSEXA) -> BOOL ---
+ GetClassInfoExW :: proc(hInsatnce: HINSTANCE, lpszClass: LPCWSTR, lpwcx: ^WNDCLASSEXW) -> BOOL ---
+
+ GetClassLongPtrA :: proc(hWnd: HWND, nIndex: c_int) -> DWORD ---
+ GetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> DWORD ---
+ SetClassLongPtrA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR ---
+ SetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR ---
+
+ GetClassNameA :: proc(hWnd: HWND, lpClassName: LPSTR, nMaxCount: c_int) -> c_int ---
+ GetClassNameW :: proc(hWnd: HWND, lpClassName: LPWSTR, nMaxCount: c_int) -> c_int ---
+
+ GetWindowLongPtrA :: proc(hWnd: HWND, nIndex: c_int) -> LONG_PTR ---
+ GetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> LONG_PTR ---
+ SetWindowLongPtrA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> LONG_PTR ---
+ SetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> LONG_PTR ---
+
+ RegisterClassA :: proc(lpWndClass: ^WNDCLASSA) -> ATOM ---
+ RegisterClassW :: proc(lpWndClass: ^WNDCLASSW) -> ATOM ---
+ RegisterClassExA :: proc(^WNDCLASSEXA) -> ATOM ---
+ RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
+
+ CreateWindowExA :: proc(
+ dwExStyle: DWORD,
+ lpClassName: LPCSTR,
+ lpWindowName: LPCSTR,
+ dwStyle: DWORD,
+ X: c_int,
+ Y: c_int,
+ nWidth: c_int,
+ nHeight: c_int,
+ hWndParent: HWND,
+ hMenu: HMENU,
+ hInstance: HINSTANCE,
+ lpParam: LPVOID,
+ ) -> HWND ---
+ CreateWindowExW :: proc(
+ dwExStyle: DWORD,
+ lpClassName: LPCWSTR,
+ lpWindowName: LPCWSTR,
+ dwStyle: DWORD,
+ X: c_int,
+ Y: c_int,
+ nWidth: c_int,
+ nHeight: c_int,
+ hWndParent: HWND,
+ hMenu: HMENU,
+ hInstance: HINSTANCE,
+ lpParam: LPVOID,
+ ) -> HWND ---
+
+ DestroyWindow :: proc(hWnd: HWND) -> BOOL ---
+
+ ShowWindow :: proc(hWnd: HWND, nCmdShow: c_int) -> BOOL ---
+
+ GetMessageA :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
+ GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
+
+ TranslateMessage :: proc(lpMsg: ^MSG) -> BOOL ---
+ DispatchMessageA :: proc(lpMsg: ^MSG) -> LRESULT ---
+ DispatchMessageW :: proc(lpMsg: ^MSG) -> LRESULT ---
+
+ PeekMessageA :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL ---
+ PeekMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL ---
+
+ PostMessageA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
+ PostMessageW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
+ SendMessageA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+ SendMessageW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+
+ PostThreadMessageA :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
+ PostThreadMessageW :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
+
+ PostQuitMessage :: proc(nExitCode: c_int) ---
+
+ GetQueueStatus :: proc(flags: UINT) -> DWORD ---
+
+ DefWindowProcA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+ DefWindowProcW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+
+ FindWindowA :: proc(lpClassName: LPCSTR, lpWindowName: LPCSTR) -> HWND ---
+ FindWindowW :: proc(lpClassName: LPCWSTR, lpWindowName: LPCWSTR) -> HWND ---
+ FindWindowExA :: proc(hWndParent: HWND, hWndChildAfter: HWND, lpszClass: LPCSTR, lpszWindow: LPCSTR) -> HWND ---
+ FindWindowExW :: proc(hWndParent: HWND, hWndChildAfter: HWND, lpszClass: LPCWSTR, lpszWindow: LPCWSTR) -> HWND ---
+
+ LoadIconA :: proc(hInstance: HINSTANCE, lpIconName: LPCSTR) -> HICON ---
+ LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON ---
+ LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR ---
+ LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR ---
+
+ GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
+ GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
+ ClientToScreen :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL ---
+ SetWindowPos :: proc(
+ hWnd: HWND,
+ hWndInsertAfter: HWND,
+ X: c_int,
+ Y: c_int,
+ cx: c_int,
+ cy: c_int,
+ uFlags: UINT,
+ ) -> BOOL ---
+ GetSystemMetrics :: proc(nIndex: c_int) -> c_int ---
+ AdjustWindowRect :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL) -> BOOL ---
+ AdjustWindowRectEx :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD) -> BOOL ---
+
+ GetWindowDC :: proc(hWnd: HWND) -> HDC ---
+ GetDC :: proc(hWnd: HWND) -> HDC ---
+ ReleaseDC :: proc(hWnd: HWND, hDC: HDC) -> c_int ---
+
+ GetUpdateRect :: proc(hWnd: HWND, lpRect: LPRECT, bErase: BOOL) -> BOOL ---
+ ValidateRect :: proc(hWnd: HWND, lpRect: ^RECT) -> BOOL ---
+ InvalidateRect :: proc(hWnd: HWND, lpRect: ^RECT, bErase: BOOL) -> BOOL ---
+
+ BeginPaint :: proc(hWnd: HWND, lpPaint: ^PAINTSTRUCT) -> HDC ---
+ EndPaint :: proc(hWnd: HWND, lpPaint: ^PAINTSTRUCT) -> BOOL ---
+
+ GetKeyState :: proc(nVirtKey: c_int) -> SHORT ---
+ GetAsyncKeyState :: proc(vKey: c_int) -> SHORT ---
+
+ SetTimer :: proc(hWnd: HWND, nIDEvent: UINT_PTR, uElapse: UINT, lpTimerFunc: TIMERPROC) -> UINT_PTR ---
+ KillTimer :: proc(hWnd: HWND, uIDEvent: UINT_PTR) -> BOOL ---
+
+ MessageBoxA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT) -> c_int ---
+ MessageBoxW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> c_int ---
+ MessageBoxExA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT, wLanguageId: WORD) -> c_int ---
+ MessageBoxExW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT, wLanguageId: WORD) -> c_int ---
+}
+
+CreateWindowA :: #force_inline proc "stdcall" (
+ lpClassName: LPCSTR,
+ lpWindowName: LPCSTR,
+ dwStyle: DWORD,
+ X: c_int,
+ Y: c_int,
+ nWidth: c_int,
+ nHeight: c_int,
+ hWndParent: HWND,
+ hMenu: HMENU,
+ hInstance: HINSTANCE,
+ lpParam: LPVOID,
+) -> HWND {
+ return CreateWindowExA(
+ 0,
+ lpClassName,
+ lpWindowName,
+ dwStyle,
+ X,
+ Y,
+ nWidth,
+ nHeight,
+ hWndParent,
+ hMenu,
+ hInstance,
+ lpParam,
+ )
+}
+
+CreateWindowW :: #force_inline proc "stdcall" (
+ lpClassName: LPCTSTR,
+ lpWindowName: LPCTSTR,
+ dwStyle: DWORD,
+ X: c_int,
+ Y: c_int,
+ nWidth: c_int,
+ nHeight: c_int,
+ hWndParent: HWND,
+ hMenu: HMENU,
+ hInstance: HINSTANCE,
+ lpParam: LPVOID,
+) -> HWND {
+ return CreateWindowExW(
+ 0,
+ lpClassName,
+ lpWindowName,
+ dwStyle,
+ X,
+ Y,
+ nWidth,
+ nHeight,
+ hWndParent,
+ hMenu,
+ hInstance,
+ lpParam,
+ )
+}
+
+GET_SC_WPARAM :: #force_inline proc(wparam: WPARAM) -> i32 {
+ return i32(wparam) & 0xFFF0
+}
diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin
index efb37dbc0..5797216c3 100644
--- a/core/sys/windows/util.odin
+++ b/core/sys/windows/util.odin
@@ -45,7 +45,9 @@ utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> wstri
return nil
}
-wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> string {
+wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string) {
+ context.allocator = allocator
+
if N <= 0 {
return ""
}
@@ -60,7 +62,7 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
// also null terminated.
// If N != -1 it assumes the wide string is not null terminated and the resulting string
// will not be null terminated, we therefore have to force it to be null terminated manually.
- text := make([]byte, n+1 if N != -1 else n, allocator)
+ text := make([]byte, n+1 if N != -1 else n)
n1 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), raw_data(text), n, nil, nil)
if n1 == 0 {
@@ -74,7 +76,6 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
break
}
}
-
return string(text[:n])
}
diff --git a/core/sys/windows/window_messages.odin b/core/sys/windows/window_messages.odin
new file mode 100644
index 000000000..9c3a30088
--- /dev/null
+++ b/core/sys/windows/window_messages.odin
@@ -0,0 +1,1048 @@
+// +build windows
+package sys_windows
+
+WM_NULL :: 0x0000
+WM_CREATE :: 0x0001
+WM_DESTROY :: 0x0002
+WM_MOVE :: 0x0003
+WM_SIZE :: 0x0005
+WM_ACTIVATE :: 0x0006
+WM_SETFOCUS :: 0x0007
+WM_KILLFOCUS :: 0x0008
+WM_ENABLE :: 0x000a
+WM_SETREDRAW :: 0x000b
+WM_SETTEXT :: 0x000c
+WM_GETTEXT :: 0x000d
+WM_GETTEXTLENGTH :: 0x000e
+WM_PAINT :: 0x000f
+WM_CLOSE :: 0x0010
+WM_QUERYENDSESSION :: 0x0011
+WM_QUIT :: 0x0012
+WM_QUERYOPEN :: 0x0013
+WM_ERASEBKGND :: 0x0014
+WM_SYSCOLORCHANGE :: 0x0015
+WM_ENDSESSION :: 0x0016
+WM_SHOWWINDOW :: 0x0018
+WM_CTLCOLOR :: 0x0019
+WM_WININICHANGE :: 0x001a
+WM_DEVMODECHANGE :: 0x001b
+WM_ACTIVATEAPP :: 0x001c
+WM_FONTCHANGE :: 0x001d
+WM_TIMECHANGE :: 0x001e
+WM_CANCELMODE :: 0x001f
+WM_SETCURSOR :: 0x0020
+WM_MOUSEACTIVATE :: 0x0021
+WM_CHILDACTIVATE :: 0x0022
+WM_QUEUESYNC :: 0x0023
+WM_GETMINMAXINFO :: 0x0024
+WM_PAINTICON :: 0x0026
+WM_ICONERASEBKGND :: 0x0027
+WM_NEXTDLGCTL :: 0x0028
+WM_SPOOLERSTATUS :: 0x002a
+WM_DRAWITEM :: 0x002b
+WM_MEASUREITEM :: 0x002c
+WM_DELETEITEM :: 0x002d
+WM_VKEYTOITEM :: 0x002e
+WM_CHARTOITEM :: 0x002f
+WM_SETFONT :: 0x0030
+WM_GETFONT :: 0x0031
+WM_SETHOTKEY :: 0x0032
+WM_GETHOTKEY :: 0x0033
+WM_QUERYDRAGICON :: 0x0037
+WM_COMPAREITEM :: 0x0039
+WM_GETOBJECT :: 0x003d
+WM_COMPACTING :: 0x0041
+WM_COMMNOTIFY :: 0x0044
+WM_WINDOWPOSCHANGING :: 0x0046
+WM_WINDOWPOSCHANGED :: 0x0047
+WM_POWER :: 0x0048
+WM_COPYGLOBALDATA :: 0x0049
+WM_COPYDATA :: 0x004a
+WM_CANCELJOURNAL :: 0x004b
+WM_NOTIFY :: 0x004e
+WM_INPUTLANGCHANGEREQUEST :: 0x0050
+WM_INPUTLANGCHANGE :: 0x0051
+WM_TCARD :: 0x0052
+WM_HELP :: 0x0053
+WM_USERCHANGED :: 0x0054
+WM_NOTIFYFORMAT :: 0x0055
+WM_CONTEXTMENU :: 0x007b
+WM_STYLECHANGING :: 0x007c
+WM_STYLECHANGED :: 0x007d
+WM_DISPLAYCHANGE :: 0x007e
+WM_GETICON :: 0x007f
+WM_SETICON :: 0x0080
+WM_NCCREATE :: 0x0081
+WM_NCDESTROY :: 0x0082
+WM_NCCALCSIZE :: 0x0083
+WM_NCHITTEST :: 0x0084
+WM_NCPAINT :: 0x0085
+WM_NCACTIVATE :: 0x0086
+WM_GETDLGCODE :: 0x0087
+WM_SYNCPAINT :: 0x0088
+WM_NCMOUSEMOVE :: 0x00a0
+WM_NCLBUTTONDOWN :: 0x00a1
+WM_NCLBUTTONUP :: 0x00a2
+WM_NCLBUTTONDBLCLK :: 0x00a3
+WM_NCRBUTTONDOWN :: 0x00a4
+WM_NCRBUTTONUP :: 0x00a5
+WM_NCRBUTTONDBLCLK :: 0x00a6
+WM_NCMBUTTONDOWN :: 0x00a7
+WM_NCMBUTTONUP :: 0x00a8
+WM_NCMBUTTONDBLCLK :: 0x00a9
+WM_NCXBUTTONDOWN :: 0x00ab
+WM_NCXBUTTONUP :: 0x00ac
+WM_NCXBUTTONDBLCLK :: 0x00ad
+EM_GETSEL :: 0x00b0
+EM_SETSEL :: 0x00b1
+EM_GETRECT :: 0x00b2
+EM_SETRECT :: 0x00b3
+EM_SETRECTNP :: 0x00b4
+EM_SCROLL :: 0x00b5
+EM_LINESCROLL :: 0x00b6
+EM_SCROLLCARET :: 0x00b7
+EM_GETMODIFY :: 0x00b8
+EM_SETMODIFY :: 0x00b9
+EM_GETLINECOUNT :: 0x00ba
+EM_LINEINDEX :: 0x00bb
+EM_SETHANDLE :: 0x00bc
+EM_GETHANDLE :: 0x00bd
+EM_GETTHUMB :: 0x00be
+EM_LINELENGTH :: 0x00c1
+EM_REPLACESEL :: 0x00c2
+EM_SETFONT :: 0x00c3
+EM_GETLINE :: 0x00c4
+EM_LIMITTEXT :: 0x00c5
+EM_SETLIMITTEXT :: 0x00c5
+EM_CANUNDO :: 0x00c6
+EM_UNDO :: 0x00c7
+EM_FMTLINES :: 0x00c8
+EM_LINEFROMCHAR :: 0x00c9
+EM_SETWORDBREAK :: 0x00ca
+EM_SETTABSTOPS :: 0x00cb
+EM_SETPASSWORDCHAR :: 0x00cc
+EM_EMPTYUNDOBUFFER :: 0x00cd
+EM_GETFIRSTVISIBLELINE :: 0x00ce
+EM_SETREADONLY :: 0x00cf
+EM_SETWORDBREAKPROC :: 0x00d0
+EM_GETWORDBREAKPROC :: 0x00d1
+EM_GETPASSWORDCHAR :: 0x00d2
+EM_SETMARGINS :: 0x00d3
+EM_GETMARGINS :: 0x00d4
+EM_GETLIMITTEXT :: 0x00d5
+EM_POSFROMCHAR :: 0x00d6
+EM_CHARFROMPOS :: 0x00d7
+EM_SETIMESTATUS :: 0x00d8
+EM_GETIMESTATUS :: 0x00d9
+SBM_SETPOS :: 0x00e0
+SBM_GETPOS :: 0x00e1
+SBM_SETRANGE :: 0x00e2
+SBM_GETRANGE :: 0x00e3
+SBM_ENABLE_ARROWS :: 0x00e4
+SBM_SETRANGEREDRAW :: 0x00e6
+SBM_SETSCROLLINFO :: 0x00e9
+SBM_GETSCROLLINFO :: 0x00ea
+SBM_GETSCROLLBARINFO :: 0x00eb
+BM_GETCHECK :: 0x00f0
+BM_SETCHECK :: 0x00f1
+BM_GETSTATE :: 0x00f2
+BM_SETSTATE :: 0x00f3
+BM_SETSTYLE :: 0x00f4
+BM_CLICK :: 0x00f5
+BM_GETIMAGE :: 0x00f6
+BM_SETIMAGE :: 0x00f7
+BM_SETDONTCLICK :: 0x00f8
+WM_INPUT :: 0x00ff
+WM_KEYDOWN :: 0x0100
+WM_KEYFIRST :: 0x0100
+WM_KEYUP :: 0x0101
+WM_CHAR :: 0x0102
+WM_DEADCHAR :: 0x0103
+WM_SYSKEYDOWN :: 0x0104
+WM_SYSKEYUP :: 0x0105
+WM_SYSCHAR :: 0x0106
+WM_SYSDEADCHAR :: 0x0107
+WM_UNICHAR :: 0x0109
+WM_KEYLAST :: 0x0109
+WM_WNT_CONVERTREQUESTEX :: 0x0109
+WM_CONVERTREQUEST :: 0x010a
+WM_CONVERTRESULT :: 0x010b
+WM_INTERIM :: 0x010c
+WM_IME_STARTCOMPOSITION :: 0x010d
+WM_IME_ENDCOMPOSITION :: 0x010e
+WM_IME_COMPOSITION :: 0x010f
+WM_IME_KEYLAST :: 0x010f
+WM_INITDIALOG :: 0x0110
+WM_COMMAND :: 0x0111
+WM_SYSCOMMAND :: 0x0112
+WM_TIMER :: 0x0113
+WM_HSCROLL :: 0x0114
+WM_VSCROLL :: 0x0115
+WM_INITMENU :: 0x0116
+WM_INITMENUPOPUP :: 0x0117
+WM_SYSTIMER :: 0x0118
+WM_MENUSELECT :: 0x011f
+WM_MENUCHAR :: 0x0120
+WM_ENTERIDLE :: 0x0121
+WM_MENURBUTTONUP :: 0x0122
+WM_MENUDRAG :: 0x0123
+WM_MENUGETOBJECT :: 0x0124
+WM_UNINITMENUPOPUP :: 0x0125
+WM_MENUCOMMAND :: 0x0126
+WM_CHANGEUISTATE :: 0x0127
+WM_UPDATEUISTATE :: 0x0128
+WM_QUERYUISTATE :: 0x0129
+WM_LBTRACKPOINT :: 0x0131
+WM_CTLCOLORMSGBOX :: 0x0132
+WM_CTLCOLOREDIT :: 0x0133
+WM_CTLCOLORLISTBOX :: 0x0134
+WM_CTLCOLORBTN :: 0x0135
+WM_CTLCOLORDLG :: 0x0136
+WM_CTLCOLORSCROLLBAR :: 0x0137
+WM_CTLCOLORSTATIC :: 0x0138
+CB_GETEDITSEL :: 0x0140
+CB_LIMITTEXT :: 0x0141
+CB_SETEDITSEL :: 0x0142
+CB_ADDSTRING :: 0x0143
+CB_DELETESTRING :: 0x0144
+CB_DIR :: 0x0145
+CB_GETCOUNT :: 0x0146
+CB_GETCURSEL :: 0x0147
+CB_GETLBTEXT :: 0x0148
+CB_GETLBTEXTLEN :: 0x0149
+CB_INSERTSTRING :: 0x014a
+CB_RESETCONTENT :: 0x014b
+CB_FINDSTRING :: 0x014c
+CB_SELECTSTRING :: 0x014d
+CB_SETCURSEL :: 0x014e
+CB_SHOWDROPDOWN :: 0x014f
+CB_GETITEMDATA :: 0x0150
+CB_SETITEMDATA :: 0x0151
+CB_GETDROPPEDCONTROLRECT :: 0x0152
+CB_SETITEMHEIGHT :: 0x0153
+CB_GETITEMHEIGHT :: 0x0154
+CB_SETEXTENDEDUI :: 0x0155
+CB_GETEXTENDEDUI :: 0x0156
+CB_GETDROPPEDSTATE :: 0x0157
+CB_FINDSTRINGEXACT :: 0x0158
+CB_SETLOCALE :: 0x0159
+CB_GETLOCALE :: 0x015a
+CB_GETTOPINDEX :: 0x015b
+CB_SETTOPINDEX :: 0x015c
+CB_GETHORIZONTALEXTENT :: 0x015d
+CB_SETHORIZONTALEXTENT :: 0x015e
+CB_GETDROPPEDWIDTH :: 0x015f
+CB_SETDROPPEDWIDTH :: 0x0160
+CB_INITSTORAGE :: 0x0161
+CB_MULTIPLEADDSTRING :: 0x0163
+CB_GETCOMBOBOXINFO :: 0x0164
+CB_MSGMAX :: 0x0165
+WM_MOUSEFIRST :: 0x0200
+WM_MOUSEMOVE :: 0x0200
+WM_LBUTTONDOWN :: 0x0201
+WM_LBUTTONUP :: 0x0202
+WM_LBUTTONDBLCLK :: 0x0203
+WM_RBUTTONDOWN :: 0x0204
+WM_RBUTTONUP :: 0x0205
+WM_RBUTTONDBLCLK :: 0x0206
+WM_MBUTTONDOWN :: 0x0207
+WM_MBUTTONUP :: 0x0208
+WM_MBUTTONDBLCLK :: 0x0209
+WM_MOUSELAST :: 0x0209
+WM_MOUSEWHEEL :: 0x020a
+WM_XBUTTONDOWN :: 0x020b
+WM_XBUTTONUP :: 0x020c
+WM_XBUTTONDBLCLK :: 0x020d
+WM_MOUSEHWHEEL :: 0x020e
+WM_PARENTNOTIFY :: 0x0210
+WM_ENTERMENULOOP :: 0x0211
+WM_EXITMENULOOP :: 0x0212
+WM_NEXTMENU :: 0x0213
+WM_SIZING :: 0x0214
+WM_CAPTURECHANGED :: 0x0215
+WM_MOVING :: 0x0216
+WM_POWERBROADCAST :: 0x0218
+WM_DEVICECHANGE :: 0x0219
+WM_MDICREATE :: 0x0220
+WM_MDIDESTROY :: 0x0221
+WM_MDIACTIVATE :: 0x0222
+WM_MDIRESTORE :: 0x0223
+WM_MDINEXT :: 0x0224
+WM_MDIMAXIMIZE :: 0x0225
+WM_MDITILE :: 0x0226
+WM_MDICASCADE :: 0x0227
+WM_MDIICONARRANGE :: 0x0228
+WM_MDIGETACTIVE :: 0x0229
+WM_MDISETMENU :: 0x0230
+WM_ENTERSIZEMOVE :: 0x0231
+WM_EXITSIZEMOVE :: 0x0232
+WM_DROPFILES :: 0x0233
+WM_MDIREFRESHMENU :: 0x0234
+WM_IME_REPORT :: 0x0280
+WM_IME_SETCONTEXT :: 0x0281
+WM_IME_NOTIFY :: 0x0282
+WM_IME_CONTROL :: 0x0283
+WM_IME_COMPOSITIONFULL :: 0x0284
+WM_IME_SELECT :: 0x0285
+WM_IME_CHAR :: 0x0286
+WM_IME_REQUEST :: 0x0288
+WM_IMEKEYDOWN :: 0x0290
+WM_IME_KEYDOWN :: 0x0290
+WM_IMEKEYUP :: 0x0291
+WM_IME_KEYUP :: 0x0291
+WM_NCMOUSEHOVER :: 0x02a0
+WM_MOUSEHOVER :: 0x02a1
+WM_NCMOUSELEAVE :: 0x02a2
+WM_MOUSELEAVE :: 0x02a3
+WM_CUT :: 0x0300
+WM_COPY :: 0x0301
+WM_PASTE :: 0x0302
+WM_CLEAR :: 0x0303
+WM_UNDO :: 0x0304
+WM_RENDERFORMAT :: 0x0305
+WM_RENDERALLFORMATS :: 0x0306
+WM_DESTROYCLIPBOARD :: 0x0307
+WM_DRAWCLIPBOARD :: 0x0308
+WM_PAINTCLIPBOARD :: 0x0309
+WM_VSCROLLCLIPBOARD :: 0x030a
+WM_SIZECLIPBOARD :: 0x030b
+WM_ASKCBFORMATNAME :: 0x030c
+WM_CHANGECBCHAIN :: 0x030d
+WM_HSCROLLCLIPBOARD :: 0x030e
+WM_QUERYNEWPALETTE :: 0x030f
+WM_PALETTEISCHANGING :: 0x0310
+WM_PALETTECHANGED :: 0x0311
+WM_HOTKEY :: 0x0312
+WM_PRINT :: 0x0317
+WM_PRINTCLIENT :: 0x0318
+WM_APPCOMMAND :: 0x0319
+WM_HANDHELDFIRST :: 0x0358
+WM_HANDHELDLAST :: 0x035f
+WM_AFXFIRST :: 0x0360
+WM_AFXLAST :: 0x037f
+WM_PENWINFIRST :: 0x0380
+WM_RCRESULT :: 0x0381
+WM_HOOKRCRESULT :: 0x0382
+WM_GLOBALRCCHANGE :: 0x0383
+WM_PENMISCINFO :: 0x0383
+WM_SKB :: 0x0384
+WM_HEDITCTL :: 0x0385
+WM_PENCTL :: 0x0385
+WM_PENMISC :: 0x0386
+WM_CTLINIT :: 0x0387
+WM_PENEVENT :: 0x0388
+WM_PENWINLAST :: 0x038f
+DDM_SETFMT :: 0x0400
+DM_GETDEFID :: 0x0400
+NIN_SELECT :: 0x0400
+TBM_GETPOS :: 0x0400
+WM_PSD_PAGESETUPDLG :: 0x0400
+WM_USER :: 0x0400
+CBEM_INSERTITEMA :: 0x0401
+DDM_DRAW :: 0x0401
+DM_SETDEFID :: 0x0401
+HKM_SETHOTKEY :: 0x0401
+PBM_SETRANGE :: 0x0401
+RB_INSERTBANDA :: 0x0401
+SB_SETTEXTA :: 0x0401
+TB_ENABLEBUTTON :: 0x0401
+TBM_GETRANGEMIN :: 0x0401
+TTM_ACTIVATE :: 0x0401
+WM_CHOOSEFONT_GETLOGFONT :: 0x0401
+WM_PSD_FULLPAGERECT :: 0x0401
+CBEM_SETIMAGELIST :: 0x0402
+DDM_CLOSE :: 0x0402
+DM_REPOSITION :: 0x0402
+HKM_GETHOTKEY :: 0x0402
+PBM_SETPOS :: 0x0402
+RB_DELETEBAND :: 0x0402
+SB_GETTEXTA :: 0x0402
+TB_CHECKBUTTON :: 0x0402
+TBM_GETRANGEMAX :: 0x0402
+WM_PSD_MINMARGINRECT :: 0x0402
+CBEM_GETIMAGELIST :: 0x0403
+DDM_BEGIN :: 0x0403
+HKM_SETRULES :: 0x0403
+PBM_DELTAPOS :: 0x0403
+RB_GETBARINFO :: 0x0403
+SB_GETTEXTLENGTHA :: 0x0403
+TBM_GETTIC :: 0x0403
+TB_PRESSBUTTON :: 0x0403
+TTM_SETDELAYTIME :: 0x0403
+WM_PSD_MARGINRECT :: 0x0403
+CBEM_GETITEMA :: 0x0404
+DDM_END :: 0x0404
+PBM_SETSTEP :: 0x0404
+RB_SETBARINFO :: 0x0404
+SB_SETPARTS :: 0x0404
+TB_HIDEBUTTON :: 0x0404
+TBM_SETTIC :: 0x0404
+TTM_ADDTOOLA :: 0x0404
+WM_PSD_GREEKTEXTRECT :: 0x0404
+CBEM_SETITEMA :: 0x0405
+PBM_STEPIT :: 0x0405
+TB_INDETERMINATE :: 0x0405
+TBM_SETPOS :: 0x0405
+TTM_DELTOOLA :: 0x0405
+WM_PSD_ENVSTAMPRECT :: 0x0405
+CBEM_GETCOMBOCONTROL :: 0x0406
+PBM_SETRANGE32 :: 0x0406
+RB_SETBANDINFOA :: 0x0406
+SB_GETPARTS :: 0x0406
+TB_MARKBUTTON :: 0x0406
+TBM_SETRANGE :: 0x0406
+TTM_NEWTOOLRECTA :: 0x0406
+WM_PSD_YAFULLPAGERECT :: 0x0406
+CBEM_GETEDITCONTROL :: 0x0407
+PBM_GETRANGE :: 0x0407
+RB_SETPARENT :: 0x0407
+SB_GETBORDERS :: 0x0407
+TBM_SETRANGEMIN :: 0x0407
+TTM_RELAYEVENT :: 0x0407
+CBEM_SETEXSTYLE :: 0x0408
+PBM_GETPOS :: 0x0408
+RB_HITTEST :: 0x0408
+SB_SETMINHEIGHT :: 0x0408
+TBM_SETRANGEMAX :: 0x0408
+TTM_GETTOOLINFOA :: 0x0408
+CBEM_GETEXSTYLE :: 0x0409
+CBEM_GETEXTENDEDSTYLE :: 0x0409
+PBM_SETBARCOLOR :: 0x0409
+RB_GETRECT :: 0x0409
+SB_SIMPLE :: 0x0409
+TB_ISBUTTONENABLED :: 0x0409
+TBM_CLEARTICS :: 0x0409
+TTM_SETTOOLINFOA :: 0x0409
+CBEM_HASEDITCHANGED :: 0x040a
+RB_INSERTBANDW :: 0x040a
+SB_GETRECT :: 0x040a
+TB_ISBUTTONCHECKED :: 0x040a
+TBM_SETSEL :: 0x040a
+TTM_HITTESTA :: 0x040a
+WIZ_QUERYNUMPAGES :: 0x040a
+CBEM_INSERTITEMW :: 0x040b
+RB_SETBANDINFOW :: 0x040b
+SB_SETTEXTW :: 0x040b
+TB_ISBUTTONPRESSED :: 0x040b
+TBM_SETSELSTART :: 0x040b
+TTM_GETTEXTA :: 0x040b
+WIZ_NEXT :: 0x040b
+CBEM_SETITEMW :: 0x040c
+RB_GETBANDCOUNT :: 0x040c
+SB_GETTEXTLENGTHW :: 0x040c
+TB_ISBUTTONHIDDEN :: 0x040c
+TBM_SETSELEND :: 0x040c
+TTM_UPDATETIPTEXTA :: 0x040c
+WIZ_PREV :: 0x040c
+CBEM_GETITEMW :: 0x040d
+RB_GETROWCOUNT :: 0x040d
+SB_GETTEXTW :: 0x040d
+TB_ISBUTTONINDETERMINATE :: 0x040d
+TTM_GETTOOLCOUNT :: 0x040d
+CBEM_SETEXTENDEDSTYLE :: 0x040e
+RB_GETROWHEIGHT :: 0x040e
+SB_ISSIMPLE :: 0x040e
+TB_ISBUTTONHIGHLIGHTED :: 0x040e
+TBM_GETPTICS :: 0x040e
+TTM_ENUMTOOLSA :: 0x040e
+SB_SETICON :: 0x040f
+TBM_GETTICPOS :: 0x040f
+TTM_GETCURRENTTOOLA :: 0x040f
+RB_IDTOINDEX :: 0x0410
+SB_SETTIPTEXTA :: 0x0410
+TBM_GETNUMTICS :: 0x0410
+TTM_WINDOWFROMPOINT :: 0x0410
+RB_GETTOOLTIPS :: 0x0411
+SB_SETTIPTEXTW :: 0x0411
+TBM_GETSELSTART :: 0x0411
+TB_SETSTATE :: 0x0411
+TTM_TRACKACTIVATE :: 0x0411
+RB_SETTOOLTIPS :: 0x0412
+SB_GETTIPTEXTA :: 0x0412
+TB_GETSTATE :: 0x0412
+TBM_GETSELEND :: 0x0412
+TTM_TRACKPOSITION :: 0x0412
+RB_SETBKCOLOR :: 0x0413
+SB_GETTIPTEXTW :: 0x0413
+TB_ADDBITMAP :: 0x0413
+TBM_CLEARSEL :: 0x0413
+TTM_SETTIPBKCOLOR :: 0x0413
+RB_GETBKCOLOR :: 0x0414
+SB_GETICON :: 0x0414
+TB_ADDBUTTONSA :: 0x0414
+TBM_SETTICFREQ :: 0x0414
+TTM_SETTIPTEXTCOLOR :: 0x0414
+RB_SETTEXTCOLOR :: 0x0415
+TB_INSERTBUTTONA :: 0x0415
+TBM_SETPAGESIZE :: 0x0415
+TTM_GETDELAYTIME :: 0x0415
+RB_GETTEXTCOLOR :: 0x0416
+TB_DELETEBUTTON :: 0x0416
+TBM_GETPAGESIZE :: 0x0416
+TTM_GETTIPBKCOLOR :: 0x0416
+RB_SIZETORECT :: 0x0417
+TB_GETBUTTON :: 0x0417
+TBM_SETLINESIZE :: 0x0417
+TTM_GETTIPTEXTCOLOR :: 0x0417
+RB_BEGINDRAG :: 0x0418
+TB_BUTTONCOUNT :: 0x0418
+TBM_GETLINESIZE :: 0x0418
+TTM_SETMAXTIPWIDTH :: 0x0418
+RB_ENDDRAG :: 0x0419
+TB_COMMANDTOINDEX :: 0x0419
+TBM_GETTHUMBRECT :: 0x0419
+TTM_GETMAXTIPWIDTH :: 0x0419
+RB_DRAGMOVE :: 0x041a
+TBM_GETCHANNELRECT :: 0x041a
+TB_SAVERESTOREA :: 0x041a
+TTM_SETMARGIN :: 0x041a
+RB_GETBARHEIGHT :: 0x041b
+TB_CUSTOMIZE :: 0x041b
+TBM_SETTHUMBLENGTH :: 0x041b
+TTM_GETMARGIN :: 0x041b
+RB_GETBANDINFOW :: 0x041c
+TB_ADDSTRINGA :: 0x041c
+TBM_GETTHUMBLENGTH :: 0x041c
+TTM_POP :: 0x041c
+RB_GETBANDINFOA :: 0x041d
+TB_GETITEMRECT :: 0x041d
+TBM_SETTOOLTIPS :: 0x041d
+TTM_UPDATE :: 0x041d
+RB_MINIMIZEBAND :: 0x041e
+TB_BUTTONSTRUCTSIZE :: 0x041e
+TBM_GETTOOLTIPS :: 0x041e
+TTM_GETBUBBLESIZE :: 0x041e
+RB_MAXIMIZEBAND :: 0x041f
+TBM_SETTIPSIDE :: 0x041f
+TB_SETBUTTONSIZE :: 0x041f
+TTM_ADJUSTRECT :: 0x041f
+TBM_SETBUDDY :: 0x0420
+TB_SETBITMAPSIZE :: 0x0420
+TTM_SETTITLEA :: 0x0420
+MSG_FTS_JUMP_VA :: 0x0421
+TB_AUTOSIZE :: 0x0421
+TBM_GETBUDDY :: 0x0421
+TTM_SETTITLEW :: 0x0421
+RB_GETBANDBORDERS :: 0x0422
+MSG_FTS_JUMP_QWORD :: 0x0423
+RB_SHOWBAND :: 0x0423
+TB_GETTOOLTIPS :: 0x0423
+MSG_REINDEX_REQUEST :: 0x0424
+TB_SETTOOLTIPS :: 0x0424
+MSG_FTS_WHERE_IS_IT :: 0x0425
+RB_SETPALETTE :: 0x0425
+TB_SETPARENT :: 0x0425
+RB_GETPALETTE :: 0x0426
+RB_MOVEBAND :: 0x0427
+TB_SETROWS :: 0x0427
+TB_GETROWS :: 0x0428
+TB_GETBITMAPFLAGS :: 0x0429
+TB_SETCMDID :: 0x042a
+RB_PUSHCHEVRON :: 0x042b
+TB_CHANGEBITMAP :: 0x042b
+TB_GETBITMAP :: 0x042c
+MSG_GET_DEFFONT :: 0x042d
+TB_GETBUTTONTEXTA :: 0x042d
+TB_REPLACEBITMAP :: 0x042e
+TB_SETINDENT :: 0x042f
+TB_SETIMAGELIST :: 0x0430
+TB_GETIMAGELIST :: 0x0431
+TB_LOADIMAGES :: 0x0432
+EM_CANPASTE :: 0x0432
+TTM_ADDTOOLW :: 0x0432
+EM_DISPLAYBAND :: 0x0433
+TB_GETRECT :: 0x0433
+TTM_DELTOOLW :: 0x0433
+EM_EXGETSEL :: 0x0434
+TB_SETHOTIMAGELIST :: 0x0434
+TTM_NEWTOOLRECTW :: 0x0434
+EM_EXLIMITTEXT :: 0x0435
+TB_GETHOTIMAGELIST :: 0x0435
+TTM_GETTOOLINFOW :: 0x0435
+EM_EXLINEFROMCHAR :: 0x0436
+TB_SETDISABLEDIMAGELIST :: 0x0436
+TTM_SETTOOLINFOW :: 0x0436
+EM_EXSETSEL :: 0x0437
+TB_GETDISABLEDIMAGELIST :: 0x0437
+TTM_HITTESTW :: 0x0437
+EM_FINDTEXT :: 0x0438
+TB_SETSTYLE :: 0x0438
+TTM_GETTEXTW :: 0x0438
+EM_FORMATRANGE :: 0x0439
+TB_GETSTYLE :: 0x0439
+TTM_UPDATETIPTEXTW :: 0x0439
+EM_GETCHARFORMAT :: 0x043a
+TB_GETBUTTONSIZE :: 0x043a
+TTM_ENUMTOOLSW :: 0x043a
+EM_GETEVENTMASK :: 0x043b
+TB_SETBUTTONWIDTH :: 0x043b
+TTM_GETCURRENTTOOLW :: 0x043b
+EM_GETOLEINTERFACE :: 0x043c
+TB_SETMAXTEXTROWS :: 0x043c
+EM_GETPARAFORMAT :: 0x043d
+TB_GETTEXTROWS :: 0x043d
+EM_GETSELTEXT :: 0x043e
+TB_GETOBJECT :: 0x043e
+EM_HIDESELECTION :: 0x043f
+TB_GETBUTTONINFOW :: 0x043f
+EM_PASTESPECIAL :: 0x0440
+TB_SETBUTTONINFOW :: 0x0440
+EM_REQUESTRESIZE :: 0x0441
+TB_GETBUTTONINFOA :: 0x0441
+EM_SELECTIONTYPE :: 0x0442
+TB_SETBUTTONINFOA :: 0x0442
+EM_SETBKGNDCOLOR :: 0x0443
+TB_INSERTBUTTONW :: 0x0443
+EM_SETCHARFORMAT :: 0x0444
+TB_ADDBUTTONSW :: 0x0444
+EM_SETEVENTMASK :: 0x0445
+TB_HITTEST :: 0x0445
+EM_SETOLECALLBACK :: 0x0446
+TB_SETDRAWTEXTFLAGS :: 0x0446
+EM_SETPARAFORMAT :: 0x0447
+TB_GETHOTITEM :: 0x0447
+EM_SETTARGETDEVICE :: 0x0448
+TB_SETHOTITEM :: 0x0448
+EM_STREAMIN :: 0x0449
+TB_SETANCHORHIGHLIGHT :: 0x0449
+EM_STREAMOUT :: 0x044a
+TB_GETANCHORHIGHLIGHT :: 0x044a
+EM_GETTEXTRANGE :: 0x044b
+TB_GETBUTTONTEXTW :: 0x044b
+EM_FINDWORDBREAK :: 0x044c
+TB_SAVERESTOREW :: 0x044c
+EM_SETOPTIONS :: 0x044d
+TB_ADDSTRINGW :: 0x044d
+EM_GETOPTIONS :: 0x044e
+TB_MAPACCELERATORA :: 0x044e
+EM_FINDTEXTEX :: 0x044f
+TB_GETINSERTMARK :: 0x044f
+EM_GETWORDBREAKPROCEX :: 0x0450
+TB_SETINSERTMARK :: 0x0450
+EM_SETWORDBREAKPROCEX :: 0x0451
+TB_INSERTMARKHITTEST :: 0x0451
+EM_SETUNDOLIMIT :: 0x0452
+TB_MOVEBUTTON :: 0x0452
+TB_GETMAXSIZE :: 0x0453
+EM_REDO :: 0x0454
+TB_SETEXTENDEDSTYLE :: 0x0454
+EM_CANREDO :: 0x0455
+TB_GETEXTENDEDSTYLE :: 0x0455
+EM_GETUNDONAME :: 0x0456
+TB_GETPADDING :: 0x0456
+EM_GETREDONAME :: 0x0457
+TB_SETPADDING :: 0x0457
+EM_STOPGROUPTYPING :: 0x0458
+TB_SETINSERTMARKCOLOR :: 0x0458
+EM_SETTEXTMODE :: 0x0459
+TB_GETINSERTMARKCOLOR :: 0x0459
+EM_GETTEXTMODE :: 0x045a
+TB_MAPACCELERATORW :: 0x045a
+EM_AUTOURLDETECT :: 0x045b
+TB_GETSTRINGW :: 0x045b
+EM_GETAUTOURLDETECT :: 0x045c
+TB_GETSTRINGA :: 0x045c
+EM_SETPALETTE :: 0x045d
+EM_GETTEXTEX :: 0x045e
+EM_GETTEXTLENGTHEX :: 0x045f
+EM_SHOWSCROLLBAR :: 0x0460
+EM_SETTEXTEX :: 0x0461
+TAPI_REPLY :: 0x0463
+ACM_OPENA :: 0x0464
+BFFM_SETSTATUSTEXTA :: 0x0464
+CDM_FIRST :: 0x0464
+CDM_GETSPEC :: 0x0464
+EM_SETPUNCTUATION :: 0x0464
+IPM_CLEARADDRESS :: 0x0464
+WM_CAP_UNICODE_START :: 0x0464
+ACM_PLAY :: 0x0465
+BFFM_ENABLEOK :: 0x0465
+CDM_GETFILEPATH :: 0x0465
+EM_GETPUNCTUATION :: 0x0465
+IPM_SETADDRESS :: 0x0465
+PSM_SETCURSEL :: 0x0465
+UDM_SETRANGE :: 0x0465
+WM_CHOOSEFONT_SETLOGFONT :: 0x0465
+ACM_STOP :: 0x0466
+BFFM_SETSELECTIONA :: 0x0466
+CDM_GETFOLDERPATH :: 0x0466
+EM_SETWORDWRAPMODE :: 0x0466
+IPM_GETADDRESS :: 0x0466
+PSM_REMOVEPAGE :: 0x0466
+UDM_GETRANGE :: 0x0466
+WM_CAP_SET_CALLBACK_ERRORW :: 0x0466
+WM_CHOOSEFONT_SETFLAGS :: 0x0466
+ACM_OPENW :: 0x0467
+BFFM_SETSELECTIONW :: 0x0467
+CDM_GETFOLDERIDLIST :: 0x0467
+EM_GETWORDWRAPMODE :: 0x0467
+IPM_SETRANGE :: 0x0467
+PSM_ADDPAGE :: 0x0467
+UDM_SETPOS :: 0x0467
+WM_CAP_SET_CALLBACK_STATUSW :: 0x0467
+BFFM_SETSTATUSTEXTW :: 0x0468
+CDM_SETCONTROLTEXT :: 0x0468
+EM_SETIMECOLOR :: 0x0468
+IPM_SETFOCUS :: 0x0468
+PSM_CHANGED :: 0x0468
+UDM_GETPOS :: 0x0468
+CDM_HIDECONTROL :: 0x0469
+EM_GETIMECOLOR :: 0x0469
+IPM_ISBLANK :: 0x0469
+PSM_RESTARTWINDOWS :: 0x0469
+UDM_SETBUDDY :: 0x0469
+CDM_SETDEFEXT :: 0x046a
+EM_SETIMEOPTIONS :: 0x046a
+PSM_REBOOTSYSTEM :: 0x046a
+UDM_GETBUDDY :: 0x046a
+EM_GETIMEOPTIONS :: 0x046b
+PSM_CANCELTOCLOSE :: 0x046b
+UDM_SETACCEL :: 0x046b
+EM_CONVPOSITION :: 0x046c
+PSM_QUERYSIBLINGS :: 0x046c
+UDM_GETACCEL :: 0x046c
+MCIWNDM_GETZOOM :: 0x046d
+PSM_UNCHANGED :: 0x046d
+UDM_SETBASE :: 0x046d
+PSM_APPLY :: 0x046e
+UDM_GETBASE :: 0x046e
+PSM_SETTITLEA :: 0x046f
+UDM_SETRANGE32 :: 0x046f
+PSM_SETWIZBUTTONS :: 0x0470
+UDM_GETRANGE32 :: 0x0470
+WM_CAP_DRIVER_GET_NAMEW :: 0x0470
+PSM_PRESSBUTTON :: 0x0471
+UDM_SETPOS32 :: 0x0471
+WM_CAP_DRIVER_GET_VERSIONW :: 0x0471
+PSM_SETCURSELID :: 0x0472
+UDM_GETPOS32 :: 0x0472
+PSM_SETFINISHTEXTA :: 0x0473
+PSM_GETTABCONTROL :: 0x0474
+PSM_ISDIALOGMESSAGE :: 0x0475
+MCIWNDM_REALIZE :: 0x0476
+PSM_GETCURRENTPAGEHWND :: 0x0476
+MCIWNDM_SETTIMEFORMATA :: 0x0477
+PSM_INSERTPAGE :: 0x0477
+EM_SETLANGOPTIONS :: 0x0478
+MCIWNDM_GETTIMEFORMATA :: 0x0478
+PSM_SETTITLEW :: 0x0478
+WM_CAP_FILE_SET_CAPTURE_FILEW :: 0x0478
+EM_GETLANGOPTIONS :: 0x0479
+MCIWNDM_VALIDATEMEDIA :: 0x0479
+PSM_SETFINISHTEXTW :: 0x0479
+WM_CAP_FILE_GET_CAPTURE_FILEW :: 0x0479
+EM_GETIMECOMPMODE :: 0x047a
+EM_FINDTEXTW :: 0x047b
+MCIWNDM_PLAYTO :: 0x047b
+WM_CAP_FILE_SAVEASW :: 0x047b
+EM_FINDTEXTEXW :: 0x047c
+MCIWNDM_GETFILENAMEA :: 0x047c
+EM_RECONVERSION :: 0x047d
+MCIWNDM_GETDEVICEA :: 0x047d
+PSM_SETHEADERTITLEA :: 0x047d
+WM_CAP_FILE_SAVEDIBW :: 0x047d
+EM_SETIMEMODEBIAS :: 0x047e
+MCIWNDM_GETPALETTE :: 0x047e
+PSM_SETHEADERTITLEW :: 0x047e
+EM_GETIMEMODEBIAS :: 0x047f
+MCIWNDM_SETPALETTE :: 0x047f
+PSM_SETHEADERSUBTITLEA :: 0x047f
+MCIWNDM_GETERRORA :: 0x0480
+PSM_SETHEADERSUBTITLEW :: 0x0480
+PSM_HWNDTOINDEX :: 0x0481
+PSM_INDEXTOHWND :: 0x0482
+MCIWNDM_SETINACTIVETIMER :: 0x0483
+PSM_PAGETOINDEX :: 0x0483
+PSM_INDEXTOPAGE :: 0x0484
+DL_BEGINDRAG :: 0x0485
+MCIWNDM_GETINACTIVETIMER :: 0x0485
+PSM_IDTOINDEX :: 0x0485
+DL_DRAGGING :: 0x0486
+PSM_INDEXTOID :: 0x0486
+DL_DROPPED :: 0x0487
+PSM_GETRESULT :: 0x0487
+DL_CANCELDRAG :: 0x0488
+PSM_RECALCPAGESIZES :: 0x0488
+MCIWNDM_GET_SOURCE :: 0x048c
+MCIWNDM_PUT_SOURCE :: 0x048d
+MCIWNDM_GET_DEST :: 0x048e
+MCIWNDM_PUT_DEST :: 0x048f
+MCIWNDM_CAN_PLAY :: 0x0490
+MCIWNDM_CAN_WINDOW :: 0x0491
+MCIWNDM_CAN_RECORD :: 0x0492
+MCIWNDM_CAN_SAVE :: 0x0493
+MCIWNDM_CAN_EJECT :: 0x0494
+MCIWNDM_CAN_CONFIG :: 0x0495
+IE_GETINK :: 0x0496
+IE_MSGFIRST :: 0x0496
+MCIWNDM_PALETTEKICK :: 0x0496
+IE_SETINK :: 0x0497
+IE_GETPENTIP :: 0x0498
+IE_SETPENTIP :: 0x0499
+IE_GETERASERTIP :: 0x049a
+IE_SETERASERTIP :: 0x049b
+IE_GETBKGND :: 0x049c
+IE_SETBKGND :: 0x049d
+IE_GETGRIDORIGIN :: 0x049e
+IE_SETGRIDORIGIN :: 0x049f
+IE_GETGRIDPEN :: 0x04a0
+IE_SETGRIDPEN :: 0x04a1
+IE_GETGRIDSIZE :: 0x04a2
+IE_SETGRIDSIZE :: 0x04a3
+IE_GETMODE :: 0x04a4
+IE_SETMODE :: 0x04a5
+IE_GETINKRECT :: 0x04a6
+WM_CAP_SET_MCI_DEVICEW :: 0x04a6
+WM_CAP_GET_MCI_DEVICEW :: 0x04a7
+WM_CAP_PAL_OPENW :: 0x04b4
+WM_CAP_PAL_SAVEW :: 0x04b5
+IE_GETAPPDATA :: 0x04b8
+IE_SETAPPDATA :: 0x04b9
+IE_GETDRAWOPTS :: 0x04ba
+IE_SETDRAWOPTS :: 0x04bb
+IE_GETFORMAT :: 0x04bc
+IE_SETFORMAT :: 0x04bd
+IE_GETINKINPUT :: 0x04be
+IE_SETINKINPUT :: 0x04bf
+IE_GETNOTIFY :: 0x04c0
+IE_SETNOTIFY :: 0x04c1
+IE_GETRECOG :: 0x04c2
+IE_SETRECOG :: 0x04c3
+IE_GETSECURITY :: 0x04c4
+IE_SETSECURITY :: 0x04c5
+IE_GETSEL :: 0x04c6
+IE_SETSEL :: 0x04c7
+CDM_LAST :: 0x04c8
+EM_SETBIDIOPTIONS :: 0x04c8
+IE_DOCOMMAND :: 0x04c8
+MCIWNDM_NOTIFYMODE :: 0x04c8
+EM_GETBIDIOPTIONS :: 0x04c9
+IE_GETCOMMAND :: 0x04c9
+EM_SETTYPOGRAPHYOPTIONS :: 0x04ca
+IE_GETCOUNT :: 0x04ca
+EM_GETTYPOGRAPHYOPTIONS :: 0x04cb
+IE_GETGESTURE :: 0x04cb
+MCIWNDM_NOTIFYMEDIA :: 0x04cb
+EM_SETEDITSTYLE :: 0x04cc
+IE_GETMENU :: 0x04cc
+EM_GETEDITSTYLE :: 0x04cd
+IE_GETPAINTDC :: 0x04cd
+MCIWNDM_NOTIFYERROR :: 0x04cd
+IE_GETPDEVENT :: 0x04ce
+IE_GETSELCOUNT :: 0x04cf
+IE_GETSELITEMS :: 0x04d0
+IE_GETSTYLE :: 0x04d1
+MCIWNDM_SETTIMEFORMATW :: 0x04db
+EM_OUTLINE :: 0x04dc
+MCIWNDM_GETTIMEFORMATW :: 0x04dc
+EM_GETSCROLLPOS :: 0x04dd
+EM_SETSCROLLPOS :: 0x04de
+EM_SETFONTSIZE :: 0x04df
+EM_GETZOOM :: 0x04e0
+MCIWNDM_GETFILENAMEW :: 0x04e0
+EM_SETZOOM :: 0x04e1
+MCIWNDM_GETDEVICEW :: 0x04e1
+EM_GETVIEWKIND :: 0x04e2
+EM_SETVIEWKIND :: 0x04e3
+EM_GETPAGE :: 0x04e4
+MCIWNDM_GETERRORW :: 0x04e4
+EM_SETPAGE :: 0x04e5
+EM_GETHYPHENATEINFO :: 0x04e6
+EM_SETHYPHENATEINFO :: 0x04e7
+EM_GETPAGEROTATE :: 0x04eb
+EM_SETPAGEROTATE :: 0x04ec
+EM_GETCTFMODEBIAS :: 0x04ed
+EM_SETCTFMODEBIAS :: 0x04ee
+EM_GETCTFOPENSTATUS :: 0x04f0
+EM_SETCTFOPENSTATUS :: 0x04f1
+EM_GETIMECOMPTEXT :: 0x04f2
+EM_ISIME :: 0x04f3
+EM_GETIMEPROPERTY :: 0x04f4
+EM_GETQUERYRTFOBJ :: 0x050d
+EM_SETQUERYRTFOBJ :: 0x050e
+FM_GETFOCUS :: 0x0600
+FM_GETDRIVEINFOA :: 0x0601
+FM_GETSELCOUNT :: 0x0602
+FM_GETSELCOUNTLFN :: 0x0603
+FM_GETFILESELA :: 0x0604
+FM_GETFILESELLFNA :: 0x0605
+FM_REFRESH_WINDOWS :: 0x0606
+FM_RELOAD_EXTENSIONS :: 0x0607
+FM_GETDRIVEINFOW :: 0x0611
+FM_GETFILESELW :: 0x0614
+FM_GETFILESELLFNW :: 0x0615
+WLX_WM_SAS :: 0x0659
+SM_GETSELCOUNT :: 0x07e8
+UM_GETSELCOUNT :: 0x07e8
+WM_CPL_LAUNCH :: 0x07e8
+SM_GETSERVERSELA :: 0x07e9
+UM_GETUSERSELA :: 0x07e9
+WM_CPL_LAUNCHED :: 0x07e9
+SM_GETSERVERSELW :: 0x07ea
+UM_GETUSERSELW :: 0x07ea
+SM_GETCURFOCUSA :: 0x07eb
+UM_GETGROUPSELA :: 0x07eb
+SM_GETCURFOCUSW :: 0x07ec
+UM_GETGROUPSELW :: 0x07ec
+SM_GETOPTIONS :: 0x07ed
+UM_GETCURFOCUSA :: 0x07ed
+UM_GETCURFOCUSW :: 0x07ee
+UM_GETOPTIONS :: 0x07ef
+UM_GETOPTIONS2 :: 0x07f0
+LVM_FIRST :: 0x1000
+LVM_GETBKCOLOR :: 0x1000
+LVM_SETBKCOLOR :: 0x1001
+LVM_GETIMAGELIST :: 0x1002
+LVM_SETIMAGELIST :: 0x1003
+LVM_GETITEMCOUNT :: 0x1004
+LVM_GETITEMA :: 0x1005
+LVM_SETITEMA :: 0x1006
+LVM_INSERTITEMA :: 0x1007
+LVM_DELETEITEM :: 0x1008
+LVM_DELETEALLITEMS :: 0x1009
+LVM_GETCALLBACKMASK :: 0x100a
+LVM_SETCALLBACKMASK :: 0x100b
+LVM_GETNEXTITEM :: 0x100c
+LVM_FINDITEMA :: 0x100d
+LVM_GETITEMRECT :: 0x100e
+LVM_SETITEMPOSITION :: 0x100f
+LVM_GETITEMPOSITION :: 0x1010
+LVM_GETSTRINGWIDTHA :: 0x1011
+LVM_HITTEST :: 0x1012
+LVM_ENSUREVISIBLE :: 0x1013
+LVM_SCROLL :: 0x1014
+LVM_REDRAWITEMS :: 0x1015
+LVM_ARRANGE :: 0x1016
+LVM_EDITLABELA :: 0x1017
+LVM_GETEDITCONTROL :: 0x1018
+LVM_GETCOLUMNA :: 0x1019
+LVM_SETCOLUMNA :: 0x101a
+LVM_INSERTCOLUMNA :: 0x101b
+LVM_DELETECOLUMN :: 0x101c
+LVM_GETCOLUMNWIDTH :: 0x101d
+LVM_SETCOLUMNWIDTH :: 0x101e
+LVM_GETHEADER :: 0x101f
+LVM_CREATEDRAGIMAGE :: 0x1021
+LVM_GETVIEWRECT :: 0x1022
+LVM_GETTEXTCOLOR :: 0x1023
+LVM_SETTEXTCOLOR :: 0x1024
+LVM_GETTEXTBKCOLOR :: 0x1025
+LVM_SETTEXTBKCOLOR :: 0x1026
+LVM_GETTOPINDEX :: 0x1027
+LVM_GETCOUNTPERPAGE :: 0x1028
+LVM_GETORIGIN :: 0x1029
+LVM_UPDATE :: 0x102a
+LVM_SETITEMSTATE :: 0x102b
+LVM_GETITEMSTATE :: 0x102c
+LVM_GETITEMTEXTA :: 0x102d
+LVM_SETITEMTEXTA :: 0x102e
+LVM_SETITEMCOUNT :: 0x102f
+LVM_SORTITEMS :: 0x1030
+LVM_SETITEMPOSITION32 :: 0x1031
+LVM_GETSELECTEDCOUNT :: 0x1032
+LVM_GETITEMSPACING :: 0x1033
+LVM_GETISEARCHSTRINGA :: 0x1034
+LVM_SETICONSPACING :: 0x1035
+LVM_SETEXTENDEDLISTVIEWSTYLE :: 0x1036
+LVM_GETEXTENDEDLISTVIEWSTYLE :: 0x1037
+LVM_GETSUBITEMRECT :: 0x1038
+LVM_SUBITEMHITTEST :: 0x1039
+LVM_SETCOLUMNORDERARRAY :: 0x103a
+LVM_GETCOLUMNORDERARRAY :: 0x103b
+LVM_SETHOTITEM :: 0x103c
+LVM_GETHOTITEM :: 0x103d
+LVM_SETHOTCURSOR :: 0x103e
+LVM_GETHOTCURSOR :: 0x103f
+LVM_APPROXIMATEVIEWRECT :: 0x1040
+LVM_SETWORKAREAS :: 0x1041
+LVM_GETSELECTIONMARK :: 0x1042
+LVM_SETSELECTIONMARK :: 0x1043
+LVM_SETBKIMAGEA :: 0x1044
+LVM_GETBKIMAGEA :: 0x1045
+LVM_GETWORKAREAS :: 0x1046
+LVM_SETHOVERTIME :: 0x1047
+LVM_GETHOVERTIME :: 0x1048
+LVM_GETNUMBEROFWORKAREAS :: 0x1049
+LVM_SETTOOLTIPS :: 0x104a
+LVM_GETITEMW :: 0x104b
+LVM_SETITEMW :: 0x104c
+LVM_INSERTITEMW :: 0x104d
+LVM_GETTOOLTIPS :: 0x104e
+LVM_FINDITEMW :: 0x1053
+LVM_GETSTRINGWIDTHW :: 0x1057
+LVM_GETCOLUMNW :: 0x105f
+LVM_SETCOLUMNW :: 0x1060
+LVM_INSERTCOLUMNW :: 0x1061
+LVM_GETITEMTEXTW :: 0x1073
+LVM_SETITEMTEXTW :: 0x1074
+LVM_GETISEARCHSTRINGW :: 0x1075
+LVM_EDITLABELW :: 0x1076
+LVM_GETBKIMAGEW :: 0x108b
+LVM_SETSELECTEDCOLUMN :: 0x108c
+LVM_SETTILEWIDTH :: 0x108d
+LVM_SETVIEW :: 0x108e
+LVM_GETVIEW :: 0x108f
+LVM_INSERTGROUP :: 0x1091
+LVM_SETGROUPINFO :: 0x1093
+LVM_GETGROUPINFO :: 0x1095
+LVM_REMOVEGROUP :: 0x1096
+LVM_MOVEGROUP :: 0x1097
+LVM_MOVEITEMTOGROUP :: 0x109a
+LVM_SETGROUPMETRICS :: 0x109b
+LVM_GETGROUPMETRICS :: 0x109c
+LVM_ENABLEGROUPVIEW :: 0x109d
+LVM_SORTGROUPS :: 0x109e
+LVM_INSERTGROUPSORTED :: 0x109f
+LVM_REMOVEALLGROUPS :: 0x10a0
+LVM_HASGROUP :: 0x10a1
+LVM_SETTILEVIEWINFO :: 0x10a2
+LVM_GETTILEVIEWINFO :: 0x10a3
+LVM_SETTILEINFO :: 0x10a4
+LVM_GETTILEINFO :: 0x10a5
+LVM_SETINSERTMARK :: 0x10a6
+LVM_GETINSERTMARK :: 0x10a7
+LVM_INSERTMARKHITTEST :: 0x10a8
+LVM_GETINSERTMARKRECT :: 0x10a9
+LVM_SETINSERTMARKCOLOR :: 0x10aa
+LVM_GETINSERTMARKCOLOR :: 0x10ab
+LVM_SETINFOTIP :: 0x10ad
+LVM_GETSELECTEDCOLUMN :: 0x10ae
+LVM_ISGROUPVIEWENABLED :: 0x10af
+LVM_GETOUTLINECOLOR :: 0x10b0
+LVM_SETOUTLINECOLOR :: 0x10b1
+LVM_CANCELEDITLABEL :: 0x10b3
+LVM_MAPINDEXTOID :: 0x10b4
+LVM_MAPIDTOINDEX :: 0x10b5
+LVM_ISITEMVISIBLE :: 0x10b6
+LVM_GETEMPTYTEXT :: 0x10cc
+LVM_GETFOOTERRECT :: 0x10cd
+LVM_GETFOOTERINFO :: 0x10ce
+LVM_GETFOOTERITEMRECT :: 0x10cf
+LVM_GETFOOTERITEM :: 0x10d0
+LVM_GETITEMINDEXRECT :: 0x10d1
+LVM_SETITEMINDEXSTATE :: 0x10d2
+LVM_GETNEXTITEMINDEX :: 0x10d3
+OCM__BASE :: 0x2000
+LVM_SETUNICODEFORMAT :: 0x2005
+LVM_GETUNICODEFORMAT :: 0x2006
+OCM_CTLCOLOR :: 0x2019
+OCM_DRAWITEM :: 0x202b
+OCM_MEASUREITEM :: 0x202c
+OCM_DELETEITEM :: 0x202d
+OCM_VKEYTOITEM :: 0x202e
+OCM_CHARTOITEM :: 0x202f
+OCM_COMPAREITEM :: 0x2039
+OCM_NOTIFY :: 0x204e
+OCM_COMMAND :: 0x2111
+OCM_HSCROLL :: 0x2114
+OCM_VSCROLL :: 0x2115
+OCM_CTLCOLORMSGBOX :: 0x2132
+OCM_CTLCOLOREDIT :: 0x2133
+OCM_CTLCOLORLISTBOX :: 0x2134
+OCM_CTLCOLORBTN :: 0x2135
+OCM_CTLCOLORDLG :: 0x2136
+OCM_CTLCOLORSCROLLBAR :: 0x2137
+OCM_CTLCOLORSTATIC :: 0x2138
+OCM_PARENTNOTIFY :: 0x2210
+WM_APP :: 0x8000
+WM_RASDIALEVENT :: 0xcccd
diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin
index 560f23956..e812c410a 100644
--- a/core/testing/runner_windows.odin
+++ b/core/testing/runner_windows.odin
@@ -21,7 +21,7 @@ sema_wait :: proc "contextless" (s: ^Sema) {
win32.WaitOnAddress(&s.count, &original_count, size_of(original_count), win32.INFINITE)
original_count = s.count
}
- if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) {
+ if original_count == intrinsics.atomic_compare_exchange_strong(&s.count, original_count-1, original_count) {
return
}
}
@@ -46,7 +46,7 @@ sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration)
}
original_count = s.count
}
- if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) {
+ if original_count == intrinsics.atomic_compare_exchange_strong(&s.count, original_count-1, original_count) {
return true
}
}
diff --git a/core/thread/thread_pool.odin b/core/thread/thread_pool.odin
index 37ee4fa98..3f782cf73 100644
--- a/core/thread/thread_pool.odin
+++ b/core/thread/thread_pool.odin
@@ -1,67 +1,75 @@
package thread
+/*
+ thread.Pool
+ Copyright 2022 eisbehr
+ Made available under Odin's BSD-3 license.
+*/
+
import "core:intrinsics"
import "core:sync"
import "core:mem"
-Task_Status :: enum i32 {
- Ready,
- Busy,
- Waiting,
- Term,
-}
-
-Task_Proc :: #type proc(task: ^Task)
+Task_Proc :: #type proc(task: Task)
Task :: struct {
- procedure: Task_Proc,
- data: rawptr,
+ procedure: Task_Proc,
+ data: rawptr,
user_index: int,
+ allocator: mem.Allocator,
}
-Task_Id :: distinct i32
-INVALID_TASK_ID :: Task_Id(-1)
-
-
+// Do not access the pool's members directly while the pool threads are running,
+// since they use different kinds of locking and mutual exclusion devices.
+// Careless access can and will lead to nasty bugs. Once initialized, the
+// pool's memory address is not allowed to change until it is destroyed.
Pool :: struct {
- allocator: mem.Allocator,
- mutex: sync.Mutex,
- sem_available: sync.Semaphore,
- processing_task_count: int, // atomic
- is_running: bool,
+ allocator: mem.Allocator,
+ mutex: sync.Mutex,
+ sem_available: sync.Sema,
+
+ // the following values are atomic
+ num_waiting: int,
+ num_in_processing: int,
+ num_outstanding: int, // num_waiting + num_in_processing
+ num_done: int,
+ // end of atomics
+
+ is_running: bool,
threads: []^Thread,
- tasks: [dynamic]Task,
+ tasks: [dynamic]Task,
+ tasks_done: [dynamic]Task,
}
-pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator) {
- worker_thread_internal :: proc(t: ^Thread) {
- pool := (^Pool)(t.data)
-
- for pool.is_running {
- sync.semaphore_wait_for(&pool.sem_available)
-
- if task, ok := pool_try_and_pop_task(pool); ok {
- pool_do_work(pool, &task)
- }
- }
-
- sync.semaphore_post(&pool.sem_available, 1)
- }
-
-
+// Once initialized, the pool's memory address is not allowed to change until
+// it is destroyed. If thread_count < 1, thread count 1 will be used.
+//
+// The thread pool requires an allocator which it either owns, or which is thread safe.
+pool_init :: proc(pool: ^Pool, thread_count: int, allocator: mem.Allocator) {
context.allocator = allocator
pool.allocator = allocator
- pool.tasks = make([dynamic]Task)
- pool.threads = make([]^Thread, thread_count)
+ pool.tasks = make([dynamic]Task)
+ pool.tasks_done = make([dynamic]Task)
+ pool.threads = make([]^Thread, max(thread_count, 1))
- sync.mutex_init(&pool.mutex)
- sync.semaphore_init(&pool.sem_available)
pool.is_running = true
for _, i in pool.threads {
- t := create(worker_thread_internal)
+ t := create(proc(t: ^Thread) {
+ pool := (^Pool)(t.data)
+
+ for intrinsics.atomic_load(&pool.is_running) {
+ sync.wait(&pool.sem_available)
+
+ if task, ok := pool_pop_waiting(pool); ok {
+ pool_do_work(pool, task)
+ }
+ }
+
+ sync.post(&pool.sem_available, 1)
+ })
t.user_index = i
t.data = pool
pool.threads[i] = t
@@ -70,15 +78,13 @@ pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator
pool_destroy :: proc(pool: ^Pool) {
delete(pool.tasks)
+ delete(pool.tasks_done)
- for thread in &pool.threads {
- destroy(thread)
+ for t in &pool.threads {
+ destroy(t)
}
delete(pool.threads, pool.allocator)
-
- sync.mutex_destroy(&pool.mutex)
- sync.semaphore_destroy(&pool.sem_available)
}
pool_start :: proc(pool: ^Pool) {
@@ -87,10 +93,12 @@ pool_start :: proc(pool: ^Pool) {
}
}
+// Finish tasks that have already started processing, then shut down all pool
+// threads. Might leave over waiting tasks, any memory allocated for the
+// user data of those tasks will not be freed.
pool_join :: proc(pool: ^Pool) {
- pool.is_running = false
-
- sync.semaphore_post(&pool.sem_available, len(pool.threads))
+ intrinsics.atomic_store(&pool.is_running, false)
+ sync.post(&pool.sem_available, len(pool.threads))
yield()
@@ -99,53 +107,112 @@ pool_join :: proc(pool: ^Pool) {
}
}
-pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
- sync.mutex_lock(&pool.mutex)
- defer sync.mutex_unlock(&pool.mutex)
+// Add a task to the thread pool.
+//
+// Tasks can be added from any thread, not just the thread that created
+// the thread pool. You can even add tasks from inside other tasks.
+//
+// Each task also needs an allocator which it either owns, or which is thread
+// safe. By default, allocations in the task are disabled by use of the
+// nil_allocator.
+pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0, allocator := context.allocator) {
+ sync.guard(&pool.mutex)
- task: Task
- task.procedure = procedure
- task.data = data
- task.user_index = user_index
-
- append(&pool.tasks, task)
- sync.semaphore_post(&pool.sem_available, 1)
+ append(&pool.tasks, Task{
+ procedure = procedure,
+ data = data,
+ user_index = user_index,
+ allocator = allocator,
+ })
+ intrinsics.atomic_add(&pool.num_waiting, 1)
+ intrinsics.atomic_add(&pool.num_outstanding, 1)
+ sync.post(&pool.sem_available, 1)
}
-pool_try_and_pop_task :: proc(pool: ^Pool) -> (task: Task, got_task: bool = false) {
- if sync.mutex_try_lock(&pool.mutex) {
- if len(pool.tasks) != 0 {
- intrinsics.atomic_add(&pool.processing_task_count, 1)
- task = pop_front(&pool.tasks)
- got_task = true
- }
- sync.mutex_unlock(&pool.mutex)
+// Number of tasks waiting to be processed. Only informational, mostly for
+// debugging. Don't rely on this value being consistent with other num_*
+// values.
+pool_num_waiting :: #force_inline proc(pool: ^Pool) -> int {
+ return intrinsics.atomic_load(&pool.num_waiting)
+}
+
+// Number of tasks currently being processed. Only informational, mostly for
+// debugging. Don't rely on this value being consistent with other num_*
+// values.
+pool_num_in_processing :: #force_inline proc(pool: ^Pool) -> int {
+ return intrinsics.atomic_load(&pool.num_in_processing)
+}
+
+// Outstanding tasks are all tasks that are not done, that is, tasks that are
+// waiting, as well as tasks that are currently being processed. Only
+// informational, mostly for debugging. Don't rely on this value being
+// consistent with other num_* values.
+pool_num_outstanding :: #force_inline proc(pool: ^Pool) -> int {
+ return intrinsics.atomic_load(&pool.num_outstanding)
+}
+
+// Number of tasks which are done processing. Only informational, mostly for
+// debugging. Don't rely on this value being consistent with other num_*
+// values.
+pool_num_done :: #force_inline proc(pool: ^Pool) -> int {
+ return intrinsics.atomic_load(&pool.num_done)
+}
+
+// If tasks are only being added from one thread, and this procedure is being
+// called from that same thread, it will reliably tell if the thread pool is
+// empty or not. Empty in this case means there are no tasks waiting, being
+// processed, or _done_.
+pool_is_empty :: #force_inline proc(pool: ^Pool) -> bool {
+ return pool_num_outstanding(pool) == 0 && pool_num_done(pool) == 0
+}
+
+// Mostly for internal use.
+pool_pop_waiting :: proc(pool: ^Pool) -> (task: Task, got_task: bool) {
+ sync.guard(&pool.mutex)
+
+ if len(pool.tasks) != 0 {
+ intrinsics.atomic_sub(&pool.num_waiting, 1)
+ intrinsics.atomic_add(&pool.num_in_processing, 1)
+ task = pop_front(&pool.tasks)
+ got_task = true
}
+
return
}
+// Use this to take out finished tasks.
+pool_pop_done :: proc(pool: ^Pool) -> (task: Task, got_task: bool) {
+ sync.guard(&pool.mutex)
-pool_do_work :: proc(pool: ^Pool, task: ^Task) {
- task.procedure(task)
- intrinsics.atomic_sub(&pool.processing_task_count, 1)
-}
-
-
-pool_wait_and_process :: proc(pool: ^Pool) {
- for len(pool.tasks) != 0 || intrinsics.atomic_load(&pool.processing_task_count) != 0 {
- if task, ok := pool_try_and_pop_task(pool); ok {
- pool_do_work(pool, &task)
- }
-
- // Safety kick
- if len(pool.tasks) != 0 && intrinsics.atomic_load(&pool.processing_task_count) == 0 {
- sync.mutex_lock(&pool.mutex)
- sync.semaphore_post(&pool.sem_available, len(pool.tasks))
- sync.mutex_unlock(&pool.mutex)
- }
-
- yield()
+ if len(pool.tasks_done) != 0 {
+ task = pop_front(&pool.tasks_done)
+ got_task = true
+ intrinsics.atomic_sub(&pool.num_done, 1)
}
+ return
+}
+
+// Mostly for internal use.
+pool_do_work :: proc(pool: ^Pool, task: Task) {
+ {
+ context.allocator = task.allocator
+ task.procedure(task)
+ }
+
+ sync.guard(&pool.mutex)
+
+ append(&pool.tasks_done, task)
+ intrinsics.atomic_add(&pool.num_done, 1)
+ intrinsics.atomic_sub(&pool.num_outstanding, 1)
+ intrinsics.atomic_sub(&pool.num_in_processing, 1)
+}
+
+// Process the rest of the tasks, also use this thread for processing, then join
+// all the pool threads.
+pool_finish :: proc(pool: ^Pool) {
+ for task in pool_pop_waiting(pool) {
+ pool_do_work(pool, task)
+ }
pool_join(pool)
}
diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin
index b6679bbc2..8452df112 100644
--- a/core/thread/thread_unix.odin
+++ b/core/thread/thread_unix.odin
@@ -7,30 +7,19 @@ import "core:intrinsics"
import "core:sync"
import "core:sys/unix"
+Thread_State :: enum u8 {
+ Started,
+ Joined,
+ Done,
+}
+
// NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t.
// Also see core/sys/darwin/mach_darwin.odin/semaphore_t.
Thread_Os_Specific :: struct #align 16 {
unix_thread: unix.pthread_t, // NOTE: very large on Darwin, small on Linux.
-
- // NOTE: pthread has a proc to query this, but it is marked
- // as non-portable ("np") so we do this instead.
- done: bool,
-
- // since libpthread doesn't seem to have a way to create a thread
- // in a suspended state, we have it wait on this gate, which we
- // signal to start it.
- // destroyed after thread is started.
- start_gate: sync.Condition,
- start_mutex: sync.Mutex,
-
- // if true, the thread has been started and the start_gate has been destroyed.
- started: bool,
-
- // NOTE: with pthreads, it is undefined behavior for multiple threads
- // to call join on the same thread at the same time.
- // this value is atomically updated to detect this.
- // See the comment in `join`.
- already_joined: bool,
+ cond: sync.Cond,
+ mutex: sync.Mutex,
+ flags: bit_set[Thread_State; u8],
}
//
// Creates a thread which will run the given procedure.
@@ -38,26 +27,31 @@ Thread_Os_Specific :: struct #align 16 {
//
_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
__linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
- context = runtime.default_context()
t := (^Thread)(t)
- sync.condition_wait_for(&t.start_gate)
- sync.condition_destroy(&t.start_gate)
- sync.mutex_destroy(&t.start_mutex)
- t.start_gate = {}
- t.start_mutex = {}
- context = t.init_context.? or_else runtime.default_context()
-
+ context = runtime.default_context()
+
+ sync.lock(&t.mutex)
+
t.id = sync.current_thread_id()
- t.procedure(t)
- if t.init_context == nil {
- if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
- runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
- }
+ for (.Started not_in t.flags) {
+ sync.wait(&t.cond, &t.mutex)
+ }
+
+ init_context := t.init_context
+ context = init_context.? or_else runtime.default_context()
+
+ t.procedure(t)
+
+ intrinsics.atomic_store(&t.flags, t.flags + { .Done })
+
+ sync.unlock(&t.mutex)
+
+ if init_context == nil && context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
+ runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
}
- intrinsics.atomic_store(&t.done, true)
return nil
}
@@ -76,9 +70,6 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
return nil
}
thread.creation_allocator = context.allocator
-
- sync.mutex_init(&thread.start_mutex)
- sync.condition_init(&thread.start_gate, &thread.start_mutex)
// Set thread priority.
policy: i32
@@ -97,65 +88,35 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
res = unix.pthread_attr_setschedparam(&attrs, ¶ms)
assert(res == 0)
+ thread.procedure = procedure
if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
free(thread, thread.creation_allocator)
-
- sync.condition_destroy(&thread.start_gate)
- sync.mutex_destroy(&thread.start_mutex)
return nil
}
- thread.procedure = procedure
-
return thread
}
_start :: proc(t: ^Thread) {
- if intrinsics.atomic_xchg(&t.started, true) {
- return
- }
- sync.condition_signal(&t.start_gate)
+ sync.guard(&t.mutex)
+ t.flags += { .Started }
+ sync.signal(&t.cond)
}
_is_done :: proc(t: ^Thread) -> bool {
- return intrinsics.atomic_load(&t.done)
+ return .Done in intrinsics.atomic_load(&t.flags)
}
_join :: proc(t: ^Thread) {
- if unix.pthread_equal(unix.pthread_self(), t.unix_thread) {
- return
- }
- // if unix.pthread_self().x == t.unix_thread.x do return;
+ sync.guard(&t.mutex)
- // NOTE(tetra): It's apparently UB for multiple threads to join the same thread
- // at the same time.
- // If someone else already did, spin until the thread dies.
- // See note on `already_joined` field.
- // TODO(tetra): I'm not sure if we should do this, or panic, since I'm not
- // sure it makes sense to need to join from multiple threads?
- if intrinsics.atomic_xchg(&t.already_joined, true) {
- for {
- if intrinsics.atomic_load(&t.done) {
- return
- }
- intrinsics.cpu_relax()
- }
- }
-
- // NOTE(tetra): If we're already dead, don't bother calling to pthread_join as that
- // will just return 3 (ESRCH).
- // We do this instead because I don't know if there is a danger
- // that you may join a different thread from the one you called join on,
- // if the thread handle is reused.
- if intrinsics.atomic_load(&t.done) {
+ if .Joined in t.flags || unix.pthread_equal(unix.pthread_self(), t.unix_thread) {
return
}
- ret_val: rawptr
- _ = unix.pthread_join(t.unix_thread, &ret_val)
- if !intrinsics.atomic_load(&t.done) {
- panic("thread not done after join")
- }
+ unix.pthread_join(t.unix_thread, nil)
+
+ t.flags += { .Joined }
}
_join_multiple :: proc(threads: ..^Thread) {
@@ -164,14 +125,12 @@ _join_multiple :: proc(threads: ..^Thread) {
}
}
-
_destroy :: proc(t: ^Thread) {
_join(t)
t.unix_thread = {}
free(t, t.creation_allocator)
}
-
_terminate :: proc(t: ^Thread, exit_code: int) {
// TODO(bill)
}
diff --git a/core/thread/thread_windows.odin b/core/thread/thread_windows.odin
index 428e241d0..68382444c 100644
--- a/core/thread/thread_windows.odin
+++ b/core/thread/thread_windows.odin
@@ -3,13 +3,21 @@
package thread
import "core:runtime"
-import sync "core:sync/sync2"
+import "core:intrinsics"
+import "core:sync"
import win32 "core:sys/windows"
+Thread_State :: enum u8 {
+ Started,
+ Joined,
+ Done,
+}
+
Thread_Os_Specific :: struct {
win32_thread: win32.HANDLE,
win32_thread_id: win32.DWORD,
- done: bool, // see note in `is_done`
+ mutex: sync.Mutex,
+ flags: bit_set[Thread_State; u8],
}
_thread_priority_map := [Thread_Priority]i32{
@@ -26,15 +34,16 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
context = t.init_context.? or_else runtime.default_context()
t.id = sync.current_thread_id()
+
t.procedure(t)
+ intrinsics.atomic_store(&t.flags, t.flags + {.Done})
+
if t.init_context == nil {
if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
}
}
-
- sync.atomic_store(&t.done, true)
return 0
}
@@ -61,23 +70,31 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
return thread
}
-_start :: proc(thread: ^Thread) {
- win32.ResumeThread(thread.win32_thread)
+_start :: proc(t: ^Thread) {
+ sync.guard(&t.mutex)
+ t.flags += {.Started}
+ win32.ResumeThread(t.win32_thread)
}
-_is_done :: proc(using thread: ^Thread) -> bool {
+_is_done :: proc(t: ^Thread) -> bool {
// NOTE(tetra, 2019-10-31): Apparently using wait_for_single_object and
// checking if it didn't time out immediately, is not good enough,
// so we do it this way instead.
- return sync.atomic_load(&done)
+ return .Done in sync.atomic_load(&t.flags)
}
-_join :: proc(using thread: ^Thread) {
- if win32_thread != win32.INVALID_HANDLE {
- win32.WaitForSingleObject(win32_thread, win32.INFINITE)
- win32.CloseHandle(win32_thread)
- win32_thread = win32.INVALID_HANDLE
+_join :: proc(t: ^Thread) {
+ sync.guard(&t.mutex)
+
+ if .Joined in t.flags || t.win32_thread == win32.INVALID_HANDLE {
+ return
}
+
+ win32.WaitForSingleObject(t.win32_thread, win32.INFINITE)
+ win32.CloseHandle(t.win32_thread)
+ t.win32_thread = win32.INVALID_HANDLE
+
+ t.flags += {.Joined}
}
_join_multiple :: proc(threads: ..^Thread) {
diff --git a/core/unicode/utf8/utf8string/string.odin b/core/unicode/utf8/utf8string/string.odin
index 23e2eefc6..86267defb 100644
--- a/core/unicode/utf8/utf8string/string.odin
+++ b/core/unicode/utf8/utf8string/string.odin
@@ -16,140 +16,140 @@ String :: struct {
}
@(private)
-_len :: builtin.len; // helper procedure
+_len :: builtin.len // helper procedure
init :: proc(s: ^String, contents: string) -> ^String {
- s.contents = contents;
- s.byte_pos = 0;
- s.rune_pos = 0;
+ s.contents = contents
+ s.byte_pos = 0
+ s.rune_pos = 0
for i in 0..<_len(contents) {
if contents[i] >= utf8.RUNE_SELF {
- s.rune_count = utf8.rune_count_in_string(contents);
- _, s.width = utf8.decode_rune_in_string(contents);
- s.non_ascii = i;
- return s;
+ s.rune_count = utf8.rune_count_in_string(contents)
+ _, s.width = utf8.decode_rune_in_string(contents)
+ s.non_ascii = i
+ return s
}
}
- s.rune_count = _len(contents);
- s.width = 0;
- s.non_ascii = _len(contents);
- return s;
+ s.rune_count = _len(contents)
+ s.width = 0
+ s.non_ascii = _len(contents)
+ return s
}
to_string :: proc(s: ^String) -> string {
- return s.contents;
+ return s.contents
}
len :: proc(s: ^String) -> int {
- return s.rune_count;
+ return s.rune_count
}
is_ascii :: proc(s: ^String) -> bool {
- return s.width == 0;
+ return s.width == 0
}
at :: proc(s: ^String, i: int, loc := #caller_location) -> (r: rune) {
- runtime.bounds_check_error_loc(loc, i, s.rune_count);
+ runtime.bounds_check_error_loc(loc, i, s.rune_count)
if i < s.non_ascii {
- return rune(s.contents[i]);
+ return rune(s.contents[i])
}
switch i {
case 0:
- r, s.width = utf8.decode_rune_in_string(s.contents);
- s.rune_pos = 0;
- s.byte_pos = 0;
- return;
+ r, s.width = utf8.decode_rune_in_string(s.contents)
+ s.rune_pos = 0
+ s.byte_pos = 0
+ return
case s.rune_count-1:
- r, s.width = utf8.decode_rune_in_string(s.contents);
- s.rune_pos = i;
- s.byte_pos = _len(s.contents) - s.width;
- return;
+ r, s.width = utf8.decode_rune_in_string(s.contents)
+ s.rune_pos = i
+ s.byte_pos = _len(s.contents) - s.width
+ return
case s.rune_pos-1:
- r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos]);
- s.rune_pos = i;
- s.byte_pos -= s.width;
- return;
+ r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos])
+ s.rune_pos = i
+ s.byte_pos -= s.width
+ return
case s.rune_pos+1:
- s.rune_pos = i;
- s.byte_pos += s.width;
- fallthrough;
+ s.rune_pos = i
+ s.byte_pos += s.width
+ fallthrough
case s.rune_pos:
- r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
- return;
+ r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:])
+ return
}
// Linear scan
- scan_forward := true;
+ scan_forward := true
if i < s.rune_pos {
if i < (s.rune_pos-s.non_ascii)/2 {
- s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii;
+ s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii
} else {
- scan_forward = false;
+ scan_forward = false
}
} else if i-s.rune_pos < (s.rune_count-s.rune_pos)/2 {
- // scan_forward = true;
+ // scan_forward = true
} else {
- s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count;
- scan_forward = false;
+ s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count
+ scan_forward = false
}
if scan_forward {
for {
- r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
+ r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:])
if s.rune_pos == i {
- return;
+ return
}
- s.rune_pos += 1;
- s.byte_pos += s.width;
+ s.rune_pos += 1
+ s.byte_pos += s.width
}
} else {
for {
- r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos]);
- s.rune_pos -= 1;
- s.byte_pos -= s.width;
+ r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos])
+ s.rune_pos -= 1
+ s.byte_pos -= s.width
if s.rune_pos == i {
- return;
+ return
}
}
}
}
slice :: proc(s: ^String, i, j: int, loc := #caller_location) -> string {
- runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count);
+ runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count)
if j < s.non_ascii {
- return s.contents[i:j];
+ return s.contents[i:j]
}
if i == j {
- return "";
+ return ""
}
- lo, hi: int;
+ lo, hi: int
if i < s.non_ascii {
- lo = i;
+ lo = i
} else if i == s.rune_count {
- lo = _len(s.contents);
+ lo = _len(s.contents)
} else {
- at(s, i, loc);
- lo = s.byte_pos;
+ at(s, i, loc)
+ lo = s.byte_pos
}
if j == s.rune_count {
- hi = _len(s.contents);
+ hi = _len(s.contents)
} else {
- at(s, j, loc);
- hi = s.byte_pos;
+ at(s, j, loc)
+ hi = s.byte_pos
}
- return s.contents[lo:hi];
+ return s.contents[lo:hi]
}
diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin
index 7d2082250..6a039e4dd 100644
--- a/examples/all/all_main.odin
+++ b/examples/all/all_main.odin
@@ -96,7 +96,6 @@ import sort "core:sort"
import strconv "core:strconv"
import strings "core:strings"
import sync "core:sync"
-import sync2 "core:sync/sync2"
import testing "core:testing"
import scanner "core:text/scanner"
import thread "core:thread"
@@ -104,6 +103,7 @@ import time "core:time"
import unicode "core:unicode"
import utf8 "core:unicode/utf8"
+import utf8string "core:unicode/utf8/utf8string"
import utf16 "core:unicode/utf16"
main :: proc(){}
@@ -186,11 +186,11 @@ _ :: sort
_ :: strconv
_ :: strings
_ :: sync
-_ :: sync2
_ :: testing
_ :: scanner
_ :: thread
_ :: time
_ :: unicode
_ :: utf8
+_ :: utf8string
_ :: utf16
\ No newline at end of file
diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin
index 8c2321dae..b3ce3ade9 100644
--- a/examples/demo/demo.odin
+++ b/examples/demo/demo.odin
@@ -1107,11 +1107,6 @@ prefix_table := [?]string{
}
threading_example :: proc() {
- if ODIN_OS == .Darwin {
- // TODO: Fix threads on darwin/macOS
- return
- }
-
fmt.println("\n# threading_example")
{ // Basic Threads
@@ -1152,7 +1147,7 @@ threading_example :: proc() {
{ // Thread Pool
fmt.println("\n## Thread Pool")
- task_proc :: proc(t: ^thread.Task) {
+ task_proc :: proc(t: thread.Task) {
index := t.user_index % len(prefix_table)
for iteration in 1..=5 {
fmt.printf("Worker Task %d is on iteration %d\n", t.user_index, iteration)
@@ -1162,7 +1157,7 @@ threading_example :: proc() {
}
pool: thread.Pool
- thread.pool_init(pool=&pool, thread_count=3)
+ thread.pool_init(pool=&pool, thread_count=3, allocator=context.allocator)
defer thread.pool_destroy(&pool)
@@ -1171,7 +1166,7 @@ threading_example :: proc() {
}
thread.pool_start(&pool)
- thread.pool_wait_and_process(&pool)
+ thread.pool_finish(&pool)
}
}
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index 4cd09b8e9..8b8814176 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -304,7 +304,7 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call
} else if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) {
gbString e = expr_to_string(self.expr);
gbString t = type_to_string(self.type);
- error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s %d", LIT(builtin_name), e, t, self.type->kind);
+ error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
gb_string_free(t);
gb_string_free(e);
return false;
@@ -379,6 +379,35 @@ bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call
}
}
+bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String const &builtin_name, OdinAtomicMemoryOrder *memory_order_, char const *extra_message = nullptr) {
+ Operand x = {};
+ check_expr_with_type_hint(c, &x, expr, t_atomic_memory_order);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+ if (!are_types_identical(x.type, t_atomic_memory_order) || x.mode != Addressing_Constant) {
+ gbString str = type_to_string(x.type);
+ if (extra_message) {
+ error(x.expr, "Expected a constant Atomic_Memory_Order value for the %s of '%.*s', got %s", extra_message, LIT(builtin_name), str);
+ } else {
+ error(x.expr, "Expected a constant Atomic_Memory_Order value for '%.*s', got %s", LIT(builtin_name), str);
+ }
+ gb_string_free(str);
+ return false;
+ }
+ i64 value = exact_value_to_i64(x.value);
+ if (value < 0 || value >= OdinAtomicMemoryOrder_COUNT) {
+ error(x.expr, "Illegal Atomic_Memory_Order value, got %lld", cast(long long)value);
+ return false;
+ }
+ if (memory_order_) {
+ *memory_order_ = cast(OdinAtomicMemoryOrder)value;
+ }
+
+ return true;
+
+}
+
bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
ast_node(ce, CallExpr, call);
if (ce->inlining != ProcInlining_none) {
@@ -423,6 +452,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
// NOTE(bill): The first arg may be a Type, this will be checked case by case
break;
+ case BuiltinProc_atomic_thread_fence:
+ case BuiltinProc_atomic_signal_fence:
+ // NOTE(bill): first type will require a type hint
+ break;
+
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
@@ -3198,11 +3232,27 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
- case BuiltinProc_atomic_fence:
- case BuiltinProc_atomic_fence_acq:
- case BuiltinProc_atomic_fence_rel:
- case BuiltinProc_atomic_fence_acqrel:
- operand->mode = Addressing_NoValue;
+
+ case BuiltinProc_atomic_thread_fence:
+ case BuiltinProc_atomic_signal_fence:
+ {
+ OdinAtomicMemoryOrder memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[0], builtin_name, &memory_order)) {
+ return false;
+ }
+ switch (memory_order) {
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_release:
+ case OdinAtomicMemoryOrder_acq_rel:
+ case OdinAtomicMemoryOrder_seq_cst:
+ break;
+ default:
+ error(ce->args[0], "Illegal memory ordering for '%.*s', got .%s", LIT(builtin_name), OdinAtomicMemoryOrder_strings[memory_order]);
+ break;
+ }
+
+ operand->mode = Addressing_NoValue;
+ }
break;
case BuiltinProc_volatile_store:
@@ -3210,9 +3260,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_unaligned_store:
/*fallthrough*/
case BuiltinProc_atomic_store:
- case BuiltinProc_atomic_store_rel:
- case BuiltinProc_atomic_store_relaxed:
- case BuiltinProc_atomic_store_unordered:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -3228,14 +3275,40 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
+ case BuiltinProc_atomic_store_explicit:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ check_expr_with_type_hint(c, &x, ce->args[1], elem);
+ check_assignment(c, &x, elem, builtin_name);
+
+ OdinAtomicMemoryOrder memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, &memory_order)) {
+ return false;
+ }
+ switch (memory_order) {
+ case OdinAtomicMemoryOrder_consume:
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_acq_rel:
+ error(ce->args[2], "Illegal memory order .%s for '%.*s'", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name));
+ break;
+ }
+
+ operand->type = nullptr;
+ operand->mode = Addressing_NoValue;
+ break;
+ }
+
+
case BuiltinProc_volatile_load:
/*fallthrough*/
case BuiltinProc_unaligned_load:
/*fallthrough*/
case BuiltinProc_atomic_load:
- case BuiltinProc_atomic_load_acq:
- case BuiltinProc_atomic_load_relaxed:
- case BuiltinProc_atomic_load_unordered:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -3247,41 +3320,38 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
+ case BuiltinProc_atomic_load_explicit:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+
+ OdinAtomicMemoryOrder memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[1], builtin_name, &memory_order)) {
+ return false;
+ }
+
+ switch (memory_order) {
+ case OdinAtomicMemoryOrder_release:
+ case OdinAtomicMemoryOrder_acq_rel:
+ error(ce->args[1], "Illegal memory order .%s for '%.*s'", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name));
+ break;
+ }
+
+ operand->type = elem;
+ operand->mode = Addressing_Value;
+ break;
+ }
+
case BuiltinProc_atomic_add:
- case BuiltinProc_atomic_add_acq:
- case BuiltinProc_atomic_add_rel:
- case BuiltinProc_atomic_add_acqrel:
- case BuiltinProc_atomic_add_relaxed:
case BuiltinProc_atomic_sub:
- case BuiltinProc_atomic_sub_acq:
- case BuiltinProc_atomic_sub_rel:
- case BuiltinProc_atomic_sub_acqrel:
- case BuiltinProc_atomic_sub_relaxed:
case BuiltinProc_atomic_and:
- case BuiltinProc_atomic_and_acq:
- case BuiltinProc_atomic_and_rel:
- case BuiltinProc_atomic_and_acqrel:
- case BuiltinProc_atomic_and_relaxed:
case BuiltinProc_atomic_nand:
- case BuiltinProc_atomic_nand_acq:
- case BuiltinProc_atomic_nand_rel:
- case BuiltinProc_atomic_nand_acqrel:
- case BuiltinProc_atomic_nand_relaxed:
case BuiltinProc_atomic_or:
- case BuiltinProc_atomic_or_acq:
- case BuiltinProc_atomic_or_rel:
- case BuiltinProc_atomic_or_acqrel:
- case BuiltinProc_atomic_or_relaxed:
case BuiltinProc_atomic_xor:
- case BuiltinProc_atomic_xor_acq:
- case BuiltinProc_atomic_xor_rel:
- case BuiltinProc_atomic_xor_acqrel:
- case BuiltinProc_atomic_xor_relaxed:
- case BuiltinProc_atomic_xchg:
- case BuiltinProc_atomic_xchg_acq:
- case BuiltinProc_atomic_xchg_rel:
- case BuiltinProc_atomic_xchg_acqrel:
- case BuiltinProc_atomic_xchg_relaxed:
+ case BuiltinProc_atomic_exchange:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -3297,25 +3367,35 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
- case BuiltinProc_atomic_cxchg:
- case BuiltinProc_atomic_cxchg_acq:
- case BuiltinProc_atomic_cxchg_rel:
- case BuiltinProc_atomic_cxchg_acqrel:
- case BuiltinProc_atomic_cxchg_relaxed:
- case BuiltinProc_atomic_cxchg_failrelaxed:
- case BuiltinProc_atomic_cxchg_failacq:
- case BuiltinProc_atomic_cxchg_acq_failrelaxed:
- case BuiltinProc_atomic_cxchg_acqrel_failrelaxed:
+ case BuiltinProc_atomic_add_explicit:
+ case BuiltinProc_atomic_sub_explicit:
+ case BuiltinProc_atomic_and_explicit:
+ case BuiltinProc_atomic_nand_explicit:
+ case BuiltinProc_atomic_or_explicit:
+ case BuiltinProc_atomic_xor_explicit:
+ case BuiltinProc_atomic_exchange_explicit:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ check_expr_with_type_hint(c, &x, ce->args[1], elem);
+ check_assignment(c, &x, elem, builtin_name);
- case BuiltinProc_atomic_cxchgweak:
- case BuiltinProc_atomic_cxchgweak_acq:
- case BuiltinProc_atomic_cxchgweak_rel:
- case BuiltinProc_atomic_cxchgweak_acqrel:
- case BuiltinProc_atomic_cxchgweak_relaxed:
- case BuiltinProc_atomic_cxchgweak_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_failacq:
- case BuiltinProc_atomic_cxchgweak_acq_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed:
+
+ if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, nullptr)) {
+ return false;
+ }
+
+ operand->type = elem;
+ operand->mode = Addressing_Value;
+ break;
+ }
+
+ case BuiltinProc_atomic_compare_exchange_strong:
+ case BuiltinProc_atomic_compare_exchange_weak:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -3333,7 +3413,92 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->type = elem;
break;
}
- break;
+
+ case BuiltinProc_atomic_compare_exchange_strong_explicit:
+ case BuiltinProc_atomic_compare_exchange_weak_explicit:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ Operand y = {};
+ check_expr_with_type_hint(c, &x, ce->args[1], elem);
+ check_expr_with_type_hint(c, &y, ce->args[2], elem);
+ check_assignment(c, &x, elem, builtin_name);
+ check_assignment(c, &y, elem, builtin_name);
+
+ OdinAtomicMemoryOrder success_memory_order = {};
+ OdinAtomicMemoryOrder failure_memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[3], builtin_name, &success_memory_order, "success ordering")) {
+ return false;
+ }
+ if (!check_atomic_memory_order_argument(c, ce->args[4], builtin_name, &failure_memory_order, "failure ordering")) {
+ return false;
+ }
+
+ bool invalid_combination = false;
+
+ switch (success_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_release:
+ if (failure_memory_order != OdinAtomicMemoryOrder_relaxed) {
+ invalid_combination = true;
+ }
+ break;
+ case OdinAtomicMemoryOrder_consume:
+ switch (failure_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_consume:
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+ break;
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_acq_rel:
+ switch (failure_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_consume:
+ case OdinAtomicMemoryOrder_acquire:
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+ break;
+ case OdinAtomicMemoryOrder_seq_cst:
+ switch (failure_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_consume:
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_seq_cst:
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+
+
+ if (invalid_combination) {
+ error(ce->args[3], "Illegal memory order pairing for '%.*s', success = .%s, failure = .%s",
+ LIT(builtin_name),
+ OdinAtomicMemoryOrder_strings[success_memory_order],
+ OdinAtomicMemoryOrder_strings[failure_memory_order]
+ );
+ }
+
+ operand->mode = Addressing_OptionalOk;
+ operand->type = elem;
+ break;
+ }
case BuiltinProc_fixed_point_mul:
case BuiltinProc_fixed_point_div:
@@ -4111,6 +4276,73 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
+ case BuiltinProc_wasm_memory_grow:
+ {
+ if (!is_arch_wasm()) {
+ error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
+ return false;
+ }
+
+ Operand index = {};
+ Operand delta = {};
+ check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false;
+ check_expr(c, &delta, ce->args[1]); if (delta.mode == Addressing_Invalid) return false;
+
+ convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &delta, t_uintptr); if (delta.mode == Addressing_Invalid) return false;
+
+ if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) {
+ gbString e = expr_to_string(index.expr);
+ gbString t = type_to_string(index.type);
+ error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ if (!is_operand_value(delta) || !check_is_assignable_to(c, &delta, t_uintptr)) {
+ gbString e = expr_to_string(delta.expr);
+ gbString t = type_to_string(delta.type);
+ error(delta.expr, "'%.*s' expected a uintptr for the memory delta, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = t_int;
+ operand->value = {};
+ break;
+ }
+ break;
+ case BuiltinProc_wasm_memory_size:
+ {
+ if (!is_arch_wasm()) {
+ error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
+ return false;
+ }
+
+ Operand index = {};
+ check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false;
+
+ convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false;
+
+ if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) {
+ gbString e = expr_to_string(index.expr);
+ gbString t = type_to_string(index.type);
+ error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = t_int;
+ operand->value = {};
+ break;
+ }
+ break;
+
}
return true;
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index d4a320f03..5acd56097 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -1137,7 +1137,9 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) {
- error(e->token, "@(thread_local) is not supported for this target platform");
+ e->Variable.thread_local_model.len = 0;
+ // NOTE(bill): ignore this message for the time begin
+ // error(e->token, "@(thread_local) is not supported for this target platform");
}
String context_name = str_lit("variable declaration");
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 577f3b07c..dcf17af39 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -4872,25 +4872,16 @@ bool is_expr_constant_zero(Ast *expr) {
return false;
}
-
-CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
- ast_node(ce, CallExpr, call);
- GB_ASSERT(is_type_proc(proc_type));
- proc_type = base_type(proc_type);
- TypeProc *pt = &proc_type->Proc;
-
+isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_) {
+ GB_ASSERT(pt != nullptr);
+ GB_ASSERT(pt->kind == Type_Proc);
isize param_count = 0;
isize param_count_excluding_defaults = 0;
- bool variadic = pt->variadic;
- bool vari_expand = (ce->ellipsis.pos.line != 0);
- i64 score = 0;
- bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
-
-
+ bool variadic = pt->Proc.variadic;
TypeTuple *param_tuple = nullptr;
- if (pt->params != nullptr) {
- param_tuple = &pt->params->Tuple;
+ if (pt->Proc.params != nullptr) {
+ param_tuple = &pt->Proc.params->Tuple;
param_count = param_tuple->variables.count;
if (variadic) {
@@ -4930,6 +4921,31 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
}
}
+ if (param_count_) *param_count_ = param_count;
+ return param_count_excluding_defaults;
+}
+
+
+CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
+ ast_node(ce, CallExpr, call);
+ GB_ASSERT(is_type_proc(proc_type));
+ proc_type = base_type(proc_type);
+ TypeProc *pt = &proc_type->Proc;
+
+ isize param_count = 0;
+ isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, ¶m_count);
+ bool variadic = pt->variadic;
+ bool vari_expand = (ce->ellipsis.pos.line != 0);
+ i64 score = 0;
+ bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
+
+
+ TypeTuple *param_tuple = nullptr;
+ if (pt->params != nullptr) {
+ param_tuple = &pt->params->Tuple;
+ }
+
+
CallArgumentError err = CallArgumentError_None;
Type *final_proc_type = proc_type;
Entity *gen_entity = nullptr;
@@ -5602,7 +5618,37 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
if (operand->mode == Addressing_ProcGroup) {
check_entity_decl(c, operand->proc_group, nullptr, nullptr);
- Array procs = proc_group_entities(c, *operand);
+ auto procs = proc_group_entities_cloned(c, *operand);
+
+ if (procs.count > 1) {
+ isize max_arg_count = args.count;
+ for_array(i, args) {
+ // NOTE(bill): The only thing that may have multiple values
+ // will be a call expression (assuming `or_return` and `()` will be stripped)
+ Ast *arg = strip_or_return_expr(args[i]);
+ if (arg && arg->kind == Ast_CallExpr) {
+ max_arg_count = ISIZE_MAX;
+ break;
+ }
+ }
+
+ for (isize proc_index = 0; proc_index < procs.count; /**/) {
+ Entity *proc = procs[proc_index];
+ Type *pt = base_type(proc->type);
+ if (!(pt != nullptr && is_type_proc(pt))) {
+ continue;
+ }
+
+ isize param_count = 0;
+ isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, ¶m_count);
+
+ if (param_count_excluding_defaults > max_arg_count) {
+ array_unordered_remove(&procs, proc_index);
+ } else {
+ proc_index++;
+ }
+ }
+ }
if (procs.count == 1) {
Ast *ident = operand->expr;
@@ -5632,6 +5678,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
return data;
}
+
Entity **lhs = nullptr;
isize lhs_count = -1;
@@ -8953,6 +9000,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
o->mode = Addressing_Invalid;
o->type = t_invalid;
+ o->value = {ExactValue_Invalid};
switch (node->kind) {
default:
@@ -9999,8 +10047,11 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
str = write_expr_to_string(str, st->polymorphic_params, shorthand);
str = gb_string_appendc(str, ") ");
}
- if (st->no_nil) str = gb_string_appendc(str, "#no_nil ");
- if (st->maybe) str = gb_string_appendc(str, "#maybe ");
+ switch (st->kind) {
+ case UnionType_maybe: str = gb_string_appendc(str, "#maybe "); break;
+ case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break;
+ case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break;
+ }
if (st->align) {
str = gb_string_appendc(str, "#align ");
str = write_expr_to_string(str, st->align, shorthand);
diff --git a/src/check_type.cpp b/src/check_type.cpp
index ecb2c26ea..51f472961 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -675,22 +675,31 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Arraykind == UnionType_shared_nil) {
+ if (!type_has_nil(t)) {
+ gbString s = type_to_string(t);
+ error(node, "Each variant of a union with #shared_nil must have a 'nil' value, got %s", s);
+ gb_string_free(s);
+ }
+ }
}
}
}
union_type->Union.variants = slice_from_array(variants);
- union_type->Union.no_nil = ut->no_nil;
- union_type->Union.maybe = ut->maybe;
- if (union_type->Union.no_nil) {
+ union_type->Union.kind = ut->kind;
+ switch (ut->kind) {
+ case UnionType_no_nil:
if (variants.count < 2) {
error(ut->align, "A union with #no_nil must have at least 2 variants");
}
- }
- if (union_type->Union.maybe) {
+ break;
+ case UnionType_maybe:
if (variants.count != 1) {
error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count);
}
+ break;
}
if (ut->align != nullptr) {
@@ -1960,20 +1969,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
if (params) param_count = params ->Tuple.variables.count;
if (results) result_count = results->Tuple.variables.count;
- if (param_count > 0) {
- for_array(i, params->Tuple.variables) {
- Entity *param = params->Tuple.variables[i];
- if (param->kind == Entity_Variable) {
- ParameterValue pv = param->Variable.param_value;
- if (pv.kind == ParameterValue_Constant &&
- pv.value.kind == ExactValue_Procedure) {
- type->Proc.has_proc_default_values = true;
- break;
- }
- }
- }
- }
-
if (result_count > 0) {
Entity *first = results->Tuple.variables[0];
type->Proc.has_named_results = first->token.string != "";
diff --git a/src/checker.cpp b/src/checker.cpp
index ea300afd9..1bb786ea1 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -829,15 +829,16 @@ struct GlobalEnumValue {
i64 value;
};
-Slice add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count) {
+Slice add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count, Type **enum_type_ = nullptr) {
Scope *scope = create_scope(nullptr, builtin_pkg->scope);
- Entity *e = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved);
+ Entity *entity = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved);
Type *enum_type = alloc_type_enum();
- Type *named_type = alloc_type_named(type_name, enum_type, e);
+ Type *named_type = alloc_type_named(type_name, enum_type, entity);
set_base_type(named_type, enum_type);
enum_type->Enum.base_type = t_int;
enum_type->Enum.scope = scope;
+ entity->type = named_type;
auto fields = array_make(permanent_allocator(), value_count);
for (isize i = 0; i < value_count; i++) {
@@ -858,6 +859,9 @@ Slice add_global_enum_type(String const &type_name, GlobalEnumValue *v
enum_type->Enum.min_value = &enum_type->Enum.fields[enum_type->Enum.min_value_index]->Constant.value;
enum_type->Enum.max_value = &enum_type->Enum.fields[enum_type->Enum.max_value_index]->Constant.value;
+
+ if (enum_type_) *enum_type_ = named_type;
+
return slice_from_array(fields);
}
void add_global_enum_constant(Slice const &fields, char const *name, i64 value) {
@@ -986,6 +990,21 @@ void init_universal(void) {
add_global_enum_constant(fields, "ODIN_ERROR_POS_STYLE", build_context.ODIN_ERROR_POS_STYLE);
}
+ {
+ GlobalEnumValue values[OdinAtomicMemoryOrder_COUNT] = {
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_relaxed], OdinAtomicMemoryOrder_relaxed},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_consume], OdinAtomicMemoryOrder_consume},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acquire], OdinAtomicMemoryOrder_acquire},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_release], OdinAtomicMemoryOrder_release},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acq_rel], OdinAtomicMemoryOrder_acq_rel},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_seq_cst], OdinAtomicMemoryOrder_seq_cst},
+ };
+
+ add_global_enum_type(str_lit("Atomic_Memory_Order"), values, gb_count_of(values), &t_atomic_memory_order);
+ GB_ASSERT(t_atomic_memory_order->kind == Type_Named);
+ scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name);
+ }
+
add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
@@ -2657,6 +2676,15 @@ Array proc_group_entities(CheckerContext *c, Operand o) {
return procs;
}
+Array proc_group_entities_cloned(CheckerContext *c, Operand o) {
+ auto entities = proc_group_entities(c, o);
+ if (entities.count == 0) {
+ return {};
+ }
+ return array_clone(permanent_allocator(), entities);
+}
+
+
void init_core_type_info(Checker *c) {
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index cba952ddf..c6b0afee0 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -86,77 +86,30 @@ enum BuiltinProcId {
BuiltinProc_prefetch_write_instruction,
BuiltinProc_prefetch_write_data,
- BuiltinProc_atomic_fence,
- BuiltinProc_atomic_fence_acq,
- BuiltinProc_atomic_fence_rel,
- BuiltinProc_atomic_fence_acqrel,
-
+ BuiltinProc_atomic_thread_fence,
+ BuiltinProc_atomic_signal_fence,
BuiltinProc_atomic_store,
- BuiltinProc_atomic_store_rel,
- BuiltinProc_atomic_store_relaxed,
- BuiltinProc_atomic_store_unordered,
-
+ BuiltinProc_atomic_store_explicit,
BuiltinProc_atomic_load,
- BuiltinProc_atomic_load_acq,
- BuiltinProc_atomic_load_relaxed,
- BuiltinProc_atomic_load_unordered,
-
+ BuiltinProc_atomic_load_explicit,
BuiltinProc_atomic_add,
- BuiltinProc_atomic_add_acq,
- BuiltinProc_atomic_add_rel,
- BuiltinProc_atomic_add_acqrel,
- BuiltinProc_atomic_add_relaxed,
+ BuiltinProc_atomic_add_explicit,
BuiltinProc_atomic_sub,
- BuiltinProc_atomic_sub_acq,
- BuiltinProc_atomic_sub_rel,
- BuiltinProc_atomic_sub_acqrel,
- BuiltinProc_atomic_sub_relaxed,
+ BuiltinProc_atomic_sub_explicit,
BuiltinProc_atomic_and,
- BuiltinProc_atomic_and_acq,
- BuiltinProc_atomic_and_rel,
- BuiltinProc_atomic_and_acqrel,
- BuiltinProc_atomic_and_relaxed,
+ BuiltinProc_atomic_and_explicit,
BuiltinProc_atomic_nand,
- BuiltinProc_atomic_nand_acq,
- BuiltinProc_atomic_nand_rel,
- BuiltinProc_atomic_nand_acqrel,
- BuiltinProc_atomic_nand_relaxed,
+ BuiltinProc_atomic_nand_explicit,
BuiltinProc_atomic_or,
- BuiltinProc_atomic_or_acq,
- BuiltinProc_atomic_or_rel,
- BuiltinProc_atomic_or_acqrel,
- BuiltinProc_atomic_or_relaxed,
+ BuiltinProc_atomic_or_explicit,
BuiltinProc_atomic_xor,
- BuiltinProc_atomic_xor_acq,
- BuiltinProc_atomic_xor_rel,
- BuiltinProc_atomic_xor_acqrel,
- BuiltinProc_atomic_xor_relaxed,
-
- BuiltinProc_atomic_xchg,
- BuiltinProc_atomic_xchg_acq,
- BuiltinProc_atomic_xchg_rel,
- BuiltinProc_atomic_xchg_acqrel,
- BuiltinProc_atomic_xchg_relaxed,
-
- BuiltinProc_atomic_cxchg,
- BuiltinProc_atomic_cxchg_acq,
- BuiltinProc_atomic_cxchg_rel,
- BuiltinProc_atomic_cxchg_acqrel,
- BuiltinProc_atomic_cxchg_relaxed,
- BuiltinProc_atomic_cxchg_failrelaxed,
- BuiltinProc_atomic_cxchg_failacq,
- BuiltinProc_atomic_cxchg_acq_failrelaxed,
- BuiltinProc_atomic_cxchg_acqrel_failrelaxed,
-
- BuiltinProc_atomic_cxchgweak,
- BuiltinProc_atomic_cxchgweak_acq,
- BuiltinProc_atomic_cxchgweak_rel,
- BuiltinProc_atomic_cxchgweak_acqrel,
- BuiltinProc_atomic_cxchgweak_relaxed,
- BuiltinProc_atomic_cxchgweak_failrelaxed,
- BuiltinProc_atomic_cxchgweak_failacq,
- BuiltinProc_atomic_cxchgweak_acq_failrelaxed,
- BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed,
+ BuiltinProc_atomic_xor_explicit,
+ BuiltinProc_atomic_exchange,
+ BuiltinProc_atomic_exchange_explicit,
+ BuiltinProc_atomic_compare_exchange_strong,
+ BuiltinProc_atomic_compare_exchange_strong_explicit,
+ BuiltinProc_atomic_compare_exchange_weak,
+ BuiltinProc_atomic_compare_exchange_weak_explicit,
BuiltinProc_fixed_point_mul,
BuiltinProc_fixed_point_div,
@@ -260,6 +213,8 @@ BuiltinProc__type_end,
BuiltinProc_constant_utf16_cstring,
+ BuiltinProc_wasm_memory_grow,
+ BuiltinProc_wasm_memory_size,
BuiltinProc_COUNT,
};
@@ -350,78 +305,30 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("prefetch_write_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_write_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
+ {STR_LIT("atomic_thread_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_signal_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_store_explicit"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_load_explicit"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_add_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_sub_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_and_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_nand_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_or_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_xor_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_exchange"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_exchange_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_compare_exchange_strong"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_compare_exchange_strong_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_compare_exchange_weak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_compare_exchange_weak_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_mul"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_div"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -523,4 +430,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("wasm_memory_grow"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("wasm_memory_size"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
};
diff --git a/src/docs_format.cpp b/src/docs_format.cpp
index 7ce93d2bf..ee32d0e05 100644
--- a/src/docs_format.cpp
+++ b/src/docs_format.cpp
@@ -99,6 +99,7 @@ enum OdinDocTypeFlag_Union : u32 {
OdinDocTypeFlag_Union_polymorphic = 1<<0,
OdinDocTypeFlag_Union_no_nil = 1<<1,
OdinDocTypeFlag_Union_maybe = 1<<2,
+ OdinDocTypeFlag_Union_shared_nil = 1<<3,
};
enum OdinDocTypeFlag_Proc : u32 {
diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp
index 2c5186c39..0ad10ac49 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -619,9 +619,11 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
case Type_Union:
doc_type.kind = OdinDocType_Union;
if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; }
- if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; }
- if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; }
-
+ switch (type->Union.kind) {
+ case UnionType_maybe: doc_type.flags |= OdinDocTypeFlag_Union_maybe; break;
+ case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break;
+ case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break;
+ }
{
auto variants = array_make(heap_allocator(), type->Union.variants.count);
defer (array_free(&variants));
diff --git a/src/entity.cpp b/src/entity.cpp
index f5720293f..17fa884e1 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -115,6 +115,16 @@ struct ParameterValue {
};
};
+bool has_parameter_value(ParameterValue const ¶m_value) {
+ if (param_value.kind != ParameterValue_Invalid) {
+ return true;
+ }
+ if (param_value.original_ast_expr != nullptr) {
+ return true;
+ }
+ return false;
+}
+
enum EntityConstantFlags : u32 {
EntityConstantFlag_ImplicitEnumValue = 1<<0,
};
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index 4294747b9..133df4d41 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -3028,7 +3028,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later
lbBlock *else_ = lb_create_block(p, "if.else");
- lbValue cond = lb_build_cond(p, te->cond, then, else_);
+ lb_build_cond(p, te->cond, then, else_);
lb_start_block(p, then);
Type *type = default_type(type_of_expr(expr));
@@ -4646,7 +4646,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later
lbBlock *else_ = lb_create_block(p, "if.else");
- lbValue cond = lb_build_cond(p, te->cond, then, else_);
+ lb_build_cond(p, te->cond, then, else_);
lb_start_block(p, then);
Type *ptr_type = alloc_type_pointer(default_type(type_of_expr(expr)));
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index 1c98aa77f..330059622 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -1176,10 +1176,35 @@ void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *varia
}
void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) {
- lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+ Type *pt = base_type(type_deref(parent.type));
+ GB_ASSERT(pt->kind == Type_Union);
+ if (pt->Union.kind == UnionType_shared_nil) {
+ lbBlock *if_nil = lb_create_block(p, "shared_nil.if_nil");
+ lbBlock *if_not_nil = lb_create_block(p, "shared_nil.if_not_nil");
+ lbBlock *done = lb_create_block(p, "shared_nil.done");
- lb_emit_store(p, underlying, variant);
- lb_emit_store_union_variant_tag(p, parent, variant_type);
+ lbValue cond_is_nil = lb_emit_comp_against_nil(p, Token_CmpEq, variant);
+ lb_emit_if(p, cond_is_nil, if_nil, if_not_nil);
+
+ lb_start_block(p, if_nil);
+ lb_emit_store(p, parent, lb_const_nil(p->module, type_deref(parent.type)));
+ lb_emit_jump(p, done);
+
+ lb_start_block(p, if_not_nil);
+ lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+ lb_emit_store(p, underlying, variant);
+ lb_emit_store_union_variant_tag(p, parent, variant_type);
+ lb_emit_jump(p, done);
+
+ lb_start_block(p, done);
+
+
+ } else {
+ lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+
+ lb_emit_store(p, underlying, variant);
+ lb_emit_store_union_variant_tag(p, parent, variant_type);
+ }
}
@@ -2312,7 +2337,7 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) {
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
LLVMSetInitializer(global_data, data);
- LLVMSetLinkage(global_data, LLVMInternalLinkage);
+ LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2);
string_map_set(&m->const_strings, key, ptr);
@@ -2354,7 +2379,7 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str)
}
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
LLVMSetInitializer(global_data, data);
- LLVMSetLinkage(global_data, LLVMInternalLinkage);
+ LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMValueRef ptr = nullptr;
if (str.len != 0) {
@@ -2590,7 +2615,7 @@ lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String
g.value = LLVMAddGlobal(m->mod, lb_type(m, t), text);
g.type = alloc_type_pointer(t);
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, t)));
- LLVMSetLinkage(g.value, LLVMInternalLinkage);
+ LLVMSetLinkage(g.value, LLVMPrivateLinkage);
string_map_set(&m->members, s, g);
return g;
}
@@ -2602,6 +2627,9 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
GB_ASSERT(true_block != nullptr);
GB_ASSERT(false_block != nullptr);
+ // Use to signal not to do compile time short circuit for consts
+ lbValue no_comptime_short_circuit = {};
+
switch (cond->kind) {
case_ast_node(pe, ParenExpr, cond);
return lb_build_cond(p, pe->expr, true_block, false_block);
@@ -2609,7 +2637,11 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
case_ast_node(ue, UnaryExpr, cond);
if (ue->op.kind == Token_Not) {
- return lb_build_cond(p, ue->expr, false_block, true_block);
+ lbValue cond_val = lb_build_cond(p, ue->expr, false_block, true_block);
+ if (cond_val.value && LLVMIsConstant(cond_val.value)) {
+ return lb_const_bool(p->module, cond_val.type, LLVMConstIntGetZExtValue(cond_val.value) == 0);
+ }
+ return no_comptime_short_circuit;
}
case_end;
@@ -2618,12 +2650,14 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
lbBlock *block = lb_create_block(p, "cmp.and");
lb_build_cond(p, be->left, block, false_block);
lb_start_block(p, block);
- return lb_build_cond(p, be->right, true_block, false_block);
+ lb_build_cond(p, be->right, true_block, false_block);
+ return no_comptime_short_circuit;
} else if (be->op.kind == Token_CmpOr) {
lbBlock *block = lb_create_block(p, "cmp.or");
lb_build_cond(p, be->left, true_block, block);
lb_start_block(p, block);
- return lb_build_cond(p, be->right, true_block, false_block);
+ lb_build_cond(p, be->right, true_block, false_block);
+ return no_comptime_short_circuit;
}
case_end;
}
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index 27a6ec209..4de2af9d8 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -1372,12 +1372,20 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
if (build_context.metrics.arch == TargetArch_i386 ||
build_context.metrics.arch == TargetArch_amd64) {
LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
- LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {});
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {}, true);
GB_ASSERT(the_asm != nullptr);
LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
} else if (build_context.metrics.arch == TargetArch_arm64) {
LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
- LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("yield"), {});
+ // NOTE(bill, 2022-03-30): `isb` appears to a better option that `yield`
+ // See: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8258604
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("isb"), {}, true);
+ GB_ASSERT(the_asm != nullptr);
+ LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
+ } else {
+ // NOTE: default to something to prevent optimization
+ LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit(""), {}, true);
GB_ASSERT(the_asm != nullptr);
LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
}
@@ -1598,36 +1606,26 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
-
- case BuiltinProc_atomic_fence:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, "");
+ // TODO(bill): Which is correct?
+ case BuiltinProc_atomic_thread_fence:
+ LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), false, "");
return {};
- case BuiltinProc_atomic_fence_acq:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquire, false, "");
- return {};
- case BuiltinProc_atomic_fence_rel:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingRelease, false, "");
- return {};
- case BuiltinProc_atomic_fence_acqrel:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquireRelease, false, "");
+ case BuiltinProc_atomic_signal_fence:
+ LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), true, "");
return {};
case BuiltinProc_volatile_store:
case BuiltinProc_atomic_store:
- case BuiltinProc_atomic_store_rel:
- case BuiltinProc_atomic_store_relaxed:
- case BuiltinProc_atomic_store_unordered: {
+ case BuiltinProc_atomic_store_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue val = lb_build_expr(p, ce->args[1]);
val = lb_emit_conv(p, val, type_deref(dst.type));
LLVMValueRef instr = LLVMBuildStore(p->builder, val.value, dst.value);
switch (id) {
- case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break;
- case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
- case BuiltinProc_atomic_store_rel: LLVMSetOrdering(instr, LLVMAtomicOrderingRelease); break;
- case BuiltinProc_atomic_store_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break;
- case BuiltinProc_atomic_store_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break;
+ case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break;
+ case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
+ case BuiltinProc_atomic_store_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[2])); break;
}
LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
@@ -1637,18 +1635,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_volatile_load:
case BuiltinProc_atomic_load:
- case BuiltinProc_atomic_load_acq:
- case BuiltinProc_atomic_load_relaxed:
- case BuiltinProc_atomic_load_unordered: {
+ case BuiltinProc_atomic_load_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
LLVMValueRef instr = LLVMBuildLoad(p->builder, dst.value, "");
switch (id) {
- case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break;
- case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
- case BuiltinProc_atomic_load_acq: LLVMSetOrdering(instr, LLVMAtomicOrderingAcquire); break;
- case BuiltinProc_atomic_load_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break;
- case BuiltinProc_atomic_load_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break;
+ case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break;
+ case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
+ case BuiltinProc_atomic_load_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[1])); break;
}
LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
@@ -1678,40 +1672,19 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
case BuiltinProc_atomic_add:
- case BuiltinProc_atomic_add_acq:
- case BuiltinProc_atomic_add_rel:
- case BuiltinProc_atomic_add_acqrel:
- case BuiltinProc_atomic_add_relaxed:
case BuiltinProc_atomic_sub:
- case BuiltinProc_atomic_sub_acq:
- case BuiltinProc_atomic_sub_rel:
- case BuiltinProc_atomic_sub_acqrel:
- case BuiltinProc_atomic_sub_relaxed:
case BuiltinProc_atomic_and:
- case BuiltinProc_atomic_and_acq:
- case BuiltinProc_atomic_and_rel:
- case BuiltinProc_atomic_and_acqrel:
- case BuiltinProc_atomic_and_relaxed:
case BuiltinProc_atomic_nand:
- case BuiltinProc_atomic_nand_acq:
- case BuiltinProc_atomic_nand_rel:
- case BuiltinProc_atomic_nand_acqrel:
- case BuiltinProc_atomic_nand_relaxed:
case BuiltinProc_atomic_or:
- case BuiltinProc_atomic_or_acq:
- case BuiltinProc_atomic_or_rel:
- case BuiltinProc_atomic_or_acqrel:
- case BuiltinProc_atomic_or_relaxed:
case BuiltinProc_atomic_xor:
- case BuiltinProc_atomic_xor_acq:
- case BuiltinProc_atomic_xor_rel:
- case BuiltinProc_atomic_xor_acqrel:
- case BuiltinProc_atomic_xor_relaxed:
- case BuiltinProc_atomic_xchg:
- case BuiltinProc_atomic_xchg_acq:
- case BuiltinProc_atomic_xchg_rel:
- case BuiltinProc_atomic_xchg_acqrel:
- case BuiltinProc_atomic_xchg_relaxed: {
+ case BuiltinProc_atomic_exchange:
+ case BuiltinProc_atomic_add_explicit:
+ case BuiltinProc_atomic_sub_explicit:
+ case BuiltinProc_atomic_and_explicit:
+ case BuiltinProc_atomic_nand_explicit:
+ case BuiltinProc_atomic_or_explicit:
+ case BuiltinProc_atomic_xor_explicit:
+ case BuiltinProc_atomic_exchange_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue val = lb_build_expr(p, ce->args[1]);
val = lb_emit_conv(p, val, type_deref(dst.type));
@@ -1720,41 +1693,20 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMAtomicOrdering ordering = {};
switch (id) {
- case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_add_acq: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_add_rel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_add_acqrel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_add_relaxed: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_sub_acq: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_sub_rel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_sub_acqrel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_sub_relaxed: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_and_acq: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_and_rel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_and_acqrel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_and_relaxed: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_nand_acq: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_nand_rel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_nand_acqrel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_nand_relaxed: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_or_acq: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_or_rel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_or_acqrel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_or_relaxed: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_xor_acq: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_xor_rel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_xor_acqrel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_xor_relaxed: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_xchg: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_xchg_acq: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_xchg_rel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_xchg_acqrel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_xchg_relaxed: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingMonotonic; break;
+ case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_exchange: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_add_explicit: op = LLVMAtomicRMWBinOpAdd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_sub_explicit: op = LLVMAtomicRMWBinOpSub; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_and_explicit: op = LLVMAtomicRMWBinOpAnd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_nand_explicit: op = LLVMAtomicRMWBinOpNand; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_or_explicit: op = LLVMAtomicRMWBinOpOr; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_xor_explicit: op = LLVMAtomicRMWBinOpXor; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_exchange_explicit: op = LLVMAtomicRMWBinOpXchg; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
}
lbValue res = {};
@@ -1763,24 +1715,10 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return res;
}
- case BuiltinProc_atomic_cxchg:
- case BuiltinProc_atomic_cxchg_acq:
- case BuiltinProc_atomic_cxchg_rel:
- case BuiltinProc_atomic_cxchg_acqrel:
- case BuiltinProc_atomic_cxchg_relaxed:
- case BuiltinProc_atomic_cxchg_failrelaxed:
- case BuiltinProc_atomic_cxchg_failacq:
- case BuiltinProc_atomic_cxchg_acq_failrelaxed:
- case BuiltinProc_atomic_cxchg_acqrel_failrelaxed:
- case BuiltinProc_atomic_cxchgweak:
- case BuiltinProc_atomic_cxchgweak_acq:
- case BuiltinProc_atomic_cxchgweak_rel:
- case BuiltinProc_atomic_cxchgweak_acqrel:
- case BuiltinProc_atomic_cxchgweak_relaxed:
- case BuiltinProc_atomic_cxchgweak_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_failacq:
- case BuiltinProc_atomic_cxchgweak_acq_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: {
+ case BuiltinProc_atomic_compare_exchange_strong:
+ case BuiltinProc_atomic_compare_exchange_weak:
+ case BuiltinProc_atomic_compare_exchange_strong_explicit:
+ case BuiltinProc_atomic_compare_exchange_weak_explicit: {
lbValue address = lb_build_expr(p, ce->args[0]);
Type *elem = type_deref(address.type);
lbValue old_value = lb_build_expr(p, ce->args[1]);
@@ -1793,28 +1731,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMBool weak = false;
switch (id) {
- case BuiltinProc_atomic_cxchg: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchg_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchg_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = false; break;
- case BuiltinProc_atomic_cxchg_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchg_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchgweak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchgweak_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
+ case BuiltinProc_atomic_compare_exchange_strong: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
+ case BuiltinProc_atomic_compare_exchange_weak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
+ case BuiltinProc_atomic_compare_exchange_strong_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = false; break;
+ case BuiltinProc_atomic_compare_exchange_weak_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = true; break;
}
// TODO(bill): Figure out how to make it weak
- LLVMBool single_threaded = weak;
+ LLVMBool single_threaded = false;
LLVMValueRef value = LLVMBuildAtomicCmpXchg(
p->builder, address.value,
@@ -1823,6 +1747,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
failure_ordering,
single_threaded
);
+ LLVMSetWeak(value, weak);
if (tv.type->kind == Type_Tuple) {
Type *fix_typed = alloc_type_tuple();
@@ -2199,6 +2124,45 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return res;
}
+
+ case BuiltinProc_wasm_memory_grow:
+ {
+ char const *name = "llvm.wasm.memory.grow";
+ LLVMTypeRef types[1] = {
+ lb_type(p->module, t_uintptr),
+ };
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[2] = {};
+ args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value;
+ args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_uintptr).value;
+
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return res;
+ }
+ case BuiltinProc_wasm_memory_size:
+ {
+ char const *name = "llvm.wasm.memory.size";
+ LLVMTypeRef types[1] = {
+ lb_type(p->module, t_uintptr),
+ };
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[1] = {};
+ args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value;
+
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return res;
+ }
+
}
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index 916c0433e..2afb5300b 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -1652,13 +1652,16 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
}
lbValue cond = lb_build_cond(p, is->cond, then, else_);
+ // Note `cond.value` only set for non-and/or conditions and const negs so that the `LLVMIsConstant()`
+ // and `LLVMConstIntGetZExtValue()` calls below will be valid and `LLVMInstructionEraseFromParent()`
+ // will target the correct (& only) branch statement
if (is->label != nullptr) {
lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
tl->is_block = true;
}
- if (LLVMIsConstant(cond.value)) {
+ if (cond.value && LLVMIsConstant(cond.value)) {
// NOTE(bill): Do a compile time short circuit for when the condition is constantly known.
// This done manually rather than relying on the SSA passes because sometimes the SSA passes
// miss some even if they are constantly known, especially with few optimization passes.
diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp
index e245a8b40..7d73956e8 100644
--- a/src/llvm_backend_type.cpp
+++ b/src/llvm_backend_type.cpp
@@ -641,7 +641,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr);
{
- LLVMValueRef vals[7] = {};
+ LLVMValueRef vals[8] = {};
isize variant_count = gb_max(0, t->Union.variants.count);
lbValue memory_types = lb_type_info_member_types_offset(p, variant_count);
@@ -675,8 +675,9 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
}
vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
- vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value;
- vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).value;
+ vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value;
+ vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_maybe).value;
+ vals[7] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value;
for (isize i = 0; i < gb_count_of(vals); i++) {
if (vals[i] == nullptr) {
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index 399d1632d..037171637 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -2005,3 +2005,25 @@ lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
}
+
+
+LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
+ GB_ASSERT(value.kind == ExactValue_Integer);
+ i64 v = exact_value_to_i64(value);
+ switch (v) {
+ case OdinAtomicMemoryOrder_relaxed: return LLVMAtomicOrderingUnordered;
+ case OdinAtomicMemoryOrder_consume: return LLVMAtomicOrderingMonotonic;
+ case OdinAtomicMemoryOrder_acquire: return LLVMAtomicOrderingAcquire;
+ case OdinAtomicMemoryOrder_release: return LLVMAtomicOrderingRelease;
+ case OdinAtomicMemoryOrder_acq_rel: return LLVMAtomicOrderingAcquireRelease;
+ case OdinAtomicMemoryOrder_seq_cst: return LLVMAtomicOrderingSequentiallyConsistent;
+ }
+ GB_PANIC("Unknown atomic ordering");
+ return LLVMAtomicOrderingSequentiallyConsistent;
+}
+
+
+LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) {
+ ExactValue value = type_and_value_of_expr(expr).value;
+ return llvm_atomic_ordering_from_odin(value);
+}
diff --git a/src/main.cpp b/src/main.cpp
index 63b2c8386..9e470923d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -117,6 +117,9 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
gb_printf_err("%s\n\n", cmd_line);
}
exit_code = system(cmd_line);
+ if (WIFEXITED(exit_code)) {
+ exit_code = WEXITSTATUS(exit_code);
+ }
#endif
if (exit_code) {
@@ -433,6 +436,10 @@ i32 linker_stage(lbGenerator *gen) {
String output_ext = {};
gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
+ if (build_context.no_crt) {
+ link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
+ }
+
// NOTE(dweiler): We use clang as a frontend for the linker as there are
// other runtime and compiler support libraries that need to be linked in
// very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
diff --git a/src/parser.cpp b/src/parser.cpp
index a435d1317..767119aa8 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -1071,15 +1071,14 @@ Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_c
}
-Ast *ast_union_type(AstFile *f, Token token, Array const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
+Ast *ast_union_type(AstFile *f, Token token, Array const &variants, Ast *polymorphic_params, Ast *align, UnionTypeKind kind,
Token where_token, Array const &where_clauses) {
Ast *result = alloc_ast_node(f, Ast_UnionType);
result->UnionType.token = token;
result->UnionType.variants = slice_from_array(variants);
result->UnionType.polymorphic_params = polymorphic_params;
result->UnionType.align = align;
- result->UnionType.no_nil = no_nil;
- result->UnionType.maybe = maybe;
+ result->UnionType.kind = kind;
result->UnionType.where_token = where_token;
result->UnionType.where_clauses = slice_from_array(where_clauses);
return result;
@@ -2475,6 +2474,9 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Ast *align = nullptr;
bool no_nil = false;
bool maybe = false;
+ bool shared_nil = false;
+
+ UnionTypeKind union_kind = UnionType_Normal;
Token start_token = f->curr_token;
@@ -2501,6 +2503,11 @@ Ast *parse_operand(AstFile *f, bool lhs) {
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
}
no_nil = true;
+ } else if (tag.string == "shared_nil") {
+ if (shared_nil) {
+ syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
+ }
+ shared_nil = true;
} else if (tag.string == "maybe") {
if (maybe) {
syntax_error(tag, "Duplicate union tag '#%.*s'", LIT(tag.string));
@@ -2513,6 +2520,21 @@ Ast *parse_operand(AstFile *f, bool lhs) {
if (no_nil && maybe) {
syntax_error(f->curr_token, "#maybe and #no_nil cannot be applied together");
}
+ if (no_nil && shared_nil) {
+ syntax_error(f->curr_token, "#shared_nil and #no_nil cannot be applied together");
+ }
+ if (shared_nil && maybe) {
+ syntax_error(f->curr_token, "#maybe and #shared_nil cannot be applied together");
+ }
+
+
+ if (maybe) {
+ union_kind = UnionType_maybe;
+ } else if (no_nil) {
+ union_kind = UnionType_no_nil;
+ } else if (shared_nil) {
+ union_kind = UnionType_shared_nil;
+ }
skip_possible_newline_for_literal(f);
@@ -2544,7 +2566,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Token close = expect_closing_brace_of_field_list(f);
- return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, maybe, where_token, where_clauses);
+ return ast_union_type(f, token, variants, polymorphic_params, align, union_kind, where_token, where_clauses);
} break;
case Token_enum: {
diff --git a/src/parser.hpp b/src/parser.hpp
index c33d1520b..c7b4fd0d8 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -330,6 +330,13 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
"intel",
};
+enum UnionTypeKind : u8 {
+ UnionType_Normal = 0,
+ UnionType_maybe = 1,
+ UnionType_no_nil = 2,
+ UnionType_shared_nil = 3,
+};
+
#define AST_KINDS \
AST_KIND(Ident, "identifier", struct { \
Token token; \
@@ -678,8 +685,7 @@ AST_KIND(_TypeBegin, "", bool) \
Slice variants; \
Ast *polymorphic_params; \
Ast * align; \
- bool maybe; \
- bool no_nil; \
+ UnionTypeKind kind; \
Token where_token; \
Slice where_clauses; \
}) \
diff --git a/src/types.cpp b/src/types.cpp
index 58ccdf5b9..e10dae1ed 100644
--- a/src/types.cpp
+++ b/src/types.cpp
@@ -165,9 +165,8 @@ struct TypeUnion {
i16 tag_size;
bool is_polymorphic;
- bool is_poly_specialized : 1;
- bool no_nil : 1;
- bool maybe : 1;
+ bool is_poly_specialized;
+ UnionTypeKind kind;
};
struct TypeProc {
@@ -186,7 +185,6 @@ struct TypeProc {
bool c_vararg;
bool is_polymorphic;
bool is_poly_specialized;
- bool has_proc_default_values;
bool has_named_results;
bool diverging; // no return
bool return_by_pointer;
@@ -694,6 +692,28 @@ gb_global Type *t_objc_id = nullptr;
gb_global Type *t_objc_SEL = nullptr;
gb_global Type *t_objc_Class = nullptr;
+enum OdinAtomicMemoryOrder : i32 {
+ OdinAtomicMemoryOrder_relaxed = 0, // unordered
+ OdinAtomicMemoryOrder_consume = 1, // monotonic
+ OdinAtomicMemoryOrder_acquire = 2,
+ OdinAtomicMemoryOrder_release = 3,
+ OdinAtomicMemoryOrder_acq_rel = 4,
+ OdinAtomicMemoryOrder_seq_cst = 5,
+ OdinAtomicMemoryOrder_COUNT,
+};
+
+char const *OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_COUNT] = {
+ "Relaxed",
+ "Consume",
+ "Acquire",
+ "Release",
+ "Acq_Rel",
+ "Seq_Cst",
+};
+
+gb_global Type *t_atomic_memory_order = nullptr;
+
+
gb_global RecursiveMutex g_type_mutex;
@@ -1665,7 +1685,7 @@ bool is_type_map(Type *t) {
bool is_type_union_maybe_pointer(Type *t) {
t = base_type(t);
- if (t->kind == Type_Union && t->Union.maybe) {
+ if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) {
if (t->Union.variants.count == 1) {
Type *v = t->Union.variants[0];
return is_type_pointer(v) || is_type_multi_pointer(v);
@@ -1677,7 +1697,7 @@ bool is_type_union_maybe_pointer(Type *t) {
bool is_type_union_maybe_pointer_original_alignment(Type *t) {
t = base_type(t);
- if (t->kind == Type_Union && t->Union.maybe) {
+ if (t->kind == Type_Union && t->Union.kind == UnionType_maybe) {
if (t->Union.variants.count == 1) {
Type *v = t->Union.variants[0];
if (is_type_pointer(v) || is_type_multi_pointer(v)) {
@@ -2169,7 +2189,7 @@ bool type_has_nil(Type *t) {
case Type_Map:
return true;
case Type_Union:
- return !t->Union.no_nil;
+ return t->Union.kind != UnionType_no_nil;
case Type_Struct:
if (is_type_soa_struct(t)) {
switch (t->Struct.soa_kind) {
@@ -2455,7 +2475,7 @@ bool are_types_identical_internal(Type *x, Type *y, bool check_tuple_names) {
if (y->kind == Type_Union) {
if (x->Union.variants.count == y->Union.variants.count &&
x->Union.custom_align == y->Union.custom_align &&
- x->Union.no_nil == y->Union.no_nil) {
+ x->Union.kind == y->Union.kind) {
// NOTE(bill): zeroth variant is nullptr
for_array(i, x->Union.variants) {
if (!are_types_identical(x->Union.variants[i], y->Union.variants[i])) {
@@ -2599,7 +2619,7 @@ i64 union_variant_index(Type *u, Type *v) {
for_array(i, u->Union.variants) {
Type *vt = u->Union.variants[i];
if (are_types_identical(v, vt)) {
- if (u->Union.no_nil) {
+ if (u->Union.kind == UnionType_no_nil) {
return cast(i64)(i+0);
} else {
return cast(i64)(i+1);
@@ -4022,8 +4042,11 @@ gbString write_type_to_string(gbString str, Type *type, bool shorthand=false) {
case Type_Union:
str = gb_string_appendc(str, "union");
- if (type->Union.no_nil != 0) str = gb_string_appendc(str, " #no_nil");
- if (type->Union.maybe != 0) str = gb_string_appendc(str, " #maybe");
+ switch (type->Union.kind) {
+ case UnionType_maybe: str = gb_string_appendc(str, " #maybe"); break;
+ case UnionType_no_nil: str = gb_string_appendc(str, " #no_nil"); break;
+ case UnionType_shared_nil: str = gb_string_appendc(str, " #shared_nil"); break;
+ }
if (type->Union.custom_align != 0) str = gb_string_append_fmt(str, " #align %d", cast(int)type->Union.custom_align);
str = gb_string_appendc(str, " {");
for_array(i, type->Union.variants) {
diff --git a/tests/common/common.odin b/tests/common/common.odin
index 9a715d38e..07b6afef9 100644
--- a/tests/common/common.odin
+++ b/tests/common/common.odin
@@ -4,6 +4,7 @@ package common
import "core:testing"
import "core:fmt"
import "core:os"
+import "core:strings"
TEST_count := 0
TEST_fail := 0
@@ -38,3 +39,37 @@ report :: proc(t: ^testing.T) {
fmt.printf("%v/%v tests successful.\n", TEST_count, TEST_count)
}
}
+
+// Returns absolute path to `sub_path` where `sub_path` is within the "tests/" sub-directory of the Odin project root
+// and we're being run from the Odin project root or from a sub-directory of "tests/"
+// e.g. get_data_path("assets/blah") will return "/Odin_root/tests/assets/blah" if run within "/Odin_root",
+// "/Odin_root/tests" or "/Odin_root/tests/subdir" etc
+get_data_path :: proc(t: ^testing.T, sub_path: string) -> (data_path: string) {
+
+ cwd := os.get_current_directory()
+ defer delete(cwd)
+
+ when ODIN_OS == .Windows {
+ norm, was_allocation := strings.replace_all(cwd, "\\", "/")
+ if !was_allocation {
+ norm = strings.clone(norm)
+ }
+ defer delete(norm)
+ } else {
+ norm := cwd
+ }
+
+ last_index := strings.last_index(norm, "/tests/")
+ if last_index == -1 {
+ len := len(norm)
+ if len >= 6 && norm[len-6:] == "/tests" {
+ data_path = fmt.tprintf("%s/%s", norm, sub_path)
+ } else {
+ data_path = fmt.tprintf("%s/tests/%s", norm, sub_path)
+ }
+ } else {
+ data_path = fmt.tprintf("%s/tests/%s", norm[:last_index], sub_path)
+ }
+
+ return data_path
+}
diff --git a/tests/core/Makefile b/tests/core/Makefile
index 1158434bf..652ebb151 100644
--- a/tests/core/Makefile
+++ b/tests/core/Makefile
@@ -2,7 +2,7 @@ ODIN=../../odin
PYTHON=$(shell which python3)
all: download_test_assets image_test compress_test strings_test hash_test crypto_test noise_test encoding_test \
- math_test linalg_glsl_math_test
+ math_test linalg_glsl_math_test filepath_test reflect_test os_exit_test
download_test_assets:
$(PYTHON) download_assets.py
@@ -26,6 +26,7 @@ noise_test:
$(ODIN) run math/noise -out=test_noise
encoding_test:
+ $(ODIN) run encoding/hxa -out=test_hxa -collection:tests=..
$(ODIN) run encoding/json -out=test_json
$(ODIN) run encoding/varint -out=test_varint
@@ -34,3 +35,12 @@ math_test:
linalg_glsl_math_test:
$(ODIN) run math/linalg/glsl/test_linalg_glsl_math.odin -out=test_linalg_glsl_math -collection:tests=..
+
+filepath_test:
+ $(ODIN) run path/filepath/test_core_filepath.odin -out=test_core_filepath -collection:tests=..
+
+reflect_test:
+ $(ODIN) run reflect/test_core_reflect.odin -out=test_core_reflect -collection:tests=..
+
+os_exit_test:
+ $(ODIN) run os/test_core_os_exit.odin -out=test_core_os_exit && exit 1 || exit 0
diff --git a/tests/core/assets/HXA/teapot.hxa b/tests/core/assets/HXA/teapot.hxa
new file mode 100644
index 000000000..954ab5a10
Binary files /dev/null and b/tests/core/assets/HXA/teapot.hxa differ
diff --git a/tests/core/build.bat b/tests/core/build.bat
index 0227ac6bb..2f9ba672e 100644
--- a/tests/core/build.bat
+++ b/tests/core/build.bat
@@ -1,5 +1,5 @@
@echo off
-set COMMON=-show-timings -no-bounds-check -vet -strict-style
+set COMMON=-show-timings -no-bounds-check -vet -strict-style -collection:tests=..
set PATH_TO_ODIN==..\..\odin
python3 download_assets.py
echo ---
@@ -35,10 +35,31 @@ echo ---
echo ---
echo Running core:encoding tests
echo ---
+%PATH_TO_ODIN% run encoding/hxa %COMMON%
%PATH_TO_ODIN% run encoding/json %COMMON%
%PATH_TO_ODIN% run encoding/varint %COMMON%
echo ---
echo Running core:math/noise tests
echo ---
-%PATH_TO_ODIN% run math/noise %COMMON%
\ No newline at end of file
+%PATH_TO_ODIN% run math/noise %COMMON%
+
+echo ---
+echo Running core:math tests
+echo ---
+%PATH_TO_ODIN% run math %COMMON%
+
+echo ---
+echo Running core:math/linalg/glsl tests
+echo ---
+%PATH_TO_ODIN% run math/linalg/glsl %COMMON%
+
+echo ---
+echo Running core:path/filepath tests
+echo ---
+%PATH_TO_ODIN% run path/filepath %COMMON%
+
+echo ---
+echo Running core:reflect tests
+echo ---
+%PATH_TO_ODIN% run reflect %COMMON%
diff --git a/tests/core/encoding/hxa/test_core_hxa.odin b/tests/core/encoding/hxa/test_core_hxa.odin
new file mode 100644
index 000000000..b93562fd5
--- /dev/null
+++ b/tests/core/encoding/hxa/test_core_hxa.odin
@@ -0,0 +1,232 @@
+// Tests "core:encoding:hxa".
+// Must be run with `-collection:tests=` flag, e.g.
+// ./odin run tests/core/encoding/hxa/test_core_hxa.odin -out=tests/core/test_core_hxa -collection:tests=./tests
+package test_core_hxa
+
+import "core:encoding/hxa"
+import "core:fmt"
+import "core:testing"
+import tc "tests:common"
+
+TEAPOT_PATH :: "core/assets/HXA/teapot.hxa"
+
+main :: proc() {
+ t := testing.T{}
+
+ test_read(&t)
+ test_write(&t)
+
+ tc.report(&t)
+}
+
+@test
+test_read :: proc(t: ^testing.T) {
+
+ using hxa
+
+ filename := tc.get_data_path(t, TEAPOT_PATH)
+ defer delete(filename)
+
+ file, err := read_from_file(filename)
+ e :: hxa.Read_Error.None
+ tc.expect(t, err == e, fmt.tprintf("%v: read_from_file(%v) -> %v != %v", #procedure, filename, err, e))
+ defer file_destroy(file)
+
+ /* Header */
+ tc.expect(t, file.magic_number == 0x417848, fmt.tprintf("%v: file.magic_number %v != %v",
+ #procedure, file.magic_number, 0x417848))
+ tc.expect(t, file.version == 1, fmt.tprintf("%v: file.version %v != %v",
+ #procedure, file.version, 1))
+ tc.expect(t, file.internal_node_count == 1, fmt.tprintf("%v: file.internal_node_count %v != %v",
+ #procedure, file.internal_node_count, 1))
+
+ /* Nodes (only one) */
+ tc.expect(t, len(file.nodes) == 1, fmt.tprintf("%v: len(file.nodes) %v != %v", #procedure, len(file.nodes), 1))
+
+ m := &file.nodes[0].meta_data
+ tc.expect(t, len(m^) == 38, fmt.tprintf("%v: len(m^) %v != %v", #procedure, len(m^), 38))
+ {
+ e :: "Texture resolution"
+ tc.expect(t, m[0].name == e, fmt.tprintf("%v: m[0].name %v != %v", #procedure, m[0].name, e))
+
+ m_v, m_v_ok := m[0].value.([]i64le)
+ tc.expect(t, m_v_ok, fmt.tprintf("%v: m_v_ok %v != %v", #procedure, m_v_ok, true))
+ tc.expect(t, len(m_v) == 1, fmt.tprintf("%v: len(m_v) %v != %v", #procedure, len(m_v), 1))
+ tc.expect(t, m_v[0] == 1024, fmt.tprintf("%v: m_v[0] %v != %v", #procedure, len(m_v), 1024))
+ }
+ {
+ e :: "Validate"
+ tc.expect(t, m[37].name == e, fmt.tprintf("%v: m[37].name %v != %v", #procedure, m[37].name, e))
+
+ m_v, m_v_ok := m[37].value.([]i64le)
+ tc.expect(t, m_v_ok, fmt.tprintf("%v: m_v_ok %v != %v", #procedure, m_v_ok, true))
+ tc.expect(t, len(m_v) == 1, fmt.tprintf("%v: len(m_v) %v != %v", #procedure, len(m_v), 1))
+ tc.expect(t, m_v[0] == -2054847231, fmt.tprintf("%v: m_v[0] %v != %v", #procedure, len(m_v), -2054847231))
+ }
+
+ /* Node content */
+ v, v_ok := file.nodes[0].content.(hxa.Node_Geometry)
+ tc.expect(t, v_ok, fmt.tprintf("%v: v_ok %v != %v", #procedure, v_ok, true))
+
+ tc.expect(t, v.vertex_count == 530, fmt.tprintf("%v: v.vertex_count %v != %v", #procedure, v.vertex_count, 530))
+ tc.expect(t, v.edge_corner_count == 2026, fmt.tprintf("%v: v.edge_corner_count %v != %v",
+ #procedure, v.edge_corner_count, 2026))
+ tc.expect(t, v.face_count == 517, fmt.tprintf("%v: v.face_count %v != %v", #procedure, v.face_count, 517))
+
+ /* Vertex stack */
+ tc.expect(t, len(v.vertex_stack) == 1, fmt.tprintf("%v: len(v.vertex_stack) %v != %v",
+ #procedure, len(v.vertex_stack), 1))
+ {
+ e := "vertex"
+ tc.expect(t, v.vertex_stack[0].name == e, fmt.tprintf("%v: v.vertex_stack[0].name %v != %v",
+ #procedure, v.vertex_stack[0].name, e))
+ }
+ tc.expect(t, v.vertex_stack[0].components == 3, fmt.tprintf("%v: v.vertex_stack[0].components %v != %v",
+ #procedure, v.vertex_stack[0].components, 3))
+
+ /* Vertex stack data */
+ vs_d, vs_d_ok := v.vertex_stack[0].data.([]f64le)
+ tc.expect(t, vs_d_ok, fmt.tprintf("%v: vs_d_ok %v != %v", #procedure, vs_d_ok, true))
+ tc.expect(t, len(vs_d) == 1590, fmt.tprintf("%v: len(vs_d) %v != %v", #procedure, len(vs_d), 1590))
+
+ tc.expect(t, vs_d[0] == 4.06266, fmt.tprintf("%v: vs_d[0] %v (%h) != %v (%h)",
+ #procedure, vs_d[0], vs_d[0], 4.06266, 4.06266))
+ tc.expect(t, vs_d[1] == 2.83457, fmt.tprintf("%v: vs_d[1] %v (%h) != %v (%h)",
+ #procedure, vs_d[1], vs_d[1], 2.83457, 2.83457))
+ tc.expect(t, vs_d[2] == 0hbfbc5da6a4441787, fmt.tprintf("%v: vs_d[2] %v (%h) != %v (%h)",
+ #procedure, vs_d[2], vs_d[2],
+ 0hbfbc5da6a4441787, 0hbfbc5da6a4441787))
+ tc.expect(t, vs_d[3] == 0h4010074fb549f948, fmt.tprintf("%v: vs_d[3] %v (%h) != %v (%h)",
+ #procedure, vs_d[3], vs_d[3],
+ 0h4010074fb549f948, 0h4010074fb549f948))
+ tc.expect(t, vs_d[1587] == 0h400befa82e87d2c7, fmt.tprintf("%v: vs_d[1587] %v (%h) != %v (%h)",
+ #procedure, vs_d[1587], vs_d[1587],
+ 0h400befa82e87d2c7, 0h400befa82e87d2c7))
+ tc.expect(t, vs_d[1588] == 2.83457, fmt.tprintf("%v: vs_d[1588] %v (%h) != %v (%h)",
+ #procedure, vs_d[1588], vs_d[1588], 2.83457, 2.83457))
+ tc.expect(t, vs_d[1589] == -1.56121, fmt.tprintf("%v: vs_d[1589] %v (%h) != %v (%h)",
+ #procedure, vs_d[1589], vs_d[1589], -1.56121, -1.56121))
+
+ /* Corner stack */
+ tc.expect(t, len(v.corner_stack) == 1,
+ fmt.tprintf("%v: len(v.corner_stack) %v != %v", #procedure, len(v.corner_stack), 1))
+ {
+ e := "reference"
+ tc.expect(t, v.corner_stack[0].name == e, fmt.tprintf("%v: v.corner_stack[0].name %v != %v",
+ #procedure, v.corner_stack[0].name, e))
+ }
+ tc.expect(t, v.corner_stack[0].components == 1, fmt.tprintf("%v: v.corner_stack[0].components %v != %v",
+ #procedure, v.corner_stack[0].components, 1))
+
+ /* Corner stack data */
+ cs_d, cs_d_ok := v.corner_stack[0].data.([]i32le)
+ tc.expect(t, cs_d_ok, fmt.tprintf("%v: cs_d_ok %v != %v", #procedure, cs_d_ok, true))
+ tc.expect(t, len(cs_d) == 2026, fmt.tprintf("%v: len(cs_d) %v != %v", #procedure, len(cs_d), 2026))
+ tc.expect(t, cs_d[0] == 6, fmt.tprintf("%v: cs_d[0] %v != %v", #procedure, cs_d[0], 6))
+ tc.expect(t, cs_d[2025] == -32, fmt.tprintf("%v: cs_d[2025] %v != %v", #procedure, cs_d[2025], -32))
+
+ /* Edge and face stacks (empty) */
+ tc.expect(t, len(v.edge_stack) == 0, fmt.tprintf("%v: len(v.edge_stack) %v != %v",
+ #procedure, len(v.edge_stack), 0))
+ tc.expect(t, len(v.face_stack) == 0, fmt.tprintf("%v: len(v.face_stack) %v != %v",
+ #procedure, len(v.face_stack), 0))
+}
+
+@test
+test_write :: proc(t: ^testing.T) {
+
+ using hxa
+
+ n1 :Node
+
+ n1_m1_value := []f64le{0.4, -1.23, 2341.6, -333.333}
+ n1_m1 := Meta{"m1", n1_m1_value}
+
+ n1.meta_data = []Meta{n1_m1}
+
+ n1_l1 := Layer{"l1", 2, []f32le{32.1, -41.3}}
+ n1_l2 := Layer{"l2", 3, []f64le{0.64, 1.64, -2.64}}
+
+ n1_content := Node_Image{Image_Type.Image_1D, [3]u32le{1, 1, 2}, Layer_Stack{n1_l1, n1_l2}}
+
+ n1.content = n1_content
+
+ w_file :File
+ w_file.nodes = []Node{n1}
+
+ required_size := required_write_size(w_file)
+ buf := make([]u8, required_size)
+
+ n, write_err := write(buf, w_file)
+ write_e :: hxa.Write_Error.None
+ tc.expect(t, write_err == write_e, fmt.tprintf("%v: write_err %v != %v", #procedure, write_err, write_e))
+ tc.expect(t, n == required_size, fmt.tprintf("%v: n %v != %v", #procedure, n, required_size))
+
+ file, read_err := read(buf)
+ read_e :: hxa.Read_Error.None
+ tc.expect(t, read_err == read_e, fmt.tprintf("%v: read_err %v != %v", #procedure, read_err, read_e))
+ defer file_destroy(file)
+
+ delete(buf)
+
+ tc.expect(t, file.magic_number == 0x417848, fmt.tprintf("%v: file.magic_number %v != %v",
+ #procedure, file.magic_number, 0x417848))
+ tc.expect(t, file.version == 3, fmt.tprintf("%v: file.version %v != %v", #procedure, file.version, 3))
+ tc.expect(t, file.internal_node_count == 1, fmt.tprintf("%v: file.internal_node_count %v != %v",
+ #procedure, file.internal_node_count, 1))
+
+ tc.expect(t, len(file.nodes) == len(w_file.nodes), fmt.tprintf("%v: len(file.nodes) %v != %v",
+ #procedure, len(file.nodes), len(w_file.nodes)))
+
+ m := &file.nodes[0].meta_data
+ w_m := &w_file.nodes[0].meta_data
+ tc.expect(t, len(m^) == len(w_m^), fmt.tprintf("%v: len(m^) %v != %v", #procedure, len(m^), len(w_m^)))
+ tc.expect(t, m[0].name == w_m[0].name, fmt.tprintf("%v: m[0].name %v != %v", #procedure, m[0].name, w_m[0].name))
+
+ m_v, m_v_ok := m[0].value.([]f64le)
+ tc.expect(t, m_v_ok, fmt.tprintf("%v: m_v_ok %v != %v", #procedure, m_v_ok, true))
+ tc.expect(t, len(m_v) == len(n1_m1_value), fmt.tprintf("%v: %v != len(m_v) %v",
+ #procedure, len(m_v), len(n1_m1_value)))
+ for i := 0; i < len(m_v); i += 1 {
+ tc.expect(t, m_v[i] == n1_m1_value[i], fmt.tprintf("%v: m_v[%d] %v != %v",
+ #procedure, i, m_v[i], n1_m1_value[i]))
+ }
+
+ v, v_ok := file.nodes[0].content.(hxa.Node_Image)
+ tc.expect(t, v_ok, fmt.tprintf("%v: v_ok %v != %v", #procedure, v_ok, true))
+ tc.expect(t, v.type == n1_content.type, fmt.tprintf("%v: v.type %v != %v", #procedure, v.type, n1_content.type))
+ tc.expect(t, len(v.resolution) == 3, fmt.tprintf("%v: len(v.resolution) %v != %v",
+ #procedure, len(v.resolution), 3))
+ tc.expect(t, len(v.image_stack) == len(n1_content.image_stack), fmt.tprintf("%v: len(v.image_stack) %v != %v",
+ #procedure, len(v.image_stack), len(n1_content.image_stack)))
+ for i := 0; i < len(v.image_stack); i += 1 {
+ tc.expect(t, v.image_stack[i].name == n1_content.image_stack[i].name,
+ fmt.tprintf("%v: v.image_stack[%d].name %v != %v",
+ #procedure, i, v.image_stack[i].name, n1_content.image_stack[i].name))
+ tc.expect(t, v.image_stack[i].components == n1_content.image_stack[i].components,
+ fmt.tprintf("%v: v.image_stack[%d].components %v != %v",
+ #procedure, i, v.image_stack[i].components, n1_content.image_stack[i].components))
+
+ switch n1_t in n1_content.image_stack[i].data {
+ case []u8:
+ tc.expect(t, false, fmt.tprintf("%v: n1_content.image_stack[i].data []u8", #procedure))
+ case []i32le:
+ tc.expect(t, false, fmt.tprintf("%v: n1_content.image_stack[i].data []i32le", #procedure))
+ case []f32le:
+ l, l_ok := v.image_stack[i].data.([]f32le)
+ tc.expect(t, l_ok, fmt.tprintf("%v: l_ok %v != %v", #procedure, l_ok, true))
+ tc.expect(t, len(l) == len(n1_t), fmt.tprintf("%v: len(l) %v != %v", #procedure, len(l), len(n1_t)))
+ for j := 0; j < len(l); j += 1 {
+ tc.expect(t, l[j] == n1_t[j], fmt.tprintf("%v: l[%d] %v (%h) != %v (%h)",
+ #procedure, j, l[j], l[j], n1_t[j], n1_t[j]))
+ }
+ case []f64le:
+ l, l_ok := v.image_stack[i].data.([]f64le)
+ tc.expect(t, l_ok, fmt.tprintf("%v: l_ok %v != %v", #procedure, l_ok, true))
+ tc.expect(t, len(l) == len(n1_t), fmt.tprintf("%v: len(l) %v != %v", #procedure, len(l), len(n1_t)))
+ for j := 0; j < len(l); j += 1 {
+ tc.expect(t, l[j] == n1_t[j], fmt.tprintf("%v: l[%d] %v != %v", #procedure, j, l[j], n1_t[j]))
+ }
+ }
+ }
+}
diff --git a/tests/core/encoding/json/test_core_json.odin b/tests/core/encoding/json/test_core_json.odin
index 60e71bf72..285cc04a1 100644
--- a/tests/core/encoding/json/test_core_json.odin
+++ b/tests/core/encoding/json/test_core_json.odin
@@ -88,5 +88,5 @@ marshal_json :: proc(t: ^testing.T) {
_, err := json.marshal(my_struct)
- expect(t, err == .None, "expected json error to be none")
+ expect(t, err == nil, "expected json error to be none")
}
diff --git a/tests/core/os/test_core_os_exit.odin b/tests/core/os/test_core_os_exit.odin
new file mode 100644
index 000000000..2ab274f5e
--- /dev/null
+++ b/tests/core/os/test_core_os_exit.odin
@@ -0,0 +1,10 @@
+// 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/path/filepath/test_core_filepath.odin b/tests/core/path/filepath/test_core_filepath.odin
new file mode 100644
index 000000000..0268fb62c
--- /dev/null
+++ b/tests/core/path/filepath/test_core_filepath.odin
@@ -0,0 +1,123 @@
+// Tests "path.odin" in "core:path/filepath".
+// Must be run with `-collection:tests=` flag, e.g.
+// ./odin run tests/core/path/filepath/test_core_filepath.odin -collection:tests=tests
+package test_core_filepath
+
+import "core:fmt"
+import "core:path/filepath"
+import "core:testing"
+import tc "tests:common"
+
+main :: proc() {
+ t := testing.T{}
+
+ when ODIN_OS == .Windows {
+ test_split_list_windows(&t)
+ } else {
+ test_split_list_unix(&t)
+ }
+
+ tc.report(&t)
+}
+
+@test
+test_split_list_windows :: proc(t: ^testing.T) {
+
+ using filepath
+
+ Datum :: struct {
+ i: int,
+ v: string,
+ e: [3]string,
+ }
+ @static data := []Datum{
+ { 0, "C:\\Odin;C:\\Visual Studio;\"C:\\Some Other\"",
+ [3]string{"C:\\Odin", "C:\\Visual Studio", "C:\\Some Other"} }, // Issue #1537
+ { 1, "a;;b", [3]string{"a", "", "b"} },
+ { 2, "a;b;", [3]string{"a", "b", ""} },
+ { 3, ";a;b", [3]string{"", "a", "b"} },
+ { 4, ";;", [3]string{"", "", ""} },
+ { 5, "\"a;b\"c;d;\"f\"", [3]string{"a;bc", "d", "f"} },
+ { 6, "\"a;b;c\";d\";e\";f", [3]string{"a;b;c", "d;e", "f"} },
+ }
+
+ for d, i in data {
+ assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i))
+ r := split_list(d.v)
+ defer delete(r)
+ tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d",
+ i, #procedure, d.v, len(r), len(d.e)))
+ if len(r) == len(d.e) {
+ for _, j in r {
+ tc.expect(t, r[j] == d.e[j], fmt.tprintf("i:%d %s(%v) -> %v[%d] != %v",
+ i, #procedure, d.v, r[j], j, d.e[j]))
+ }
+ }
+ }
+
+ {
+ v := ""
+ r := split_list(v)
+ tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r))
+ }
+ {
+ v := "a"
+ r := split_list(v)
+ defer delete(r)
+ tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r)))
+ if len(r) == 1 {
+ tc.expect(t, r[0] == "a", fmt.tprintf("%s(%v) -> %v[0] != a", #procedure, v, r[0]))
+ }
+ }
+}
+
+@test
+test_split_list_unix :: proc(t: ^testing.T) {
+
+ using filepath
+
+ Datum :: struct {
+ i: int,
+ v: string,
+ e: [3]string,
+ }
+ @static data := []Datum{
+ { 0, "/opt/butler:/home/fancykillerpanda/Projects/Odin/Odin:/usr/local/sbin",
+ [3]string{"/opt/butler", "/home/fancykillerpanda/Projects/Odin/Odin", "/usr/local/sbin"} }, // Issue #1537
+ { 1, "a::b", [3]string{"a", "", "b"} },
+ { 2, "a:b:", [3]string{"a", "b", ""} },
+ { 3, ":a:b", [3]string{"", "a", "b"} },
+ { 4, "::", [3]string{"", "", ""} },
+ { 5, "\"a:b\"c:d:\"f\"", [3]string{"a:bc", "d", "f"} },
+ { 6, "\"a:b:c\":d\":e\":f", [3]string{"a:b:c", "d:e", "f"} },
+ }
+
+ for d, i in data {
+ assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i))
+ r := split_list(d.v)
+ defer delete(r)
+ tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d",
+ i, #procedure, d.v, len(r), len(d.e)))
+ if len(r) == len(d.e) {
+ for _, j in r {
+ tc.expect(t, r[j] == d.e[j], fmt.tprintf("i:%d %s(%v) -> %v[%d] != %v",
+ i, #procedure, d.v, r[j], j, d.e[j]))
+ }
+ }
+ }
+
+ {
+ v := ""
+ r := split_list(v)
+ tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r))
+ }
+ {
+ v := "a"
+ r := split_list(v)
+ defer delete(r)
+ tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r)))
+ if len(r) == 1 {
+ tc.expect(t, r[0] == "a", fmt.tprintf("%s(%v) -> %v[0] != a", #procedure, v, r[0]))
+ }
+ }
+}
diff --git a/tests/core/reflect/test_core_reflect.odin b/tests/core/reflect/test_core_reflect.odin
new file mode 100644
index 000000000..039501735
--- /dev/null
+++ b/tests/core/reflect/test_core_reflect.odin
@@ -0,0 +1,288 @@
+// Tests "core:reflect/reflect".
+// Must be run with `-collection:tests=` flag, e.g.
+// ./odin run tests/core/reflect/test_core_reflect.odin -out=tests/core/test_core_reflect -collection:tests=./tests
+package test_core_reflect
+
+import "core:fmt"
+import "core:reflect"
+import "core:testing"
+import tc "tests:common"
+
+main :: proc() {
+ t := testing.T{}
+
+ test_as_u64(&t)
+ test_as_f64(&t)
+
+ tc.report(&t)
+}
+
+@test
+test_as_u64 :: proc(t: ^testing.T) {
+ using reflect
+
+ {
+ /* i8 */
+ Datum :: struct { i: int, v: i8, e: u64 }
+ @static data := []Datum{
+ { 0, 0x7F, 0x7F },
+ { 1, -1, 0xFFFF_FFFF_FFFF_FFFF },
+ { 2, -0x80, 0xFFFF_FFFF_FFFF_FF80 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_u64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v (0x%X) != %v (0x%X)\n",
+ i, #procedure, d.v, r, r, d.e, d.e))
+ }
+ }
+ {
+ /* i16 */
+ Datum :: struct { i: int, v: i16, e: u64 }
+ @static data := []Datum{
+ { 0, 0x7FFF, 0x7FFF },
+ { 1, -1, 0xFFFF_FFFF_FFFF_FFFF },
+ { 2, -0x8000, 0xFFFF_FFFF_FFFF_8000 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_u64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v (0x%X) != %v (0x%X)\n",
+ i, #procedure, d.v, r, r, d.e, d.e))
+ }
+ }
+ {
+ /* i32 */
+ Datum :: struct { i: int, v: i32, e: u64 }
+ @static data := []Datum{
+ { 0, 0x7FFF_FFFF, 0x7FFF_FFFF },
+ { 1, -1, 0xFFFF_FFFF_FFFF_FFFF },
+ { 2, -0x8000_0000, 0xFFFF_FFFF_8000_0000 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_u64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v (0x%X) != %v (0x%X)\n",
+ i, #procedure, d.v, r, r, d.e, d.e))
+ }
+ }
+ {
+ /* i64 */
+ Datum :: struct { i: int, v: i64, e: u64 }
+ @static data := []Datum{
+ { 0, 0x7FFF_FFFF_FFFF_FFFF, 0x7FFF_FFFF_FFFF_FFFF },
+ { 1, -1, 0xFFFF_FFFF_FFFF_FFFF },
+ { 2, -0x8000_0000_0000_0000, 0x8000_0000_0000_0000 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_u64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v (0x%X) != %v (0x%X)\n",
+ i, #procedure, d.v, r, r, d.e, d.e))
+ }
+ }
+ {
+ /* i128 */
+ Datum :: struct { i: int, v: i128, e: u64 }
+ @static data := []Datum{
+ { 0, 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF },
+ { 1, -1, 0xFFFF_FFFF_FFFF_FFFF },
+ { 2, 0x8000_0000_0000_0000, 0x8000_0000_0000_0000 },
+ { 3, -0x8000_0000_0000_0000, 0x8000_0000_0000_0000 },
+ { 4, 0x0001_0000_0000_0000_0000, 0 },
+ { 5, -0x8000_0000_0000_0000_0000_0000_0000_0000, 0 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_u64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (0x%X) != %v (0x%X)\n",
+ i, #procedure, d.v, r, r, d.e, d.e))
+ }
+ }
+ {
+ /* f16 */
+ Datum :: struct { i: int, v: f16, e: u64 }
+ @static data := []Datum{
+ { 0, 1.2, 1 },
+ { 1, 123.12, 123 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_u64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
+ }
+ }
+ {
+ /* f32 */
+ Datum :: struct { i: int, v: f32, e: u64 }
+ @static data := []Datum{
+ { 0, 123.3415, 123 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_u64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
+ }
+ }
+ {
+ /* f64 */
+ Datum :: struct { i: int, v: f64, e: u64 }
+ @static data := []Datum{
+ { 0, 12345345345.3415234234, 12345345345 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_u64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
+ }
+ }
+}
+
+@test
+test_as_f64 :: proc(t: ^testing.T) {
+ using reflect
+
+ {
+ /* i8 */
+ Datum :: struct { i: int, v: i8, e: f64 }
+ @static data := []Datum{
+ { 0, 0x7F, 0x7F },
+ { 1, -1, -1 },
+ { 2, -0x80, -0x80 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_f64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
+ }
+ }
+ {
+ /* i16 */
+ Datum :: struct { i: int, v: i16, e: f64 }
+ @static data := []Datum{
+ { 0, 0x7FFF, 0x7FFF },
+ { 1, -1, -1 },
+ { 2, -0x8000, -0x8000 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_f64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
+ }
+ }
+ {
+ /* i32 */
+ Datum :: struct { i: int, v: i32, e: f64 }
+ @static data := []Datum{
+ { 0, 0x7FFF_FFFF, 0x7FFF_FFFF },
+ { 1, -1, -1 },
+ { 2, -0x8000_0000, -0x8000_0000 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_f64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
+ }
+ }
+ {
+ /* i64 */
+ Datum :: struct { i: int, v: i64, e: f64 }
+ @static data := []Datum{
+ { 0, 0x7FFF_FFFF_FFFF_FFFF, 0x7FFF_FFFF_FFFF_FFFF },
+ { 1, -1, -1 },
+ { 2, -0x8000_0000_0000_0000, -0x8000_0000_0000_0000 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_f64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
+ }
+ }
+ {
+ /* i128 */
+ Datum :: struct { i: int, v: i128, e: f64 }
+ @static data := []Datum{
+ { 0, 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF, 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF },
+ { 1, -1, -1 },
+ { 2, 0x8000_0000_0000_0000_0000_0000_0000, 0x8000_0000_0000_0000_0000_0000_0000 },
+ { 3, -0x8000_0000_0000_0000_0000_0000_0000_0000, -0x8000_0000_0000_0000_0000_0000_0000_0000 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_f64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (%H) != %v (%H)\n",
+ i, #procedure, d.v, r, r, d.e, d.e))
+ }
+ }
+ {
+ /* f16 */
+ Datum :: struct { i: int, v: f16, e: f64 }
+ @static data := []Datum{
+ { 0, 1.2, 0h3FF3_3400_0000_0000 }, // Precision difference TODO: check
+ { 1, 123.12, 0h405E_C800_0000_0000 }, // Precision difference TODO: check
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_f64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v (%H)) -> %v (%H) != %v (%H)\n",
+ i, #procedure, d.v, d.v, r, r, d.e, d.e))
+ }
+ }
+ {
+ /* f32 */
+ Datum :: struct { i: int, v: f32, e: f64 }
+ @static data := []Datum{
+ { 0, 123.3415, 0h405E_D5DB_2000_0000 }, // Precision difference TODO: check
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_f64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v (%H)) -> %v (%H) != %v (%H)\n",
+ i, #procedure, d.v, d.v, r, r, d.e, d.e))
+ }
+ }
+ {
+ /* f64 */
+ Datum :: struct { i: int, v: f64, e: f64 }
+ @static data := []Datum{
+ { 0, 12345345345.3415234234, 12345345345.3415234234 },
+ }
+
+ for d, i in data {
+ assert(i == d.i)
+ r, valid := as_f64(d.v)
+ tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v))
+ tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
+ }
+ }
+}
diff --git a/tests/core/strings/test_core_strings.odin b/tests/core/strings/test_core_strings.odin
index 70da1a73b..e97734dda 100644
--- a/tests/core/strings/test_core_strings.odin
+++ b/tests/core/strings/test_core_strings.odin
@@ -1,4 +1,4 @@
-package test_core_image
+package test_core_strings
import "core:strings"
import "core:testing"
@@ -32,6 +32,7 @@ main :: proc() {
test_index_any_larger_string_not_found(&t)
test_index_any_small_string_found(&t)
test_index_any_larger_string_found(&t)
+ test_cut(&t)
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
if TEST_fail > 0 {
@@ -42,7 +43,6 @@ main :: proc() {
@test
test_index_any_small_string_not_found :: proc(t: ^testing.T) {
index := strings.index_any(".", "/:\"")
- log(t, index)
expect(t, index == -1, "index_any should be negative")
}
@@ -63,3 +63,30 @@ test_index_any_larger_string_found :: proc(t: ^testing.T) {
index := strings.index_any("aaaaaaaa:aaaaaaaa", "/:\"")
expect(t, index == 8, "index_any should be 8")
}
+
+Cut_Test :: struct {
+ input: string,
+ offset: int,
+ length: int,
+ output: string,
+}
+
+cut_tests :: []Cut_Test{
+ {"some example text", 0, 4, "some" },
+ {"some example text", 2, 2, "me" },
+ {"some example text", 5, 7, "example" },
+ {"some example text", 5, 0, "example text"},
+ {"恥ずべきフクロウ", 4, 0, "フクロウ" },
+}
+
+@test
+test_cut :: proc(t: ^testing.T) {
+ for test in cut_tests {
+ res := strings.cut(test.input, test.offset, test.length)
+ defer delete(res)
+
+ msg := fmt.tprintf("cut(\"%v\", %v, %v) expected to return \"%v\", got \"%v\"",
+ test.input, test.offset, test.length, test.output, res)
+ expect(t, res == test.output, msg)
+ }
+}
\ No newline at end of file
diff --git a/tests/issues/run.bat b/tests/issues/run.bat
new file mode 100644
index 000000000..b145dda48
--- /dev/null
+++ b/tests/issues/run.bat
@@ -0,0 +1,17 @@
+@echo off
+
+if not exist "tests\issues\build\" mkdir tests\issues\build
+
+set COMMON=-collection:tests=tests -out:tests\issues\build\test_issue
+
+@echo on
+
+.\odin build tests\issues\test_issue_829.odin %COMMON%
+tests\issues\build\test_issue
+
+.\odin build tests\issues\test_issue_1592.odin %COMMON%
+tests\issues\build\test_issue
+
+@echo off
+
+rmdir /S /Q tests\issues\build
diff --git a/tests/issues/run.sh b/tests/issues/run.sh
new file mode 100755
index 000000000..c4f978771
--- /dev/null
+++ b/tests/issues/run.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+set -eu
+
+mkdir -p tests/issues/build
+
+COMMON="-collection:tests=tests -out:tests/issues/build/test_issue"
+
+set -x
+
+./odin build tests/issues/test_issue_829.odin $COMMON
+tests/issues/build/test_issue
+
+./odin build tests/issues/test_issue_1592.odin $COMMON
+tests/issues/build/test_issue
+
+set +x
+
+rm -rf tests/issues/build
diff --git a/tests/issues/test_issue_1592.odin b/tests/issues/test_issue_1592.odin
new file mode 100644
index 000000000..bb350a30b
--- /dev/null
+++ b/tests/issues/test_issue_1592.odin
@@ -0,0 +1,489 @@
+// Tests issue #1592 https://github.com/odin-lang/Odin/issues/1592
+package test_issues
+
+import "core:fmt"
+import "core:testing"
+import tc "tests:common"
+
+main :: proc() {
+ t := testing.T{}
+
+ /* This won't short-circuit */
+ test_orig()
+
+ /* These will short-circuit */
+ test_simple_const_false(&t)
+ test_simple_const_true(&t)
+
+ /* These won't short-circuit */
+ test_simple_proc_false(&t)
+ test_simple_proc_true(&t)
+
+ /* These won't short-circuit */
+ test_const_false_const_false(&t)
+ test_const_false_const_true(&t)
+ test_const_true_const_false(&t)
+ test_const_true_const_true(&t)
+
+ /* These won't short-circuit */
+ test_proc_false_const_false(&t)
+ test_proc_false_const_true(&t)
+ test_proc_true_const_false(&t)
+ test_proc_true_const_true(&t)
+
+ tc.report(&t)
+}
+
+/* Original issue #1592 example */
+
+// I get a LLVM code gen error when this constant is false, but it works when it is true
+CONSTANT_BOOL :: false
+
+bool_result :: proc() -> bool {
+ return false
+}
+
+@test
+test_orig :: proc() {
+ if bool_result() || CONSTANT_BOOL {
+ }
+}
+
+CONSTANT_FALSE :: false
+CONSTANT_TRUE :: true
+
+false_result :: proc() -> bool {
+ return false
+}
+true_result :: proc() -> bool {
+ return true
+}
+
+@test
+test_simple_const_false :: proc(t: ^testing.T) {
+ if CONSTANT_FALSE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if (CONSTANT_FALSE) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if !CONSTANT_FALSE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if (!CONSTANT_FALSE) {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if !(CONSTANT_FALSE) {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if !!CONSTANT_FALSE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if CONSTANT_FALSE == true {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if CONSTANT_FALSE == false {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if !(CONSTANT_FALSE == true) {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if !(CONSTANT_FALSE == false) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+}
+
+@test
+test_simple_const_true :: proc(t: ^testing.T) {
+ if CONSTANT_TRUE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if (CONSTANT_TRUE) {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if !CONSTANT_TRUE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if (!CONSTANT_TRUE) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if (!CONSTANT_TRUE) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if !(CONSTANT_TRUE) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if !!CONSTANT_TRUE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if CONSTANT_TRUE == true {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if CONSTANT_TRUE == false {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if !(CONSTANT_TRUE == true) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if !(CONSTANT_TRUE == false) {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+}
+
+@test
+test_simple_proc_false :: proc(t: ^testing.T) {
+ if false_result() {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if !false_result() {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+}
+
+@test
+test_simple_proc_true :: proc(t: ^testing.T) {
+ if true_result() {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if !true_result() {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+}
+
+@test
+test_const_false_const_false :: proc(t: ^testing.T) {
+ if CONSTANT_FALSE || CONSTANT_FALSE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if CONSTANT_FALSE && CONSTANT_FALSE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+
+ if !CONSTANT_FALSE || CONSTANT_FALSE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if !CONSTANT_FALSE && CONSTANT_FALSE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+
+ if CONSTANT_FALSE || !CONSTANT_FALSE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if CONSTANT_FALSE && !CONSTANT_FALSE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+
+ if !(CONSTANT_FALSE || CONSTANT_FALSE) {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if !(CONSTANT_FALSE && CONSTANT_FALSE) {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+}
+
+@test
+test_const_false_const_true :: proc(t: ^testing.T) {
+ if CONSTANT_FALSE || CONSTANT_TRUE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if CONSTANT_FALSE && CONSTANT_TRUE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+
+ if !CONSTANT_FALSE || CONSTANT_TRUE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if !CONSTANT_FALSE && CONSTANT_TRUE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+
+ if CONSTANT_FALSE || !CONSTANT_TRUE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if CONSTANT_FALSE && !CONSTANT_TRUE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+
+ if !(CONSTANT_FALSE || CONSTANT_TRUE) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if !(CONSTANT_FALSE && CONSTANT_TRUE) {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+}
+
+@test
+test_const_true_const_false :: proc(t: ^testing.T) {
+ if CONSTANT_TRUE || CONSTANT_FALSE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if CONSTANT_TRUE && CONSTANT_FALSE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+
+ if !CONSTANT_TRUE || CONSTANT_FALSE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if !CONSTANT_TRUE && CONSTANT_FALSE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+
+ if CONSTANT_TRUE || !CONSTANT_FALSE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if CONSTANT_TRUE && !CONSTANT_FALSE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+
+ if !(CONSTANT_TRUE || CONSTANT_FALSE) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if !(CONSTANT_TRUE && CONSTANT_FALSE) {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+}
+
+@test
+test_const_true_const_true :: proc(t: ^testing.T) {
+ if CONSTANT_TRUE || CONSTANT_TRUE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if CONSTANT_TRUE && CONSTANT_TRUE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+
+ if !CONSTANT_TRUE || CONSTANT_TRUE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if !CONSTANT_TRUE && CONSTANT_TRUE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+
+ if CONSTANT_TRUE || !CONSTANT_TRUE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if CONSTANT_TRUE && !CONSTANT_TRUE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+
+ if !(CONSTANT_TRUE || CONSTANT_TRUE) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if !(CONSTANT_TRUE && CONSTANT_TRUE) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+}
+
+@test
+test_proc_false_const_false :: proc(t: ^testing.T) {
+ if false_result() || CONSTANT_FALSE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if false_result() && CONSTANT_FALSE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+
+ if !(false_result() || CONSTANT_FALSE) {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if !(false_result() && CONSTANT_FALSE) {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+}
+
+@test
+test_proc_false_const_true :: proc(t: ^testing.T) {
+ if false_result() || CONSTANT_TRUE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if false_result() && CONSTANT_TRUE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+
+ if !(false_result() || CONSTANT_TRUE) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if !(false_result() && CONSTANT_TRUE) {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+}
+
+@test
+test_proc_true_const_false :: proc(t: ^testing.T) {
+ if true_result() || CONSTANT_FALSE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if true_result() && CONSTANT_FALSE {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+
+ if !(true_result() || CONSTANT_FALSE) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if !(true_result() && CONSTANT_FALSE) {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+}
+
+@test
+test_proc_true_const_true :: proc(t: ^testing.T) {
+ if true_result() || CONSTANT_TRUE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+ if true_result() && CONSTANT_TRUE {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ } else {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ }
+
+ if !(true_result() || CONSTANT_TRUE) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+ if !(true_result() && CONSTANT_TRUE) {
+ tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+ } else {
+ tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+ }
+}
diff --git a/tests/issues/test_issue_829.odin b/tests/issues/test_issue_829.odin
new file mode 100644
index 000000000..4ff3d71f1
--- /dev/null
+++ b/tests/issues/test_issue_829.odin
@@ -0,0 +1,33 @@
+// Tests issue #829 https://github.com/odin-lang/Odin/issues/829
+package test_issues
+
+import "core:fmt"
+import "core:testing"
+import tc "tests:common"
+
+/* Original issue #829 example */
+
+env : map[string]proc(a, b : int) -> int = {
+ "+" = proc(a, b : int) -> int {
+ return a + b
+ },
+}
+
+test_orig :: proc() {
+ fmt.println(env["+"](1, 2))
+}
+
+main :: proc() {
+ t := testing.T{}
+
+ test_orig()
+
+ test_orig_ret(&t)
+
+ tc.report(&t)
+}
+
+test_orig_ret :: proc(t: ^testing.T) {
+ r := fmt.tprint(env["+"](1, 2))
+ tc.expect(t, r == "3", fmt.tprintf("%s: \"%s\" != \"3\"\n", #procedure, r))
+}
diff --git a/vendor/stb/image/stb_image.odin b/vendor/stb/image/stb_image.odin
index 4f7e43171..eedce5f04 100644
--- a/vendor/stb/image/stb_image.odin
+++ b/vendor/stb/image/stb_image.odin
@@ -6,6 +6,7 @@ import c "core:c/libc"
when ODIN_OS == .Windows { foreign import stbi "../lib/stb_image.lib" }
when ODIN_OS == .Linux { foreign import stbi "../lib/stb_image.a" }
+when ODIN_OS == .Darwin { foreign import stbi "../lib/stb_image.a" }
#assert(size_of(b32) == size_of(c.int))
diff --git a/vendor/stb/image/stb_image_resize.odin b/vendor/stb/image/stb_image_resize.odin
index 65bf3e4a9..362ec9315 100644
--- a/vendor/stb/image/stb_image_resize.odin
+++ b/vendor/stb/image/stb_image_resize.odin
@@ -4,6 +4,7 @@ import c "core:c/libc"
when ODIN_OS == .Windows { foreign import lib "../lib/stb_image_resize.lib" }
when ODIN_OS == .Linux { foreign import lib "../lib/stb_image_resize.a" }
+when ODIN_OS == .Darwin { foreign import lib "../lib/stb_image_resize.a" }
//////////////////////////////////////////////////////////////////////////////
//
diff --git a/vendor/stb/image/stb_image_write.odin b/vendor/stb/image/stb_image_write.odin
index 67f4299fa..b9433e821 100644
--- a/vendor/stb/image/stb_image_write.odin
+++ b/vendor/stb/image/stb_image_write.odin
@@ -4,6 +4,7 @@ import c "core:c/libc"
when ODIN_OS == .Windows { foreign import stbiw "../lib/stb_image_write.lib" }
when ODIN_OS == .Linux { foreign import stbiw "../lib/stb_image_write.a" }
+when ODIN_OS == .Darwin { foreign import stbiw "../lib/stb_image_write.a" }
write_func :: proc "c" (ctx: rawptr, data: rawptr, size: c.int)
diff --git a/vendor/stb/rect_pack/stb_rect_pack.odin b/vendor/stb/rect_pack/stb_rect_pack.odin
index f84f1cedc..c9f999bf7 100644
--- a/vendor/stb/rect_pack/stb_rect_pack.odin
+++ b/vendor/stb/rect_pack/stb_rect_pack.odin
@@ -6,6 +6,7 @@ import c "core:c/libc"
when ODIN_OS == .Windows { foreign import lib "../lib/stb_rect_pack.lib" }
when ODIN_OS == .Linux { foreign import lib "../lib/stb_rect_pack.a" }
+when ODIN_OS == .Darwin { foreign import lib "../lib/stb_rect_pack.a" }
Coord :: distinct c.int
_MAXVAL :: max(Coord)
diff --git a/vendor/stb/truetype/stb_truetype.odin b/vendor/stb/truetype/stb_truetype.odin
index cf4af15e9..b51cb037f 100644
--- a/vendor/stb/truetype/stb_truetype.odin
+++ b/vendor/stb/truetype/stb_truetype.odin
@@ -5,6 +5,7 @@ import stbrp "vendor:stb/rect_pack"
when ODIN_OS == .Windows { foreign import stbtt "../lib/stb_truetype.lib" }
when ODIN_OS == .Linux { foreign import stbtt "../lib/stb_truetype.a" }
+when ODIN_OS == .Darwin { foreign import stbtt "../lib/stb_truetype.a" }
///////////////////////////////////////////////////////////////////////////////
diff --git a/vendor/stb/vorbis/stb_vorbis.odin b/vendor/stb/vorbis/stb_vorbis.odin
index f35b58e04..43b9bc715 100644
--- a/vendor/stb/vorbis/stb_vorbis.odin
+++ b/vendor/stb/vorbis/stb_vorbis.odin
@@ -5,6 +5,7 @@ import c "core:c/libc"
when ODIN_OS == .Windows { foreign import lib "../lib/stb_vorbis.lib" }
when ODIN_OS == .Linux { foreign import lib "../lib/stb_vorbis.a" }
+when ODIN_OS == .Darwin { foreign import lib "../lib/stb_vorbis.a" }