mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-21 23:12:03 -07:00
merge upstream/master
This commit is contained in:
@@ -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: |
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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_load_explicit :: proc(dst: ^$T, order: Atomic_Memory_Order) -> T ---
|
||||
|
||||
atomic_add :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_sub :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_and :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_nand :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_or :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_xor :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_exchange :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_exchange_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
|
||||
atomic_xchg :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_acq :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_rel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_acqrel :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xchg_relaxed :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_compare_exchange_strong :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_compare_exchange_strong_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok ---
|
||||
atomic_compare_exchange_weak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_compare_exchange_weak_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok ---
|
||||
|
||||
atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
|
||||
atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
|
||||
// Constant type tests
|
||||
|
||||
@@ -201,6 +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() ---
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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,7 +1,7 @@
|
||||
//+private
|
||||
package mem_virtual
|
||||
|
||||
import sync "core:sync/sync2"
|
||||
import "core:sync"
|
||||
|
||||
Platform_Memory_Block :: struct {
|
||||
block: Memory_Block,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
package os2
|
||||
|
||||
import sync "core:sync/sync2"
|
||||
import "core:sync"
|
||||
import "core:time"
|
||||
import "core:runtime"
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package os
|
||||
|
||||
import "core:time"
|
||||
|
||||
|
||||
File_Info :: struct {
|
||||
fullpath: string,
|
||||
name: string,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -30,4 +30,4 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
#force_no_inline _cleanup_runtime()
|
||||
return 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 :: '_'
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+597
-134
File diff suppressed because it is too large
Load Diff
+36
-159
@@ -2,167 +2,44 @@ package sync
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
Ordering :: enum {
|
||||
Relaxed, // Monotonic
|
||||
Release,
|
||||
Acquire,
|
||||
Acquire_Release,
|
||||
Sequentially_Consistent,
|
||||
}
|
||||
cpu_relax :: intrinsics.cpu_relax
|
||||
|
||||
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_Memory_Order :: enum {
|
||||
Relaxed = 0,
|
||||
Consume = 1,
|
||||
Acquire = 2,
|
||||
Release = 3,
|
||||
Acq_Rel = 4,
|
||||
Seq_Cst = 5,
|
||||
}
|
||||
*/
|
||||
Atomic_Memory_Order :: intrinsics.Atomic_Memory_Order
|
||||
|
||||
|
||||
atomic_store :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) {
|
||||
when order == .Relaxed { intrinsics.atomic_store_relaxed(dst, val) }
|
||||
else when order == .Release { intrinsics.atomic_store_rel(dst, val) }
|
||||
else when order == .Sequentially_Consistent { intrinsics.atomic_store(dst, val) }
|
||||
else when order == .Acquire { #panic("there is not such thing as an acquire store") }
|
||||
else when order == .Acquire_Release { #panic("there is not such thing as an acquire/release store") }
|
||||
else { #panic("unknown order") }
|
||||
}
|
||||
|
||||
atomic_load :: #force_inline proc(dst: ^$T, $order: Ordering) -> T {
|
||||
when order == .Relaxed { return intrinsics.atomic_load_relaxed(dst) }
|
||||
else when order == .Acquire { return intrinsics.atomic_load_acq(dst) }
|
||||
else when order == .Sequentially_Consistent { return intrinsics.atomic_load(dst) }
|
||||
else when order == .Release { #panic("there is no such thing as a release load") }
|
||||
else when order == .Acquire_Release { #panic("there is no such thing as an acquire/release load") }
|
||||
else { #panic("unknown order") }
|
||||
}
|
||||
|
||||
atomic_swap :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
when order == .Relaxed { return intrinsics.atomic_xchg_relaxed(dst, val) }
|
||||
else when order == .Release { return intrinsics.atomic_xchg_rel(dst, val) }
|
||||
else when order == .Acquire { return intrinsics.atomic_xchg_acq(dst, val) }
|
||||
else when order == .Acquire_Release { return intrinsics.atomic_xchg_acqrel(dst, val) }
|
||||
else when order == .Sequentially_Consistent { return intrinsics.atomic_xchg(dst, val) }
|
||||
else { #panic("unknown order") }
|
||||
}
|
||||
|
||||
atomic_compare_exchange :: #force_inline proc(dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) {
|
||||
when failure == .Relaxed {
|
||||
when success == .Relaxed { return intrinsics.atomic_cxchg_relaxed(dst, old, new) }
|
||||
else when success == .Acquire { return intrinsics.atomic_cxchg_acq_failrelaxed(dst, old, new) }
|
||||
else when success == .Acquire_Release { return intrinsics.atomic_cxchg_acqrel_failrelaxed(dst, old, new) }
|
||||
else when success == .Sequentially_Consistent { return intrinsics.atomic_cxchg_failrelaxed(dst, old, new) }
|
||||
else when success == .Release { return intrinsics.atomic_cxchg_rel(dst, old, new) }
|
||||
else { #panic("an unknown ordering combination") }
|
||||
} else when failure == .Acquire {
|
||||
when success == .Release { return intrinsics.atomic_cxchg_acqrel(dst, old, new) }
|
||||
else when success == .Acquire { return intrinsics.atomic_cxchg_acq(dst, old, new) }
|
||||
else { #panic("an unknown ordering combination") }
|
||||
} else when failure == .Sequentially_Consistent {
|
||||
when success == .Sequentially_Consistent { return intrinsics.atomic_cxchg(dst, old, new) }
|
||||
else { #panic("an unknown ordering combination") }
|
||||
} else when failure == .Acquire_Release {
|
||||
#panic("there is not such thing as an acquire/release failure ordering")
|
||||
} else when failure == .Release {
|
||||
when success == .Acquire { return instrinsics.atomic_cxchg_failacq(dst, old, new) }
|
||||
else { #panic("an unknown ordering combination") }
|
||||
} else {
|
||||
return T{}, false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
atomic_compare_exchange_weak :: #force_inline proc(dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) {
|
||||
when failure == .Relaxed {
|
||||
when success == .Relaxed { return intrinsics.atomic_cxchgweak_relaxed(dst, old, new) }
|
||||
else when success == .Acquire { return intrinsics.atomic_cxchgweak_acq_failrelaxed(dst, old, new) }
|
||||
else when success == .Acquire_Release { return intrinsics.atomic_cxchgweak_acqrel_failrelaxed(dst, old, new) }
|
||||
else when success == .Sequentially_Consistent { return intrinsics.atomic_cxchgweak_failrelaxed(dst, old, new) }
|
||||
else when success == .Release { return intrinsics.atomic_cxchgweak_rel(dst, old, new) }
|
||||
else { #panic("an unknown ordering combination") }
|
||||
} else when failure == .Acquire {
|
||||
when success == .Release { return intrinsics.atomic_cxchgweak_acqrel(dst, old, new) }
|
||||
else when success == .Acquire { return intrinsics.atomic_cxchgweak_acq(dst, old, new) }
|
||||
else { #panic("an unknown ordering combination") }
|
||||
} else when failure == .Sequentially_Consistent {
|
||||
when success == .Sequentially_Consistent { return intrinsics.atomic_cxchgweak(dst, old, new) }
|
||||
else { #panic("an unknown ordering combination") }
|
||||
} else when failure == .Acquire_Release {
|
||||
#panic("there is not such thing as an acquire/release failure ordering")
|
||||
} else when failure == .Release {
|
||||
when success == .Acquire { return intrinsics.atomic_cxchgweak_failacq(dst, old, new) }
|
||||
else { #panic("an unknown ordering combination") }
|
||||
} else {
|
||||
return T{}, false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
atomic_add :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
when order == .Relaxed { return intrinsics.atomic_add_relaxed(dst, val) }
|
||||
else when order == .Release { return intrinsics.atomic_add_rel(dst, val) }
|
||||
else when order == .Acquire { return intrinsics.atomic_add_acq(dst, val) }
|
||||
else when order == .Acquire_Release { return intrinsics.atomic_add_acqrel(dst, val) }
|
||||
else when order == .Sequentially_Consistent { return intrinsics.atomic_add(dst, val) }
|
||||
else { #panic("unknown order") }
|
||||
}
|
||||
|
||||
atomic_sub :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
when order == .Relaxed { return intrinsics.atomic_sub_relaxed(dst, val) }
|
||||
else when order == .Release { return intrinsics.atomic_sub_rel(dst, val) }
|
||||
else when order == .Acquire { return intrinsics.atomic_sub_acq(dst, val) }
|
||||
else when order == .Acquire_Release { return intrinsics.atomic_sub_acqrel(dst, val) }
|
||||
else when order == .Sequentially_Consistent { return intrinsics.atomic_sub(dst, val) }
|
||||
else { #panic("unknown order") }
|
||||
}
|
||||
|
||||
atomic_and :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
when order == .Relaxed { return intrinsics.atomic_and_relaxed(dst, val) }
|
||||
else when order == .Release { return intrinsics.atomic_and_rel(dst, val) }
|
||||
else when order == .Acquire { return intrinsics.atomic_and_acq(dst, val) }
|
||||
else when order == .Acquire_Release { return intrinsics.atomic_and_acqrel(dst, val) }
|
||||
else when order == .Sequentially_Consistent { return intrinsics.atomic_and(dst, val) }
|
||||
else { #panic("unknown order") }
|
||||
}
|
||||
|
||||
atomic_nand :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
when order == .Relaxed { return intrinsics.atomic_nand_relaxed(dst, val) }
|
||||
else when order == .Release { return intrinsics.atomic_nand_rel(dst, val) }
|
||||
else when order == .Acquire { return intrinsics.atomic_nand_acq(dst, val) }
|
||||
else when order == .Acquire_Release { return intrinsics.atomic_nand_acqrel(dst, val) }
|
||||
else when order == .Sequentially_Consistent { return intrinsics.atomic_nand(dst, val) }
|
||||
else { #panic("unknown order") }
|
||||
}
|
||||
|
||||
atomic_or :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
when order == .Relaxed { return intrinsics.atomic_or_relaxed(dst, val) }
|
||||
else when order == .Release { return intrinsics.atomic_or_rel(dst, val) }
|
||||
else when order == .Acquire { return intrinsics.atomic_or_acq(dst, val) }
|
||||
else when order == .Acquire_Release { return intrinsics.atomic_or_acqrel(dst, val) }
|
||||
else when order == .Sequentially_Consistent { return intrinsics.atomic_or(dst, val) }
|
||||
else { #panic("unknown order") }
|
||||
}
|
||||
|
||||
atomic_xor :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
when order == .Relaxed { return intrinsics.atomic_xor_relaxed(dst, val) }
|
||||
else when order == .Release { return intrinsics.atomic_xor_rel(dst, val) }
|
||||
else when order == .Acquire { return intrinsics.atomic_xor_acq(dst, val) }
|
||||
else when order == .Acquire_Release { return intrinsics.atomic_xor_acqrel(dst, val) }
|
||||
else when order == .Sequentially_Consistent { return intrinsics.atomic_xor(dst, val) }
|
||||
else { #panic("unknown order") }
|
||||
}
|
||||
atomic_thread_fence :: intrinsics.atomic_thread_fence
|
||||
atomic_signal_fence :: intrinsics.atomic_signal_fence
|
||||
atomic_store :: intrinsics.atomic_store
|
||||
atomic_store_explicit :: intrinsics.atomic_store_explicit
|
||||
atomic_load :: intrinsics.atomic_load
|
||||
atomic_load_explicit :: intrinsics.atomic_load_explicit
|
||||
atomic_add :: intrinsics.atomic_add
|
||||
atomic_add_explicit :: intrinsics.atomic_add_explicit
|
||||
atomic_sub :: intrinsics.atomic_sub
|
||||
atomic_sub_explicit :: intrinsics.atomic_sub_explicit
|
||||
atomic_and :: intrinsics.atomic_and
|
||||
atomic_and_explicit :: intrinsics.atomic_and_explicit
|
||||
atomic_nand :: intrinsics.atomic_nand
|
||||
atomic_nand_explicit :: intrinsics.atomic_nand_explicit
|
||||
atomic_or :: intrinsics.atomic_or
|
||||
atomic_or_explicit :: intrinsics.atomic_or_explicit
|
||||
atomic_xor :: intrinsics.atomic_xor
|
||||
atomic_xor_explicit :: intrinsics.atomic_xor_explicit
|
||||
atomic_exchange :: intrinsics.atomic_exchange
|
||||
atomic_exchange_explicit :: intrinsics.atomic_exchange_explicit
|
||||
|
||||
// Returns value and optional ok boolean
|
||||
atomic_compare_exchange_strong :: intrinsics.atomic_compare_exchange_strong
|
||||
atomic_compare_exchange_strong_explicit :: intrinsics.atomic_compare_exchange_strong_explicit
|
||||
atomic_compare_exchange_weak :: intrinsics.atomic_compare_exchange_weak
|
||||
atomic_compare_exchange_weak_explicit :: intrinsics.atomic_compare_exchange_weak_explicit
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,6 +1,6 @@
|
||||
//+private
|
||||
//+build darwin
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:c"
|
||||
import "core:time"
|
||||
@@ -1,6 +1,6 @@
|
||||
//+private
|
||||
//+build freebsd
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:c"
|
||||
import "core:os"
|
||||
@@ -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,6 +1,6 @@
|
||||
//+private
|
||||
//+build openbsd
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:c"
|
||||
import "core:os"
|
||||
@@ -1,6 +1,6 @@
|
||||
//+private
|
||||
//+build windows
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:time"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:time"
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
//+build darwin
|
||||
//+private
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:c"
|
||||
import "core:time"
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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,6 +1,6 @@
|
||||
//+build windows
|
||||
//+private
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
import "core:time"
|
||||
import win32 "core:sys/windows"
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
@@ -1,9 +0,0 @@
|
||||
//+build freebsd
|
||||
//+private
|
||||
package sync2
|
||||
|
||||
import "core:os"
|
||||
|
||||
_current_thread_id :: proc "contextless" () -> int {
|
||||
return os.current_thread_id()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
//+build linux
|
||||
//+private
|
||||
package sync2
|
||||
|
||||
import "core:sys/unix"
|
||||
|
||||
_current_thread_id :: proc "contextless" () -> int {
|
||||
return unix.sys_gettid()
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
//+build openbsd
|
||||
//+private
|
||||
package sync2
|
||||
|
||||
import "core:os"
|
||||
|
||||
_current_thread_id :: proc "contextless" () -> int {
|
||||
return os.current_thread_id()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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,4 +1,4 @@
|
||||
package sync2
|
||||
package sync
|
||||
|
||||
/*
|
||||
Example:
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ---
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// +build windows
|
||||
package sys_windows
|
||||
|
||||
foreign import dwmapi "system:Dwmapi.lib"
|
||||
|
||||
@(default_calling_convention="stdcall")
|
||||
foreign dwmapi {
|
||||
DwmFlush :: proc() -> HRESULT ---
|
||||
}
|
||||
@@ -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 ---
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
+154
-87
@@ -1,67 +1,75 @@
|
||||
package thread
|
||||
|
||||
/*
|
||||
thread.Pool
|
||||
Copyright 2022 eisbehr
|
||||
Made available under Odin's BSD-3 license.
|
||||
*/
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:sync"
|
||||
import "core:mem"
|
||||
|
||||
Task_Status :: enum i32 {
|
||||
Ready,
|
||||
Busy,
|
||||
Waiting,
|
||||
Term,
|
||||
}
|
||||
|
||||
Task_Proc :: #type proc(task: ^Task)
|
||||
Task_Proc :: #type proc(task: Task)
|
||||
|
||||
Task :: struct {
|
||||
procedure: Task_Proc,
|
||||
data: rawptr,
|
||||
procedure: Task_Proc,
|
||||
data: rawptr,
|
||||
user_index: int,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
Task_Id :: distinct i32
|
||||
INVALID_TASK_ID :: Task_Id(-1)
|
||||
|
||||
|
||||
// Do not access the pool's members directly while the pool threads are running,
|
||||
// since they use different kinds of locking and mutual exclusion devices.
|
||||
// Careless access can and will lead to nasty bugs. Once initialized, the
|
||||
// pool's memory address is not allowed to change until it is destroyed.
|
||||
Pool :: struct {
|
||||
allocator: mem.Allocator,
|
||||
mutex: sync.Mutex,
|
||||
sem_available: sync.Semaphore,
|
||||
processing_task_count: int, // atomic
|
||||
is_running: bool,
|
||||
allocator: mem.Allocator,
|
||||
mutex: sync.Mutex,
|
||||
sem_available: sync.Sema,
|
||||
|
||||
// the following values are atomic
|
||||
num_waiting: int,
|
||||
num_in_processing: int,
|
||||
num_outstanding: int, // num_waiting + num_in_processing
|
||||
num_done: int,
|
||||
// end of atomics
|
||||
|
||||
is_running: bool,
|
||||
|
||||
threads: []^Thread,
|
||||
|
||||
tasks: [dynamic]Task,
|
||||
tasks: [dynamic]Task,
|
||||
tasks_done: [dynamic]Task,
|
||||
}
|
||||
|
||||
pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator) {
|
||||
worker_thread_internal :: proc(t: ^Thread) {
|
||||
pool := (^Pool)(t.data)
|
||||
|
||||
for pool.is_running {
|
||||
sync.semaphore_wait_for(&pool.sem_available)
|
||||
|
||||
if task, ok := pool_try_and_pop_task(pool); ok {
|
||||
pool_do_work(pool, &task)
|
||||
}
|
||||
}
|
||||
|
||||
sync.semaphore_post(&pool.sem_available, 1)
|
||||
}
|
||||
|
||||
|
||||
// Once initialized, the pool's memory address is not allowed to change until
|
||||
// it is destroyed. If thread_count < 1, thread count 1 will be used.
|
||||
//
|
||||
// The thread pool requires an allocator which it either owns, or which is thread safe.
|
||||
pool_init :: proc(pool: ^Pool, thread_count: int, allocator: mem.Allocator) {
|
||||
context.allocator = allocator
|
||||
pool.allocator = allocator
|
||||
pool.tasks = make([dynamic]Task)
|
||||
pool.threads = make([]^Thread, thread_count)
|
||||
pool.tasks = make([dynamic]Task)
|
||||
pool.tasks_done = make([dynamic]Task)
|
||||
pool.threads = make([]^Thread, max(thread_count, 1))
|
||||
|
||||
sync.mutex_init(&pool.mutex)
|
||||
sync.semaphore_init(&pool.sem_available)
|
||||
pool.is_running = true
|
||||
|
||||
for _, i in pool.threads {
|
||||
t := create(worker_thread_internal)
|
||||
t := create(proc(t: ^Thread) {
|
||||
pool := (^Pool)(t.data)
|
||||
|
||||
for intrinsics.atomic_load(&pool.is_running) {
|
||||
sync.wait(&pool.sem_available)
|
||||
|
||||
if task, ok := pool_pop_waiting(pool); ok {
|
||||
pool_do_work(pool, task)
|
||||
}
|
||||
}
|
||||
|
||||
sync.post(&pool.sem_available, 1)
|
||||
})
|
||||
t.user_index = i
|
||||
t.data = pool
|
||||
pool.threads[i] = t
|
||||
@@ -70,15 +78,13 @@ pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator
|
||||
|
||||
pool_destroy :: proc(pool: ^Pool) {
|
||||
delete(pool.tasks)
|
||||
delete(pool.tasks_done)
|
||||
|
||||
for thread in &pool.threads {
|
||||
destroy(thread)
|
||||
for t in &pool.threads {
|
||||
destroy(t)
|
||||
}
|
||||
|
||||
delete(pool.threads, pool.allocator)
|
||||
|
||||
sync.mutex_destroy(&pool.mutex)
|
||||
sync.semaphore_destroy(&pool.sem_available)
|
||||
}
|
||||
|
||||
pool_start :: proc(pool: ^Pool) {
|
||||
@@ -87,10 +93,12 @@ pool_start :: proc(pool: ^Pool) {
|
||||
}
|
||||
}
|
||||
|
||||
// Finish tasks that have already started processing, then shut down all pool
|
||||
// threads. Might leave over waiting tasks, any memory allocated for the
|
||||
// user data of those tasks will not be freed.
|
||||
pool_join :: proc(pool: ^Pool) {
|
||||
pool.is_running = false
|
||||
|
||||
sync.semaphore_post(&pool.sem_available, len(pool.threads))
|
||||
intrinsics.atomic_store(&pool.is_running, false)
|
||||
sync.post(&pool.sem_available, len(pool.threads))
|
||||
|
||||
yield()
|
||||
|
||||
@@ -99,53 +107,112 @@ pool_join :: proc(pool: ^Pool) {
|
||||
}
|
||||
}
|
||||
|
||||
pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
|
||||
sync.mutex_lock(&pool.mutex)
|
||||
defer sync.mutex_unlock(&pool.mutex)
|
||||
// Add a task to the thread pool.
|
||||
//
|
||||
// Tasks can be added from any thread, not just the thread that created
|
||||
// the thread pool. You can even add tasks from inside other tasks.
|
||||
//
|
||||
// Each task also needs an allocator which it either owns, or which is thread
|
||||
// safe. By default, allocations in the task are disabled by use of the
|
||||
// nil_allocator.
|
||||
pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0, allocator := context.allocator) {
|
||||
sync.guard(&pool.mutex)
|
||||
|
||||
task: Task
|
||||
task.procedure = procedure
|
||||
task.data = data
|
||||
task.user_index = user_index
|
||||
|
||||
append(&pool.tasks, task)
|
||||
sync.semaphore_post(&pool.sem_available, 1)
|
||||
append(&pool.tasks, Task{
|
||||
procedure = procedure,
|
||||
data = data,
|
||||
user_index = user_index,
|
||||
allocator = allocator,
|
||||
})
|
||||
intrinsics.atomic_add(&pool.num_waiting, 1)
|
||||
intrinsics.atomic_add(&pool.num_outstanding, 1)
|
||||
sync.post(&pool.sem_available, 1)
|
||||
}
|
||||
|
||||
pool_try_and_pop_task :: proc(pool: ^Pool) -> (task: Task, got_task: bool = false) {
|
||||
if sync.mutex_try_lock(&pool.mutex) {
|
||||
if len(pool.tasks) != 0 {
|
||||
intrinsics.atomic_add(&pool.processing_task_count, 1)
|
||||
task = pop_front(&pool.tasks)
|
||||
got_task = true
|
||||
}
|
||||
sync.mutex_unlock(&pool.mutex)
|
||||
// Number of tasks waiting to be processed. Only informational, mostly for
|
||||
// debugging. Don't rely on this value being consistent with other num_*
|
||||
// values.
|
||||
pool_num_waiting :: #force_inline proc(pool: ^Pool) -> int {
|
||||
return intrinsics.atomic_load(&pool.num_waiting)
|
||||
}
|
||||
|
||||
// Number of tasks currently being processed. Only informational, mostly for
|
||||
// debugging. Don't rely on this value being consistent with other num_*
|
||||
// values.
|
||||
pool_num_in_processing :: #force_inline proc(pool: ^Pool) -> int {
|
||||
return intrinsics.atomic_load(&pool.num_in_processing)
|
||||
}
|
||||
|
||||
// Outstanding tasks are all tasks that are not done, that is, tasks that are
|
||||
// waiting, as well as tasks that are currently being processed. Only
|
||||
// informational, mostly for debugging. Don't rely on this value being
|
||||
// consistent with other num_* values.
|
||||
pool_num_outstanding :: #force_inline proc(pool: ^Pool) -> int {
|
||||
return intrinsics.atomic_load(&pool.num_outstanding)
|
||||
}
|
||||
|
||||
// Number of tasks which are done processing. Only informational, mostly for
|
||||
// debugging. Don't rely on this value being consistent with other num_*
|
||||
// values.
|
||||
pool_num_done :: #force_inline proc(pool: ^Pool) -> int {
|
||||
return intrinsics.atomic_load(&pool.num_done)
|
||||
}
|
||||
|
||||
// If tasks are only being added from one thread, and this procedure is being
|
||||
// called from that same thread, it will reliably tell if the thread pool is
|
||||
// empty or not. Empty in this case means there are no tasks waiting, being
|
||||
// processed, or _done_.
|
||||
pool_is_empty :: #force_inline proc(pool: ^Pool) -> bool {
|
||||
return pool_num_outstanding(pool) == 0 && pool_num_done(pool) == 0
|
||||
}
|
||||
|
||||
// Mostly for internal use.
|
||||
pool_pop_waiting :: proc(pool: ^Pool) -> (task: Task, got_task: bool) {
|
||||
sync.guard(&pool.mutex)
|
||||
|
||||
if len(pool.tasks) != 0 {
|
||||
intrinsics.atomic_sub(&pool.num_waiting, 1)
|
||||
intrinsics.atomic_add(&pool.num_in_processing, 1)
|
||||
task = pop_front(&pool.tasks)
|
||||
got_task = true
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Use this to take out finished tasks.
|
||||
pool_pop_done :: proc(pool: ^Pool) -> (task: Task, got_task: bool) {
|
||||
sync.guard(&pool.mutex)
|
||||
|
||||
pool_do_work :: proc(pool: ^Pool, task: ^Task) {
|
||||
task.procedure(task)
|
||||
intrinsics.atomic_sub(&pool.processing_task_count, 1)
|
||||
}
|
||||
|
||||
|
||||
pool_wait_and_process :: proc(pool: ^Pool) {
|
||||
for len(pool.tasks) != 0 || intrinsics.atomic_load(&pool.processing_task_count) != 0 {
|
||||
if task, ok := pool_try_and_pop_task(pool); ok {
|
||||
pool_do_work(pool, &task)
|
||||
}
|
||||
|
||||
// Safety kick
|
||||
if len(pool.tasks) != 0 && intrinsics.atomic_load(&pool.processing_task_count) == 0 {
|
||||
sync.mutex_lock(&pool.mutex)
|
||||
sync.semaphore_post(&pool.sem_available, len(pool.tasks))
|
||||
sync.mutex_unlock(&pool.mutex)
|
||||
}
|
||||
|
||||
yield()
|
||||
if len(pool.tasks_done) != 0 {
|
||||
task = pop_front(&pool.tasks_done)
|
||||
got_task = true
|
||||
intrinsics.atomic_sub(&pool.num_done, 1)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Mostly for internal use.
|
||||
pool_do_work :: proc(pool: ^Pool, task: Task) {
|
||||
{
|
||||
context.allocator = task.allocator
|
||||
task.procedure(task)
|
||||
}
|
||||
|
||||
sync.guard(&pool.mutex)
|
||||
|
||||
append(&pool.tasks_done, task)
|
||||
intrinsics.atomic_add(&pool.num_done, 1)
|
||||
intrinsics.atomic_sub(&pool.num_outstanding, 1)
|
||||
intrinsics.atomic_sub(&pool.num_in_processing, 1)
|
||||
}
|
||||
|
||||
// Process the rest of the tasks, also use this thread for processing, then join
|
||||
// all the pool threads.
|
||||
pool_finish :: proc(pool: ^Pool) {
|
||||
for task in pool_pop_waiting(pool) {
|
||||
pool_do_work(pool, task)
|
||||
}
|
||||
pool_join(pool)
|
||||
}
|
||||
|
||||
@@ -7,30 +7,19 @@ import "core:intrinsics"
|
||||
import "core:sync"
|
||||
import "core:sys/unix"
|
||||
|
||||
Thread_State :: enum u8 {
|
||||
Started,
|
||||
Joined,
|
||||
Done,
|
||||
}
|
||||
|
||||
// NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t.
|
||||
// Also see core/sys/darwin/mach_darwin.odin/semaphore_t.
|
||||
Thread_Os_Specific :: struct #align 16 {
|
||||
unix_thread: unix.pthread_t, // NOTE: very large on Darwin, small on Linux.
|
||||
|
||||
// NOTE: pthread has a proc to query this, but it is marked
|
||||
// as non-portable ("np") so we do this instead.
|
||||
done: bool,
|
||||
|
||||
// since libpthread doesn't seem to have a way to create a thread
|
||||
// in a suspended state, we have it wait on this gate, which we
|
||||
// signal to start it.
|
||||
// destroyed after thread is started.
|
||||
start_gate: sync.Condition,
|
||||
start_mutex: sync.Mutex,
|
||||
|
||||
// if true, the thread has been started and the start_gate has been destroyed.
|
||||
started: bool,
|
||||
|
||||
// NOTE: with pthreads, it is undefined behavior for multiple threads
|
||||
// to call join on the same thread at the same time.
|
||||
// this value is atomically updated to detect this.
|
||||
// See the comment in `join`.
|
||||
already_joined: bool,
|
||||
cond: sync.Cond,
|
||||
mutex: sync.Mutex,
|
||||
flags: bit_set[Thread_State; u8],
|
||||
}
|
||||
//
|
||||
// Creates a thread which will run the given procedure.
|
||||
@@ -38,26 +27,31 @@ Thread_Os_Specific :: struct #align 16 {
|
||||
//
|
||||
_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
|
||||
__linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
|
||||
context = runtime.default_context()
|
||||
t := (^Thread)(t)
|
||||
sync.condition_wait_for(&t.start_gate)
|
||||
sync.condition_destroy(&t.start_gate)
|
||||
sync.mutex_destroy(&t.start_mutex)
|
||||
t.start_gate = {}
|
||||
t.start_mutex = {}
|
||||
|
||||
context = t.init_context.? or_else runtime.default_context()
|
||||
|
||||
context = runtime.default_context()
|
||||
|
||||
sync.lock(&t.mutex)
|
||||
|
||||
t.id = sync.current_thread_id()
|
||||
t.procedure(t)
|
||||
|
||||
if t.init_context == nil {
|
||||
if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
|
||||
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
|
||||
}
|
||||
for (.Started not_in t.flags) {
|
||||
sync.wait(&t.cond, &t.mutex)
|
||||
}
|
||||
|
||||
init_context := t.init_context
|
||||
context = init_context.? or_else runtime.default_context()
|
||||
|
||||
t.procedure(t)
|
||||
|
||||
intrinsics.atomic_store(&t.flags, t.flags + { .Done })
|
||||
|
||||
sync.unlock(&t.mutex)
|
||||
|
||||
if init_context == nil && context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
|
||||
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
|
||||
}
|
||||
|
||||
intrinsics.atomic_store(&t.done, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -76,9 +70,6 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
|
||||
return nil
|
||||
}
|
||||
thread.creation_allocator = context.allocator
|
||||
|
||||
sync.mutex_init(&thread.start_mutex)
|
||||
sync.condition_init(&thread.start_gate, &thread.start_mutex)
|
||||
|
||||
// Set thread priority.
|
||||
policy: i32
|
||||
@@ -97,65 +88,35 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
|
||||
res = unix.pthread_attr_setschedparam(&attrs, ¶ms)
|
||||
assert(res == 0)
|
||||
|
||||
thread.procedure = procedure
|
||||
if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
|
||||
free(thread, thread.creation_allocator)
|
||||
|
||||
sync.condition_destroy(&thread.start_gate)
|
||||
sync.mutex_destroy(&thread.start_mutex)
|
||||
return nil
|
||||
}
|
||||
thread.procedure = procedure
|
||||
|
||||
|
||||
return thread
|
||||
}
|
||||
|
||||
_start :: proc(t: ^Thread) {
|
||||
if intrinsics.atomic_xchg(&t.started, true) {
|
||||
return
|
||||
}
|
||||
sync.condition_signal(&t.start_gate)
|
||||
sync.guard(&t.mutex)
|
||||
t.flags += { .Started }
|
||||
sync.signal(&t.cond)
|
||||
}
|
||||
|
||||
_is_done :: proc(t: ^Thread) -> bool {
|
||||
return intrinsics.atomic_load(&t.done)
|
||||
return .Done in intrinsics.atomic_load(&t.flags)
|
||||
}
|
||||
|
||||
_join :: proc(t: ^Thread) {
|
||||
if unix.pthread_equal(unix.pthread_self(), t.unix_thread) {
|
||||
return
|
||||
}
|
||||
// if unix.pthread_self().x == t.unix_thread.x do return;
|
||||
sync.guard(&t.mutex)
|
||||
|
||||
// NOTE(tetra): It's apparently UB for multiple threads to join the same thread
|
||||
// at the same time.
|
||||
// If someone else already did, spin until the thread dies.
|
||||
// See note on `already_joined` field.
|
||||
// TODO(tetra): I'm not sure if we should do this, or panic, since I'm not
|
||||
// sure it makes sense to need to join from multiple threads?
|
||||
if intrinsics.atomic_xchg(&t.already_joined, true) {
|
||||
for {
|
||||
if intrinsics.atomic_load(&t.done) {
|
||||
return
|
||||
}
|
||||
intrinsics.cpu_relax()
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(tetra): If we're already dead, don't bother calling to pthread_join as that
|
||||
// will just return 3 (ESRCH).
|
||||
// We do this instead because I don't know if there is a danger
|
||||
// that you may join a different thread from the one you called join on,
|
||||
// if the thread handle is reused.
|
||||
if intrinsics.atomic_load(&t.done) {
|
||||
if .Joined in t.flags || unix.pthread_equal(unix.pthread_self(), t.unix_thread) {
|
||||
return
|
||||
}
|
||||
|
||||
ret_val: rawptr
|
||||
_ = unix.pthread_join(t.unix_thread, &ret_val)
|
||||
if !intrinsics.atomic_load(&t.done) {
|
||||
panic("thread not done after join")
|
||||
}
|
||||
unix.pthread_join(t.unix_thread, nil)
|
||||
|
||||
t.flags += { .Joined }
|
||||
}
|
||||
|
||||
_join_multiple :: proc(threads: ..^Thread) {
|
||||
@@ -164,14 +125,12 @@ _join_multiple :: proc(threads: ..^Thread) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_destroy :: proc(t: ^Thread) {
|
||||
_join(t)
|
||||
t.unix_thread = {}
|
||||
free(t, t.creation_allocator)
|
||||
}
|
||||
|
||||
|
||||
_terminate :: proc(t: ^Thread, exit_code: int) {
|
||||
// TODO(bill)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package basic
|
||||
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
fmt.println("Hellope!");
|
||||
}
|
||||
@@ -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,
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+22
-1
@@ -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};
|
||||
|
||||
|
||||
+416
-87
@@ -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,60 +3327,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
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)) {
|
||||
error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
|
||||
return false;
|
||||
}
|
||||
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_store_explicit:
|
||||
{
|
||||
Type *elem = nullptr;
|
||||
if (!is_type_normal_pointer(operand->type, &elem)) {
|
||||
@@ -3292,30 +3338,147 @@ 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);
|
||||
|
||||
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:
|
||||
{
|
||||
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->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_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;
|
||||
}
|
||||
|
||||
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:
|
||||
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_sub:
|
||||
case BuiltinProc_atomic_and:
|
||||
case BuiltinProc_atomic_nand:
|
||||
case BuiltinProc_atomic_or:
|
||||
case BuiltinProc_atomic_xor:
|
||||
case BuiltinProc_atomic_exchange:
|
||||
{
|
||||
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);
|
||||
|
||||
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_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
@@ -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
@@ -4872,25 +4872,16 @@ bool is_expr_constant_zero(Ast *expr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
||||
ast_node(ce, CallExpr, call);
|
||||
GB_ASSERT(is_type_proc(proc_type));
|
||||
proc_type = base_type(proc_type);
|
||||
TypeProc *pt = &proc_type->Proc;
|
||||
|
||||
isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_) {
|
||||
GB_ASSERT(pt != nullptr);
|
||||
GB_ASSERT(pt->kind == Type_Proc);
|
||||
isize param_count = 0;
|
||||
isize param_count_excluding_defaults = 0;
|
||||
bool variadic = pt->variadic;
|
||||
bool vari_expand = (ce->ellipsis.pos.line != 0);
|
||||
i64 score = 0;
|
||||
bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
|
||||
|
||||
|
||||
bool variadic = pt->Proc.variadic;
|
||||
TypeTuple *param_tuple = nullptr;
|
||||
|
||||
if (pt->params != nullptr) {
|
||||
param_tuple = &pt->params->Tuple;
|
||||
if (pt->Proc.params != nullptr) {
|
||||
param_tuple = &pt->Proc.params->Tuple;
|
||||
|
||||
param_count = param_tuple->variables.count;
|
||||
if (variadic) {
|
||||
@@ -4930,6 +4921,31 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
||||
}
|
||||
}
|
||||
|
||||
if (param_count_) *param_count_ = param_count;
|
||||
return param_count_excluding_defaults;
|
||||
}
|
||||
|
||||
|
||||
CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
|
||||
ast_node(ce, CallExpr, call);
|
||||
GB_ASSERT(is_type_proc(proc_type));
|
||||
proc_type = base_type(proc_type);
|
||||
TypeProc *pt = &proc_type->Proc;
|
||||
|
||||
isize param_count = 0;
|
||||
isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, ¶m_count);
|
||||
bool variadic = pt->variadic;
|
||||
bool vari_expand = (ce->ellipsis.pos.line != 0);
|
||||
i64 score = 0;
|
||||
bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
|
||||
|
||||
|
||||
TypeTuple *param_tuple = nullptr;
|
||||
if (pt->params != nullptr) {
|
||||
param_tuple = &pt->params->Tuple;
|
||||
}
|
||||
|
||||
|
||||
CallArgumentError err = CallArgumentError_None;
|
||||
Type *final_proc_type = proc_type;
|
||||
Entity *gen_entity = nullptr;
|
||||
@@ -5602,7 +5618,37 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
||||
if (operand->mode == Addressing_ProcGroup) {
|
||||
check_entity_decl(c, operand->proc_group, nullptr, nullptr);
|
||||
|
||||
Array<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, ¶m_count);
|
||||
|
||||
if (param_count_excluding_defaults > max_arg_count) {
|
||||
array_unordered_remove(&procs, proc_index);
|
||||
} else {
|
||||
proc_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (procs.count == 1) {
|
||||
Ast *ident = operand->expr;
|
||||
@@ -5632,6 +5678,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
Entity **lhs = nullptr;
|
||||
isize lhs_count = -1;
|
||||
|
||||
@@ -8953,6 +9000,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
|
||||
|
||||
o->mode = Addressing_Invalid;
|
||||
o->type = t_invalid;
|
||||
o->value = {ExactValue_Invalid};
|
||||
|
||||
switch (node->kind) {
|
||||
default:
|
||||
@@ -9999,8 +10047,11 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
|
||||
str = write_expr_to_string(str, st->polymorphic_params, shorthand);
|
||||
str = gb_string_appendc(str, ") ");
|
||||
}
|
||||
if (st->no_nil) str = gb_string_appendc(str, "#no_nil ");
|
||||
if (st->maybe) str = gb_string_appendc(str, "#maybe ");
|
||||
switch (st->kind) {
|
||||
case UnionType_maybe: str = gb_string_appendc(str, "#maybe "); break;
|
||||
case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break;
|
||||
case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break;
|
||||
}
|
||||
if (st->align) {
|
||||
str = gb_string_appendc(str, "#align ");
|
||||
str = write_expr_to_string(str, st->align, shorthand);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user