Browse Source

merge upstream/master

CiD- 3 years ago
parent
commit
56737c1431
100 changed files with 5267 additions and 5113 deletions
  1. 12 0
      .github/workflows/ci.yml
  2. 6 6
      .github/workflows/nightly.yml
  3. 4 75
      Makefile
  4. 9 7
      README.md
  5. 11 1
      build_odin.sh
  6. 3 6
      core/bufio/scanner.odin
  7. 111 146
      core/c/libc/stdatomic.odin
  8. 6 1
      core/compress/common.odin
  9. 29 29
      core/compress/zlib/zlib.odin
  10. 3 3
      core/container/small_array/small_array.odin
  11. 25 4
      core/encoding/hxa/read.odin
  12. 3 3
      core/encoding/hxa/write.odin
  13. 4 3
      core/encoding/json/marshal.odin
  14. 2 1
      core/image/common.odin
  15. 3 3
      core/image/png/helpers.odin
  16. 41 68
      core/intrinsics/intrinsics.odin
  17. 483 0
      core/math/ease/ease.odin
  18. 2 2
      core/math/math.odin
  19. 18 1
      core/mem/allocators.odin
  20. 1 1
      core/mem/mem.odin
  21. 1 1
      core/mem/virtual/virtual_platform.odin
  22. 8 2
      core/odin/ast/ast.odin
  23. 4 1
      core/odin/ast/walk.odin
  24. 28 4
      core/odin/parser/parser.odin
  25. 5 2
      core/odin/printer/visit.odin
  26. 1 0
      core/os/dir_windows.odin
  27. 3 6
      core/os/file_windows.odin
  28. 1 1
      core/os/os2/process.odin
  29. 0 1
      core/os/stat.odin
  30. 6 6
      core/os/stat_windows.odin
  31. 10 4
      core/path/filepath/match.odin
  32. 9 3
      core/path/filepath/path.odin
  33. 2 0
      core/reflect/reflect.odin
  34. 1 0
      core/runtime/core.odin
  35. 17 14
      core/runtime/core_builtin.odin
  36. 1 1
      core/runtime/entry_unix.odin
  37. 2 0
      core/strings/ascii_set.odin
  38. 117 13
      core/strings/builder.odin
  39. 47 7
      core/strings/conversion.odin
  40. 12 0
      core/strings/intern.odin
  41. 34 2
      core/strings/reader.odin
  42. 543 134
      core/strings/strings.odin
  43. 41 164
      core/sync/atomic.odin
  44. 0 80
      core/sync/barrier.odin
  45. 0 889
      core/sync/channel.odin
  46. 0 16
      core/sync/channel_unix.odin
  47. 0 33
      core/sync/channel_windows.odin
  48. 16 16
      core/sync/extended.odin
  49. 1 1
      core/sync/futex_darwin.odin
  50. 1 1
      core/sync/futex_freebsd.odin
  51. 1 7
      core/sync/futex_linux.odin
  52. 1 1
      core/sync/futex_openbsd.odin
  53. 1 1
      core/sync/futex_windows.odin
  54. 1 1
      core/sync/primitives.odin
  55. 12 13
      core/sync/primitives_atomic.odin
  56. 1 1
      core/sync/primitives_darwin.odin
  57. 46 0
      core/sync/primitives_freebsd.odin
  58. 125 0
      core/sync/primitives_internal.odin
  59. 47 0
      core/sync/primitives_linux.odin
  60. 46 0
      core/sync/primitives_openbsd.odin
  61. 1 1
      core/sync/primitives_windows.odin
  62. 6 6
      core/sync/sema_internal.odin
  63. 0 123
      core/sync/sync.odin
  64. 0 79
      core/sync/sync2/atomic.odin
  65. 0 9
      core/sync/sync2/primitives_freebsd.odin
  66. 0 184
      core/sync/sync2/primitives_internal.odin
  67. 0 9
      core/sync/sync2/primitives_linux.odin
  68. 0 9
      core/sync/sync2/primitives_openbsd.odin
  69. 0 58
      core/sync/sync2/primitives_pthreads.odin
  70. 0 54
      core/sync/sync_darwin.odin
  71. 0 40
      core/sync/sync_freebsd.odin
  72. 0 36
      core/sync/sync_linux.odin
  73. 0 36
      core/sync/sync_openbsd.odin
  74. 0 248
      core/sync/sync_unix.odin
  75. 1 1
      core/sync/sync_util.odin
  76. 0 180
      core/sync/sync_windows.odin
  77. 0 58
      core/sync/wait_group.odin
  78. 2 0
      core/sys/win32/user32.odin
  79. 9 0
      core/sys/windows/dwmapi.odin
  80. 66 0
      core/sys/windows/gdi32.odin
  81. 10 1
      core/sys/windows/kernel32.odin
  82. 252 0
      core/sys/windows/key_codes.odin
  83. 861 32
      core/sys/windows/types.odin
  84. 252 0
      core/sys/windows/user32.odin
  85. 8 4
      core/sys/windows/util.odin
  86. 1048 0
      core/sys/windows/window_messages.odin
  87. 2 2
      core/testing/runner_windows.odin
  88. 148 81
      core/thread/thread_pool.odin
  89. 38 79
      core/thread/thread_unix.odin
  90. 30 13
      core/thread/thread_windows.odin
  91. 60 60
      core/unicode/utf8/utf8string/string.odin
  92. 2 2
      examples/all/all_main.odin
  93. 14 8
      examples/demo/demo.odin
  94. 0 7
      examples/hms2019/basic.odin
  95. 0 67
      examples/hms2019/eca.odin
  96. 0 1754
      examples/hms2019/hms2019.odin
  97. 22 1
      src/build_settings.cpp
  98. 395 66
      src/check_builtin.cpp
  99. 3 1
      src/check_decl.cpp
  100. 69 18
      src/check_expr.cpp

+ 12 - 0
.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: |

+ 6 - 6
.github/workflows/nightly.yml

@@ -19,7 +19,7 @@ jobs:
         shell: cmd
         run: |
           call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
-          odin run examples/demo/demo.odin
+          odin run examples/demo
       - name: Copy artifacts
         run: |
           rm bin/llvm/windows/LLVM-C.lib
@@ -41,11 +41,11 @@ jobs:
     steps:
       - uses: actions/checkout@v1
       - name: (Linux) Download LLVM
-        run: sudo apt-get install llvm-11 clang-11 llvm
+        run: sudo apt-get install llvm-11 clang-11
       - name: build odin
         run: make nightly
       - name: Odin run
-        run: ./odin run examples/demo/demo.odin
+        run: ./odin run examples/demo
       - name: Copy artifacts
         run: |
           mkdir dist
@@ -72,7 +72,7 @@ jobs:
       - name: build odin
         run: make nightly
       - name: Odin run
-        run: ./odin run examples/demo/demo.odin
+        run: ./odin run examples/demo
       - name: Copy artifacts
         run: |
           mkdir dist
@@ -129,7 +129,7 @@ jobs:
         run: |
           echo Authorizing B2 account
           b2 authorize-account "$APPID" "$APPKEY"
-          
+
           echo Uploading artifcates to B2
           chmod +x ./ci/upload_create_nightly.sh
           ./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
@@ -141,7 +141,7 @@ jobs:
 
           echo Creating nightly.json
           python3 ci/create_nightly_json.py "$BUCKET" > nightly.json
-          
+
           echo Uploading nightly.json
           b2 upload-file "$BUCKET" nightly.json nightly.json
 

+ 4 - 75
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

+ 9 - 7
README.md

@@ -11,7 +11,7 @@
         <img src="https://img.shields.io/badge/platforms-Windows%20|%20Linux%20|%20macOS-green.svg">
     </a>
     <br>
-    <a href="https://discord.gg/hnwN2Rj">
+    <a href="https://discord.gg/odinlang">
         <img src="https://img.shields.io/discord/568138951836172421?logo=discord">
     </a>
     <a href="https://github.com/odin-lang/odin/actions">
@@ -58,6 +58,10 @@ main :: proc() {
 
 Instructions for downloading and installing the Odin compiler and libraries.
 
+#### [Nightly Builds](https://odin-lang.org/docs/nightly/)
+
+Get the latest nightly builds of Odin.
+
 ### Learning Odin
 
 #### [Overview of Odin](https://odin-lang.org/docs/overview)
@@ -68,6 +72,10 @@ An overview of the Odin programming language.
 
 Answers to common questions about Odin.
 
+#### [Packages](https://pkg.odin-lang.org/)
+
+Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections.
+
 #### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki)
 
 A wiki maintained by the Odin community.
@@ -76,12 +84,6 @@ A wiki maintained by the Odin community.
 
 Get live support and talk with other odiners on the Odin Discord.
 
-### References
-
-#### [Language Specification](https://odin-lang.org/docs/spec/)
-
-The official Odin Language specification.
-
 ### Articles
 
 #### [The Odin Blog](https://odin-lang.org/news/)

+ 11 - 1
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
 
@@ -95,7 +102,7 @@ build_odin() {
 }
 
 run_demo() {
-	./odin run examples/demo/demo.odin
+	./odin run examples/demo/demo.odin -file
 }
 
 case $OS in
@@ -108,6 +115,9 @@ Darwin)
 OpenBSD)
 	config_openbsd
 	;;
+FreeBSD)
+	config_freebsd
+	;;
 *)
 	panic "Platform unsupported!"
 esac

+ 3 - 6
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

+ 111 - 146
core/c/libc/stdatomic.odin

@@ -47,29 +47,30 @@ 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
 atomic_is_lock_free :: #force_inline proc(obj: ^$T) -> bool {
-	return size_of(T) <= 8 && (intrinsics.type_is_integer(T) || intrinsics.type_is_pointer(T))
+	return intrinsics.atomic_type_is_lock_free(T)
 }
 
 // 7.17.6 Atomic integer types
@@ -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)
 	}
 }
 

+ 6 - 1
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,

+ 29 - 29
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

+ 3 - 3
core/container/small_array/small_array.odin

@@ -25,14 +25,14 @@ slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
 }
 
 
-get :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> T {
+get :: proc(a: $A/Small_Array($N, $T), index: int) -> T {
 	return a.data[index]
 }
-get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T {
+get_ptr :: proc(a: ^$A/Small_Array($N, $T), index: int) -> ^T {
 	return &a.data[index]
 }
 
-set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) {
+set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) {
 	a.data[index] = item
 }
 

+ 25 - 4
core/encoding/hxa/read.odin

@@ -39,6 +39,9 @@ read :: proc(data: []byte, filename := "<input>", 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 := "<input>", 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 := "<input>", 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 := "<input>", 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 := "<input>", 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 := "<input>", 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 := "<input>", 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

+ 3 - 3
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)),

+ 4 - 3
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 {

+ 2 - 1
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,

+ 3 - 3
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)

+ 41 - 68
core/intrinsics/intrinsics.odin

@@ -62,77 +62,46 @@ 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_type_is_lock_free :: proc($T: typeid) -> bool ---
+
+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_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_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_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 ---
+atomic_load_explicit  :: proc(dst: ^$T, order: Atomic_Memory_Order) -> 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_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 ---
+
 
 // Constant type tests
 
@@ -201,6 +170,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() ---

+ 483 - 0
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
+	}
+}

+ 2 - 2
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..<len(x) {
 		x[i] = x[i-1] + x[i]
@@ -1736,4 +1736,4 @@ INF_F64     :f64: 0h7FF0_0000_0000_0000
 NEG_INF_F64 :f64: 0hFFF0_0000_0000_0000
 
 SNAN_F64    :f64: 0h7FF0_0000_0000_0001
-QNAN_F64    :f64: 0h7FF8_0000_0000_0001
+QNAN_F64    :f64: 0h7FF8_0000_0000_0001

+ 18 - 1
core/mem/allocators.odin

@@ -6,7 +6,24 @@ import "core:runtime"
 nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
                            size, alignment: int,
                            old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
-	return nil, 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 {

+ 1 - 1
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) {

+ 1 - 1
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,

+ 8 - 2
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,

+ 4 - 1
core/odin/ast/walk.odin

@@ -52,8 +52,11 @@ walk :: proc(v: ^Visitor, node: ^Node) {
 		}
 	}
 
-
 	v := v
+	if v == nil || node == nil {
+		return
+	}
+
 	if v = v->visit(node); v == nil {
 		return
 	}

+ 28 - 4
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
 

+ 5 - 2
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)

+ 1 - 0
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)

+ 3 - 6
core/os/file_windows.odin

@@ -20,13 +20,13 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errn
 	case O_RDWR:   access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE
 	}
 
+	if mode&O_CREATE != 0 {
+		access |= win32.FILE_GENERIC_WRITE
+	}
 	if mode&O_APPEND != 0 {
 		access &~= win32.FILE_GENERIC_WRITE
 		access |=  win32.FILE_APPEND_DATA
 	}
-	if mode&O_CREATE != 0 {
-		access |= win32.FILE_GENERIC_WRITE
-	}
 
 	share_mode := win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE
 	sa: ^win32.SECURITY_ATTRIBUTES = nil
@@ -320,9 +320,6 @@ stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
 
 get_std_handle :: proc "contextless" (h: uint) -> Handle {
 	fd := win32.GetStdHandle(win32.DWORD(h))
-	when size_of(uintptr) == 8 {
-		win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0)
-	}
 	return Handle(fd)
 }
 

+ 1 - 1
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"
 

+ 0 - 1
core/os/stat.odin

@@ -2,7 +2,6 @@ package os
 
 import "core:time"
 
-
 File_Info :: struct {
 	fullpath: string,
 	name:     string,

+ 6 - 6
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
 }
 
 

+ 10 - 4
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
 		}

+ 9 - 3
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)

+ 2 - 0
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)

+ 1 - 0
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,

+ 17 - 14
core/runtime/core_builtin.odin

@@ -386,12 +386,13 @@ insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
 	if array == nil {
 		return
 	}
-	n := len(array)
+	n := max(len(array), index)
 	m :: 1
-	resize(array, n+m, loc)
-	if n+m <= len(array) {
+	new_size := n + m
+
+	if resize(array, new_size, loc) {
 		when size_of(E) != 0 {
-			copy(array[index+m:], array[index:])
+			copy(array[index + m:], array[index:])
 			array[index] = arg
 		}
 		ok = true
@@ -409,12 +410,13 @@ insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c
 		return
 	}
 
-	n := len(array)
+	n := max(len(array), index)
 	m := len(args)
-	resize(array, n+m, loc)
-	if n+m <= len(array) {
+	new_size := n + m
+
+	if resize(array, new_size, loc) {
 		when size_of(E) != 0 {
-			copy(array[index+m:], array[index:])
+			copy(array[index + m:], array[index:])
 			copy(array[index:], args)
 		}
 		ok = true
@@ -427,17 +429,18 @@ insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
 	if array == nil {
 		return
 	}
-	if len(args) == 0 {
+	if len(arg) == 0 {
 		ok = true
 		return
 	}
 
-	n := len(array)
-	m := len(args)
-	resize(array, n+m, loc)
-	if n+m <= len(array) {
+	n := max(len(array), index)
+	m := len(arg)
+	new_size := n + m
+
+	if resize(array, new_size, loc) {
 		copy(array[index+m:], array[index:])
-		copy(array[index:], args)
+		copy(array[index:], arg)
 		ok = true
 	}
 	return

+ 1 - 1
core/runtime/entry_unix.odin

@@ -30,4 +30,4 @@ when ODIN_BUILD_MODE == .Dynamic {
 		#force_no_inline _cleanup_runtime()
 		return 0
 	}
-}
+}

+ 2 - 0
core/strings/ascii_set.odin

@@ -5,6 +5,7 @@ import "core:unicode/utf8"
 
 Ascii_Set :: distinct [8]u32
 
+// create an ascii set of all unique characters in the string
 ascii_set_make :: proc(chars: string) -> (as: Ascii_Set, ok: bool) #no_bounds_check {
 	for i in 0..<len(chars) {
 		c := chars[i]
@@ -17,6 +18,7 @@ ascii_set_make :: proc(chars: string) -> (as: Ascii_Set, ok: bool) #no_bounds_ch
 	return
 }
 
+// returns true when the `c` byte is contained in the `as` ascii set
 ascii_set_contains :: proc(as: Ascii_Set, c: byte) -> bool #no_bounds_check {
 	return as[c>>5] & (1<<(c&31)) != 0
 }

+ 117 - 13
core/strings/builder.odin

@@ -7,40 +7,56 @@ import "core:io"
 
 Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool)
 
+/*
+	dynamic byte buffer / string builder with helper procedures
+	the dynamic array is wrapped inside the struct to be more opaque
+	you can use `fmt.sbprint*` procedures with a `^strings.Builder` directly
+*/
 Builder :: struct {
 	buf: [dynamic]byte,
 }
 
+// return a builder, default length 0 / cap 16 are done through make
 make_builder_none :: proc(allocator := context.allocator) -> Builder {
 	return Builder{buf=make([dynamic]byte, allocator)}
 }
 
+// return a builder, with a set length `len` and cap 16 byte buffer
 make_builder_len :: proc(len: int, allocator := context.allocator) -> Builder {
 	return Builder{buf=make([dynamic]byte, len, allocator)}
 }
 
+// return a builder, with a set length `len` byte buffer and a custom `cap`
 make_builder_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder {
 	return Builder{buf=make([dynamic]byte, len, cap, allocator)}
 }
 
+// overload simple `make_builder_*` with or without len / cap parameters
 make_builder :: proc{
 	make_builder_none,
 	make_builder_len,
 	make_builder_len_cap,
 }
 
+// initialize a builder, default length 0 / cap 16 are done through make
+// replaces the existing `buf`
 init_builder_none :: proc(b: ^Builder, allocator := context.allocator) {
 	b.buf = make([dynamic]byte, allocator)
 }
 
+// initialize a builder, with a set length `len` and cap 16 byte buffer
+// replaces the existing `buf`
 init_builder_len :: proc(b: ^Builder, len: int, allocator := context.allocator) {
 	b.buf = make([dynamic]byte, len, allocator)
 }
 
+// initialize a builder, with a set length `len` byte buffer and a custom `cap`
+// replaces the existing `buf`
 init_builder_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) {
 	b.buf = make([dynamic]byte, len, cap, allocator)
 }
 
+// overload simple `init_builder_*` with or without len / ap parameters
 init_builder :: proc{
 	init_builder_none,
 	init_builder_len,
@@ -76,30 +92,42 @@ _builder_stream_vtable := &io.Stream_VTable{
 	},
 }
 
+// return an `io.Stream` from a builder
 to_stream :: proc(b: ^Builder) -> io.Stream {
 	return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b}
 }
+
+// return an `io.Writer` from a builder
 to_writer :: proc(b: ^Builder) -> io.Writer {
 	return io.to_writer(to_stream(b))
 }
 
-
-
-
+// delete and clear the builder byte buffer content
 destroy_builder :: proc(b: ^Builder) {
 	delete(b.buf)
 	clear(&b.buf)
 }
 
+// reserve the builfer byte buffer to a specific cap, when it's higher than before
 grow_builder :: proc(b: ^Builder, cap: int) {
 	reserve(&b.buf, cap)
 }
 
+// clear the builder byte buffer content
 reset_builder :: proc(b: ^Builder) {
 	clear(&b.buf)
 }
 
-
+/*
+	create an empty builder with the same slice length as its cap
+	uses the `mem.nil_allocator` to avoid allocation and keep a fixed length
+	used in `fmt.bprint*`
+	
+	bytes: [8]byte // <-- gets filled
+	builder := strings.builder_from_slice(bytes[:])
+	strings.write_byte(&builder, 'a') -> "a"
+	strings.write_byte(&builder, 'b') -> "ab"
+*/
 builder_from_slice :: proc(backing: []byte) -> Builder {
 	s := transmute(mem.Raw_Slice)backing
 	d := mem.Raw_Dynamic_Array{
@@ -112,20 +140,36 @@ builder_from_slice :: proc(backing: []byte) -> Builder {
 		buf = transmute([dynamic]byte)d,
 	}
 }
+
+// cast the builder byte buffer to a string and return it
 to_string :: proc(b: Builder) -> string {
 	return string(b.buf[:])
 }
 
+// return the length of the builder byte buffer
 builder_len :: proc(b: Builder) -> int {
 	return len(b.buf)
 }
+
+// return the cap of the builder byte buffer
 builder_cap :: proc(b: Builder) -> int {
 	return cap(b.buf)
 }
+
+// returns the space left in the builder byte buffer to use up
 builder_space :: proc(b: Builder) -> int {
-	return max(cap(b.buf), len(b.buf), 0)
+	return cap(b.buf) - len(b.buf)
 }
 
+/*
+	appends a byte to the builder, returns the append diff
+
+	builder := strings.make_builder()
+	strings.write_byte(&builder, 'a') // 1
+	strings.write_byte(&builder, 'b') // 1
+	strings.write_byte(&builder, 'c') // 1
+	fmt.println(strings.to_string(builder)) // -> abc
+*/
 write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
 	n0 := len(b.buf)
 	append(&b.buf, x)
@@ -133,6 +177,14 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
 	return n1-n0
 }
 
+/*
+	appends a slice of bytes to the builder, returns the append diff
+
+	builder := strings.make_builder()
+	bytes := [?]byte { 'a', 'b', 'c' }
+	strings.write_bytes(&builder, bytes[:]) // 3
+	fmt.println(strings.to_string(builder)) // -> abc
+*/
 write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
 	n0 := len(b.buf)
 	append(&b.buf, ..x)
@@ -140,11 +192,28 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
 	return n1-n0
 }
 
+/*
+	appends a single rune into the builder, returns written rune size and an `io.Error`
+
+	builder := strings.make_builder()
+	strings.write_rune_builder(&builder, 'ä') // 2 None
+	strings.write_rune_builder(&builder, 'b') // 1 None
+	strings.write_rune_builder(&builder, 'c') // 1 None
+	fmt.println(strings.to_string(builder)) // -> äbc
+*/
 write_rune_builder :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
 	return io.write_rune(to_writer(b), r)
 }
 
+/*
+	appends a quoted rune into the builder, returns written size
 
+	builder := strings.make_builder()
+	strings.write_string(&builder, "abc") // 3
+	strings.write_quoted_rune_builder(&builder, 'ä') // 4
+	strings.write_string(&builder, "abc") // 3
+	fmt.println(strings.to_string(builder)) // -> abc'ä'abc
+*/
 write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) {
 	return write_quoted_rune(to_writer(b), r)
 }
@@ -155,7 +224,7 @@ _write_byte :: proc(w: io.Writer, c: byte) -> int {
 	return 1 if err == nil else 0
 }
 
-
+// writer append a quoted rune into the byte buffer, return the written size
 write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
 	quote := byte('\'')
 	n += _write_byte(w, quote)
@@ -173,50 +242,75 @@ write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
 	return
 }
 
-
+// overload for `write_string_*` variants
 write_string :: proc{
 	write_string_builder,
 	write_string_writer,
 }
 
+/*
+	appends a string to the builder, return the written byte size
+	
+	builder := strings.make_builder()
+	strings.write_string(&builder, "a") // 1
+	strings.write_string(&builder, "bc") // 2	
+	strings.write_string(&builder, "xyz") // 3
+	fmt.println(strings.to_string(builder)) // -> abcxyz
+*/
 write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) {
 	return write_string_writer(to_writer(b), s)
 }
 
+// appends a string to the writer
 write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) {
 	n, _ = io.write(w, transmute([]byte)s)
 	return
 }
 
-
-
-
+// pops and returns the last byte in the builder
+// returns 0 when the builder is empty
 pop_byte :: proc(b: ^Builder) -> (r: byte) {
 	if len(b.buf) == 0 {
 		return 0
 	}
+
 	r = b.buf[len(b.buf)-1]
 	d := cast(^mem.Raw_Dynamic_Array)&b.buf
 	d.len = max(d.len-1, 0)
 	return
 }
 
+// pops the last rune in the builder and returns the popped rune and its rune width
+// returns 0, 0 when the builder is empty
 pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
+	if len(b.buf) == 0 {
+		return 0, 0
+	}
+
 	r, width = utf8.decode_last_rune(b.buf[:])
 	d := cast(^mem.Raw_Dynamic_Array)&b.buf
 	d.len = max(d.len-width, 0)
 	return
 }
 
-
 @(private)
 DIGITS_LOWER := "0123456789abcdefx"
 
+// overload for `write_quoted_string_*` variants
 write_quoted_string :: proc{
 	write_quoted_string_builder,
 	write_quoted_string_writer,
 }
 
+/*
+	append a quoted string into the builder, return the written byte size
+
+	builder := strings.make_builder()
+	strings.write_quoted_string(&builder, "a") // 3
+	strings.write_quoted_string(&builder, "bc", '\'') // 4	
+	strings.write_quoted_string(&builder, "xyz") // 5
+	fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz"
+*/
 write_quoted_string_builder :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
 	n, _ = io.write_quoted_string(to_writer(b), str, quote)
 	return
@@ -228,11 +322,13 @@ write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"')
 	return	
 }
 
+// overload for `write_encoded_rune_*`
 write_encoded_rune :: proc{
 	write_encoded_rune_builder,
 	write_encoded_rune_writer,
 }
 
+// appends a rune to the builder, optional `write_quote` boolean tag, returns the written rune size
 write_encoded_rune_builder :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
 	n, _ = io.write_encoded_rune(to_writer(b), r, write_quote)
 	return
@@ -244,12 +340,15 @@ write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) ->
 	return
 }
 
-
+// overload for `write_escaped_rune_*`
 write_escaped_rune :: proc{
 	write_escaped_rune_builder,
 	write_escaped_rune_writer,
 }
 
+// appends a rune to the builder, fully written out in case of escaped runes e.g. '\a' will be written as such
+// when `r` and `quote` match and `quote` is `\\` - they will be written as two slashes
+// `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026`
 write_escaped_rune_builder :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
 	n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe)
 	return
@@ -261,21 +360,26 @@ write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe
 	return
 }
 
-
+// writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters
 write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
 	buf: [32]byte
 	s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil)
 	return write_string(b, s)
 }
+
+// writes a i64 value `i` in `base` = 10 into the builder, returns the written amount of characters
 write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) {
 	buf: [32]byte
 	s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
 	return write_string(b, s)
 }
 
+// writes a uint value `i` in `base` = 10 into the builder, returns the written amount of characters
 write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) {
 	return write_u64(b, u64(i), base)
 }
+
+// writes a int value `i` in `base` = 10 into the builder, returns the written amount of characters
 write_int :: proc(b: ^Builder, i: int, base: int = 10) -> (n: int) {
 	return write_i64(b, i64(i), base)
 }

+ 47 - 7
core/strings/conversion.odin

@@ -58,6 +58,13 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
 	return to_string(b)
 }
 
+/*
+	returns the input string `s` with all runes set to lowered case
+	always allocates using the `allocator`
+
+	strings.to_lower("test") -> test	
+	strings.to_lower("Test") -> test	
+*/
 to_lower :: proc(s: string, allocator := context.allocator) -> string {
 	b: Builder
 	init_builder(&b, 0, len(s), allocator)
@@ -66,6 +73,14 @@ to_lower :: proc(s: string, allocator := context.allocator) -> string {
 	}
 	return to_string(b)
 }
+
+/*
+	returns the input string `s` with all runes set to upper case
+	always allocates using the `allocator`
+
+	strings.to_lower("test") -> TEST
+	strings.to_lower("Test") -> TEST
+*/
 to_upper :: proc(s: string, allocator := context.allocator) -> string {
 	b: Builder
 	init_builder(&b, 0, len(s), allocator)
@@ -75,13 +90,13 @@ to_upper :: proc(s: string, allocator := context.allocator) -> string {
 	return to_string(b)
 }
 
-
-
-
+// returns true when the `c` rune is a space, '-' or '_' 
+// useful when treating strings like words in a text editor or html paths 
 is_delimiter :: proc(c: rune) -> bool {
 	return c == '-' || c == '_' || is_space(c)
 }
 
+// returns true when the `r` rune is a non alpha or `unicode.is_space` rune
 is_separator :: proc(r: rune) -> bool {
 	if r <= 0x7f {
 		switch r {
@@ -101,7 +116,10 @@ is_separator :: proc(r: rune) -> bool {
 	return unicode.is_space(r)
 }
 
-
+/*
+	iterator that loops through the string and calls the callback with the `prev`, `curr` and `next` rune
+	on empty string `s` the callback gets called once with empty runes
+*/
 string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Writer, prev, curr, next: rune)) {
 	prev, curr: rune
 	for next in s {
@@ -122,8 +140,9 @@ string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Write
 	}
 }
 
-
 to_lower_camel_case :: to_camel_case
+
+// converts the `s` string to "lowerCamelCase"
 to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
 	s := s
 	s = trim_space(s)
@@ -147,6 +166,8 @@ to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
 }
 
 to_upper_camel_case :: to_pascal_case
+
+// converts the `s` string to "PascalCase"
 to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
 	s := s
 	s = trim_space(s)
@@ -169,6 +190,15 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
 	return to_string(b)
 }
 
+/* 
+	returns the `s` string to words seperated by the given `delimiter` rune
+	all runes will be upper or lowercased based on the `all_uppercase` bool
+
+	strings.to_delimiter_case("Hello World", '_', false) -> hello_world
+	strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD
+	strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD
+	strings.to_delimiter_case("aBC", '_', false) -> a_b_c
+*/
 to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string {
 	s := s
 	s = trim_space(s)
@@ -208,24 +238,34 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo
 	return to_string(b)
 }
 
-
+/* 
+	converts the `s` string to "snake_case" with all runes lowercased
+	
+	strings.to_snake_case("HelloWorld") -> hello_world
+	strings.to_snake_case("Hello World") -> hello_world
+*/
 to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
 	return to_delimiter_case(s, '_', false, allocator)
 }
 
 to_screaming_snake_case :: to_upper_snake_case
+
+// converts the `s` string to "SNAKE_CASE" with all runes uppercased
 to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
 	return to_delimiter_case(s, '_', true, allocator)
 }
 
+// converts the `s` string to "kebab-case" with all runes lowercased
 to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
 	return to_delimiter_case(s, '-', false, allocator)
 }
 
-to_upper_case :: proc(s: string, allocator := context.allocator) -> string {
+// converts the `s` string to "KEBAB-CASE" with all runes uppercased
+to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
 	return to_delimiter_case(s, '-', true, allocator)
 }
 
+// converts the `s` string to "Ada_case"
 to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
 	delimiter :: '_'
 

+ 12 - 0
core/strings/intern.odin

@@ -2,21 +2,26 @@ package strings
 
 import "core:mem"
 
+// custom string entry struct
 Intern_Entry :: struct {
 	len:  int,
 	str:  [1]byte, // string is allocated inline with the entry to keep allocations simple
 }
 
+// "intern" is a more memory efficient string map
+// `allocator` is used to allocate the actual `Intern_Entry` strings
 Intern :: struct {
 	allocator: mem.Allocator,
 	entries: map[string]^Intern_Entry,
 }
 
+// initialize the entries map and set the allocator for the string entries
 intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator) {
 	m.allocator = allocator
 	m.entries = make(map[string]^Intern_Entry, 16, map_allocator)
 }
 
+// free the map and all its content allocated using the `.allocator`
 intern_destroy :: proc(m: ^Intern) {
 	for _, value in m.entries {
 		free(value, m.allocator)
@@ -24,15 +29,22 @@ intern_destroy :: proc(m: ^Intern) {
 	delete(m.entries)
 }
 
+// returns the `text` string from the intern map - gets set if it didnt exist yet
+// the returned string lives as long as the map entry lives
 intern_get :: proc(m: ^Intern, text: string) -> string {
 	entry := _intern_get_entry(m, text)
 	#no_bounds_check return string(entry.str[:entry.len])
 }
+
+// returns the `text` cstring from the intern map - gets set if it didnt exist yet
+// the returned cstring lives as long as the map entry lives
 intern_get_cstring :: proc(m: ^Intern, text: string) -> cstring {
 	entry := _intern_get_entry(m, text)
 	return cstring(&entry.str[0])
 }
 
+// looks up wether the `text` string exists in the map, returns the entry
+// sets & allocates the entry if it wasnt set yet
 _intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_check {
 	if prev, ok := m.entries[text]; ok {
 		return prev

+ 34 - 2
core/strings/reader.odin

@@ -3,46 +3,60 @@ package strings
 import "core:io"
 import "core:unicode/utf8"
 
+/*
+	io stream data for a string reader that can read based on bytes or runes
+	implements the vtable when using the io.Reader variants
+	"read" calls advance the current reading offset `i`
+*/
 Reader :: struct {
 	s:         string, // read-only buffer
 	i:         i64,    // current reading index
 	prev_rune: int,    // previous reading index of rune or < 0
 }
 
+// init the reader to the string `s` 
 reader_init :: proc(r: ^Reader, s: string) {
 	r.s = s
 	r.i = 0
 	r.prev_rune = -1
 }
 
+// returns a stream from the reader data
 reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
 	s.stream_data = r
 	s.stream_vtable = _reader_vtable
 	return
 }
 
+// init a reader to the string `s` and return an io.Reader
 to_reader :: proc(r: ^Reader, s: string) -> io.Reader {
 	reader_init(r, s)
 	rr, _ := io.to_reader(reader_to_stream(r))
 	return rr
 }
+
+// init a reader to the string `s` and return an io.Reader_At
 to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At {
 	reader_init(r, s)
 	rr, _ := io.to_reader_at(reader_to_stream(r))
 	return rr
 }
+
+// init a reader to the string `s` and return an io.Byte_Reader
 to_byte_reader :: proc(r: ^Reader, s: string) -> io.Byte_Reader {
 	reader_init(r, s)
 	rr, _ := io.to_byte_reader(reader_to_stream(r))
 	return rr
 }
+
+// init a reader to the string `s` and return an io.Rune_Reader
 to_rune_reader :: proc(r: ^Reader, s: string) -> io.Rune_Reader {
 	reader_init(r, s)
 	rr, _ := io.to_rune_reader(reader_to_stream(r))
 	return rr
 }
 
-
+// remaining length of the reader 
 reader_length :: proc(r: ^Reader) -> int {
 	if r.i >= i64(len(r.s)) {
 		return 0
@@ -50,10 +64,13 @@ reader_length :: proc(r: ^Reader) -> int {
 	return int(i64(len(r.s)) - r.i)
 }
 
+// returns the string length stored by the reader
 reader_size :: proc(r: ^Reader) -> i64 {
 	return i64(len(r.s))
 }
 
+// reads len(p) bytes into the slice from the string in the reader
+// returns `n` amount of read bytes and an io.Error
 reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
 	if r.i >= i64(len(r.s)) {
 		return 0, .EOF
@@ -63,6 +80,9 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
 	r.i += i64(n)
 	return
 }
+
+// reads len(p) bytes into the slice from the string in the reader at an offset
+// returns `n` amount of read bytes and an io.Error
 reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
 	if off < 0 {
 		return 0, .Invalid_Offset
@@ -76,6 +96,8 @@ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Erro
 	}
 	return
 }
+
+// reads and returns a single byte - error when out of bounds
 reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
 	r.prev_rune = -1
 	if r.i >= i64(len(r.s)) {
@@ -85,6 +107,8 @@ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
 	r.i += 1
 	return b, nil
 }
+
+// decreases the reader offset - error when below 0
 reader_unread_byte :: proc(r: ^Reader) -> io.Error {
 	if r.i <= 0 {
 		return .Invalid_Unread
@@ -93,6 +117,8 @@ reader_unread_byte :: proc(r: ^Reader) -> io.Error {
 	r.i -= 1
 	return nil
 }
+
+// reads and returns a single rune and the rune size - error when out bounds
 reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
 	if r.i >= i64(len(r.s)) {
 		r.prev_rune = -1
@@ -107,6 +133,9 @@ reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
 	r.i += i64(size)
 	return
 }
+
+// decreases the reader offset by the last rune
+// can only be used once and after a valid read_rune call
 reader_unread_rune :: proc(r: ^Reader) -> io.Error {
 	if r.i <= 0 {
 		return .Invalid_Unread
@@ -118,6 +147,8 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error {
 	r.prev_rune = -1
 	return nil
 }
+
+// seeks the reader offset to a wanted offset 
 reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
 	r.prev_rune = -1
 	abs: i64
@@ -138,6 +169,8 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E
 	r.i = abs
 	return abs, nil
 }
+
+// writes the string content left to read into the io.Writer `w`
 reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
 	r.prev_rune = -1
 	if r.i >= i64(len(r.s)) {
@@ -157,7 +190,6 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
 	return
 }
 
-
 @(private)
 _reader_vtable := &io.Stream_VTable{
 	impl_size = proc(s: io.Stream) -> i64 {

File diff suppressed because it is too large
+ 543 - 134
core/strings/strings.odin


+ 41 - 164
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") }
-}
-
-
-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") }
-}
-
+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_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

+ 0 - 80
core/sync/barrier.odin

@@ -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
-}

+ 0 - 889
core/sync/channel.odin

@@ -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
-}
-

+ 0 - 16
core/sync/channel_unix.odin

@@ -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
-}

+ 0 - 33
core/sync/channel_windows.odin

@@ -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)
-	}
-}

+ 16 - 16
core/sync/sync2/extended.odin → 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, 0, 1, .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, 0, 1, .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)
 	}
 }

+ 1 - 1
core/sync/sync2/futex_darwin.odin → core/sync/futex_darwin.odin

@@ -1,6 +1,6 @@
 //+private
 //+build darwin
-package sync2
+package sync
 
 import "core:c"
 import "core:time"

+ 1 - 1
core/sync/sync2/futex_freebsd.odin → core/sync/futex_freebsd.odin

@@ -1,6 +1,6 @@
 //+private
 //+build freebsd
-package sync2
+package sync
 
 import "core:c"
 import "core:os"

+ 1 - 7
core/sync/sync2/futex_linux.odin → 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

+ 1 - 1
core/sync/sync2/futex_openbsd.odin → core/sync/futex_openbsd.odin

@@ -1,6 +1,6 @@
 //+private
 //+build openbsd
-package sync2
+package sync
 
 import "core:c"
 import "core:os"

+ 1 - 1
core/sync/sync2/futex_windows.odin → core/sync/futex_windows.odin

@@ -1,6 +1,6 @@
 //+private
 //+build windows
-package sync2
+package sync
 
 import "core:time"
 

+ 1 - 1
core/sync/sync2/primitives.odin → core/sync/primitives.odin

@@ -1,4 +1,4 @@
-package sync2
+package sync
 
 import "core:time"
 

+ 12 - 13
core/sync/sync2/primitives_atomic.odin → 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..<i32(100) {
-			state, ok := atomic_compare_exchange_weak_acquire(&m.state, .Unlocked, new_state)
+			state, ok := atomic_compare_exchange_weak_explicit(&m.state, .Unlocked, new_state, .Acquire, .Consume)
 			if ok {
 				return
 			}
@@ -38,8 +38,11 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
 			}
 		}
 
+		// Set just in case 100 iterations did not do it
+		new_state = .Waiting
+
 		for {
-			if atomic_exchange_acquire(&m.state, .Waiting) == .Unlocked {
+			if atomic_exchange_explicit(&m.state, .Waiting, .Acquire) == .Unlocked {
 				return
 			}
 			
@@ -49,11 +52,7 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
 	}
 
 
-	switch v := atomic_exchange_acquire(&m.state, .Locked); v {
-	case .Unlocked:
-		// Okay
-	case: fallthrough
-	case .Locked, .Waiting:
+	if v := atomic_exchange_explicit(&m.state, .Locked, .Acquire); v != .Unlocked {
 		lock_slow(m, v)
 	}
 }
@@ -66,7 +65,7 @@ atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
 	}
 
 
-	switch atomic_exchange_release(&m.state, .Unlocked) {
+	switch atomic_exchange_explicit(&m.state, .Unlocked, .Release) {
 	case .Unlocked:
 		unreachable()
 	case .Locked:
@@ -78,7 +77,7 @@ atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
 
 // atomic_mutex_try_lock tries to lock m, will return true on success, and false on failure
 atomic_mutex_try_lock :: proc(m: ^Atomic_Mutex) -> 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)
 }
 

+ 1 - 1
core/sync/sync2/primitives_darwin.odin → core/sync/primitives_darwin.odin

@@ -1,6 +1,6 @@
 //+build darwin
 //+private
-package sync2
+package sync
 
 import "core:c"
 import "core:time"

+ 46 - 0
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)
+}

+ 125 - 0
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, 0, tid, .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, 0, tid, .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)
+	}
+
+}

+ 47 - 0
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)
+}

+ 46 - 0
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)
+}

+ 1 - 1
core/sync/sync2/primitives_windows.odin → core/sync/primitives_windows.odin

@@ -1,6 +1,6 @@
 //+build windows
 //+private
-package sync2
+package sync
 
 import "core:time"
 import win32 "core:sys/windows"

+ 6 - 6
core/sync/sync2/sema_internal.odin → core/sync/sema_internal.odin

@@ -1,5 +1,5 @@
 //+private
-package sync2
+package sync
 
 import "core:time"
 
@@ -10,7 +10,7 @@ when #config(ODIN_SYNC_SEMA_USE_FUTEX, true) {
 	}
 
 	_sema_post :: proc(s: ^Sema, count := 1) {
-		atomic_add(&s.impl.count, Futex(count))
+		atomic_add_explicit(&s.impl.count, Futex(count), .Release)
 		if count == 1 {
 			futex_signal(&s.impl.count)
 		} else {
@@ -20,12 +20,12 @@ when #config(ODIN_SYNC_SEMA_USE_FUTEX, true) {
 
 	_sema_wait :: proc(s: ^Sema) {
 		for {
-			original_count := atomic_load(&s.impl.count)
+			original_count := atomic_load_explicit(&s.impl.count, .Relaxed)
 			for original_count == 0 {
 				futex_wait(&s.impl.count, u32(original_count))
 				original_count = s.impl.count
 			}
-			if original_count == atomic_compare_exchange_strong(&s.impl.count, original_count-1, original_count) {
+			if original_count == atomic_compare_exchange_strong_explicit(&s.impl.count, original_count, original_count-1, .Acquire, .Acquire) {
 				return
 			}
 		}
@@ -37,7 +37,7 @@ when #config(ODIN_SYNC_SEMA_USE_FUTEX, true) {
 		}
 		for {
 		
-			original_count := atomic_load(&s.impl.count)
+			original_count := atomic_load_explicit(&s.impl.count, .Relaxed)
 			for start := time.tick_now(); original_count == 0; /**/ {
 				remaining := duration - time.tick_since(start)
 				if remaining < 0 {
@@ -49,7 +49,7 @@ when #config(ODIN_SYNC_SEMA_USE_FUTEX, true) {
 				}
 				original_count = s.impl.count
 			}
-			if original_count == atomic_compare_exchange_strong(&s.impl.count, original_count-1, original_count) {
+			if original_count == atomic_compare_exchange_strong_explicit(&s.impl.count, original_count, original_count-1, .Acquire, .Acquire) {
 				return true
 			}
 		}

+ 0 - 123
core/sync/sync.odin

@@ -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
-}

+ 0 - 79
core/sync/sync2/atomic.odin

@@ -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

+ 0 - 9
core/sync/sync2/primitives_freebsd.odin

@@ -1,9 +0,0 @@
-//+build freebsd
-//+private
-package sync2
-
-import "core:os"
-
-_current_thread_id :: proc "contextless" () -> int {
-	return os.current_thread_id()
-}

+ 0 - 184
core/sync/sync2/primitives_internal.odin

@@ -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)<<RW_Mutex_State_Half_Width
-
-	RW_Mutex_State_Writer_Mask :: RW_Mutex_State(1<<(RW_Mutex_State_Half_Width-1) - 1) << 1
-	RW_Mutex_State_Reader_Mask :: RW_Mutex_State(1<<(RW_Mutex_State_Half_Width-1) - 1) << RW_Mutex_State_Half_Width
-
-
-	_RW_Mutex :: struct {
-		// NOTE(bill): pthread_rwlock_t cannot be used since pthread_rwlock_destroy is required on some platforms
-		// TODO(bill): Can we determine which platforms exactly?
-		state: RW_Mutex_State,
-		mutex: Mutex,
-		sema:  Sema,
-	}
-
-	_rw_mutex_lock :: proc(rw: ^RW_Mutex) {
-		_ = atomic_add(&rw.impl.state, RW_Mutex_State_Writer)
-		mutex_lock(&rw.impl.mutex)
-
-		state := atomic_or(&rw.impl.state, RW_Mutex_State_Writer)
-		if state & RW_Mutex_State_Reader_Mask != 0 {
-			sema_wait(&rw.impl.sema)
-		}
-	}
-
-	_rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
-		_ = atomic_and(&rw.impl.state, ~RW_Mutex_State_Is_Writing)
-		mutex_unlock(&rw.impl.mutex)
-	}
-
-	_rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> 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
-	}
-
-}

+ 0 - 9
core/sync/sync2/primitives_linux.odin

@@ -1,9 +0,0 @@
-//+build linux
-//+private
-package sync2
-
-import "core:sys/unix"
-
-_current_thread_id :: proc "contextless" () -> int {
-	return unix.sys_gettid()
-}

+ 0 - 9
core/sync/sync2/primitives_openbsd.odin

@@ -1,9 +0,0 @@
-//+build openbsd
-//+private
-package sync2
-
-import "core:os"
-
-_current_thread_id :: proc "contextless" () -> int {
-	return os.current_thread_id()
-}

+ 0 - 58
core/sync/sync2/primitives_pthreads.odin

@@ -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)
-}

+ 0 - 54
core/sync/sync_darwin.odin

@@ -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..<count {
-		res := darwin.semaphore_signal(s.handle)
-		assert(res == 0)
-	}
-}
-
-semaphore_wait_for :: proc(s: ^Semaphore) {
-	res := darwin.semaphore_wait(s.handle)
-	assert(res == 0)
-}

+ 0 - 40
core/sync/sync_freebsd.odin

@@ -1,40 +0,0 @@
-package sync
-
-import "core:sys/unix"
-import "core:intrinsics"
-
-
-current_thread_id :: proc "contextless" () -> 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..<count {
-		assert(unix.sem_post(&s.handle) == 0)
-	}
-}
-
-semaphore_wait_for :: proc(s: ^Semaphore) {
-	assert(unix.sem_wait(&s.handle) == 0)
-}
-

+ 0 - 36
core/sync/sync_linux.odin

@@ -1,36 +0,0 @@
-package sync
-
-import "core:sys/unix"
-
-current_thread_id :: proc "contextless" () -> 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..<count {
-	    assert(unix.sem_post(&s.handle) == 0)
-    }
-}
-
-semaphore_wait_for :: proc(s: ^Semaphore) {
-	assert(unix.sem_wait(&s.handle) == 0)
-}

+ 0 - 36
core/sync/sync_openbsd.odin

@@ -1,36 +0,0 @@
-package sync
-
-import "core:sys/unix"
-import "core:os"
-
-current_thread_id :: proc "contextless" () -> 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..<count {
-	    assert(unix.sem_post(&s.handle) == 0)
-    }
-}
-
-semaphore_wait_for :: proc(s: ^Semaphore) {
-	assert(unix.sem_wait(&s.handle) == 0)
-}

+ 0 - 248
core/sync/sync_unix.odin

@@ -1,248 +0,0 @@
-// +build linux, darwin, freebsd, openbsd
-package sync
-
-import "core:sys/unix"
-import "core:time"
-
-// A recursive lock that can only be held by one thread at once
-Mutex :: struct {
-	handle: unix.pthread_mutex_t,
-}
-
-
-mutex_init :: proc(m: ^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
-	unix.pthread_mutexattr_settype(&attrs, unix.PTHREAD_MUTEX_RECURSIVE)
-
-	assert(unix.pthread_mutex_init(&m.handle, &attrs) == 0)
-}
-
-mutex_destroy :: proc(m: ^Mutex) {
-	assert(unix.pthread_mutex_destroy(&m.handle) == 0)
-	m.handle = {}
-}
-
-mutex_lock :: proc(m: ^Mutex) {
-	assert(unix.pthread_mutex_lock(&m.handle) == 0)
-}
-
-// Returns false if someone else holds the lock.
-mutex_try_lock :: proc(m: ^Mutex) -> 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()
-}

+ 1 - 1
core/sync/sync2/sync_util.odin → core/sync/sync_util.odin

@@ -1,4 +1,4 @@
-package sync2
+package sync
 
 /*
 Example:

+ 0 - 180
core/sync/sync_windows.odin

@@ -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()
-}
-

+ 0 - 58
core/sync/wait_group.odin

@@ -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")
-		}
-	}
-}
-

+ 2 - 0
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 ---
 

+ 9 - 0
core/sys/windows/dwmapi.odin

@@ -0,0 +1,9 @@
+// +build windows
+package sys_windows
+
+foreign import dwmapi "system:Dwmapi.lib"
+
+@(default_calling_convention="stdcall")
+foreign dwmapi {
+	DwmFlush :: proc() -> HRESULT ---
+}

+ 66 - 0
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 ---
+}

+ 10 - 1
core/sys/windows/kernel32.odin

@@ -342,6 +342,7 @@ MEM_TOP_DOWN    :: 0x100000
 MEM_LARGE_PAGES :: 0x20000000
 MEM_4MB_PAGES   :: 0x80000000
 
+@(default_calling_convention="stdcall")
 foreign kernel32 {
 	VirtualAlloc :: proc(
 		lpAddress: LPVOID,
@@ -484,6 +485,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,
@@ -499,6 +501,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,
@@ -528,6 +531,7 @@ WIN32_MEMORY_RANGE_ENTRY :: struct {
 
 PWIN32_MEMORY_RANGE_ENTRY :: ^WIN32_MEMORY_RANGE_ENTRY
 
+@(default_calling_convention="stdcall")
 foreign kernel32 {
 	PrefetchVirtualMemory :: proc(
 		hProcess: HANDLE,
@@ -585,6 +589,7 @@ foreign kernel32 {
 
 MEHC_PATROL_SCRUBBER_PRESENT :: ULONG(0x1)
 
+@(default_calling_convention="stdcall")
 foreign kernel32 {
 	GetMemoryErrorHandlingCapabilities :: proc(
 		Capabilities: PULONG,
@@ -593,6 +598,7 @@ foreign kernel32 {
 
 PBAD_MEMORY_CALLBACK_ROUTINE :: #type proc "stdcall" ()
 
+@(default_calling_convention="stdcall")
 foreign kernel32 {
 	RegisterBadMemoryNotification :: proc(
 		Callback: PBAD_MEMORY_CALLBACK_ROUTINE,
@@ -613,6 +619,7 @@ VmOfferPriorityLow         :: OFFER_PRIORITY.VmOfferPriorityLow
 VmOfferPriorityBelowNormal :: OFFER_PRIORITY.VmOfferPriorityBelowNormal
 VmOfferPriorityNormal      :: OFFER_PRIORITY.VmOfferPriorityNormal
 
+@(default_calling_convention="stdcall")
 foreign kernel32 {
 	OfferVirtualMemory :: proc(
 		VirtualAddress: PVOID,
@@ -677,6 +684,7 @@ WIN32_MEMORY_REGION_INFORMATION_u_s_Bitfield :: distinct ULONG
 	Reserved       : 32-6,
 }*/
 
+@(default_calling_convention="stdcall")
 foreign kernel32 {
 	QueryVirtualMemoryInformation :: proc(
 		Process: HANDLE,
@@ -701,7 +709,7 @@ foreign kernel32 {
 
 NUMA_NO_PREFERRED_NODE :: 0xffffffff
 
-MapViewOfFile2 :: #force_inline proc(
+MapViewOfFile2 :: #force_inline proc "stdcall" (
 	FileMappingHandle: HANDLE,
 	ProcessHandle: HANDLE,
 	Offset: ULONG64,
@@ -722,6 +730,7 @@ MapViewOfFile2 :: #force_inline proc(
 	)
 }
 
+@(default_calling_convention="stdcall")
 foreign kernel32 {
 	UnmapViewOfFile2 :: proc(
 		ProcessHandle: HANDLE,

+ 252 - 0
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

+ 861 - 32
core/sys/windows/types.odin

@@ -21,7 +21,16 @@ 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
+HHOOK :: distinct HANDLE
 BOOL :: distinct b32
 BYTE :: distinct u8
 BOOLEAN :: distinct b8
@@ -42,9 +51,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 +86,7 @@ PBOOL :: ^BOOL
 LPBOOL :: ^BOOL
 LPCSTR :: cstring
 LPCWSTR :: wstring
+LPCTSTR :: wstring
 LPDWORD :: ^DWORD
 PCSTR :: cstring
 PCWSTR :: wstring
@@ -81,7 +97,9 @@ LPPROCESS_INFORMATION :: ^PROCESS_INFORMATION
 PSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
 LPSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
 LPSTARTUPINFO :: ^STARTUPINFO
-PVOID  :: rawptr
+LPTRACKMOUSEEVENT :: ^TRACKMOUSEEVENT
+VOID :: rawptr
+PVOID :: rawptr
 LPVOID :: rawptr
 PINT :: ^INT
 LPINT :: ^INT
@@ -178,6 +196,109 @@ 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
+
+HOOKPROC :: #type proc "stdcall" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT
+
+CWPRETSTRUCT :: struct {
+	lResult: LRESULT,
+	lParam: LPARAM,
+	wParam: WPARAM,
+	message: UINT,
+	hwnd: HWND,
+}
+
+KBDLLHOOKSTRUCT :: struct {
+	vkCode: DWORD,
+	scanCode: DWORD,
+	flags: DWORD,
+	time: DWORD,
+	dwExtraInfo: ULONG_PTR,
+}
+
+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,
+}
+
+TRACKMOUSEEVENT :: struct {
+	cbSize: DWORD,
+	dwFlags: DWORD,
+	hwndTrack: HWND,
+	dwHoverTime: DWORD,
+}
+
 WIN32_FIND_DATAW :: struct {
 	dwFileAttributes: DWORD,
 	ftCreationTime: FILETIME,
@@ -191,6 +312,705 @@ 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
+
+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
+
+// Window field offsets for GetWindowLong()
+GWL_STYLE   :: -16
+GWL_EXSTYLE :: -20
+GWL_ID      :: -12
+
+when ODIN_ARCH == .i386 {
+	GWL_WNDPROC    :: -4
+	GWL_HINSTANCE  :: -6
+	GWL_HWNDPARENT :: -8
+	GWL_USERDATA   :: -21
+}
+
+GWLP_WNDPROC    :: -4
+GWLP_HINSTANCE  :: -6
+GWLP_HWNDPARENT :: -8
+GWLP_USERDATA   :: -21
+GWLP_ID         :: -12
+
+// Class field offsets for GetClassLong()
+GCL_CBWNDEXTRA :: -18
+GCL_CBCLSEXTRA :: -20
+GCL_STYLE      :: -26
+GCW_ATOM       :: -32
+
+when ODIN_ARCH == .i386 {
+	GCL_MENUNAME      :: -8
+	GCL_HBRBACKGROUND :: -10
+	GCL_HCURSOR       :: -12
+	GCL_HICON         :: -14
+	GCL_HMODULE       :: -16
+	GCL_WNDPROC       :: -24
+	GCL_HICONSM       :: -34
+}
+
+GCLP_MENUNAME      :: -8
+GCLP_HBRBACKGROUND :: -10
+GCLP_HCURSOR       :: -12
+GCLP_HICON         :: -14
+GCLP_HMODULE       :: -16
+GCLP_WNDPROC       :: -24
+GCLP_HICONSM       :: -34
+
+// 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
+
+// Value for rolling one detent
+WHEEL_DELTA :: 120
+
+// Setting to scroll one page for SPI_GET/SETWHEELSCROLLLINES
+WHEEL_PAGESCROLL :: max(UINT)
+
+// XButton values are WORD flags
+XBUTTON1 :: 0x0001
+XBUTTON2 :: 0x0002
+// Were there to be an XBUTTON3, its value would be 0x0004
+
+MAPVK_VK_TO_VSC    :: 0
+MAPVK_VSC_TO_VK    :: 1
+MAPVK_VK_TO_CHAR   :: 2
+MAPVK_VSC_TO_VK_EX :: 3
+MAPVK_VK_TO_VSC_EX :: 4
+
+TME_HOVER     :: 0x00000001
+TME_LEAVE     :: 0x00000002
+TME_NONCLIENT :: 0x00000010
+TME_QUERY     :: 0x40000000
+TME_CANCEL    :: 0x80000000
+HOVER_DEFAULT :: 0xFFFFFFFF
+
+USER_TIMER_MAXIMUM :: 0x7FFFFFFF
+USER_TIMER_MINIMUM :: 0x0000000A
+
+
+// SetWindowsHook() codes
+WH_MIN             :: -1
+WH_MSGFILTER       :: -1
+WH_JOURNALRECORD   :: 0
+WH_JOURNALPLAYBACK :: 1
+WH_KEYBOARD        :: 2
+WH_GETMESSAGE      :: 3
+WH_CALLWNDPROC     :: 4
+WH_CBT             :: 5
+WH_SYSMSGFILTER    :: 6
+WH_MOUSE           :: 7
+WH_HARDWARE        :: 8
+WH_DEBUG           :: 9
+WH_SHELL           :: 10
+WH_FOREGROUNDIDLE  :: 11
+WH_CALLWNDPROCRET  :: 12
+WH_KEYBOARD_LL     :: 13
+WH_MOUSE_LL        :: 14
+WH_MAX             :: 14
+WH_MINHOOK         :: WH_MIN
+WH_MAXHOOK         :: WH_MAX
+
+// Hook Codes
+HC_ACTION      :: 0
+HC_GETNEXT     :: 1
+HC_SKIP        :: 2
+HC_NOREMOVE    :: 3
+HC_NOREM       :: HC_NOREMOVE
+HC_SYSMODALON  :: 4
+HC_SYSMODALOFF :: 5
+
+// CBT Hook Codes
+HCBT_MOVESIZE     :: 0
+HCBT_MINMAX       :: 1
+HCBT_QS           :: 2
+HCBT_CREATEWND    :: 3
+HCBT_DESTROYWND   :: 4
+HCBT_ACTIVATE     :: 5
+HCBT_CLICKSKIPPED :: 6
+HCBT_KEYSKIPPED   :: 7
+HCBT_SYSCOMMAND   :: 8
+HCBT_SETFOCUS     :: 9
+
+_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 +1199,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 +1407,7 @@ PROCESS_INFORMATION :: struct {
 }
 
 // FYI: This is STARTUPINFOW, not STARTUPINFOA
-STARTUPINFO :: struct #packed {
+STARTUPINFO :: struct {
 	cb: DWORD,
 	lpReserved: LPWSTR,
 	lpDesktop: LPWSTR,
@@ -784,17 +1613,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 +1666,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 +1698,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 +1730,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 +2081,4 @@ SYSTEMTIME :: struct {
 	minute:       WORD,
 	second:       WORD,
 	milliseconds: WORD,
-}
+}

+ 252 - 0
core/sys/windows/user32.odin

@@ -0,0 +1,252 @@
+// +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 ---
+
+	GetClassLongA :: proc(hWnd: HWND, nIndex: c_int) -> DWORD ---
+	GetClassLongW :: proc(hWnd: HWND, nIndex: c_int) -> DWORD ---
+	SetClassLongA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> DWORD ---
+	SetClassLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> DWORD ---
+
+	GetWindowLongA :: proc(hWnd: HWND, nIndex: c_int) -> LONG ---
+	GetWindowLongW :: proc(hWnd: HWND, nIndex: c_int) -> LONG ---
+	SetWindowLongA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> LONG ---
+	SetWindowLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> LONG ---
+
+	GetClassNameA :: proc(hWnd: HWND, lpClassName: LPSTR, nMaxCount: c_int) -> c_int ---
+	GetClassNameW :: proc(hWnd: HWND, lpClassName: LPWSTR, nMaxCount: c_int) -> c_int ---
+
+	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 ---
+
+	GetCapture :: proc() -> HWND ---
+	SetCapture :: proc(hWnd: HWND) -> HWND ---
+	ReleaseCapture :: proc() -> BOOL ---
+	TrackMouseEvent :: proc(lpEventTrack: LPTRACKMOUSEEVENT) -> BOOL ---
+
+	GetKeyState :: proc(nVirtKey: c_int) -> SHORT ---
+	GetAsyncKeyState :: proc(vKey: c_int) -> SHORT ---
+
+	MapVirtualKeyA :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
+	MapVirtualKeyW :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
+
+	SetWindowsHookExA :: proc(idHook: c_int, lpfn: HOOKPROC, hmod: HINSTANCE, dwThreadId: DWORD) -> HHOOK ---
+	SetWindowsHookExW :: proc(idHook: c_int, lpfn: HOOKPROC, hmod: HINSTANCE, dwThreadId: DWORD) -> HHOOK ---
+	UnhookWindowsHookEx :: proc(hhk: HHOOK) -> BOOL ---
+	CallNextHookEx :: proc(hhk: HHOOK, nCode: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+
+	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,
+	)
+}
+
+when ODIN_ARCH == .amd64 {
+	@(default_calling_convention="stdcall")
+	foreign user32 {
+		GetClassLongPtrA :: proc(hWnd: HWND, nIndex: c_int) -> ULONG_PTR ---
+		GetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> ULONG_PTR ---
+		SetClassLongPtrA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR ---
+		SetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR ---
+
+		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 ---
+	}
+} else when ODIN_ARCH == .i386 {
+	GetClassLongPtrA :: GetClassLongA
+	GetClassLongPtrW :: GetClassLongW
+	SetClassLongPtrA :: SetClassLongA
+	SetClassLongPtrW :: SetClassLongW
+
+	GetWindowLongPtrA :: GetWindowLongA
+	GetWindowLongPtrW :: GetWindowLongW
+	SetWindowLongPtrA :: GetWindowLongA
+	SetWindowLongPtrW :: GetWindowLongW
+}
+
+GET_SC_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_int {
+	return c_int(wParam) & 0xFFF0
+}
+
+GET_WHEEL_DELTA_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_short {
+	return cast(c_short)HIWORD(cast(DWORD)wParam)
+}
+
+GET_KEYSTATE_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> WORD {
+	return LOWORD(cast(DWORD)wParam)
+}
+
+GET_NCHITTEST_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_short {
+	return cast(c_short)LOWORD(cast(DWORD)wParam)
+}
+
+GET_XBUTTON_WPARAM ::  #force_inline proc "contextless" (wParam: WPARAM) -> WORD {
+	return HIWORD(cast(DWORD)wParam)
+}

+ 8 - 4
core/sys/windows/util.odin

@@ -3,6 +3,9 @@ package sys_windows
 
 import "core:strings"
 import "core:sys/win32"
+import "core:intrinsics"
+
+L :: intrinsics.constant_utf16_cstring
 
 LOWORD :: #force_inline proc "contextless" (x: DWORD) -> WORD {
 	return WORD(x & 0xffff)
@@ -45,7 +48,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 +65,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 +79,6 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
 			break
 		}
 	}
-
 	return string(text[:n])
 }
 
@@ -455,4 +459,4 @@ run_as_user :: proc(username, password, application, commandline: string, pi: ^P
     } else {
     	return false
     }
-}
+}

+ 1048 - 0
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

+ 2 - 2
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
 		}
 	}

+ 148 - 81
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.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
 
-Pool :: struct {
-	allocator:             mem.Allocator,
-	mutex:                 sync.Mutex,
-	sem_available:         sync.Semaphore,
-	processing_task_count: int, // atomic
-	is_running:            bool,
+	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)
+
+	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)
+}
 
-	task: Task
-	task.procedure = procedure
-	task.data = data
-	task.user_index = user_index
+// 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)
+}
 
-	append(&pool.tasks, task)
-	sync.semaphore_post(&pool.sem_available, 1)
+// 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
 }
 
-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)
+// 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)
+
+	if len(pool.tasks_done) != 0 {
+		task = pop_front(&pool.tasks_done)
+		got_task = true
+		intrinsics.atomic_sub(&pool.num_done, 1)
+	}
 
-pool_do_work :: proc(pool: ^Pool, task: ^Task) {
-	task.procedure(task)
-	intrinsics.atomic_sub(&pool.processing_task_count, 1)
+	return
 }
 
+// Mostly for internal use.
+pool_do_work :: proc(pool: ^Pool, task: Task) {
+	{
+		context.allocator = task.allocator
+		task.procedure(task)
+	}
 
-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)
-		}
+	sync.guard(&pool.mutex)
 
-		// 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)
-		}
+	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)
+}
 
-		yield()
+// 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)
 }

+ 38 - 79
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()
+
+		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)
 
-		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)
-			}
+		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, &params)
 	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;
-
-	// 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()
-		}
-	}
+	sync.guard(&t.mutex)
 
-	// 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)
 }

+ 30 - 13
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) {

+ 60 - 60
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]
 }

+ 2 - 2
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

+ 14 - 8
examples/demo/demo.odin

@@ -33,6 +33,10 @@ import "core:math/big"
 		core and vendor library collections.
 	Nightly Builds - https://odin-lang.org/docs/nightly/
 		Get the latest nightly builds of Odin.
+	More Odin Examples - https://github.com/odin-lang/examples
+		This repository contains examples of how certain things can be accomplished 
+		in idiomatic Odin, allowing you learn its semantics, as well as how to use 
+		parts of the core and vendor package collections.
 */
 
 the_basics :: proc() {
@@ -1107,11 +1111,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 +1151,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 +1161,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 +1170,7 @@ threading_example :: proc() {
 		}
 
 		thread.pool_start(&pool)
-		thread.pool_wait_and_process(&pool)
+		thread.pool_finish(&pool)
 	}
 }
 
@@ -2424,6 +2423,13 @@ matrix_type :: proc() {
 }
 
 main :: proc() {
+	/*
+		For More Odin Examples - https://github.com/odin-lang/examples
+			This repository contains examples of how certain things can be accomplished 
+			in idiomatic Odin, allowing you learn its semantics, as well as how to use 
+			parts of the core and vendor package collections.
+	*/
+
 	when true {
 		the_basics()
 		control_flow()

+ 0 - 7
examples/hms2019/basic.odin

@@ -1,7 +0,0 @@
-package basic
-
-import "core:fmt"
-
-main :: proc() {
-	fmt.println("Hellope!");
-}

+ 0 - 67
examples/hms2019/eca.odin

@@ -1,67 +0,0 @@
-package eca
-
-import "core:fmt"
-import "core:math/rand"
-import "core:time"
-import "intrinsics"
-
-elementary_cellular_automata :: proc(state: $T, rule: u8, generations: int, pause: time.Duration = 0)
-	where intrinsics.type_is_integer(T),
-		  intrinsics.type_is_unsigned(T) {
-	N :: 8*size_of(state);
-
-	output :: proc(state: T) {
-		buf: [N]byte;
-		for i in 0..<T(N) {
-			c := byte('#');
-			// c := byte(rand.int_max(26) + 'A' + ('a'-'A')*rand.int_max(2));
-			buf[N-1-i] = state & (1<<i) != 0 ? c : ' ';
-		}
-		fmt.println(string(buf[:]));
-	}
-
-	bit :: proc(x, i: T) -> T {
-		return (x >> i) & 0x1;
-	}
-	set :: proc(x: ^T, cell, k: T, rule: u8) {
-		x^ &~= 1<<cell;
-		if rule>>k&1 != 0 {
-			x^ |= 1<<cell;
-		}
-	}
-
-
-	a := state;
-	a1 := T(0);
-
-	output(a);
-
-	last := T(N-1);
-	for r in 0..<generations {
-		if pause > 0 do time.sleep(pause);
-	
-
-		k := bit(a, last) | bit(a, 0)<<1 | bit(a, 1)<<2;
-		set(&a1, 0, k, rule);
-		a1 |= (1<<0) * T(rule>>k&1);
-		for c in 1..<last {
-			k = k>>1 | bit(a, c+1)<<2;
-			set(&a1, c, k, rule);
-		}
-		set(&a1, last, k>>1|bit(a, 0)<<2, rule);
-		a, a1 = a1, a;
-		output(a);
-		if a == a1 {
-			return;
-		}
-	}
-}
-
-main :: proc() {
-	elementary_cellular_automata(
-		state=rand.uint128(),
-		rule=30,
-		generations=5000,
-		pause=100*time.Millisecond,
-	);
-}

+ 0 - 1754
examples/hms2019/hms2019.odin

@@ -1,1754 +0,0 @@
-package hms2019
-
-import "core:fmt"
-import "core:mem"
-import "core:os"
-import "core:reflect"
-import "intrinsics"
-
-/*
-	Welcome to Handmade Seattle 2019!
-
-	The Odin programming language is fast, concise, readable, pragmatic and open sourced. 
-	It is designed with the intent of replacing C with the following goals:
-	 * simplicity
-	 * high performance
-	 * built for modern systems
-	 * joy of programming
-
-	# Installing Odin
-	Getting Started - https://odin-lang.org/docs/install/
-		Instructions for downloading and install the Odin compiler and libraries.
-
-	# Learning Odin
-	Overview of Odin - https://odin-lang.org/docs/overview/
-		An overview of the Odin programming language.
-	Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/
-		Answers to common questions about Odin.
-*/
-
-the_basics :: proc() {
-	fmt.println("\n# the basics");
-
-	{ // The Basics
-		fmt.println("Hellope");
-
-		// Lexical elements and literals
-		// A comment
-
-		my_integer_variable: int; // A comment for documentaton
-
-		// Multi-line comments begin with /* and end with */. Multi-line comments can 
-		// also be nested (unlike in C):
-		/*
-			You can have any text or code here and
-			have it be commented.
-			/*
-				NOTE: comments can be nested!
-			*/
-		*/
-
-		// String literals are enclosed in double quotes and character literals in single quotes.
-		// Special characters are escaped with a backslash \
-
-		some_string := "This is a string";
-		_ = 'A'; // unicode codepoint literal
-		_ = '\n';
-		_ = "C:\\Windows\\notepad.exe";
-		// Raw string literals are enclosed with single back ticks
-		_ = `C:\Windows\notepad.exe`;
-
-		// The length of a string in bytes can be found using the built-in `len` procedure:
-		_ = len("Foo");
-		_ = len(some_string);
-
-
-		// Numbers
-
-		// Numerical literals are written similar to most other programming languages. 
-		// A useful feature in Odin is that underscores are allowed for better 
-		// readability: 1_000_000_000 (one billion). A number that contains a dot is a 
-		// floating point literal: 1.0e9 (one billion). If a number literal is suffixed 
-		// with i, is an imaginary number literal: 2i (2 multiply the square root of -1).
-
-		// Binary literals are prefixed with 0b, octal literals with 0o, and hexadecimal 
-		// literals 0x. A leading zero does not produce an octal constant (unlike C).
-
-		// In Odin, if a number constant is possible to be represented by a type without 
-		// precision loss, it will automatically convert to that type.
-
-		x: int = 1.0; // A float literal but it can be represented by an integer without precision loss
-		// Constant literals are “untyped” which means that they can implicitly convert to a type.
-
-		y: int; // `y` is typed of type `int`
-		y = 1;  // `1` is an untyped integer literal which can implicitly convert to `int`
-
-		z: f64; // `z` is typed of type `f64` (64-bit floating point number)
-		z = 1;  // `1` is an untyped integer literals which can be implicity conver to `f64`
-				// No need for any suffixes or decimal places like in other languages
-				// CONSTANTS JUST WORK!!!
-
-
-		// Assignment statements
-		h: int = 123; // declares a new variable `h` with type `int` and assigns a value to it
-		h = 637; // assigns a new value to `h`
-
-		// `=` is the assignment operator
-
-		// You can assign multiple variables with it:
-		a, b := 1, "hello"; // declares `a` and `b` and infers the types from the assignments
-		b, a = "byte", 0;
-
-		// Note: `:=` is two tokens, `:` and `=`. The follow are equivalent
-		/*
-			i: int = 123;
-			i:     = 123;
-			i := 123
-		*/
-
-		// Constant declarations
-		// Constants are entities (symbols) which have an assigned value. 
-		// The constant’s value cannot be changed. 
-		// The constant’s value must be able to be evaluated at compile time:
-		X :: "what"; // constant `X` has the untyped string value "what"
-
-		// Constants can be explicitly typed like a variable declaration:
-		Y : int : 123;
-		Z :: Y + 7; // constant computations are possible
-	}
-}
-
-control_flow :: proc() {
-	fmt.println("\n# control flow");
-	{ // Control flow
-		// For loop
-		// Odin has only one loop statement, the `for` loop
-
-		// Basic for loop
-		for i := 0; i < 10; i += 1 {
-			fmt.println(i);
-		}
-
-		// NOTE: Unlike other languages like C, there are no parentheses `( )` surrounding the three components.
-		// Braces `{ }` or a `do` are always required>
-		for i := 0; i < 10; i += 1 { }
-		for i := 0; i < 10; i += 1 do fmt.print();
-
-		// The initial and post statements are optional
-		i := 0;
-		for ; i < 10; {
-			i += 1;
-		}
-
-		// These semicolons can be dropped. This `for` loop is equivalent to C's `while` loop
-		i = 0;
-		for i < 10 {
-			i += 1;
-		}
-
-		// If the condition is omitted, this produces an infinite loop:
-		for {
-			break;
-		}
-
-		// Range-based for loop
-		// The basic for loop
-		for i := 0; i < 10; i += 1 {
-			fmt.println(i);
-		}
-		// can also be written
-		for i in 0..<10 {
-			fmt.println(i);
-		}
-		for i in 0..9 {
-			fmt.println(i);
-		}
-
-		// Certain built-in types can be iterated over
-		some_string := "Hello, 世界";
-		for character in some_string { // Strings are assumed to be UTF-8
-			fmt.println(character);
-		}
-
-		some_array := [3]int{1, 4, 9};
-		for value in some_array {
-			fmt.println(value);
-		}
-
-		some_slice := []int{1, 4, 9};
-		for value in some_slice {
-			fmt.println(value);
-		}
-
-		some_dynamic_array := [dynamic]int{1, 4, 9};
-		defer delete(some_dynamic_array);
-		for value in some_dynamic_array {
-			fmt.println(value);
-		}
-
-
-		some_map := map[string]int{"A" = 1, "C" = 9, "B" = 4};
-		defer delete(some_map);
-		for key in some_map {
-			fmt.println(key);
-		}
-
-		// Alternatively a second index value can be added
-		for character, index in some_string {
-			fmt.println(index, character);
-		}
-		for value, index in some_array {
-			fmt.println(index, value);
-		}
-		for value, index in some_slice {
-			fmt.println(index, value);
-		}
-		for value, index in some_dynamic_array {
-			fmt.println(index, value);
-		}
-		for key, value in some_map {
-			fmt.println(key, value);
-		}
-
-		// The iterated values are copies and cannot be written to.
-		// The following idiom is useful for iterating over a container in a by-reference manner:
-		for _, i in some_slice {
-			some_slice[i] = (i+1)*(i+1);
-		}
-
-
-		// If statements
-		x := 123;
-		if x >= 0 {
-			fmt.println("x is positive");
-		}
-
-		if y := -34; y < 0 {
-			fmt.println("y is negative");
-		}
-
-		if y := 123; y < 0 {
-			fmt.println("y is negative");
-		} else if y == 0 {
-			fmt.println("y is zero");
-		} else {
-			fmt.println("y is positive");
-		}
-
-		// Switch statement
-		// A switch statement is another way to write a sequence of if-else statements. 
-		// In Odin, the default case is denoted as a case without any expression.
-
-		switch arch := ODIN_ARCH; arch {
-		case "386":
-			fmt.println("32-bit");
-		case "amd64":
-			fmt.println("64-bit");
-		case: // default
-			fmt.println("Unsupported architecture");
-		}
-
-		// Odin’s `switch` is like one in C or C++, except that Odin only runs the selected case. 
-		// This means that a `break` statement is not needed at the end of each case. 
-		// Another important difference is that the case values need not be integers nor constants.
-
-		// To achieve a C-like fall through into the next case block, the keyword `fallthrough` can be used.
-		one_angry_dwarf :: proc() -> int { 
-			fmt.println("one_angry_dwarf was called");
-			return 1;
-		}
-
-		switch i := 0; i {
-		case 0:
-		case one_angry_dwarf():
-		}
-
-		// A switch statement without a condition is the same as `switch true`. 
-		// This can be used to write a clean and long if-else chain and have the 
-		// ability to break if needed
-
-		switch {
-		case x < 0:
-			fmt.println("x is negative");
-		case x == 0:
-			fmt.println("x is zero");
-		case:
-			fmt.println("x is positive");
-		}
-
-		// A `switch` statement can also use ranges like a range-based loop:
-		switch c := 'j'; c {
-		case 'A'..'Z', 'a'..'z', '0'..'9':
-			fmt.println("c is alphanumeric");
-		}
-
-		switch x {
-		case 0..<10:
-			fmt.println("units");
-		case 10..<13:
-			fmt.println("pre-teens");
-		case 13..<20:
-			fmt.println("teens");
-		case 20..<30:
-			fmt.println("twenties");
-		}
-	}
-
-	{ // Defer statement
-		// A defer statement defers the execution of a statement until the end of 
-		// the scope it is in.
-
-		// The following will print 4 then 234:
-		{
-			x := 123;
-			defer fmt.println(x);
-			{
-				defer x = 4;
-				x = 2;
-			}
-			fmt.println(x);
-
-			x = 234;
-		}
-
-		// You can defer an entire block too:
-		{
-			bar :: proc() {}
-
-			defer {
-				fmt.println("1");
-				fmt.println("2");
-			}
-
-			cond := false; 
-			defer if cond {
-				bar();
-			}
-		}	
-
-		// Defer statements are executed in the reverse order that they were declared:
-		{
-			defer fmt.println("1");
-			defer fmt.println("2");
-			defer fmt.println("3");
-		}
-		// Will print 3, 2, and then 1.
-
-		if false {
-			f, err := os.open("my_file.txt");
-			if err != 0 {
-				// handle error
-			}
-			defer os.close(f);
-			// rest of code
-		}
-	}
-
-	{ // When statement
-		/* 
-			The when statement is almost identical to the if statement but with some differences:
-
-			* Each condition must be a constant expression as a when 
-			  statement is evaluated at compile time.
-			* The statements within a branch do not create a new scope
-			* The compiler checks the semantics and code only for statements 
-			  that belong to the first condition that is true
-			* An initial statement is not allowed in a when statement
-			* when statements are allowed at file scope
-		*/
-
-		// Example
-		when ODIN_ARCH == "386" {
-			fmt.println("32 bit");
-		} else when ODIN_ARCH == "amd64" {
-			fmt.println("64 bit");
-		} else {
-			fmt.println("Unsupported architecture");
-		}
-		// The when statement is very useful for writing platform specific code. 
-		// This is akin to the #if construct in C’s preprocessor however, in Odin, 
-		// it is type checked.
-	}
-
-	{ // Branch statements
-		cond, cond1, cond2 := false, false, false;
-		one_step :: proc() { fmt.println("one_step"); }
-		beyond :: proc() { fmt.println("beyond"); }
-
-		// Break statement
-		for cond {
-			switch {
-			case:
-				if cond {
-					break; // break out of the `switch` statement
-				}
-			}
-
-			break; // break out of the `for` statement
-		}
-
-		loop: for cond1 {
-			for cond2 {
-				break loop; // leaves both loops
-			}
-		}
-
-		// Continue statement
-		for cond {
-			if cond2 {
-				continue;
-			}
-			fmt.println("Hellope");
-		}
-
-		// Fallthrough statement
-
-		// Odin’s switch is like one in C or C++, except that Odin only runs the selected 
-		// case. This means that a break statement is not needed at the end of each case. 
-		// Another important difference is that the case values need not be integers nor 
-		// constants.
-
-		// fallthrough can be used to explicitly fall through into the next case block:
-
-		switch i := 0; i {
-		case 0:
-			one_step();
-			fallthrough;
-		case 1:
-			beyond();
-		}
-	}
-}
-
-
-named_proc_return_parameters :: proc() {
-	fmt.println("\n# named proc return parameters");
-
-	foo0 :: proc() -> int {
-		return 123;
-	}
-	foo1 :: proc() -> (a: int) {
-		a = 123;
-		return;
-	}
-	foo2 :: proc() -> (a, b: int) {
-		// Named return values act like variables within the scope
-		a = 321;
-		b = 567;
-		return b, a;
-	}
-	fmt.println("foo0 =", foo0()); // 123
-	fmt.println("foo1 =", foo1()); // 123
-	fmt.println("foo2 =", foo2()); // 567 321
-}
-
-
-explicit_procedure_overloading :: proc() {
-	fmt.println("\n# explicit procedure overloading");
-
-	add_ints :: proc(a, b: int) -> int {
-		x := a + b;
-		fmt.println("add_ints", x);
-		return x;
-	}
-	add_floats :: proc(a, b: f32) -> f32 {
-		x := a + b;
-		fmt.println("add_floats", x);
-		return x;
-	}
-	add_numbers :: proc(a: int, b: f32, c: u8) -> int {
-		x := int(a) + int(b) + int(c);
-		fmt.println("add_numbers", x);
-		return x;
-	}
-
-	add :: proc{add_ints, add_floats, add_numbers};
-
-	add(int(1), int(2));
-	add(f32(1), f32(2));
-	add(int(1), f32(2), u8(3));
-
-	add(1, 2);     // untyped ints coerce to int tighter than f32
-	add(1.0, 2.0); // untyped floats coerce to f32 tighter than int
-	add(1, 2, 3);  // three parameters
-
-	// Ambiguous answers
-	// add(1.0, 2);
-	// add(1, 2.0);
-}
-
-struct_type :: proc() {
-	fmt.println("\n# struct type");
-	// A struct is a record type in Odin. It is a collection of fields. 
-	// Struct fields are accessed by using a dot:	
-	{
-		Vector2 :: struct {
-			x: f32,
-			y: f32,
-		};
-		v := Vector2{1, 2};
-		v.x = 4;
-		fmt.println(v.x);
-
-		// Struct fields can be accessed through a struct pointer:
-
-		v = Vector2{1, 2};
-		p := &v;
-		p.x = 1335;
-		fmt.println(v);
-
-		// We could write p^.x, however, it is to nice abstract the ability 
-		// to not explicitly dereference the pointer. This is very useful when 
-		// refactoring code to use a pointer rather than a value, and vice versa.
-	}
-	{
-		// A struct literal can be denoted by providing the struct’s type 
-		// followed by {}. A struct literal must either provide all the 
-		// arguments or none:
-		Vector3 :: struct {
-			x, y, z: f32,
-		};
-		v: Vector3;
-		v = Vector3{}; // Zero value
-		v = Vector3{1, 4, 9};
-
-		// You can list just a subset of the fields if you specify the 
-		// field by name (the order of the named fields does not matter):
-		v = Vector3{z=1, y=2};
-		assert(v.x == 0);
-		assert(v.y == 2);
-		assert(v.z == 1);		
-	}
-	{
-		// Structs can tagged with different memory layout and alignment requirements:
-
-		a :: struct #align 4   {}; // align to 4 bytes
-		b :: struct #packed    {}; // remove padding between fields
-		c :: struct #raw_union {}; // all fields share the same offset (0). This is the same as C's union
-	}
-
-}
-
-
-union_type :: proc() {
-	fmt.println("\n# union type");
-	{
-		val: union{int, bool};
-		val = 137;
-		if i, ok := val.(int); ok {
-			fmt.println(i);
-		}
-		val = true;
-		fmt.println(val);
-
-		val = nil;
-
-		switch v in val {
-		case int:  fmt.println("int",  v);
-		case bool: fmt.println("bool", v);
-		case:      fmt.println("nil");
-		}
-	}
-	{
-		// There is a duality between `any` and `union`
-		// An `any` has a pointer to the data and allows for any type (open)
-		// A `union` has as binary blob to store the data and allows only certain types (closed)
-		// The following code is with `any` but has the same syntax
-		val: any;
-		val = 137;
-		if i, ok := val.(int); ok {
-			fmt.println(i);
-		}
-		val = true;
-		fmt.println(val);
-
-		val = nil;
-
-		switch v in val {
-		case int:  fmt.println("int",  v);
-		case bool: fmt.println("bool", v);
-		case:      fmt.println("nil");
-		}
-	}
-
-	Vector3 :: distinct [3]f32;
-	Quaternion :: distinct quaternion128;
-
-	// More realistic examples
-	{
-		// NOTE(bill): For the above basic examples, you may not have any
-		// particular use for it. However, my main use for them is not for these
-		// simple cases. My main use is for hierarchical types. Many prefer
-		// subtyping, embedding the base data into the derived types. Below is
-		// an example of this for a basic game Entity.
-
-		Entity :: struct {
-			id:          u64,
-			name:        string,
-			position:    Vector3,
-			orientation: Quaternion,
-
-			derived: any,
-		};
-
-		Frog :: struct {
-			using entity: Entity,
-			jump_height:  f32,
-		};
-
-		Monster :: struct {
-			using entity: Entity,
-			is_robot:     bool,
-			is_zombie:    bool,
-		};
-
-		// See `parametric_polymorphism` procedure for details
-		new_entity :: proc($T: typeid) -> ^Entity {
-			t := new(T);
-			t.derived = t^;
-			return t;
-		}
-
-		entity := new_entity(Monster);
-
-		switch e in entity.derived {
-		case Frog:
-			fmt.println("Ribbit");
-		case Monster:
-			if e.is_robot  do fmt.println("Robotic");
-			if e.is_zombie do fmt.println("Grrrr!");
-			fmt.println("I'm a monster");
-		}
-	}
-
-	{
-		// NOTE(bill): A union can be used to achieve something similar. Instead
-		// of embedding the base data into the derived types, the derived data
-		// in embedded into the base type. Below is the same example of the
-		// basic game Entity but using an union.
-
-		Entity :: struct {
-			id:          u64,
-			name:        string,
-			position:    Vector3,
-			orientation: Quaternion,
-
-			derived: union {Frog, Monster},
-		};
-
-		Frog :: struct {
-			using entity: ^Entity,
-			jump_height:  f32,
-		};
-
-		Monster :: struct {
-			using entity: ^Entity,
-			is_robot:     bool,
-			is_zombie:    bool,
-		};
-
-		// See `parametric_polymorphism` procedure for details
-		new_entity :: proc($T: typeid) -> ^Entity {
-			t := new(Entity);
-			t.derived = T{entity = t};
-			return t;
-		}
-
-		entity := new_entity(Monster);
-
-		switch e in entity.derived {
-		case Frog:
-			fmt.println("Ribbit");
-		case Monster:
-			if e.is_robot  do fmt.println("Robotic");
-			if e.is_zombie do fmt.println("Grrrr!");
-		}
-
-		// NOTE(bill): As you can see, the usage code has not changed, only its
-		// memory layout. Both approaches have their own advantages but they can
-		// be used together to achieve different results. The subtyping approach
-		// can allow for a greater control of the memory layout and memory
-		// allocation, e.g. storing the derivatives together. However, this is
-		// also its disadvantage. You must either preallocate arrays for each
-		// derivative separation (which can be easily missed) or preallocate a
-		// bunch of "raw" memory; determining the maximum size of the derived
-		// types would require the aid of metaprogramming. Unions solve this
-		// particular problem as the data is stored with the base data.
-		// Therefore, it is possible to preallocate, e.g. [100]Entity.
-
-		// It should be noted that the union approach can have the same memory
-		// layout as the any and with the same type restrictions by using a
-		// pointer type for the derivatives.
-
-		/*
-			Entity :: struct {
-				...
-				derived: union{^Frog, ^Monster},
-			}
-
-			Frog :: struct {
-				using entity: Entity,
-				...
-			}
-			Monster :: struct {
-				using entity: Entity,
-				...
-
-			}
-			new_entity :: proc(T: type) -> ^Entity {
-				t := new(T);
-				t.derived = t;
-				return t;
-			}
-		*/
-	}
-}
-
-using_statement :: proc() {
-	fmt.println("\n# using statement");
-	// using can used to bring entities declared in a scope/namespace 
-	// into the current scope. This can be applied to import declarations, 
-	// import names, struct fields, procedure fields, and struct values.
-
-	Vector3 :: struct{x, y, z: f32};
-	{
-		Entity :: struct {
-			position: Vector3,
-			orientation: quaternion128,
-		};
-
-		// It can used like this:
-		foo0 :: proc(entity: ^Entity) {
-			fmt.println(entity.position.x, entity.position.y, entity.position.z);
-		}
-
-		// The entity members can be brought into the procedure scope by using it:
-		foo1 :: proc(entity: ^Entity) {
-			using entity;
-			fmt.println(position.x, position.y, position.z);
-		}
-
-		// The using can be applied to the parameter directly:
-		foo2 :: proc(using entity: ^Entity) {
-			fmt.println(position.x, position.y, position.z);
-		}
-
-		// It can also be applied to sub-fields:
-		foo3 :: proc(entity: ^Entity) {
-			using entity.position;
-			fmt.println(x, y, z);
-		}
-	}
-	{
-		// We can also apply the using statement to the struct fields directly, 
-		// making all the fields of position appear as if they on Entity itself:
-		Entity :: struct {
-			using position: Vector3,
-			orientation: quaternion128,
-		};
-		foo :: proc(entity: ^Entity) {
-			fmt.println(entity.x, entity.y, entity.z);
-		}
-		
-
-		// Subtype polymorphism
-		// It is possible to get subtype polymorphism, similar to inheritance-like 
-		// functionality in C++, but without the requirement of vtables or unknown 
-		// struct layout:
-
-		Colour :: struct {r, g, b, a: u8};
-		Frog :: struct {
-			ribbit_volume: f32,
-			using entity: Entity,
-			colour: Colour,
-		};
-
-		frog: Frog;
-		// Both work
-		foo(&frog.entity);
-		foo(&frog);
-		frog.x = 123;
-
-		// Note: using can be applied to arbitrarily many things, which allows 
-		// the ability to have multiple subtype polymorphism (but also its issues).
-
-		// Note: using’d fields can still be referred by name.
-	}
-	{ // using on an enum declaration
-
-		using Foo :: enum {A, B, C};
-
-		f0 := A;
-		f1 := B;
-		f2 := C;
-		fmt.println(f0, f1, f2);
-		fmt.println(len(Foo));
-	}
-}
-
-
-implicit_context_system :: proc() {
-	fmt.println("\n# implicit context system");
-	// In each scope, there is an implicit value named context. This 
-	// context variable is local to each scope and is implicitly passed 
-	// by pointer to any procedure call in that scope (if the procedure 
-	// has the Odin calling convention).
-
-	// The main purpose of the implicit context system is for the ability 
-	// to intercept third-party code and libraries and modify their 
-	// functionality. One such case is modifying how a library allocates
-	// something or logs something. In C, this was usually achieved with 
-	// the library defining macros which could be overridden so that the 
-	// user could define what he wanted. However, not many libraries 
-	// supported this in many languages by default which meant intercepting 
-	// third-party code to see what it does and to change how it does it is 
-	// not possible.
-
-	c := context; // copy the current scope's context
-
-	context.user_index = 456;
-	{
-		context.allocator = my_custom_allocator();
-		context.user_index = 123;
-		what_a_fool_believes(); // the `context` for this scope is implicitly passed to `what_a_fool_believes`
-	}
-
-	// `context` value is local to the scope it is in
-	assert(context.user_index == 456);
-
-	what_a_fool_believes :: proc() {
-		c := context; // this `context` is the same as the parent procedure that it was called from
-		// From this example, context.user_index == 123
-		// An context.allocator is assigned to the return value of `my_custom_allocator()`
-		assert(context.user_index == 123);
-
-		// The memory management procedure use the `context.allocator` by 
-		// default unless explicitly specified otherwise
-		china_grove := new(int);
-		free(china_grove);
-	}
-
-	my_custom_allocator :: mem.nil_allocator;
-
-	// By default, the context value has default values for its parameters which is 
-	// decided in the package runtime. What the defaults are are compiler specific.
-
-	// To see what the implicit context value contains, please see the following 
-	// definition in package runtime.
-}
-
-parametric_polymorphism :: proc() {
-	fmt.println("\n# parametric polymorphism");
-
-	print_value :: proc(value: $T) {
-		fmt.printf("print_value: %T %v\n", value, value);
-	}
-
-	v1: int    = 1;
-	v2: f32    = 2.1;
-	v3: f64    = 3.14;
-	v4: string = "message";
-
-	print_value(v1);
-	print_value(v2);
-	print_value(v3);
-	print_value(v4);
-
-	fmt.println();
-
-	add :: proc(p, q: $T) -> T {
-		x: T = p + q;
-		return x;
-	}
-
-	a := add(3, 4);
-	fmt.printf("a: %T = %v\n", a, a);
-
-	b := add(3.2, 4.3);
-	fmt.printf("b: %T = %v\n", b, b);
-
-	// This is how `new` is implemented
-	alloc_type :: proc($T: typeid) -> ^T {
-		t := cast(^T)alloc(size_of(T), align_of(T));
-		t^ = T{}; // Use default initialization value
-		return t;
-	}
-
-	copy_slice :: proc(dst, src: []$T) -> int {
-		n := min(len(dst), len(src));
-		if n > 0 {
-			mem.copy(&dst[0], &src[0], n*size_of(T));
-		}
-		return n;
-	}
-
-	double_params :: proc(a: $A, b: $B) -> A {
-		return a + A(b);
-	}
-
-	fmt.println(double_params(12, 1.345));
-
-
-
-	{ // Polymorphic Types and Type Specialization
-		Table_Slot :: struct(Key, Value: typeid) {
-			occupied: bool,
-			hash:     u32,
-			key:      Key,
-			value:    Value,
-		};
-		TABLE_SIZE_MIN :: 32;
-		Table :: struct(Key, Value: typeid) {
-			count:     int,
-			allocator: mem.Allocator,
-			slots:     []Table_Slot(Key, Value),
-		};
-
-		// Only allow types that are specializations of a (polymorphic) slice
-		make_slice :: proc($T: typeid/[]$E, len: int) -> T {
-			return make(T, len);
-		}
-
-		// Only allow types that are specializations of `Table`
-		allocate :: proc(table: ^$T/Table, capacity: int) {
-			c := context;
-			if table.allocator.procedure != nil do c.allocator = table.allocator;
-			context = c;
-
-			table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN));
-		}
-
-		expand :: proc(table: ^$T/Table) {
-			c := context;
-			if table.allocator.procedure != nil do c.allocator = table.allocator;
-			context = c;
-
-			old_slots := table.slots;
-			defer delete(old_slots);
-
-			cap := max(2*len(table.slots), TABLE_SIZE_MIN);
-			allocate(table, cap);
-
-			for s in old_slots do if s.occupied {
-				put(table, s.key, s.value);
-			}
-		}
-
-		// Polymorphic determination of a polymorphic struct
-		// put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) {
-		put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) {
-			hash := get_hash(key); // Ad-hoc method which would fail in a different scope
-			index := find_index(table, key, hash);
-			if index < 0 {
-				if f64(table.count) >= 0.75*f64(len(table.slots)) {
-					expand(table);
-				}
-				assert(table.count <= len(table.slots));
-
-				index = int(hash % u32(len(table.slots)));
-
-				for table.slots[index].occupied {
-					if index += 1; index >= len(table.slots) {
-						index = 0;
-					}
-				}
-
-				table.count += 1;
-			}
-
-			slot := &table.slots[index];
-			slot.occupied = true;
-			slot.hash     = hash;
-			slot.key      = key;
-			slot.value    = value;
-		}
-
-
-		// find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
-		find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
-			hash := get_hash(key);
-			index := find_index(table, key, hash);
-			if index < 0 {
-				return Value{}, false;
-			}
-			return table.slots[index].value, true;
-		}
-
-		find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
-			if len(table.slots) <= 0 do return -1;
-
-			index := int(hash % u32(len(table.slots)));
-			for table.slots[index].occupied {
-				if table.slots[index].hash == hash {
-					if table.slots[index].key == key {
-						return index;
-					}
-				}
-
-				if index += 1; index >= len(table.slots) {
-					index = 0;
-				}
-			}
-
-			return -1;
-		}
-
-		get_hash :: proc(s: string) -> u32 { // fnv32a
-			h: u32 = 0x811c9dc5;
-			for i in 0..<len(s) {
-				h = (h ~ u32(s[i])) * 0x01000193;
-			}
-			return h;
-		}
-
-
-		table: Table(string, int);
-
-		for i in 0..36 do put(&table, "Hellope", i);
-		for i in 0..42 do put(&table, "World!",  i);
-
-		found, _ := find(&table, "Hellope");
-		fmt.printf("`found` is %v\n", found);
-
-		found, _ = find(&table, "World!");
-		fmt.printf("`found` is %v\n", found);
-
-		// I would not personally design a hash table like this in production
-		// but this is a nice basic example
-		// A better approach would either use a `u64` or equivalent for the key
-		// and let the user specify the hashing function or make the user store
-		// the hashing procedure with the table
-	}
-
-	{ // Parametric polymorphic union
-		Error :: enum {
-			Foo0,
-			Foo1,
-			Foo2,
-			Foo3,
-		};
-		Para_Union :: union(T: typeid) {T, Error};
-		r: Para_Union(int);
-		fmt.println(typeid_of(type_of(r)));
-
-		fmt.println(r);
-		r = 123;
-		fmt.println(r);
-		r = Error.Foo0; // r = .Foo0; is allow too, see implicit selector expressions below
-		fmt.println(r);
-	}
-
-	{ // Polymorphic names
-		foo :: proc($N: $I, $T: typeid) -> (res: [N]T) {
-			// `N` is the constant value passed
-			// `I` is the type of N
-			// `T` is the type passed
-			fmt.printf("Generating an array of type %v from the value %v of type %v\n",
-					   typeid_of(type_of(res)), N, typeid_of(I));
-			for i in 0..<N {
-				res[i] = T(i*i);
-			}
-			return;
-		}
-
-		T :: int;
-		array := foo(4, T);
-		for v, i in array {
-			assert(v == T(i*i));
-		}
-
-		// Matrix multiplication
-		mul :: proc(a: [$M][$N]$T, b: [N][$P]T) -> (c: [M][P]T) {
-			for i in 0..<M {
-				for j in 0..<P {
-					for k in 0..<N {
-						c[i][j] += a[i][k] * b[k][j];
-					}
-				}
-			}
-			return;
-		}
-
-		x := [2][3]f32{
-			{1, 2, 3},
-			{3, 2, 1},
-		};
-		y := [3][2]f32{
-			{0, 8},
-			{6, 2},
-			{8, 4},
-		};
-		z := mul(x, y);
-		assert(z == {{36, 24}, {20, 32}});
-	}
-}
-
-
-array_programming :: proc() {
-	fmt.println("\n# array programming");
-	{
-		a := [3]f32{1, 2, 3};
-		b := [3]f32{5, 6, 7};
-		c := a * b;
-		d := a + b;
-		e := 1 +  (c - d) / 2;
-		fmt.printf("%.1f\n", e); // [0.5, 3.0, 6.5]
-	}
-
-	{
-		a := [3]f32{1, 2, 3};
-		b := swizzle(a, 2, 1, 0);
-		assert(b == [3]f32{3, 2, 1});
-
-		c := swizzle(a, 0, 0);
-		assert(c == [2]f32{1, 1});
-		assert(c == 1);
-	}
-
-	{
-		Vector3 :: distinct [3]f32;
-		a := Vector3{1, 2, 3};
-		b := Vector3{5, 6, 7};
-		c := (a * b)/2 + 1;
-		d := c.x + c.y + c.z;
-		fmt.printf("%.1f\n", d); // 22.0
-
-		cross :: proc(a, b: Vector3) -> Vector3 {
-			i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1);
-			j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0);
-			return i - j;
-		}
-
-		blah :: proc(a: Vector3) -> f32 {
-			return a.x + a.y + a.z;
-		}
-
-		x := cross(a, b);
-		fmt.println(x);
-		fmt.println(blah(x));
-	}
-}
-
-map_type :: proc() {
-	fmt.println("\n# map type");
-
-	m := make(map[string]int);
-	defer delete(m);
-
-	m["Bob"] = 2; 
-	m["Ted"] = 5;
-	fmt.println(m["Bob"]);
-
-	delete_key(&m, "Ted");
-
-	// If an element of a key does not exist, the zero value of the 
-	// element will be returned. To check to see if an element exists 
-	// can be done in two ways:
-	elem, ok := m["Bob"];
-	exists := "Bob" in m;
-
-}
-
-implicit_selector_expression :: proc() {
-	fmt.println("\n# implicit selector expression");
-
-	Foo :: enum {A, B, C};
-
-	f: Foo;
-	f = Foo.A;
-	f = .A;
-
-	BAR :: bit_set[Foo]{.B, .C};
-
-	switch f {
-	case .A:
-		fmt.println("HERE");
-	case .B:
-		fmt.println("NEVER");
-	case .C:
-		fmt.println("FOREVER");
-	}
-
-	my_map := make(map[Foo]int);
-	defer delete(my_map);
-
-	my_map[.A] = 123;
-	my_map[Foo.B] = 345;
-
-	fmt.println(my_map[.A] + my_map[Foo.B] + my_map[.C]);
-}
-
-
-complete_switch :: proc() {
-	fmt.println("\n# complete_switch");
-	{ // enum
-		Foo :: enum {
-			A,
-			B,
-			C,
-			D,
-		};
-
-		f := Foo.A;
-		#complete switch f {
-		case .A: fmt.println("A");
-		case .B: fmt.println("B");
-		case .C: fmt.println("C");
-		case .D: fmt.println("D");
-		case:    fmt.println("?");
-		}
-	}
-	{ // union
-		Foo :: union {int, bool};
-		f: Foo = 123;
-		#complete switch in f {
-		case int:  fmt.println("int");
-		case bool: fmt.println("bool");
-		case:
-		}
-	}
-}
-
-cstring_example :: proc() {
-	fmt.println("\n# cstring_example");
-
-	W :: "Hellope";
-	X :: cstring(W);
-	Y :: string(X);
-
-	w := W;
-	_ = w;
-	x: cstring = X;
-	y: string = Y;
-	z := string(x);
-	fmt.println(x, y, z);
-	fmt.println(len(x), len(y), len(z));
-	fmt.println(len(W), len(X), len(Y));
-	// IMPORTANT NOTE for cstring variables
-	// len(cstring) is O(N)
-	// cast(string)cstring is O(N)
-}
-
-bit_set_type :: proc() {
-	fmt.println("\n# bit_set type");
-
-	{
-		using Day :: enum {
-			Sunday,
-			Monday,
-			Tuesday,
-			Wednesday,
-			Thursday,
-			Friday,
-			Saturday,
-		};
-
-		Days :: distinct bit_set[Day];
-		WEEKEND :: Days{Sunday, Saturday};
-
-		d: Days;
-		d = {Sunday, Monday};
-		e := d | WEEKEND;
-		e |= {Monday};
-		fmt.println(d, e);
-
-		ok := Saturday in e; // `in` is only allowed for `map` and `bit_set` types
-		fmt.println(ok);
-		if Saturday in e {
-			fmt.println("Saturday in", e);
-		}
-		X :: Saturday in WEEKEND; // Constant evaluation
-		fmt.println(X);
-		fmt.println("Cardinality:", card(e));
-	}
-	{
-		x: bit_set['A'..'Z'];
-		#assert(size_of(x) == size_of(u32));
-		y: bit_set[0..8; u16];
-		fmt.println(typeid_of(type_of(x))); // bit_set[A..Z]
-		fmt.println(typeid_of(type_of(y))); // bit_set[0..8; u16]
-
-		incl(&x, 'F');
-		assert('F' in x);
-		excl(&x, 'F');
-		assert('F' not_in x);
-
-		y |= {1, 4, 2};
-		assert(2 in y);
-	}
-	{
-		Letters :: bit_set['A'..'Z'];
-		a := Letters{'A', 'B'};
-		b := Letters{'A', 'B', 'C', 'D', 'F'};
-		c := Letters{'A', 'B'};
-
-		assert(a <= b); // 'a' is a subset of 'b'
-		assert(b >= a); // 'b' is a superset of 'a'
-		assert(a < b);  // 'a' is a strict subset of 'b'
-		assert(b > a);  // 'b' is a strict superset of 'a'
-
-		assert(!(a < c)); // 'a' is a not strict subset of 'c'
-		assert(!(c > a)); // 'c' is a not strict superset of 'a'
-	}
-}
-
-deferred_procedure_associations :: proc() {
-	fmt.println("\n# deferred procedure associations");
-
-	@(deferred_out=closure)
-	open :: proc(s: string) -> bool {
-		fmt.println(s);
-		return true;
-	}
-
-	closure :: proc(ok: bool) {
-		fmt.println("Goodbye?", ok);
-	}
-
-	if open("Welcome") {
-		fmt.println("Something in the middle, mate.");
-	}
-}
-
-reflection :: proc() {
-	fmt.println("\n# reflection");
-
-	Foo :: struct {
-		x: int    `tag1`,
-		y: string `json:"y_field"`,
-		z: bool, // no tag
-	};
-
-	id := typeid_of(Foo);
-	names := reflect.struct_field_names(id);
-	types := reflect.struct_field_types(id);
-	tags  := reflect.struct_field_tags(id);
-
-	assert(len(names) == len(types) && len(names) == len(tags));
-
-	fmt.println("Foo :: struct {");
-	for tag, i in tags {
-		name, type := names[i], types[i];
-		if tag != "" {
-			fmt.printf("\t%s: %T `%s`,\n", name, type, tag);
-		} else {
-			fmt.printf("\t%s: %T,\n", name, type);
-		}
-	}
-	fmt.println("}");
-
-
-	for tag, i in tags {
-		if val, ok := reflect.struct_tag_lookup(tag, "json"); ok {
-			fmt.printf("json: %s -> %s\n", names[i], val);
-		}
-	}
-}
-
-quaternions :: proc() {
-	// Not just an April Fool's Joke any more, but a fully working thing!
-	fmt.println("\n# quaternions");
-
-	{ // Quaternion operations
-		q := 1 + 2i + 3j + 4k;
-		r := quaternion(5, 6, 7, 8);
-		t := q * r;
-		fmt.printf("(%v) * (%v) = %v\n", q, r, t);
-		v := q / r;
-		fmt.printf("(%v) / (%v) = %v\n", q, r, v);
-		u := q + r;
-		fmt.printf("(%v) + (%v) = %v\n", q, r, u);
-		s := q - r;
-		fmt.printf("(%v) - (%v) = %v\n", q, r, s);
-	}
-	{ // The quaternion types
-		q128: quaternion128; // 4xf32
-		q256: quaternion256; // 4xf64
-		q128 = quaternion(1, 0, 0, 0);
-		q256 = 1; // quaternion(1, 0, 0, 0);
-	}
-	{ // Built-in procedures
-		q := 1 + 2i + 3j + 4k;
-		fmt.println("q =", q);
-		fmt.println("real(q) =", real(q));
-		fmt.println("imag(q) =", imag(q));
-		fmt.println("jmag(q) =", jmag(q));
-		fmt.println("kmag(q) =", kmag(q));
-		fmt.println("conj(q) =", conj(q));
-		fmt.println("abs(q)  =", abs(q));
-	}
-	{ // Conversion of a complex type to a quaternion type
-		c := 1 + 2i;
-		q := quaternion256(c);
-		fmt.println(c);
-		fmt.println(q);
-	}
-	{ // Memory layout of Quaternions
-		q := 1 + 2i + 3j + 4k;
-		a := transmute([4]f64)q;
-		fmt.println("Quaternion memory layout: xyzw/(ijkr)");
-		fmt.println(q); // 1.000+2.000i+3.000j+4.000k
-		fmt.println(a); // [2.000, 3.000, 4.000, 1.000]
-	}
-}
-
-inline_for_statement :: proc() {
-	fmt.println("\n#inline for statements");
-
-	// 'inline for' works the same as if the 'inline' prefix did not
-	// exist but these ranged loops are explicitly unrolled which can
-	// be very very useful for certain optimizations
-
-	fmt.println("Ranges");
-	inline for x, i in 1..<4 {
-		fmt.println(x, i);
-	}
-
-	fmt.println("Strings");
-	inline for r, i in "Hello, 世界" {
-		fmt.println(r, i);
-	}
-
-	fmt.println("Arrays");
-	inline for elem, idx in ([4]int{1, 4, 9, 16}) {
-		fmt.println(elem, idx);
-	}
-
-
-	Foo_Enum :: enum {
-		A = 1,
-		B,
-		C = 6,
-		D,
-	};
-	fmt.println("Enum types");
-	inline for elem, idx in Foo_Enum {
-		fmt.println(elem, idx);
-	}
-}
-
-where_clauses :: proc() {
-	fmt.println("\n#procedure 'where' clauses");
-
-	{ // Sanity checks
-		simple_sanity_check :: proc(x: [2]int)
-			where len(x) > 1,
-				  type_of(x) == [2]int {
-			fmt.println(x);
-		}
-	}
-	{ // Parametric polymorphism checks
-		cross_2d :: proc(a, b: $T/[2]$E) -> E
-			where intrinsics.type_is_numeric(E) {
-			return a.x*b.y - a.y*b.x;
-		}
-		cross_3d :: proc(a, b: $T/[3]$E) -> T
-			where intrinsics.type_is_numeric(E) {
-			x := a.y*b.z - a.z*b.y;
-			y := a.z*b.x - a.x*b.z;
-			z := a.x*b.y - a.y*b.z;
-			return T{x, y, z};
-		}
-
-		a := [2]int{1, 2};
-		b := [2]int{5, -3};
-		fmt.println(cross_2d(a, b));
-
-		x := [3]f32{1, 4, 9};
-		y := [3]f32{-5, 0, 3};
-		fmt.println(cross_3d(x, y));
-
-		// Failure case
-		// i := [2]bool{true, false};
-		// j := [2]bool{false, true};
-		// fmt.println(cross_2d(i, j));
-
-	}
-
-	{ // Procedure groups usage
-		foo :: proc(x: [$N]int) -> bool
-			where N > 2 {
-			fmt.println(#procedure, "was called with the parameter", x);
-			return true;
-		}
-
-		bar :: proc(x: [$N]int) -> bool
-			where 0 < N,
-				  N <= 2 {
-			fmt.println(#procedure, "was called with the parameter", x);
-			return false;
-		}
-
-		baz :: proc{foo, bar};
-
-		x := [3]int{1, 2, 3};
-		y := [2]int{4, 9};
-		ok_x := baz(x);
-		ok_y := baz(y);
-		assert(ok_x == true);
-		assert(ok_y == false);
-	}
-
-	{ // Record types
-		Foo :: struct(T: typeid, N: int)
-			where intrinsics.type_is_integer(T),
-				  N > 2 {
-			x: [N]T,
-			y: [N-2]T,
-		};
-
-		T :: i32;
-		N :: 5;
-		f: Foo(T, N);
-		#assert(size_of(f) == (N+N-2)*size_of(T));
-	}
-}
-
-
-when ODIN_OS == "windows" do foreign import kernel32 "system:kernel32.lib"
-
-foreign_system :: proc() {
-	fmt.println("\n#foreign system");
-	when ODIN_OS == "windows" {
-		// It is sometimes necessarily to interface with foreign code, 
-		// such as a C library. In Odin, this is achieved through the 
-		// foreign system. You can “import” a library into the code 
-		// using the same semantics as a normal import declaration.
-
-		// This foreign import declaration will create a 
-		// “foreign import name” which can then be used to associate 
-		// entities within a foreign block.
-
-		foreign kernel32 {
-			ExitProcess :: proc "stdcall" (exit_code: u32) ---
-		}
-
-		// Foreign procedure declarations have the cdecl/c calling 
-		// convention by default unless specified otherwise. Due to 
-		// foreign procedures do not have a body declared within this 
-		// code, you need append the --- symbol to the end to distinguish 
-		// it as a procedure literal without a body and not a procedure type.
-
-		// The attributes system can be used to change specific properties 
-		// of entities declared within a block:
-
-		@(default_calling_convention = "std")
-		foreign kernel32 {
-			@(link_name="GetLastError") get_last_error :: proc() -> i32 ---
-		}
-
-		// Example using the link_prefix attribute
-		@(default_calling_convention = "std")
-		@(link_prefix = "Get")
-		foreign kernel32 {
-			LastError :: proc() -> i32 ---
-		}
-	}
-}
-
-ranged_fields_for_array_compound_literals :: proc() {
-	fmt.println("\n#ranged fields for array compound literals");
-	{ // Normal Array Literal
-		foo := [?]int{1, 4, 9, 16};
-		fmt.println(foo);
-	}
-	{ // Indexed
-		foo := [?]int{
-			3 = 16,
-			1 = 4,
-			2 = 9,
-			0 = 1,
-		};
-		fmt.println(foo);
-	}
-	{ // Ranges
-		i := 2;
-		foo := [?]int {
-			0 = 123,
-			5..9 = 54,
-			10..<16 = i*3 + (i-1)*2,
-		};
-		#assert(len(foo) == 16);
-		fmt.println(foo); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
-	}
-	{ // Slice and Dynamic Array support
-		i := 2;
-		foo_slice := []int {
-			0 = 123,
-			5..9 = 54,
-			10..<16 = i*3 + (i-1)*2,
-		};
-		assert(len(foo_slice) == 16);
-		fmt.println(foo_slice); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
-
-		foo_dynamic_array := [dynamic]int {
-			0 = 123,
-			5..9 = 54,
-			10..<16 = i*3 + (i-1)*2,
-		};
-		assert(len(foo_dynamic_array) == 16);
-		fmt.println(foo_dynamic_array); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
-	}
-}
-
-deprecated_attribute :: proc() {
-	@(deprecated="Use foo_v2 instead")
-	foo_v1 :: proc(x: int) {
-		fmt.println("foo_v1");
-	}
-	foo_v2 :: proc(x: int) {
-		fmt.println("foo_v2");
-	}
-
-	// NOTE: Uncomment to see the warning messages
-	// foo_v1(1);
-}
-
-range_statements_with_multiple_return_values :: proc() {
-	// IMPORTANT NOTE(bill, 2019-11-02): This feature is subject to be changed/removed
-	fmt.println("\n#range statements with multiple return values");
-	My_Iterator :: struct {
-		index: int,
-		data:  []i32,
-	};
-	make_my_iterator :: proc(data: []i32) -> My_Iterator {
-		return My_Iterator{data = data};
-	}
-	my_iterator :: proc(it: ^My_Iterator) -> (val: i32, idx: int, cond: bool) {
-		if cond = it.index < len(it.data); cond {
-			val = it.data[it.index];
-			idx = it.index;
-			it.index += 1;
-		}
-		return;
-	}
-
-	data := make([]i32, 6);
-	for _, i in data {
-		data[i] = i32(i*i);
-	}
-
-	{
-		it := make_my_iterator(data);
-		for val in my_iterator(&it) {
-			fmt.println(val);
-		}
-	}
-	{
-		it := make_my_iterator(data);
-		for val, idx in my_iterator(&it) {
-			fmt.println(val, idx);
-		}
-	}
-	{
-		it := make_my_iterator(data);
-		for {
-			val, _, cond := my_iterator(&it);
-			if !cond do break;
-			fmt.println(val);
-		}
-	}
-}
-
-soa_struct_layout :: proc() {
-	// IMPORTANT NOTE(bill, 2019-11-03): This feature is subject to be changed/removed
-	// NOTE(bill): Most likely #soa [N]T
-	fmt.println("\n#SOA Struct Layout");
-
-	{
-		Vector3 :: struct {x, y, z: f32};
-
-		N :: 2;
-		v_aos: [N]Vector3;
-		v_aos[0].x = 1;
-		v_aos[0].y = 4;
-		v_aos[0].z = 9;
-
-		fmt.println(len(v_aos));
-		fmt.println(v_aos[0]);
-		fmt.println(v_aos[0].x);
-		fmt.println(&v_aos[0].x);
-
-		v_aos[1] = {0, 3, 4};
-		v_aos[1].x = 2;
-		fmt.println(v_aos[1]);
-		fmt.println(v_aos);
-
-		v_soa: #soa[N]Vector3;
-
-		v_soa[0].x = 1;
-		v_soa[0].y = 4;
-		v_soa[0].z = 9;
-
-
-		// Same syntax as AOS and treat as if it was an array
-		fmt.println(len(v_soa));
-		fmt.println(v_soa[0]);
-		fmt.println(v_soa[0].x);
-		v_soa[1] = {0, 3, 4};
-		v_soa[1].x = 2;
-		fmt.println(v_soa[1]);
-
-		// Can use SOA syntax if necessary
-		v_soa.x[0] = 1;
-		v_soa.y[0] = 4;
-		v_soa.z[0] = 9;
-		fmt.println(v_soa.x[0]);
-
-		// Same pointer addresses with both syntaxes
-		assert(&v_soa[0].x == &v_soa.x[0]);
-
-
-		// Same fmt printing
-		fmt.println(v_aos);
-		fmt.println(v_soa);
-	}
-	{
-		// Works with arrays of length <= 4 which have the implicit fields xyzw/rgba
-		Vector3 :: distinct [3]f32;
-
-		N :: 2;
-		v_aos: [N]Vector3;
-		v_aos[0].x = 1;
-		v_aos[0].y = 4;
-		v_aos[0].z = 9;
-
-		v_soa: #soa[N]Vector3;
-
-		v_soa[0].x = 1;
-		v_soa[0].y = 4;
-		v_soa[0].z = 9;
-	}
-}
-
-
-main :: proc() {
-	when true {
-		the_basics();
-		control_flow();
-		named_proc_return_parameters();
-		explicit_procedure_overloading();
-		struct_type();
-		union_type();
-		using_statement();
-		implicit_context_system();
-		parametric_polymorphism();
-		array_programming();
-		map_type();
-		implicit_selector_expression();
-		complete_switch();
-		cstring_example();
-		bit_set_type();
-		deferred_procedure_associations();
-		reflection();
-		quaternions();
-		inline_for_statement();
-		where_clauses();
-		foreign_system();
-		ranged_fields_for_array_compound_literals();
-		deprecated_attribute();
-		range_statements_with_multiple_return_values();
-		soa_struct_layout();
-	}
-}
-

+ 22 - 1
src/build_settings.cpp

@@ -598,7 +598,6 @@ bool allow_check_foreign_filepath(void) {
 	return true;
 }
 
-
 // TODO(bill): OS dependent versions for the BuildContext
 // join_path
 // is_dir
@@ -606,6 +605,28 @@ bool allow_check_foreign_filepath(void) {
 // is_abs_path
 // has_subdir
 
+enum TargetFileValidity : u8 {
+	TargetFileValidity_Invalid,
+
+	TargetFileValidity_Writable_File,
+	TargetFileValidity_No_Write_Permission,
+	TargetFileValidity_Directory,
+
+	TargetTargetFileValidity_COUNT,
+};
+
+TargetFileValidity set_output_filename(void) {
+	// Assembles the output filename from build_context information.
+	// Returns `true`  if it doesn't exist or is a file.
+	// Returns `false` if a directory or write-protected file.
+
+
+
+
+	return TargetFileValidity_Writable_File;
+}
+
+
 String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
 String const NIX_SEPARATOR_STRING   = {cast(u8 *)"/",  1};
 

+ 395 - 66
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) {
@@ -420,9 +449,15 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 	case BuiltinProc_objc_find_class: 
 	case BuiltinProc_objc_register_selector: 
 	case BuiltinProc_objc_register_class: 
+	case BuiltinProc_atomic_type_is_lock_free:
 		// 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;
@@ -796,8 +831,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			}
 
 		} else if (name == "assert") {
-			if (ce->args.count != 1) {
-				error(call, "'#assert' expects 1 argument, got %td", ce->args.count);
+			if (ce->args.count != 1 && ce->args.count != 2) {
+				error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count);
 				return false;
 			}
 			if (!is_type_boolean(operand->type) || operand->mode != Addressing_Constant) {
@@ -806,15 +841,37 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 				gb_string_free(str);
 				return false;
 			}
+			if (ce->args.count == 2) {
+				Ast *arg = unparen_expr(ce->args[1]);
+				if (arg == nullptr || arg->kind != Ast_BasicLit || arg->BasicLit.token.kind != Token_String) {
+					gbString str = expr_to_string(arg);
+					error(call, "'%s' is not a constant string", str);
+					gb_string_free(str);
+					return false;
+				}
+			}
+
 			if (!operand->value.value_bool) {
-				gbString arg = expr_to_string(ce->args[0]);
-				error(call, "Compile time assertion: %s", arg);
+				gbString arg1 = expr_to_string(ce->args[0]);
+				gbString arg2 = {};
+
+				if (ce->args.count == 1) {
+					error(call, "Compile time assertion: %s", arg1);
+				} else {
+					arg2 = expr_to_string(ce->args[1]);
+					error(call, "Compile time assertion: %s (%s)", arg1, arg2);
+				}			
+				
 				if (c->proc_name != "") {
 					gbString str = type_to_string(c->curr_proc_sig);
 					error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
 					gb_string_free(str);
 				}
-				gb_string_free(arg);
+
+				gb_string_free(arg1);
+				if (ce->args.count == 2) {
+					gb_string_free(arg2);
+				}
 			}
 
 			operand->type = t_untyped_bool;
@@ -3198,11 +3255,56 @@ 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_type_is_lock_free:
+		{
+			Ast *expr = ce->args[0];
+			Operand o = {};
+			check_expr_or_type(c, &o, expr);
+
+			if (o.mode == Addressing_Invalid || o.mode == Addressing_Builtin) {
+				return false;
+			}
+			if (o.type == nullptr || o.type == t_invalid || is_type_asm_proc(o.type)) {
+				error(o.expr, "Invalid argument to '%.*s'", LIT(builtin_name));
+				return false;
+			}
+			if (is_type_polymorphic(o.type)) {
+				error(o.expr, "'%.*s' of polymorphic type cannot be determined", LIT(builtin_name));
+				return false;
+			}
+			if (is_type_untyped(o.type)) {
+				error(o.expr, "'%.*s' of untyped type is not allowed", LIT(builtin_name));
+				return false;
+			}
+			Type *t = o.type;
+			bool is_lock_free = is_type_lock_free(t);
+
+			operand->mode = Addressing_Constant;
+			operand->type = t_untyped_bool;
+			operand->value = exact_value_bool(is_lock_free);
+			break;
+		}
+
+	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 +3312,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 +3327,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 +3372,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)) {
@@ -3292,30 +3414,71 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			check_expr_with_type_hint(c, &x, ce->args[1], elem);
 			check_assignment(c, &x, elem, builtin_name);
 
+			Type *t = type_deref(operand->type);
+			switch (id) {
+			case BuiltinProc_atomic_add:
+			case BuiltinProc_atomic_sub:
+				if (!is_type_numeric(t)) {
+					gbString str = type_to_string(t);
+					error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
+					gb_string_free(str);
+				} else if (is_type_different_to_arch_endianness(t)) {
+					gbString str = type_to_string(t);
+					error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
+					gb_string_free(str);
+				}
+			}
+
 			operand->type = elem;
 			operand->mode = Addressing_Value;
 			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_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_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);
+
+
+			if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, nullptr)) {
+				return false;
+			}
+
+			Type *t = type_deref(operand->type);
+			switch (id) {
+			case BuiltinProc_atomic_add_explicit:
+			case BuiltinProc_atomic_sub_explicit:
+				if (!is_type_numeric(t)) {
+					gbString str = type_to_string(t);
+					error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
+					gb_string_free(str);
+				} else if (is_type_different_to_arch_endianness(t)) {
+					gbString str = type_to_string(t);
+					error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
+					gb_string_free(str);
+				}
+				break;
+			}
+
+			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)) {
@@ -3329,11 +3492,110 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 			check_assignment(c, &x, elem, builtin_name);
 			check_assignment(c, &y, elem, builtin_name);
 
+			Type *t = type_deref(operand->type);
+			if (!is_type_comparable(t)) {
+				gbString str = type_to_string(t);
+				error(operand->expr, "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), str);
+				gb_string_free(str);
+			}
+
+			operand->mode = Addressing_OptionalOk;
+			operand->type = elem;
+			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;
+			}
+
+			Type *t = type_deref(operand->type);
+			if (!is_type_comparable(t)) {
+				gbString str = type_to_string(t);
+				error(operand->expr, "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), str);
+				gb_string_free(str);
+			}
+
+			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;
 		}
-		break;
 
 	case BuiltinProc_fixed_point_mul:
 	case BuiltinProc_fixed_point_div:
@@ -4111,6 +4373,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;

+ 3 - 1
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");

+ 69 - 18
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, &param_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<Entity *> 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, &param_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);

Some files were not shown because too many files changed in this diff