merge upstream/master

This commit is contained in:
CiD-
2022-04-08 13:50:06 -04:00
140 changed files with 7283 additions and 5591 deletions
+12
View File
@@ -38,6 +38,9 @@ jobs:
cd tests/vendor
make
timeout-minutes: 10
- name: Odin issues tests
run: tests/issues/run.sh
timeout-minutes: 10
- name: Odin check examples/all for Linux i386
run: ./odin check examples/all -vet -strict-style -target:linux_i386
timeout-minutes: 10
@@ -87,6 +90,9 @@ jobs:
cd tests/vendor
make
timeout-minutes: 10
- name: Odin issues tests
run: tests/issues/run.sh
timeout-minutes: 10
- name: Odin check examples/all for Darwin arm64
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
timeout-minutes: 10
@@ -153,6 +159,12 @@ jobs:
cd tests\core\math\big
call build.bat
timeout-minutes: 10
- name: Odin issues tests
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
call tests\issues\run.bat
timeout-minutes: 10
- name: Odin check examples/all for Windows 32bits
shell: cmd
run: |
+6 -6
View File
@@ -19,7 +19,7 @@ jobs:
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin run examples/demo/demo.odin
odin run examples/demo
- name: Copy artifacts
run: |
rm bin/llvm/windows/LLVM-C.lib
@@ -41,11 +41,11 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: (Linux) Download LLVM
run: sudo apt-get install llvm-11 clang-11 llvm
run: sudo apt-get install llvm-11 clang-11
- name: build odin
run: make nightly
- name: Odin run
run: ./odin run examples/demo/demo.odin
run: ./odin run examples/demo
- name: Copy artifacts
run: |
mkdir dist
@@ -72,7 +72,7 @@ jobs:
- name: build odin
run: make nightly
- name: Odin run
run: ./odin run examples/demo/demo.odin
run: ./odin run examples/demo
- name: Copy artifacts
run: |
mkdir dist
@@ -129,7 +129,7 @@ jobs:
run: |
echo Authorizing B2 account
b2 authorize-account "$APPID" "$APPKEY"
echo Uploading artifcates to B2
chmod +x ./ci/upload_create_nightly.sh
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
@@ -141,7 +141,7 @@ jobs:
echo Creating nightly.json
python3 ci/create_nightly_json.py "$BUCKET" > nightly.json
echo Uploading nightly.json
b2 upload-file "$BUCKET" nightly.json nightly.json
+4 -75
View File
@@ -1,74 +1,3 @@
GIT_SHA=$(shell git rev-parse --short HEAD)
DISABLED_WARNINGS=-Wno-switch -Wno-macro-redefined -Wno-unused-value
LDFLAGS=-pthread -lm -lstdc++
CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
CC=clang
OS=$(shell uname)
ifeq ($(OS), Darwin)
ARCH=$(shell uname -m)
LLVM_CONFIG=llvm-config
# allow for arm only llvm's with version 13
ifeq ($(ARCH), arm64)
LLVM_VERSIONS = "13.%.%"
else
# allow for x86 / amd64 all llvm versions begining from 11
LLVM_VERSIONS = "13.%.%" "12.0.1" "11.1.0"
endif
LLVM_VERSION_PATTERN_SEPERATOR = )|(
LLVM_VERSION_PATTERNS_ESCAPED_DOT = $(subst .,\.,$(LLVM_VERSIONS))
LLVM_VERSION_PATTERNS_REPLACE_PERCENT = $(subst %,.*,$(LLVM_VERSION_PATTERNS_ESCAPED_DOT))
LLVM_VERSION_PATTERN_REMOVE_ELEMENTS = $(subst " ",$(LLVM_VERSION_PATTERN_SEPERATOR),$(LLVM_VERSION_PATTERNS_REPLACE_PERCENT))
LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR = $(subst ",,$(LLVM_VERSION_PATTERN_REMOVE_ELEMENTS))
LLVM_VERSION_PATTERN = "^(($(LLMV_VERSION_PATTERN_REMOVE_SINGLE_STR)))"
ifeq ($(shell $(LLVM_CONFIG) --version | grep -E $(LLVM_VERSION_PATTERN)),)
ifeq ($(ARCH), arm64)
$(error "Requirement: llvm-config must be base version 13 for arm64")
else
$(error "Requirement: llvm-config must be base version greater than 11 for amd64/x86")
endif
endif
LDFLAGS:=$(LDFLAGS) -liconv -ldl
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) -lLLVM-C
endif
ifeq ($(OS), Linux)
LLVM_CONFIG=llvm-config-11
ifneq ($(shell which llvm-config-11 2>/dev/null),)
LLVM_CONFIG=llvm-config-11
else ifneq ($(shell which llvm-config-11-64 2>/dev/null),)
LLVM_CONFIG=llvm-config-11-64
else
ifeq ($(shell $(LLVM_CONFIG) --version | grep '^11\.'),)
$(error "Requirement: llvm-config must be version 11")
endif
endif
LDFLAGS:=$(LDFLAGS) -ldl
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
endif
ifeq ($(OS), OpenBSD)
LLVM_CONFIG=/usr/local/bin/llvm-config
LDFLAGS:=$(LDFLAGS) -liconv
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
endif
ifeq ($(OS), FreeBSD)
LLVM_CONFIG=/usr/local/bin/llvm-config11
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
endif
all: debug demo
demo:
@@ -78,13 +7,13 @@ report:
./odin report
debug:
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
./build_odin.sh debug
release:
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 $(LDFLAGS) -o odin
./build_odin.sh release
release_native:
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
./build_odin.sh release-native
nightly:
$(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
./build_odin.sh nightly
+9 -7
View File
@@ -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
View File
@@ -41,6 +41,13 @@ config_darwin() {
LDFLAGS="$LDFLAGS -lLLVM-C"
}
config_freebsd() {
LLVM_CONFIG=/usr/local/bin/llvm-config11
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
}
config_openbsd() {
LLVM_CONFIG=/usr/local/bin/llvm-config
@@ -95,7 +102,7 @@ build_odin() {
}
run_demo() {
./odin run examples/demo/demo.odin
./odin run examples/demo/demo.odin -file
}
case $OS in
@@ -108,6 +115,9 @@ Darwin)
OpenBSD)
config_openbsd
;;
FreeBSD)
config_freebsd
;;
*)
panic "Platform unsupported!"
esac
+3 -6
View File
@@ -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
View File
@@ -47,29 +47,30 @@ kill_dependency :: #force_inline proc(value: $T) -> T {
// 7.17.4 Fences
atomic_thread_fence :: #force_inline proc(order: memory_order) {
switch (order) {
case .relaxed:
return
case .consume:
intrinsics.atomic_fence_acq()
case .acquire:
intrinsics.atomic_fence_acq()
case .release:
intrinsics.atomic_fence_rel()
case .acq_rel:
intrinsics.atomic_fence_acqrel()
case .seq_cst:
intrinsics.atomic_fence_acqrel()
assert(order != .relaxed)
assert(order != .consume)
#partial switch order {
case .acquire: intrinsics.atomic_thread_fence(.Acquire)
case .release: intrinsics.atomic_thread_fence(.Release)
case .acq_rel: intrinsics.atomic_thread_fence(.Acq_Rel)
case .seq_cst: intrinsics.atomic_thread_fence(.Seq_Cst)
}
}
atomic_signal_fence :: #force_inline proc(order: memory_order) {
atomic_thread_fence(order)
assert(order != .relaxed)
assert(order != .consume)
#partial switch order {
case .acquire: intrinsics.atomic_signal_fence(.Acquire)
case .release: intrinsics.atomic_signal_fence(.Release)
case .acq_rel: intrinsics.atomic_signal_fence(.Acq_Rel)
case .seq_cst: intrinsics.atomic_signal_fence(.Seq_Cst)
}
}
// 7.17.5 Lock-free property
atomic_is_lock_free :: #force_inline proc(obj: ^$T) -> bool {
return size_of(T) <= 8 && (intrinsics.type_is_integer(T) || intrinsics.type_is_pointer(T))
return intrinsics.atomic_type_is_lock_free(T)
}
// 7.17.6 Atomic integer types
@@ -121,13 +122,10 @@ atomic_store_explicit :: #force_inline proc(object: ^$T, desired: T, order: memo
assert(order != .acquire)
assert(order != .acq_rel)
#partial switch (order) {
case .relaxed:
intrinsics.atomic_store_relaxed(object, desired)
case .release:
intrinsics.atomic_store_rel(object, desired)
case .seq_cst:
intrinsics.atomic_store(object, desired)
#partial switch order {
case .relaxed: intrinsics.atomic_store_explicit(object, desired, .Relaxed)
case .release: intrinsics.atomic_store_explicit(object, desired, .Release)
case .seq_cst: intrinsics.atomic_store_explicit(object, desired, .Seq_Cst)
}
}
@@ -139,36 +137,26 @@ atomic_load_explicit :: #force_inline proc(object: ^$T, order: memory_order) {
assert(order != .release)
assert(order != .acq_rel)
#partial switch (order) {
case .relaxed:
return intrinsics.atomic_load_relaxed(object)
case .consume:
return intrinsics.atomic_load_acq(object)
case .acquire:
return intrinsics.atomic_load_acq(object)
case .seq_cst:
return intrinsics.atomic_load(object)
#partial switch order {
case .relaxed: return intrinsics.atomic_load_explicit(object, .Relaxed)
case .consume: return intrinsics.atomic_load_explicit(object, .Consume)
case .acquire: return intrinsics.atomic_load_explicit(object, .Acquire)
case .seq_cst: return intrinsics.atomic_load_explicit(object, .Seq_Cst)
}
}
atomic_exchange :: #force_inline proc(object: ^$T, desired: T) -> T {
return intrinsics.atomic_xchg(object, desired)
return intrinsics.atomic_exchange(object, desired)
}
atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_xchg_relaxed(object, desired)
case .consume:
return intrinsics.atomic_xchg_acq(object, desired)
case .acquire:
return intrinsics.atomic_xchg_acq(object, desired)
case .release:
return intrinsics.atomic_xchg_rel(object, desired)
case .acq_rel:
return intrinsics.atomic_xchg_acqrel(object, desired)
case .seq_cst:
return intrinsics.atomic_xchg(object, desired)
switch order {
case .relaxed: return intrinsics.atomic_exchange_explicit(object, desired, .Relaxed)
case .consume: return intrinsics.atomic_exchange_explicit(object, desired, .Consume)
case .acquire: return intrinsics.atomic_exchange_explicit(object, desired, .Acquire)
case .release: return intrinsics.atomic_exchange_explicit(object, desired, .Release)
case .acq_rel: return intrinsics.atomic_exchange_explicit(object, desired, .Acq_Rel)
case .seq_cst: return intrinsics.atomic_exchange_explicit(object, desired, .Seq_Cst)
}
return false
}
@@ -189,102 +177,104 @@ atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: m
// [success = seq_cst, failure = acquire] => failacq
// [success = acquire, failure = relaxed] => acq_failrelaxed
// [success = acq_rel, failure = relaxed] => acqrel_failrelaxed
atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) {
value, ok := intrinsics.atomic_cxchg(object, expected^, desired)
atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) -> bool {
value, ok := intrinsics.atomic_compare_exchange_strong(object, expected^, desired)
if !ok { expected^ = value }
return ok
}
atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) {
atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) -> bool {
assert(failure != .release)
assert(failure != .acq_rel)
value: T; ok: bool
#partial switch (failure) {
#partial switch failure {
case .seq_cst:
assert(success != .relaxed)
#partial switch (success) {
#partial switch success {
case .seq_cst:
value, ok := intrinsics.atomic_cxchg(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst)
case .acquire:
value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Seq_Cst)
case .consume:
value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Seq_Cst)
case .release:
value, ok := intrinsics.atomic_cxchg_rel(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Release, .Seq_Cst)
case .acq_rel:
value, ok := intrinsics.atomic_cxchg_acqrel(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst)
}
case .relaxed:
assert(success != .release)
#partial switch (success) {
#partial switch success {
case .relaxed:
value, ok := intrinsics.atomic_cxchg_relaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Relaxed, .Relaxed)
case .seq_cst:
value, ok := intrinsics.atomic_cxchg_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Relaxed)
case .acquire:
value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Relaxed)
case .consume:
value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Relaxed)
case .acq_rel:
value, ok := intrinsics.atomic_cxchg_acqrel_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Relaxed)
}
case .consume:
fallthrough
assert(success == .seq_cst)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Consume)
case .acquire:
assert(success == .seq_cst)
value, ok := intrinsics.atomic_cxchg_failacq(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Acquire)
}
if !ok { expected^ = value }
return ok
}
atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) {
value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired)
atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) -> bool {
value, ok := intrinsics.atomic_compare_exchange_weak(object, expected^, desired)
if !ok { expected^ = value }
return ok
}
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) {
atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) -> bool {
assert(failure != .release)
assert(failure != .acq_rel)
value: T; ok: bool
#partial switch (failure) {
#partial switch failure {
case .seq_cst:
assert(success != .relaxed)
#partial switch (success) {
#partial switch success {
case .seq_cst:
value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst)
case .acquire:
value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Seq_Cst)
case .consume:
value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Seq_Cst)
case .release:
value, ok := intrinsics.atomic_cxchgweak_rel(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Release, .Seq_Cst)
case .acq_rel:
value, ok := intrinsics.atomic_cxchgweak_acqrel(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst)
}
case .relaxed:
assert(success != .release)
#partial switch (success) {
#partial switch success {
case .relaxed:
value, ok := intrinsics.atomic_cxchgweak_relaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Relaxed, .Relaxed)
case .seq_cst:
value, ok := intrinsics.atomic_cxchgweak_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Relaxed)
case .acquire:
value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Relaxed)
case .consume:
value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Relaxed)
case .acq_rel:
value, ok := intrinsics.atomic_cxchgweak_acqrel_failrelaxed(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Relaxed)
}
case .consume:
fallthrough
assert(success == .seq_cst)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Consume)
case .acquire:
assert(success == .seq_cst)
value, ok := intrinsics.atomic_cxchgweak_failacq(object, expected^, desired)
value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Acquire)
}
if !ok { expected^ = value }
@@ -297,19 +287,14 @@ atomic_fetch_add :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_add_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_add_relaxed(object, operand)
case .consume:
return intrinsics.atomic_add_acq(object, operand)
case .acquire:
return intrinsics.atomic_add_acq(object, operand)
case .release:
return intrinsics.atomic_add_rel(object, operand)
case .acq_rel:
return intrinsics.atomic_add_acqrel(object, operand)
case .seq_cst:
return intrinsics.atomic_add(object, operand)
switch order {
case .relaxed: return intrinsics.atomic_add_explicit(object, operand, .Relaxed)
case .consume: return intrinsics.atomic_add_explicit(object, operand, .Consume)
case .acquire: return intrinsics.atomic_add_explicit(object, operand, .Acquire)
case .release: return intrinsics.atomic_add_explicit(object, operand, .Release)
case .acq_rel: return intrinsics.atomic_add_explicit(object, operand, .Acq_Rel)
case: fallthrough
case .seq_cst: return intrinsics.atomic_add_explicit(object, operand, .Seq_Cst)
}
}
@@ -318,19 +303,14 @@ atomic_fetch_sub :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_sub_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_sub_relaxed(object, operand)
case .consume:
return intrinsics.atomic_sub_acq(object, operand)
case .acquire:
return intrinsics.atomic_sub_acq(object, operand)
case .release:
return intrinsics.atomic_sub_rel(object, operand)
case .acq_rel:
return intrinsics.atomic_sub_acqrel(object, operand)
case .seq_cst:
return intrinsics.atomic_sub(object, operand)
switch order {
case .relaxed: return intrinsics.atomic_sub_explicit(object, operand, .Relaxed)
case .consume: return intrinsics.atomic_sub_explicit(object, operand, .Consume)
case .acquire: return intrinsics.atomic_sub_explicit(object, operand, .Acquire)
case .release: return intrinsics.atomic_sub_explicit(object, operand, .Release)
case .acq_rel: return intrinsics.atomic_sub_explicit(object, operand, .Acq_Rel)
case: fallthrough
case .seq_cst: return intrinsics.atomic_sub_explicit(object, operand, .Seq_Cst)
}
}
@@ -339,19 +319,14 @@ atomic_fetch_or :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_or_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_or_relaxed(object, operand)
case .consume:
return intrinsics.atomic_or_acq(object, operand)
case .acquire:
return intrinsics.atomic_or_acq(object, operand)
case .release:
return intrinsics.atomic_or_rel(object, operand)
case .acq_rel:
return intrinsics.atomic_or_acqrel(object, operand)
case .seq_cst:
return intrinsics.atomic_or(object, operand)
switch order {
case .relaxed: return intrinsics.atomic_or_explicit(object, operand, .Relaxed)
case .consume: return intrinsics.atomic_or_explicit(object, operand, .Consume)
case .acquire: return intrinsics.atomic_or_explicit(object, operand, .Acquire)
case .release: return intrinsics.atomic_or_explicit(object, operand, .Release)
case .acq_rel: return intrinsics.atomic_or_explicit(object, operand, .Acq_Rel)
case: fallthrough
case .seq_cst: return intrinsics.atomic_or_explicit(object, operand, .Seq_Cst)
}
}
@@ -360,19 +335,14 @@ atomic_fetch_xor :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_xor_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_xor_relaxed(object, operand)
case .consume:
return intrinsics.atomic_xor_acq(object, operand)
case .acquire:
return intrinsics.atomic_xor_acq(object, operand)
case .release:
return intrinsics.atomic_xor_rel(object, operand)
case .acq_rel:
return intrinsics.atomic_xor_acqrel(object, operand)
case .seq_cst:
return intrinsics.atomic_xor(object, operand)
switch order {
case .relaxed: return intrinsics.atomic_xor_explicit(object, operand, .Relaxed)
case .consume: return intrinsics.atomic_xor_explicit(object, operand, .Consume)
case .acquire: return intrinsics.atomic_xor_explicit(object, operand, .Acquire)
case .release: return intrinsics.atomic_xor_explicit(object, operand, .Release)
case .acq_rel: return intrinsics.atomic_xor_explicit(object, operand, .Acq_Rel)
case: fallthrough
case .seq_cst: return intrinsics.atomic_xor_explicit(object, operand, .Seq_Cst)
}
}
@@ -380,19 +350,14 @@ atomic_fetch_and :: #force_inline proc(object: ^$T, operand: T) -> T {
return intrinsics.atomic_and(object, operand)
}
atomic_fetch_and_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
switch (order) {
case .relaxed:
return intrinsics.atomic_and_relaxed(object, operand)
case .consume:
return intrinsics.atomic_and_acq(object, operand)
case .acquire:
return intrinsics.atomic_and_acq(object, operand)
case .release:
return intrinsics.atomic_and_rel(object, operand)
case .acq_rel:
return intrinsics.atomic_and_acqrel(object, operand)
case .seq_cst:
return intrinsics.atomic_and(object, operand)
switch order {
case .relaxed: return intrinsics.atomic_and_explicit(object, operand, .Relaxed)
case .consume: return intrinsics.atomic_and_explicit(object, operand, .Consume)
case .acquire: return intrinsics.atomic_and_explicit(object, operand, .Acquire)
case .release: return intrinsics.atomic_and_explicit(object, operand, .Release)
case .acq_rel: return intrinsics.atomic_and_explicit(object, operand, .Acq_Rel)
case: fallthrough
case .seq_cst: return intrinsics.atomic_and_explicit(object, operand, .Seq_Cst)
}
}
+6 -1
View File
@@ -47,7 +47,7 @@ when size_of(uintptr) == 8 {
}
Error :: union {
Error :: union #shared_nil {
General_Error,
Deflate_Error,
ZLIB_Error,
@@ -58,6 +58,7 @@ Error :: union {
}
General_Error :: enum {
None = 0,
File_Not_Found,
Cannot_Open_File,
File_Too_Short,
@@ -76,6 +77,7 @@ General_Error :: enum {
}
GZIP_Error :: enum {
None = 0,
Invalid_GZIP_Signature,
Reserved_Flag_Set,
Invalid_Extra_Data,
@@ -100,6 +102,7 @@ GZIP_Error :: enum {
}
ZIP_Error :: enum {
None = 0,
Invalid_ZIP_File_Signature,
Unexpected_Signature,
Insert_Next_Disk,
@@ -107,6 +110,7 @@ ZIP_Error :: enum {
}
ZLIB_Error :: enum {
None = 0,
Unsupported_Window_Size,
FDICT_Unsupported,
Unsupported_Compression_Level,
@@ -114,6 +118,7 @@ ZLIB_Error :: enum {
}
Deflate_Error :: enum {
None = 0,
Huffman_Bad_Sizes,
Huffman_Bad_Code_Lengths,
Inflate_Error,
+29 -29
View File
@@ -47,10 +47,10 @@ Options :: struct {
level: u8,
}
Error :: compress.Error
E_General :: compress.General_Error
E_ZLIB :: compress.ZLIB_Error
E_Deflate :: compress.Deflate_Error
Error :: compress.Error
General_Error :: compress.General_Error
ZLIB_Error :: compress.ZLIB_Error
Deflate_Error :: compress.Deflate_Error
DEFLATE_MAX_CHUNK_SIZE :: 65535
DEFLATE_MAX_LITERAL_SIZE :: 65535
@@ -258,7 +258,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
for i in 1 ..< HUFFMAN_MAX_BITS {
if sizes[i] > (1 << uint(i)) {
return E_Deflate.Huffman_Bad_Sizes
return .Huffman_Bad_Sizes
}
}
code := int(0)
@@ -270,7 +270,7 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
code = code + sizes[i]
if sizes[i] != 0 {
if code - 1 >= (1 << u16(i)) {
return E_Deflate.Huffman_Bad_Code_Lengths
return .Huffman_Bad_Code_Lengths
}
}
z.maxcode[i] = code << (HUFFMAN_MAX_BITS - uint(i))
@@ -314,15 +314,15 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
s += 1
}
if s >= 16 {
return 0, E_Deflate.Bad_Huffman_Code
return 0, .Bad_Huffman_Code
}
// code size is s, so:
b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s])
if b >= size_of(t.size) {
return 0, E_Deflate.Bad_Huffman_Code
return 0, .Bad_Huffman_Code
}
if t.size[b] != s {
return 0, E_Deflate.Bad_Huffman_Code
return 0, .Bad_Huffman_Code
}
compress.consume_bits_lsb(z, s)
@@ -335,11 +335,11 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
if z.num_bits < 16 {
if z.num_bits > 63 {
return 0, E_ZLIB.Code_Buffer_Malformed
return 0, .Code_Buffer_Malformed
}
compress.refill_lsb(z)
if z.num_bits > 63 {
return 0, E_General.Stream_Too_Short
return 0, .Stream_Too_Short
}
}
#no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK]
@@ -361,7 +361,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
if value < 256 {
e := write_byte(z, u8(value))
if e != .None {
return E_General.Output_Too_Short
return .Output_Too_Short
}
} else {
if value == 256 {
@@ -377,7 +377,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
value, e = decode_huffman(z, z_offset)
if e != nil {
return E_Deflate.Bad_Huffman_Code
return .Bad_Huffman_Code
}
distance := Z_DIST_BASE[value]
@@ -387,7 +387,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
if z.bytes_written < i64(distance) {
// Distance is longer than we've decoded so far.
return E_Deflate.Bad_Distance
return .Bad_Distance
}
/*
@@ -405,14 +405,14 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
c := z.output.buf[z.bytes_written - i64(distance)]
e := repl_byte(z, length, c)
if e != .None {
return E_General.Output_Too_Short
return .Output_Too_Short
}
}
} else {
if length > 0 {
e := repl_bytes(z, length, distance)
if e != .None {
return E_General.Output_Too_Short
return .Output_Too_Short
}
}
}
@@ -432,25 +432,25 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
if !raw {
size, size_err := compress.input_size(ctx)
if size < 6 || size_err != nil {
return E_General.Stream_Too_Short
return .Stream_Too_Short
}
cmf, _ := compress.read_u8(ctx)
method := Compression_Method(cmf & 0xf)
if method != .DEFLATE {
return E_General.Unknown_Compression_Method
return .Unknown_Compression_Method
}
if cinfo := (cmf >> 4) & 0xf; cinfo > 7 {
return E_ZLIB.Unsupported_Window_Size
return .Unsupported_Window_Size
}
flg, _ := compress.read_u8(ctx)
fcheck := flg & 0x1f
fcheck_computed := (cmf << 8 | flg) & 0x1f
if fcheck != fcheck_computed {
return E_General.Checksum_Failed
return .Checksum_Failed
}
/*
@@ -458,7 +458,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
They're application specific and PNG doesn't use them.
*/
if fdict := (flg >> 5) & 1; fdict != 0 {
return E_ZLIB.FDICT_Unsupported
return .FDICT_Unsupported
}
// flevel := Compression_Level((flg >> 6) & 3);
@@ -485,7 +485,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
output_hash := hash.adler32(ctx.output.buf[:])
if output_hash != u32(adler) {
return E_General.Checksum_Failed
return .Checksum_Failed
}
}
return nil
@@ -555,7 +555,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
if ~uncompressed_len != length_check {
return E_Deflate.Len_Nlen_Mismatch
return .Len_Nlen_Mismatch
}
/*
@@ -571,7 +571,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
assert(uncompressed_len == 0)
case 3:
return E_Deflate.BType_3
return .BType_3
case:
// fmt.printf("Err: %v | Final: %v | Type: %v\n", err, final, type)
if type == 1 {
@@ -604,7 +604,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
c = decode_huffman(z, codelength_ht) or_return
if c < 0 || c >= 19 {
return E_Deflate.Huffman_Bad_Code_Lengths
return .Huffman_Bad_Code_Lengths
}
if c < 16 {
lencodes[n] = u8(c)
@@ -616,7 +616,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
case 16:
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3)
if n == 0 {
return E_Deflate.Huffman_Bad_Code_Lengths
return .Huffman_Bad_Code_Lengths
}
fill = lencodes[n - 1]
case 17:
@@ -624,11 +624,11 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
case 18:
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11)
case:
return E_Deflate.Huffman_Bad_Code_Lengths
return .Huffman_Bad_Code_Lengths
}
if ntot - n < u32(c) {
return E_Deflate.Huffman_Bad_Code_Lengths
return .Huffman_Bad_Code_Lengths
}
nc := n + u32(c)
@@ -639,7 +639,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
}
if n != ntot {
return E_Deflate.Huffman_Bad_Code_Lengths
return .Huffman_Bad_Code_Lengths
}
build_huffman(z_repeat, lencodes[:hlit]) or_return
+3 -3
View File
@@ -25,14 +25,14 @@ slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
}
get :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> T {
get :: proc(a: $A/Small_Array($N, $T), index: int) -> T {
return a.data[index]
}
get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T {
get_ptr :: proc(a: ^$A/Small_Array($N, $T), index: int) -> ^T {
return &a.data[index]
}
set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) {
set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) {
a.data[index] = item
}
+25 -4
View File
@@ -39,6 +39,9 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
read_value :: proc(r: ^Reader, $T: typeid) -> (value: T, err: Read_Error) {
remaining := len(r.data) - r.offset
if remaining < size_of(T) {
if r.print_error {
fmt.eprintf("file '%s' failed to read value at offset %v\n", r.filename, r.offset)
}
err = .Short_Read
return
}
@@ -51,6 +54,10 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
read_array :: proc(r: ^Reader, $T: typeid, count: int) -> (value: []T, err: Read_Error) {
remaining := len(r.data) - r.offset
if remaining < size_of(T)*count {
if r.print_error {
fmt.eprintf("file '%s' failed to read array of %d elements at offset %v\n",
r.filename, count, r.offset)
}
err = .Short_Read
return
}
@@ -82,7 +89,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
type := read_value(r, Meta_Value_Type) or_return
if type > max(Meta_Value_Type) {
if r.print_error {
fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is ", r.filename, u8(type), u8(max(Meta_Value_Type)))
fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is %d\n",
r.filename, u8(type), u8(max(Meta_Value_Type)))
}
err = .Invalid_Data
return
@@ -114,7 +122,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
type := read_value(r, Layer_Data_Type) or_return
if type > max(type) {
if r.print_error {
fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is ", r.filename, u8(type), u8(max(Layer_Data_Type)))
fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is %d\n",
r.filename, u8(type), u8(max(Layer_Data_Type)))
}
err = .Invalid_Data
return
@@ -134,13 +143,23 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
}
if len(data) < size_of(Header) {
if print_error {
fmt.eprintf("HxA Error: file '%s' has no header\n", filename)
}
err = .Short_Read
return
}
context.allocator = allocator
header := cast(^Header)raw_data(data)
assert(header.magic_number == MAGIC_NUMBER)
if (header.magic_number != MAGIC_NUMBER) {
if print_error {
fmt.eprintf("HxA Error: file '%s' has invalid magic number 0x%x\n", filename, header.magic_number)
}
err = .Invalid_Data
return
}
r := &Reader{
filename = filename,
@@ -150,6 +169,7 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
}
node_count := 0
file.header = header^
file.nodes = make([]Node, header.internal_node_count)
defer if err != nil {
nodes_destroy(file.nodes)
@@ -162,7 +182,8 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
type := read_value(r, Node_Type) or_return
if type > max(Node_Type) {
if r.print_error {
fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is ", r.filename, u8(type), u8(max(Node_Type)))
fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is %d\n",
r.filename, u8(type), u8(max(Node_Type)))
}
err = .Invalid_Data
return
+3 -3
View File
@@ -84,7 +84,7 @@ write_internal :: proc(w: ^Writer, file: File) {
write_metadata :: proc(w: ^Writer, meta_data: []Meta) {
for m in meta_data {
name_len := max(len(m.name), 255)
name_len := min(len(m.name), 255)
write_value(w, u8(name_len))
write_string(w, m.name[:name_len])
@@ -127,7 +127,7 @@ write_internal :: proc(w: ^Writer, file: File) {
write_layer_stack :: proc(w: ^Writer, layers: Layer_Stack) {
write_value(w, u32(len(layers)))
for layer in layers {
name_len := max(len(layer.name), 255)
name_len := min(len(layer.name), 255)
write_value(w, u8(name_len))
write_string(w, layer .name[:name_len])
@@ -152,7 +152,7 @@ write_internal :: proc(w: ^Writer, file: File) {
return
}
write_value(w, &Header{
write_value(w, Header{
magic_number = MAGIC_NUMBER,
version = LATEST_VERSION,
internal_node_count = u32le(len(file.nodes)),
+4 -3
View File
@@ -8,17 +8,18 @@ import "core:strings"
import "core:io"
Marshal_Data_Error :: enum {
None,
Unsupported_Type,
}
Marshal_Error :: union {
Marshal_Error :: union #shared_nil {
Marshal_Data_Error,
io.Error,
}
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
b := strings.make_builder(allocator)
defer if err != .None {
defer if err != nil {
strings.destroy_builder(&b)
}
@@ -27,7 +28,7 @@ marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: M
if len(b.buf) != 0 {
data = b.buf[:]
}
return data, .None
return data, nil
}
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
+2 -1
View File
@@ -118,7 +118,7 @@ Option :: enum {
}
Options :: distinct bit_set[Option]
Error :: union {
Error :: union #shared_nil {
General_Image_Error,
PNG_Error,
@@ -137,6 +137,7 @@ General_Image_Error :: enum {
}
PNG_Error :: enum {
None = 0,
Invalid_PNG_Signature,
IHDR_Not_First_Chunk,
IHDR_Corrupt,
+3 -3
View File
@@ -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)
+38 -65
View File
@@ -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() ---
+483
View File
@@ -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
View File
@@ -1206,7 +1206,7 @@ prod :: proc "contextless" (x: $T/[]$E) -> (res: E)
return
}
cumsum_inplace :: proc "contextless" (x: $T/[]$E) -> T
cumsum_inplace :: proc "contextless" (x: $T/[]$E)
where intrinsics.type_is_numeric(E) {
for i in 1..<len(x) {
x[i] = x[i-1] + x[i]
@@ -1736,4 +1736,4 @@ INF_F64 :f64: 0h7FF0_0000_0000_0000
NEG_INF_F64 :f64: 0hFFF0_0000_0000_0000
SNAN_F64 :f64: 0h7FF0_0000_0000_0001
QNAN_F64 :f64: 0h7FF8_0000_0000_0001
QNAN_F64 :f64: 0h7FF8_0000_0000_0001
+18 -1
View File
@@ -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
View File
@@ -16,7 +16,7 @@ zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr {
// equivalent semantics to those provided by the C11 Annex K 3.7.4.1
// memset_s call.
intrinsics.mem_zero_volatile(data, len) // Use the volatile mem_zero
intrinsics.atomic_fence() // Prevent reordering
intrinsics.atomic_thread_fence(.Seq_Cst) // Prevent reordering
return data
}
zero_item :: proc "contextless" (item: $P/^$T) {
+1 -1
View File
@@ -1,7 +1,7 @@
//+private
package mem_virtual
import sync "core:sync/sync2"
import "core:sync"
Platform_Memory_Block :: struct {
block: Memory_Block,
+8 -2
View File
@@ -708,13 +708,19 @@ Struct_Type :: struct {
name_count: int,
}
Union_Type_Kind :: enum u8 {
Normal,
maybe,
no_nil,
shared_nil,
}
Union_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
poly_params: ^Field_List,
align: ^Expr,
is_maybe: bool,
is_no_nil: bool,
kind: Union_Type_Kind,
where_token: tokenizer.Token,
where_clauses: []^Expr,
variants: []^Expr,
+4 -1
View File
@@ -52,8 +52,11 @@ walk :: proc(v: ^Visitor, node: ^Node) {
}
}
v := v
if v == nil || node == nil {
return
}
if v = v->visit(node); v == nil {
return
}
+28 -4
View File
@@ -2630,8 +2630,9 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
tok := expect_token(p, .Union)
poly_params: ^ast.Field_List
align: ^ast.Expr
is_maybe: bool
is_no_nil: bool
is_maybe: bool
is_no_nil: bool
is_shared_nil: bool
if allow_token(p, .Open_Paren) {
param_count: int
@@ -2663,12 +2664,34 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
}
is_no_nil = true
case "shared_nil":
if is_shared_nil {
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
}
is_shared_nil = true
case:
error(p, tag.pos, "invalid union tag '#%s", tag.text)
}
}
p.expr_level = prev_level
if is_no_nil && is_maybe {
error(p, p.curr_tok.pos, "#maybe and #no_nil cannot be applied together")
}
if is_no_nil && is_shared_nil {
error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together")
}
if is_shared_nil && is_maybe {
error(p, p.curr_tok.pos, "#maybe and #shared_nil cannot be applied together")
}
union_kind := ast.Union_Type_Kind.Normal
switch {
case is_maybe: union_kind = .maybe
case is_no_nil: union_kind = .no_nil
case is_shared_nil: union_kind = .shared_nil
}
where_token: tokenizer.Token
where_clauses: []^ast.Expr
@@ -2699,14 +2722,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
close := expect_closing_brace_of_field_list(p)
ut := ast.new(ast.Union_Type, tok.pos, end_pos(close))
ut.poly_params = poly_params
ut.variants = variants[:]
ut.align = align
ut.where_token = where_token
ut.where_clauses = where_clauses
ut.is_maybe = is_maybe
ut.is_no_nil = is_no_nil
ut.kind = union_kind
return ut
+5 -2
View File
@@ -1046,8 +1046,11 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_poly_params(p, v.poly_params)
if v.is_maybe {
push_ident_token(p, "#maybe", 1)
switch v.kind {
case .Normal:
case .maybe: push_ident_token(p, "#maybe", 1)
case .no_nil: push_ident_token(p, "#no_nil", 1)
case .shared_nil: push_ident_token(p, "#shared_nil", 1)
}
push_where_clauses(p, v.where_clauses)
+1
View File
@@ -82,6 +82,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
wpath_search[len(wpath)+2] = 0
path := cleanpath_from_buf(wpath)
defer delete(path)
find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
+3 -6
View File
@@ -20,13 +20,13 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errn
case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE
}
if mode&O_CREATE != 0 {
access |= win32.FILE_GENERIC_WRITE
}
if mode&O_APPEND != 0 {
access &~= win32.FILE_GENERIC_WRITE
access |= win32.FILE_APPEND_DATA
}
if mode&O_CREATE != 0 {
access |= win32.FILE_GENERIC_WRITE
}
share_mode := win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE
sa: ^win32.SECURITY_ATTRIBUTES = nil
@@ -320,9 +320,6 @@ stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
get_std_handle :: proc "contextless" (h: uint) -> Handle {
fd := win32.GetStdHandle(win32.DWORD(h))
when size_of(uintptr) == 8 {
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0)
}
return Handle(fd)
}
+1 -1
View File
@@ -1,6 +1,6 @@
package os2
import sync "core:sync/sync2"
import "core:sync"
import "core:time"
import "core:runtime"
-1
View File
@@ -2,7 +2,6 @@ package os
import "core:time"
File_Info :: struct {
fullpath: string,
name: string,
+6 -6
View File
@@ -80,7 +80,7 @@ stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno)
return _stat(name, attrs, allocator)
}
fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno) {
fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, errno: Errno) {
if fd == 0 {
return {}, ERROR_INVALID_HANDLE
}
@@ -94,14 +94,14 @@ fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno)
h := win32.HANDLE(fd)
switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
fi: File_Info
fi.fullpath = path
fi.name = basename(path)
fi.mode |= file_type_mode(h)
return fi, ERROR_NONE
errno = ERROR_NONE
case:
fi, errno = file_info_from_get_file_information_by_handle(path, h)
}
return file_info_from_get_file_information_by_handle(path, h)
fi.fullpath = path
return
}
+10 -4
View File
@@ -220,9 +220,11 @@ get_escape :: proc(chunk: string) -> (r: rune, next_chunk: string, err: Match_Er
//
glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []string, err: Match_Error) {
context.allocator = allocator
if !has_meta(pattern) {
// TODO(bill): os.lstat on here to check for error
m := make([]string, 1, allocator)
m := make([]string, 1)
m[0] = pattern
return m[:], .None
}
@@ -232,6 +234,7 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
when ODIN_OS == .Windows {
temp_buf: [8]byte
volume_len, dir = clean_glob_path_windows(dir, temp_buf[:])
} else {
dir = clean_glob_path(dir)
}
@@ -246,7 +249,7 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
if err != .None {
return
}
dmatches := make([dynamic]string, 0, 0, allocator)
dmatches := make([dynamic]string, 0, 0)
for d in m {
dmatches, err = _glob(d, file, &dmatches)
if err != .None {
@@ -258,11 +261,13 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
}
return
}
_glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]string, e: Match_Error) {
_glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := context.allocator) -> (m: [dynamic]string, e: Match_Error) {
context.allocator = allocator
if matches != nil {
m = matches^
} else {
m = make([dynamic]string, 0, 0, context.allocator)
m = make([dynamic]string, 0, 0)
}
@@ -275,6 +280,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s
{
file_info, ferr := os.fstat(d)
defer os.file_info_delete(file_info)
if ferr != 0 {
return
}
+9 -3
View File
@@ -1,5 +1,5 @@
// The path/filepath package uses either forward slashes or backslashes depending on the operating system
// To process paths usch as URLs that depend on forward slashes regardless of the OS, use the path package
// To process paths such as URLs that depend on forward slashes regardless of the OS, use the path package
package filepath
import "core:strings"
@@ -122,6 +122,7 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
vol_and_path = original_path,
vol_len = vol_len,
}
defer lazy_buffer_destroy(out)
r, dot_dot := 0, 0
if rooted {
@@ -170,7 +171,6 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
cleaned, new_allocation := from_slash(s)
if new_allocation {
delete(s)
lazy_buffer_destroy(out)
}
return cleaned
}
@@ -300,6 +300,11 @@ dir :: proc(path: string, allocator := context.allocator) -> string {
// Splits the PATH-like `path` string, returning an array of its separated components (delete after use).
// For Windows the separator is `;`, for Unix it's `:`.
// An empty string returns nil. A non-empty string with no separators returns a 1-element array.
// Any empty components will be included, e.g. `a::b` will return a 3-element array, as will `::`.
// Separators within pairs of double-quotes will be ignored and stripped, e.g. `"a:b"c:d` will return []{`a:bc`, `d`}.
split_list :: proc(path: string, allocator := context.allocator) -> []string {
if path == "" {
return nil
@@ -322,7 +327,7 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string {
}
start, quote = 0, false
list := make([]string, count, allocator)
list := make([]string, count + 1, allocator)
index := 0
for i := 0; i < len(path); i += 1 {
c := path[i]
@@ -336,6 +341,7 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string {
}
}
assert(index == count)
list[index] = path[start:]
for s0, i in list {
s, new := strings.replace_all(s0, `"`, ``, allocator)
+2
View File
@@ -1054,6 +1054,7 @@ as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
case Type_Info_Float:
valid = true
switch v in a {
case f16: value = u64(v)
case f32: value = u64(v)
case f64: value = u64(v)
case f32le: value = u64(v)
@@ -1159,6 +1160,7 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
case Type_Info_Float:
valid = true
switch v in a {
case f16: value = f64(v)
case f32: value = f64(v)
case f64: value = (v)
case f32le: value = f64(v)
+1
View File
@@ -136,6 +136,7 @@ Type_Info_Union :: struct {
custom_align: bool,
no_nil: bool,
maybe: bool,
shared_nil: bool,
}
Type_Info_Enum :: struct {
base: ^Type_Info,
+17 -14
View File
@@ -386,12 +386,13 @@ insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
if array == nil {
return
}
n := len(array)
n := max(len(array), index)
m :: 1
resize(array, n+m, loc)
if n+m <= len(array) {
new_size := n + m
if resize(array, new_size, loc) {
when size_of(E) != 0 {
copy(array[index+m:], array[index:])
copy(array[index + m:], array[index:])
array[index] = arg
}
ok = true
@@ -409,12 +410,13 @@ insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c
return
}
n := len(array)
n := max(len(array), index)
m := len(args)
resize(array, n+m, loc)
if n+m <= len(array) {
new_size := n + m
if resize(array, new_size, loc) {
when size_of(E) != 0 {
copy(array[index+m:], array[index:])
copy(array[index + m:], array[index:])
copy(array[index:], args)
}
ok = true
@@ -427,17 +429,18 @@ insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
if array == nil {
return
}
if len(args) == 0 {
if len(arg) == 0 {
ok = true
return
}
n := len(array)
m := len(args)
resize(array, n+m, loc)
if n+m <= len(array) {
n := max(len(array), index)
m := len(arg)
new_size := n + m
if resize(array, new_size, loc) {
copy(array[index+m:], array[index:])
copy(array[index:], args)
copy(array[index:], arg)
ok = true
}
return
+1 -1
View File
@@ -30,4 +30,4 @@ when ODIN_BUILD_MODE == .Dynamic {
#force_no_inline _cleanup_runtime()
return 0
}
}
}
+2
View File
@@ -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
View File
@@ -7,40 +7,56 @@ import "core:io"
Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool)
/*
dynamic byte buffer / string builder with helper procedures
the dynamic array is wrapped inside the struct to be more opaque
you can use `fmt.sbprint*` procedures with a `^strings.Builder` directly
*/
Builder :: struct {
buf: [dynamic]byte,
}
// return a builder, default length 0 / cap 16 are done through make
make_builder_none :: proc(allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, allocator)}
}
// return a builder, with a set length `len` and cap 16 byte buffer
make_builder_len :: proc(len: int, allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, len, allocator)}
}
// return a builder, with a set length `len` byte buffer and a custom `cap`
make_builder_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, len, cap, allocator)}
}
// overload simple `make_builder_*` with or without len / cap parameters
make_builder :: proc{
make_builder_none,
make_builder_len,
make_builder_len_cap,
}
// initialize a builder, default length 0 / cap 16 are done through make
// replaces the existing `buf`
init_builder_none :: proc(b: ^Builder, allocator := context.allocator) {
b.buf = make([dynamic]byte, allocator)
}
// initialize a builder, with a set length `len` and cap 16 byte buffer
// replaces the existing `buf`
init_builder_len :: proc(b: ^Builder, len: int, allocator := context.allocator) {
b.buf = make([dynamic]byte, len, allocator)
}
// initialize a builder, with a set length `len` byte buffer and a custom `cap`
// replaces the existing `buf`
init_builder_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) {
b.buf = make([dynamic]byte, len, cap, allocator)
}
// overload simple `init_builder_*` with or without len / ap parameters
init_builder :: proc{
init_builder_none,
init_builder_len,
@@ -76,30 +92,42 @@ _builder_stream_vtable := &io.Stream_VTable{
},
}
// return an `io.Stream` from a builder
to_stream :: proc(b: ^Builder) -> io.Stream {
return io.Stream{stream_vtable=_builder_stream_vtable, stream_data=b}
}
// return an `io.Writer` from a builder
to_writer :: proc(b: ^Builder) -> io.Writer {
return io.to_writer(to_stream(b))
}
// delete and clear the builder byte buffer content
destroy_builder :: proc(b: ^Builder) {
delete(b.buf)
clear(&b.buf)
}
// reserve the builfer byte buffer to a specific cap, when it's higher than before
grow_builder :: proc(b: ^Builder, cap: int) {
reserve(&b.buf, cap)
}
// clear the builder byte buffer content
reset_builder :: proc(b: ^Builder) {
clear(&b.buf)
}
/*
create an empty builder with the same slice length as its cap
uses the `mem.nil_allocator` to avoid allocation and keep a fixed length
used in `fmt.bprint*`
bytes: [8]byte // <-- gets filled
builder := strings.builder_from_slice(bytes[:])
strings.write_byte(&builder, 'a') -> "a"
strings.write_byte(&builder, 'b') -> "ab"
*/
builder_from_slice :: proc(backing: []byte) -> Builder {
s := transmute(mem.Raw_Slice)backing
d := mem.Raw_Dynamic_Array{
@@ -112,20 +140,36 @@ builder_from_slice :: proc(backing: []byte) -> Builder {
buf = transmute([dynamic]byte)d,
}
}
// cast the builder byte buffer to a string and return it
to_string :: proc(b: Builder) -> string {
return string(b.buf[:])
}
// return the length of the builder byte buffer
builder_len :: proc(b: Builder) -> int {
return len(b.buf)
}
// return the cap of the builder byte buffer
builder_cap :: proc(b: Builder) -> int {
return cap(b.buf)
}
// returns the space left in the builder byte buffer to use up
builder_space :: proc(b: Builder) -> int {
return max(cap(b.buf), len(b.buf), 0)
return cap(b.buf) - len(b.buf)
}
/*
appends a byte to the builder, returns the append diff
builder := strings.make_builder()
strings.write_byte(&builder, 'a') // 1
strings.write_byte(&builder, 'b') // 1
strings.write_byte(&builder, 'c') // 1
fmt.println(strings.to_string(builder)) // -> abc
*/
write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
n0 := len(b.buf)
append(&b.buf, x)
@@ -133,6 +177,14 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
return n1-n0
}
/*
appends a slice of bytes to the builder, returns the append diff
builder := strings.make_builder()
bytes := [?]byte { 'a', 'b', 'c' }
strings.write_bytes(&builder, bytes[:]) // 3
fmt.println(strings.to_string(builder)) // -> abc
*/
write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
n0 := len(b.buf)
append(&b.buf, ..x)
@@ -140,11 +192,28 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
return n1-n0
}
/*
appends a single rune into the builder, returns written rune size and an `io.Error`
builder := strings.make_builder()
strings.write_rune_builder(&builder, 'ä') // 2 None
strings.write_rune_builder(&builder, 'b') // 1 None
strings.write_rune_builder(&builder, 'c') // 1 None
fmt.println(strings.to_string(builder)) // -> äbc
*/
write_rune_builder :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
return io.write_rune(to_writer(b), r)
}
/*
appends a quoted rune into the builder, returns written size
builder := strings.make_builder()
strings.write_string(&builder, "abc") // 3
strings.write_quoted_rune_builder(&builder, 'ä') // 4
strings.write_string(&builder, "abc") // 3
fmt.println(strings.to_string(builder)) // -> abc'ä'abc
*/
write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) {
return write_quoted_rune(to_writer(b), r)
}
@@ -155,7 +224,7 @@ _write_byte :: proc(w: io.Writer, c: byte) -> int {
return 1 if err == nil else 0
}
// writer append a quoted rune into the byte buffer, return the written size
write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
quote := byte('\'')
n += _write_byte(w, quote)
@@ -173,50 +242,75 @@ write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
return
}
// overload for `write_string_*` variants
write_string :: proc{
write_string_builder,
write_string_writer,
}
/*
appends a string to the builder, return the written byte size
builder := strings.make_builder()
strings.write_string(&builder, "a") // 1
strings.write_string(&builder, "bc") // 2
strings.write_string(&builder, "xyz") // 3
fmt.println(strings.to_string(builder)) // -> abcxyz
*/
write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) {
return write_string_writer(to_writer(b), s)
}
// appends a string to the writer
write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) {
n, _ = io.write(w, transmute([]byte)s)
return
}
// pops and returns the last byte in the builder
// returns 0 when the builder is empty
pop_byte :: proc(b: ^Builder) -> (r: byte) {
if len(b.buf) == 0 {
return 0
}
r = b.buf[len(b.buf)-1]
d := cast(^mem.Raw_Dynamic_Array)&b.buf
d.len = max(d.len-1, 0)
return
}
// pops the last rune in the builder and returns the popped rune and its rune width
// returns 0, 0 when the builder is empty
pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
if len(b.buf) == 0 {
return 0, 0
}
r, width = utf8.decode_last_rune(b.buf[:])
d := cast(^mem.Raw_Dynamic_Array)&b.buf
d.len = max(d.len-width, 0)
return
}
@(private)
DIGITS_LOWER := "0123456789abcdefx"
// overload for `write_quoted_string_*` variants
write_quoted_string :: proc{
write_quoted_string_builder,
write_quoted_string_writer,
}
/*
append a quoted string into the builder, return the written byte size
builder := strings.make_builder()
strings.write_quoted_string(&builder, "a") // 3
strings.write_quoted_string(&builder, "bc", '\'') // 4
strings.write_quoted_string(&builder, "xyz") // 5
fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz"
*/
write_quoted_string_builder :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
n, _ = io.write_quoted_string(to_writer(b), str, quote)
return
@@ -228,11 +322,13 @@ write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"')
return
}
// overload for `write_encoded_rune_*`
write_encoded_rune :: proc{
write_encoded_rune_builder,
write_encoded_rune_writer,
}
// appends a rune to the builder, optional `write_quote` boolean tag, returns the written rune size
write_encoded_rune_builder :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
n, _ = io.write_encoded_rune(to_writer(b), r, write_quote)
return
@@ -244,12 +340,15 @@ write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) ->
return
}
// overload for `write_escaped_rune_*`
write_escaped_rune :: proc{
write_escaped_rune_builder,
write_escaped_rune_writer,
}
// appends a rune to the builder, fully written out in case of escaped runes e.g. '\a' will be written as such
// when `r` and `quote` match and `quote` is `\\` - they will be written as two slashes
// `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026`
write_escaped_rune_builder :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe)
return
@@ -261,21 +360,26 @@ write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe
return
}
// writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
buf: [32]byte
s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil)
return write_string(b, s)
}
// writes a i64 value `i` in `base` = 10 into the builder, returns the written amount of characters
write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) {
buf: [32]byte
s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
return write_string(b, s)
}
// writes a uint value `i` in `base` = 10 into the builder, returns the written amount of characters
write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) {
return write_u64(b, u64(i), base)
}
// writes a int value `i` in `base` = 10 into the builder, returns the written amount of characters
write_int :: proc(b: ^Builder, i: int, base: int = 10) -> (n: int) {
return write_i64(b, i64(i), base)
}
+47 -7
View File
@@ -58,6 +58,13 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
return to_string(b)
}
/*
returns the input string `s` with all runes set to lowered case
always allocates using the `allocator`
strings.to_lower("test") -> test
strings.to_lower("Test") -> test
*/
to_lower :: proc(s: string, allocator := context.allocator) -> string {
b: Builder
init_builder(&b, 0, len(s), allocator)
@@ -66,6 +73,14 @@ to_lower :: proc(s: string, allocator := context.allocator) -> string {
}
return to_string(b)
}
/*
returns the input string `s` with all runes set to upper case
always allocates using the `allocator`
strings.to_lower("test") -> TEST
strings.to_lower("Test") -> TEST
*/
to_upper :: proc(s: string, allocator := context.allocator) -> string {
b: Builder
init_builder(&b, 0, len(s), allocator)
@@ -75,13 +90,13 @@ to_upper :: proc(s: string, allocator := context.allocator) -> string {
return to_string(b)
}
// returns true when the `c` rune is a space, '-' or '_'
// useful when treating strings like words in a text editor or html paths
is_delimiter :: proc(c: rune) -> bool {
return c == '-' || c == '_' || is_space(c)
}
// returns true when the `r` rune is a non alpha or `unicode.is_space` rune
is_separator :: proc(r: rune) -> bool {
if r <= 0x7f {
switch r {
@@ -101,7 +116,10 @@ is_separator :: proc(r: rune) -> bool {
return unicode.is_space(r)
}
/*
iterator that loops through the string and calls the callback with the `prev`, `curr` and `next` rune
on empty string `s` the callback gets called once with empty runes
*/
string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Writer, prev, curr, next: rune)) {
prev, curr: rune
for next in s {
@@ -122,8 +140,9 @@ string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Write
}
}
to_lower_camel_case :: to_camel_case
// converts the `s` string to "lowerCamelCase"
to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
s := s
s = trim_space(s)
@@ -147,6 +166,8 @@ to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
}
to_upper_camel_case :: to_pascal_case
// converts the `s` string to "PascalCase"
to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
s := s
s = trim_space(s)
@@ -169,6 +190,15 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
return to_string(b)
}
/*
returns the `s` string to words seperated by the given `delimiter` rune
all runes will be upper or lowercased based on the `all_uppercase` bool
strings.to_delimiter_case("Hello World", '_', false) -> hello_world
strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD
strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD
strings.to_delimiter_case("aBC", '_', false) -> a_b_c
*/
to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string {
s := s
s = trim_space(s)
@@ -208,24 +238,34 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo
return to_string(b)
}
/*
converts the `s` string to "snake_case" with all runes lowercased
strings.to_snake_case("HelloWorld") -> hello_world
strings.to_snake_case("Hello World") -> hello_world
*/
to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '_', false, allocator)
}
to_screaming_snake_case :: to_upper_snake_case
// converts the `s` string to "SNAKE_CASE" with all runes uppercased
to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '_', true, allocator)
}
// converts the `s` string to "kebab-case" with all runes lowercased
to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '-', false, allocator)
}
to_upper_case :: proc(s: string, allocator := context.allocator) -> string {
// converts the `s` string to "KEBAB-CASE" with all runes uppercased
to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '-', true, allocator)
}
// converts the `s` string to "Ada_case"
to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
delimiter :: '_'
+12
View File
@@ -2,21 +2,26 @@ package strings
import "core:mem"
// custom string entry struct
Intern_Entry :: struct {
len: int,
str: [1]byte, // string is allocated inline with the entry to keep allocations simple
}
// "intern" is a more memory efficient string map
// `allocator` is used to allocate the actual `Intern_Entry` strings
Intern :: struct {
allocator: mem.Allocator,
entries: map[string]^Intern_Entry,
}
// initialize the entries map and set the allocator for the string entries
intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator) {
m.allocator = allocator
m.entries = make(map[string]^Intern_Entry, 16, map_allocator)
}
// free the map and all its content allocated using the `.allocator`
intern_destroy :: proc(m: ^Intern) {
for _, value in m.entries {
free(value, m.allocator)
@@ -24,15 +29,22 @@ intern_destroy :: proc(m: ^Intern) {
delete(m.entries)
}
// returns the `text` string from the intern map - gets set if it didnt exist yet
// the returned string lives as long as the map entry lives
intern_get :: proc(m: ^Intern, text: string) -> string {
entry := _intern_get_entry(m, text)
#no_bounds_check return string(entry.str[:entry.len])
}
// returns the `text` cstring from the intern map - gets set if it didnt exist yet
// the returned cstring lives as long as the map entry lives
intern_get_cstring :: proc(m: ^Intern, text: string) -> cstring {
entry := _intern_get_entry(m, text)
return cstring(&entry.str[0])
}
// looks up wether the `text` string exists in the map, returns the entry
// sets & allocates the entry if it wasnt set yet
_intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_check {
if prev, ok := m.entries[text]; ok {
return prev
+34 -2
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+36 -159
View File
@@ -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
-80
View File
@@ -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
}
-889
View File
@@ -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
}
-16
View File
@@ -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
}
-33
View File
@@ -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"
+46
View File
@@ -0,0 +1,46 @@
//+build freebsd
//+private
package sync
import "core:os"
import "core:time"
_current_thread_id :: proc "contextless" () -> int {
return os.current_thread_id()
}
_Mutex :: struct {
mutex: Atomic_Mutex,
}
_mutex_lock :: proc(m: ^Mutex) {
atomic_mutex_lock(&m.impl.mutex)
}
_mutex_unlock :: proc(m: ^Mutex) {
atomic_mutex_unlock(&m.impl.mutex)
}
_mutex_try_lock :: proc(m: ^Mutex) -> bool {
return atomic_mutex_try_lock(&m.impl.mutex)
}
_Cond :: struct {
cond: Atomic_Cond,
}
_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
}
_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
}
_cond_signal :: proc(c: ^Cond) {
atomic_cond_signal(&c.impl.cond)
}
_cond_broadcast :: proc(c: ^Cond) {
atomic_cond_broadcast(&c.impl.cond)
}
+125
View File
@@ -0,0 +1,125 @@
//+private
package sync
when #config(ODIN_SYNC_RECURSIVE_MUTEX_USE_FUTEX, true) {
_Recursive_Mutex :: struct {
owner: Futex,
recursion: i32,
}
_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
tid := Futex(current_thread_id())
for {
prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
switch prev_owner {
case 0, tid:
m.impl.recursion += 1
// inside the lock
return
}
futex_wait(&m.impl.owner, u32(prev_owner))
}
}
_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
m.impl.recursion -= 1
if m.impl.recursion != 0 {
return
}
atomic_exchange_explicit(&m.impl.owner, 0, .Release)
futex_signal(&m.impl.owner)
// outside the lock
}
_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
tid := Futex(current_thread_id())
prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
switch prev_owner {
case 0, tid:
m.impl.recursion += 1
// inside the lock
return true
}
return false
}
} else {
_Recursive_Mutex :: struct {
owner: int,
recursion: int,
mutex: Mutex,
}
_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
tid := current_thread_id()
if tid != m.impl.owner {
mutex_lock(&m.impl.mutex)
}
// inside the lock
m.impl.owner = tid
m.impl.recursion += 1
}
_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
tid := current_thread_id()
assert(tid == m.impl.owner)
m.impl.recursion -= 1
recursion := m.impl.recursion
if recursion == 0 {
m.impl.owner = 0
}
if recursion == 0 {
mutex_unlock(&m.impl.mutex)
}
// outside the lock
}
_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
tid := current_thread_id()
if m.impl.owner == tid {
return mutex_try_lock(&m.impl.mutex)
}
if !mutex_try_lock(&m.impl.mutex) {
return false
}
// inside the lock
m.impl.owner = tid
m.impl.recursion += 1
return true
}
}
when ODIN_OS != .Windows {
_RW_Mutex :: struct {
mutex: Atomic_RW_Mutex,
}
_rw_mutex_lock :: proc(rw: ^RW_Mutex) {
atomic_rw_mutex_lock(&rw.impl.mutex)
}
_rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
atomic_rw_mutex_unlock(&rw.impl.mutex)
}
_rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
return atomic_rw_mutex_try_lock(&rw.impl.mutex)
}
_rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
atomic_rw_mutex_shared_lock(&rw.impl.mutex)
}
_rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
atomic_rw_mutex_shared_unlock(&rw.impl.mutex)
}
_rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
return atomic_rw_mutex_try_shared_lock(&rw.impl.mutex)
}
}
+47
View File
@@ -0,0 +1,47 @@
//+build linux
//+private
package sync
import "core:sys/unix"
import "core:time"
_current_thread_id :: proc "contextless" () -> int {
return unix.sys_gettid()
}
_Mutex :: struct {
mutex: Atomic_Mutex,
}
_mutex_lock :: proc(m: ^Mutex) {
atomic_mutex_lock(&m.impl.mutex)
}
_mutex_unlock :: proc(m: ^Mutex) {
atomic_mutex_unlock(&m.impl.mutex)
}
_mutex_try_lock :: proc(m: ^Mutex) -> bool {
return atomic_mutex_try_lock(&m.impl.mutex)
}
_Cond :: struct {
cond: Atomic_Cond,
}
_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
}
_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
}
_cond_signal :: proc(c: ^Cond) {
atomic_cond_signal(&c.impl.cond)
}
_cond_broadcast :: proc(c: ^Cond) {
atomic_cond_broadcast(&c.impl.cond)
}
+46
View File
@@ -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
}
}
-123
View File
@@ -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
}
-79
View File
@@ -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
-9
View File
@@ -1,9 +0,0 @@
//+build freebsd
//+private
package sync2
import "core:os"
_current_thread_id :: proc "contextless" () -> int {
return os.current_thread_id()
}
-184
View File
@@ -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
}
}
-9
View File
@@ -1,9 +0,0 @@
//+build linux
//+private
package sync2
import "core:sys/unix"
_current_thread_id :: proc "contextless" () -> int {
return unix.sys_gettid()
}
-9
View File
@@ -1,9 +0,0 @@
//+build openbsd
//+private
package sync2
import "core:os"
_current_thread_id :: proc "contextless" () -> int {
return os.current_thread_id()
}
-58
View File
@@ -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)
}
-54
View File
@@ -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)
}
-40
View File
@@ -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)
}
-36
View File
@@ -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)
}
-36
View File
@@ -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)
}
-248
View File
@@ -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:
-180
View File
@@ -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()
}
-58
View File
@@ -1,58 +0,0 @@
package sync
import "core:intrinsics"
Wait_Group :: struct {
counter: int,
mutex: Blocking_Mutex,
cond: Condition,
}
wait_group_init :: proc(wg: ^Wait_Group) {
wg.counter = 0
blocking_mutex_init(&wg.mutex)
condition_init(&wg.cond, &wg.mutex)
}
wait_group_destroy :: proc(wg: ^Wait_Group) {
condition_destroy(&wg.cond)
blocking_mutex_destroy(&wg.mutex)
}
wait_group_add :: proc(wg: ^Wait_Group, delta: int) {
if delta == 0 {
return
}
blocking_mutex_lock(&wg.mutex)
defer blocking_mutex_unlock(&wg.mutex)
intrinsics.atomic_add(&wg.counter, delta)
if wg.counter < 0 {
panic("sync.Wait_Group negative counter")
}
if wg.counter == 0 {
condition_broadcast(&wg.cond)
if wg.counter != 0 {
panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
}
}
}
wait_group_done :: proc(wg: ^Wait_Group) {
wait_group_add(wg, -1)
}
wait_group_wait :: proc(wg: ^Wait_Group) {
blocking_mutex_lock(&wg.mutex)
defer blocking_mutex_unlock(&wg.mutex)
if wg.counter != 0 {
condition_wait_for(&wg.cond)
if wg.counter != 0 {
panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
}
}
}
+2
View File
@@ -111,6 +111,8 @@ foreign user32 {
@(link_name="SetWindowTextW") set_window_text_w :: proc(hwnd: Hwnd, c_string: Wstring) -> Bool ---
@(link_name="RegisterClassA") register_class_a :: proc(wc: ^Wnd_Class_A) -> i16 ---
@(link_name="RegisterClassW") register_class_w :: proc(wc: ^Wnd_Class_W) -> i16 ---
@(link_name="UnregisterClassA") unregister_class_a :: proc(class_name: cstring, instance: Hinstance) -> Bool ---
@(link_name="UnregisterClassW") unregister_class_w :: proc(class_name: Wstring, instance: Hinstance) -> Bool ---
@(link_name="RegisterClassExA") register_class_ex_a :: proc(wc: ^Wnd_Class_Ex_A) -> i16 ---
@(link_name="RegisterClassExW") register_class_ex_w :: proc(wc: ^Wnd_Class_Ex_W) -> i16 ---
+9
View File
@@ -0,0 +1,9 @@
// +build windows
package sys_windows
foreign import dwmapi "system:Dwmapi.lib"
@(default_calling_convention="stdcall")
foreign dwmapi {
DwmFlush :: proc() -> HRESULT ---
}
+66
View File
@@ -0,0 +1,66 @@
// +build windows
package sys_windows
foreign import gdi32 "system:Gdi32.lib"
@(default_calling_convention="stdcall")
foreign gdi32 {
GetStockObject :: proc(i: c_int) -> HGDIOBJ ---
SelectObject :: proc(hdc: HDC, h: HGDIOBJ) -> HGDIOBJ ---
CreateDIBPatternBrush :: proc(h: HGLOBAL, iUsage: UINT) -> HBRUSH ---
CreateDIBitmap :: proc(
hdc: HDC,
pbmih: ^BITMAPINFOHEADER,
flInit: DWORD,
pjBits: VOID,
pbmi: ^BITMAPINFO,
iUsage: UINT,
) -> HBITMAP ---
CreateDIBSection :: proc(
hdc: HDC,
pbmi: ^BITMAPINFO,
usage: UINT,
ppvBits: VOID,
hSection: HANDLE,
offset: DWORD,
) -> HBITMAP ---
StretchDIBits :: proc(
hdc: HDC,
xDest: c_int,
yDest: c_int,
DestWidth: c_int,
DestHeight: c_int,
xSrc: c_int,
ySrc: c_int,
SrcWidth: c_int,
SrcHeight: c_int,
lpBits: VOID,
lpbmi: ^BITMAPINFO,
iUsage: UINT,
rop: DWORD,
) -> c_int ---
StretchBlt :: proc(
hdcDest: HDC,
xDest: c_int,
yDest: c_int,
wDest: c_int,
hDest: c_int,
hdcSrc: HDC,
xSrc: c_int,
ySrc: c_int,
wSrc: c_int,
hSrc: c_int,
rop: DWORD,
) -> BOOL ---
SetPixelFormat :: proc(hdc: HDC, format: c_int, ppfd: ^PIXELFORMATDESCRIPTOR) -> BOOL ---
ChoosePixelFormat :: proc(hdc: HDC, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int ---
SwapBuffers :: proc(HDC) -> BOOL ---
PatBlt :: proc(hdc: HDC, x, y, w, h: c_int, rop: DWORD) -> BOOL ---
}
+10 -1
View File
@@ -342,6 +342,7 @@ MEM_TOP_DOWN :: 0x100000
MEM_LARGE_PAGES :: 0x20000000
MEM_4MB_PAGES :: 0x80000000
@(default_calling_convention="stdcall")
foreign kernel32 {
VirtualAlloc :: proc(
lpAddress: LPVOID,
@@ -484,6 +485,7 @@ LowMemoryResourceNotification :: MEMORY_RESOURCE_NOTIFICATION_TYPE.LowMemoryRes
HighMemoryResourceNotification :: MEMORY_RESOURCE_NOTIFICATION_TYPE.HighMemoryResourceNotification
@(default_calling_convention="stdcall")
foreign kernel32 {
CreateMemoryResourceNotification :: proc(
NotificationType: MEMORY_RESOURCE_NOTIFICATION_TYPE,
@@ -499,6 +501,7 @@ FILE_CACHE_MAX_HARD_DISABLE :: DWORD(0x00000002)
FILE_CACHE_MIN_HARD_ENABLE :: DWORD(0x00000004)
FILE_CACHE_MIN_HARD_DISABLE :: DWORD(0x00000008)
@(default_calling_convention="stdcall")
foreign kernel32 {
GetSystemFileCacheSize :: proc(
lpMinimumFileCacheSize: PSIZE_T,
@@ -528,6 +531,7 @@ WIN32_MEMORY_RANGE_ENTRY :: struct {
PWIN32_MEMORY_RANGE_ENTRY :: ^WIN32_MEMORY_RANGE_ENTRY
@(default_calling_convention="stdcall")
foreign kernel32 {
PrefetchVirtualMemory :: proc(
hProcess: HANDLE,
@@ -585,6 +589,7 @@ foreign kernel32 {
MEHC_PATROL_SCRUBBER_PRESENT :: ULONG(0x1)
@(default_calling_convention="stdcall")
foreign kernel32 {
GetMemoryErrorHandlingCapabilities :: proc(
Capabilities: PULONG,
@@ -593,6 +598,7 @@ foreign kernel32 {
PBAD_MEMORY_CALLBACK_ROUTINE :: #type proc "stdcall" ()
@(default_calling_convention="stdcall")
foreign kernel32 {
RegisterBadMemoryNotification :: proc(
Callback: PBAD_MEMORY_CALLBACK_ROUTINE,
@@ -613,6 +619,7 @@ VmOfferPriorityLow :: OFFER_PRIORITY.VmOfferPriorityLow
VmOfferPriorityBelowNormal :: OFFER_PRIORITY.VmOfferPriorityBelowNormal
VmOfferPriorityNormal :: OFFER_PRIORITY.VmOfferPriorityNormal
@(default_calling_convention="stdcall")
foreign kernel32 {
OfferVirtualMemory :: proc(
VirtualAddress: PVOID,
@@ -677,6 +684,7 @@ WIN32_MEMORY_REGION_INFORMATION_u_s_Bitfield :: distinct ULONG
Reserved : 32-6,
}*/
@(default_calling_convention="stdcall")
foreign kernel32 {
QueryVirtualMemoryInformation :: proc(
Process: HANDLE,
@@ -701,7 +709,7 @@ foreign kernel32 {
NUMA_NO_PREFERRED_NODE :: 0xffffffff
MapViewOfFile2 :: #force_inline proc(
MapViewOfFile2 :: #force_inline proc "stdcall" (
FileMappingHandle: HANDLE,
ProcessHandle: HANDLE,
Offset: ULONG64,
@@ -722,6 +730,7 @@ MapViewOfFile2 :: #force_inline proc(
)
}
@(default_calling_convention="stdcall")
foreign kernel32 {
UnmapViewOfFile2 :: proc(
ProcessHandle: HANDLE,
+252
View File
@@ -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
View File
@@ -21,7 +21,16 @@ HINSTANCE :: HANDLE
HMODULE :: distinct HINSTANCE
HRESULT :: distinct LONG
HWND :: distinct HANDLE
HDC :: distinct HANDLE
HMONITOR :: distinct HANDLE
HICON :: distinct HANDLE
HCURSOR :: distinct HANDLE
HMENU :: distinct HANDLE
HBRUSH :: distinct HANDLE
HGDIOBJ :: distinct HANDLE
HBITMAP :: distinct HANDLE
HGLOBAL :: distinct HANDLE
HHOOK :: distinct HANDLE
BOOL :: distinct b32
BYTE :: distinct u8
BOOLEAN :: distinct b8
@@ -42,9 +51,15 @@ PULONG_PTR :: ^ULONG_PTR
LPULONG_PTR :: ^ULONG_PTR
DWORD_PTR :: ULONG_PTR
LONG_PTR :: int
UINT_PTR :: uintptr
ULONG :: c_ulong
UCHAR :: BYTE
NTSTATUS :: c.long
LPARAM :: LONG_PTR
WPARAM :: UINT_PTR
LRESULT :: LONG_PTR
LPRECT :: ^RECT
LPPOINT :: ^POINT
UINT8 :: u8
UINT16 :: u16
@@ -71,6 +86,7 @@ PBOOL :: ^BOOL
LPBOOL :: ^BOOL
LPCSTR :: cstring
LPCWSTR :: wstring
LPCTSTR :: wstring
LPDWORD :: ^DWORD
PCSTR :: cstring
PCWSTR :: wstring
@@ -81,7 +97,9 @@ LPPROCESS_INFORMATION :: ^PROCESS_INFORMATION
PSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
LPSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
LPSTARTUPINFO :: ^STARTUPINFO
PVOID :: rawptr
LPTRACKMOUSEEVENT :: ^TRACKMOUSEEVENT
VOID :: rawptr
PVOID :: rawptr
LPVOID :: rawptr
PINT :: ^INT
LPINT :: ^INT
@@ -178,6 +196,109 @@ GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0
GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1
TIMERPROC :: #type proc "stdcall" (HWND, UINT, UINT_PTR, DWORD)
WNDPROC :: #type proc "stdcall" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
HOOKPROC :: #type proc "stdcall" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT
CWPRETSTRUCT :: struct {
lResult: LRESULT,
lParam: LPARAM,
wParam: WPARAM,
message: UINT,
hwnd: HWND,
}
KBDLLHOOKSTRUCT :: struct {
vkCode: DWORD,
scanCode: DWORD,
flags: DWORD,
time: DWORD,
dwExtraInfo: ULONG_PTR,
}
WNDCLASSA :: struct {
style: UINT,
lpfnWndProc: WNDPROC,
cbClsExtra: c_int,
cbWndExtra: c_int,
hInstance: HINSTANCE,
hIcon: HICON,
hCursor: HCURSOR,
hbrBackground: HBRUSH,
lpszMenuName: LPCSTR,
lpszClassName: LPCSTR,
}
WNDCLASSW :: struct {
style: UINT,
lpfnWndProc: WNDPROC,
cbClsExtra: c_int,
cbWndExtra: c_int,
hInstance: HINSTANCE,
hIcon: HICON,
hCursor: HCURSOR,
hbrBackground: HBRUSH,
lpszMenuName: LPCWSTR,
lpszClassName: LPCWSTR,
}
WNDCLASSEXA :: struct {
cbSize: UINT,
style: UINT,
lpfnWndProc: WNDPROC,
cbClsExtra: c_int,
cbWndExtra: c_int,
hInstance: HINSTANCE,
hIcon: HICON,
hCursor: HCURSOR,
hbrBackground: HBRUSH,
lpszMenuName: LPCSTR,
lpszClassName: LPCSTR,
hIconSm: HICON,
}
WNDCLASSEXW :: struct {
cbSize: UINT,
style: UINT,
lpfnWndProc: WNDPROC,
cbClsExtra: c_int,
cbWndExtra: c_int,
hInstance: HINSTANCE,
hIcon: HICON,
hCursor: HCURSOR,
hbrBackground: HBRUSH,
lpszMenuName: LPCWSTR,
lpszClassName: LPCWSTR,
hIconSm: HICON,
}
MSG :: struct {
hwnd: HWND,
message: UINT,
wParam: WPARAM,
lParam: LPARAM,
time: DWORD,
pt: POINT,
}
PAINTSTRUCT :: struct {
hdc: HDC,
fErase: BOOL,
rcPaint: RECT,
fRestore: BOOL,
fIncUpdate: BOOL,
rgbReserved: [32]BYTE,
}
TRACKMOUSEEVENT :: struct {
cbSize: DWORD,
dwFlags: DWORD,
hwndTrack: HWND,
dwHoverTime: DWORD,
}
WIN32_FIND_DATAW :: struct {
dwFileAttributes: DWORD,
ftCreationTime: FILETIME,
@@ -191,6 +312,705 @@ WIN32_FIND_DATAW :: struct {
cAlternateFileName: [14]wchar_t,
}
CREATESTRUCTA :: struct {
lpCreateParams: LPVOID,
hInstance: HINSTANCE,
hMenu: HMENU,
hwndParent: HWND,
cy: c_int,
cx: c_int,
y: c_int,
x: c_int,
style: LONG,
lpszName: LPCSTR,
lpszClass: LPCSTR,
dwExStyle: DWORD,
}
CREATESTRUCTW:: struct {
lpCreateParams: LPVOID,
hInstance: HINSTANCE,
hMenu: HMENU,
hwndParent: HWND,
cy: c_int,
cx: c_int,
y: c_int,
x: c_int,
style: LONG,
lpszName: LPCWSTR,
lpszClass: LPCWSTR,
dwExStyle: DWORD,
}
// MessageBox() Flags
MB_OK :: 0x00000000
MB_OKCANCEL :: 0x00000001
MB_ABORTRETRYIGNORE :: 0x00000002
MB_YESNOCANCEL :: 0x00000003
MB_YESNO :: 0x00000004
MB_RETRYCANCEL :: 0x00000005
MB_CANCELTRYCONTINUE :: 0x00000006
MB_ICONHAND :: 0x00000010
MB_ICONQUESTION :: 0x00000020
MB_ICONEXCLAMATION :: 0x00000030
MB_ICONASTERISK :: 0x00000040
MB_USERICON :: 0x00000080
MB_ICONWARNING :: MB_ICONEXCLAMATION
MB_ICONERROR :: MB_ICONHAND
MB_ICONINFORMATION :: MB_ICONASTERISK
MB_ICONSTOP :: MB_ICONHAND
MB_DEFBUTTON1 :: 0x00000000
MB_DEFBUTTON2 :: 0x00000100
MB_DEFBUTTON3 :: 0x00000200
MB_DEFBUTTON4 :: 0x00000300
MB_APPLMODAL :: 0x00000000
MB_SYSTEMMODAL :: 0x00001000
MB_TASKMODAL :: 0x00002000
MB_HELP :: 0x00004000 // Help Button
MB_NOFOCUS :: 0x00008000
MB_SETFOREGROUND :: 0x00010000
MB_DEFAULT_DESKTOP_ONLY :: 0x00020000
MB_TOPMOST :: 0x00040000
MB_RIGHT :: 0x00080000
MB_RTLREADING :: 0x00100000
MB_SERVICE_NOTIFICATION :: 0x00200000
MB_SERVICE_NOTIFICATION_NT3X :: 0x00040000
MB_TYPEMASK :: 0x0000000F
MB_ICONMASK :: 0x000000F0
MB_DEFMASK :: 0x00000F00
MB_MODEMASK :: 0x00003000
MB_MISCMASK :: 0x0000C000
// Dialog Box Command IDs
IDOK :: 1
IDCANCEL :: 2
IDABORT :: 3
IDRETRY :: 4
IDIGNORE :: 5
IDYES :: 6
IDNO :: 7
IDCLOSE :: 8
IDHELP :: 9
IDTRYAGAIN :: 10
IDCONTINUE :: 11
IDTIMEOUT :: 32000
CS_VREDRAW : UINT : 0x0001
CS_HREDRAW : UINT : 0x0002
CS_DBLCLKS : UINT : 0x0008
CS_OWNDC : UINT : 0x0020
CS_CLASSDC : UINT : 0x0040
CS_PARENTDC : UINT : 0x0080
CS_NOCLOSE : UINT : 0x0200
CS_SAVEBITS : UINT : 0x0800
CS_BYTEALIGNCLIENT : UINT : 0x1000
CS_BYTEALIGNWINDOW : UINT : 0x2000
CS_GLOBALCLASS : UINT : 0x4000
CS_DROPSHADOW : UINT : 0x0002_0000
WS_BORDER : UINT : 0x0080_0000
WS_CAPTION : UINT : 0x00C0_0000
WS_CHILD : UINT : 0x4000_0000
WS_CHILDWINDOW : UINT : WS_CHILD
WS_CLIPCHILDREN : UINT : 0x0200_0000
WS_CLIPSIBLINGS : UINT : 0x0400_0000
WS_DISABLED : UINT : 0x0800_0000
WS_DLGFRAME : UINT : 0x0040_0000
WS_GROUP : UINT : 0x0002_0000
WS_HSCROLL : UINT : 0x0010_0000
WS_ICONIC : UINT : 0x2000_0000
WS_MAXIMIZE : UINT : 0x0100_0000
WS_MAXIMIZEBOX : UINT : 0x0001_0000
WS_MINIMIZE : UINT : 0x2000_0000
WS_MINIMIZEBOX : UINT : 0x0002_0000
WS_OVERLAPPED : UINT : 0x0000_0000
WS_OVERLAPPEDWINDOW : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
WS_POPUP : UINT : 0x8000_0000
WS_POPUPWINDOW : UINT : WS_POPUP | WS_BORDER | WS_SYSMENU
WS_SIZEBOX : UINT : 0x0004_0000
WS_SYSMENU : UINT : 0x0008_0000
WS_TABSTOP : UINT : 0x0001_0000
WS_THICKFRAME : UINT : 0x0004_0000
WS_TILED : UINT : 0x0000_0000
WS_TILEDWINDOW : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE
WS_VISIBLE : UINT : 0x1000_0000
WS_VSCROLL : UINT : 0x0020_0000
QS_ALLEVENTS : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY
QS_ALLINPUT : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE
QS_ALLPOSTMESSAGE : UINT : 0x0100
QS_HOTKEY : UINT : 0x0080
QS_INPUT : UINT : QS_MOUSE | QS_KEY | QS_RAWINPUT
QS_KEY : UINT : 0x0001
QS_MOUSE : UINT : QS_MOUSEMOVE | QS_MOUSEBUTTON
QS_MOUSEBUTTON : UINT : 0x0004
QS_MOUSEMOVE : UINT : 0x0002
QS_PAINT : UINT : 0x0020
QS_POSTMESSAGE : UINT : 0x0008
QS_RAWINPUT : UINT : 0x0400
QS_SENDMESSAGE : UINT : 0x0040
QS_TIMER : UINT : 0x0010
PM_NOREMOVE : UINT : 0x0000
PM_REMOVE : UINT : 0x0001
PM_NOYIELD : UINT : 0x0002
PM_QS_INPUT : UINT : QS_INPUT << 16
PM_QS_PAINT : UINT : QS_PAINT << 16
PM_QS_POSTMESSAGE : UINT : (QS_POSTMESSAGE | QS_HOTKEY | QS_TIMER) << 16
PM_QS_SENDMESSAGE : UINT : QS_SENDMESSAGE << 16
SW_HIDE : c_int : 0
SW_SHOWNORMAL : c_int : SW_NORMAL
SW_NORMAL : c_int : 1
SW_SHOWMINIMIZED : c_int : 2
SW_SHOWMAXIMIZED : c_int : SW_MAXIMIZE
SW_MAXIMIZE : c_int : 3
SW_SHOWNOACTIVATE : c_int : 4
SW_SHOW : c_int : 5
SW_MINIMIZE : c_int : 6
SW_SHOWMINNOACTIVE : c_int : 7
SW_SHOWNA : c_int : 8
SW_RESTORE : c_int : 9
SW_SHOWDEFAULT : c_int : 10
SW_FORCEMINIMIZE : c_int : 11
// SetWindowPos Flags
SWP_NOSIZE :: 0x0001
SWP_NOMOVE :: 0x0002
SWP_NOZORDER :: 0x0004
SWP_NOREDRAW :: 0x0008
SWP_NOACTIVATE :: 0x0010
SWP_FRAMECHANGED :: 0x0020 // The frame changed: send WM_NCCALCSIZE
SWP_SHOWWINDOW :: 0x0040
SWP_HIDEWINDOW :: 0x0080
SWP_NOCOPYBITS :: 0x0100
SWP_NOOWNERZORDER :: 0x0200 // Don't do owner Z ordering
SWP_NOSENDCHANGING :: 0x0400 // Don't send WM_WINDOWPOSCHANGING
SWP_DRAWFRAME :: SWP_FRAMECHANGED
SWP_NOREPOSITION :: SWP_NOOWNERZORDER
SWP_DEFERERASE :: 0x2000 // same as SWP_DEFERDRAWING
SWP_ASYNCWINDOWPOS :: 0x4000 // same as SWP_CREATESPB
HWND_TOP :: HWND( uintptr(0)) // 0
HWND_BOTTOM :: HWND( uintptr(1)) // 1
HWND_TOPMOST :: HWND(~uintptr(0)) // -1
HWND_NOTOPMOST :: HWND(~uintptr(0) - 1) // -2
// Window field offsets for GetWindowLong()
GWL_STYLE :: -16
GWL_EXSTYLE :: -20
GWL_ID :: -12
when ODIN_ARCH == .i386 {
GWL_WNDPROC :: -4
GWL_HINSTANCE :: -6
GWL_HWNDPARENT :: -8
GWL_USERDATA :: -21
}
GWLP_WNDPROC :: -4
GWLP_HINSTANCE :: -6
GWLP_HWNDPARENT :: -8
GWLP_USERDATA :: -21
GWLP_ID :: -12
// Class field offsets for GetClassLong()
GCL_CBWNDEXTRA :: -18
GCL_CBCLSEXTRA :: -20
GCL_STYLE :: -26
GCW_ATOM :: -32
when ODIN_ARCH == .i386 {
GCL_MENUNAME :: -8
GCL_HBRBACKGROUND :: -10
GCL_HCURSOR :: -12
GCL_HICON :: -14
GCL_HMODULE :: -16
GCL_WNDPROC :: -24
GCL_HICONSM :: -34
}
GCLP_MENUNAME :: -8
GCLP_HBRBACKGROUND :: -10
GCLP_HCURSOR :: -12
GCLP_HICON :: -14
GCLP_HMODULE :: -16
GCLP_WNDPROC :: -24
GCLP_HICONSM :: -34
// GetSystemMetrics() codes
SM_CXSCREEN :: 0
SM_CYSCREEN :: 1
SM_CXVSCROLL :: 2
SM_CYHSCROLL :: 3
SM_CYCAPTION :: 4
SM_CXBORDER :: 5
SM_CYBORDER :: 6
SM_CXDLGFRAME :: 7
SM_CYDLGFRAME :: 8
SM_CYVTHUMB :: 9
SM_CXHTHUMB :: 10
SM_CXICON :: 11
SM_CYICON :: 12
SM_CXCURSOR :: 13
SM_CYCURSOR :: 14
SM_CYMENU :: 15
SM_CXFULLSCREEN :: 16
SM_CYFULLSCREEN :: 17
SM_CYKANJIWINDOW :: 18
SM_MOUSEPRESENT :: 19
SM_CYVSCROLL :: 20
SM_CXHSCROLL :: 21
SM_DEBUG :: 22
SM_SWAPBUTTON :: 23
SM_RESERVED1 :: 24
SM_RESERVED2 :: 25
SM_RESERVED3 :: 26
SM_RESERVED4 :: 27
SM_CXMIN :: 28
SM_CYMIN :: 29
SM_CXSIZE :: 30
SM_CYSIZE :: 31
SM_CXFRAME :: 32
SM_CYFRAME :: 33
SM_CXMINTRACK :: 34
SM_CYMINTRACK :: 35
SM_CXDOUBLECLK :: 36
SM_CYDOUBLECLK :: 37
SM_CXICONSPACING :: 38
SM_CYICONSPACING :: 39
SM_MENUDROPALIGNMENT :: 40
SM_PENWINDOWS :: 41
SM_DBCSENABLED :: 42
SM_CMOUSEBUTTONS :: 43
SM_CXFIXEDFRAME :: SM_CXDLGFRAME // ;win40 name change
SM_CYFIXEDFRAME :: SM_CYDLGFRAME // ;win40 name change
SM_CXSIZEFRAME :: SM_CXFRAME // ;win40 name change
SM_CYSIZEFRAME :: SM_CYFRAME // ;win40 name change
SM_SECURE :: 44
SM_CXEDGE :: 45
SM_CYEDGE :: 46
SM_CXMINSPACING :: 47
SM_CYMINSPACING :: 48
SM_CXSMICON :: 49
SM_CYSMICON :: 50
SM_CYSMCAPTION :: 51
SM_CXSMSIZE :: 52
SM_CYSMSIZE :: 53
SM_CXMENUSIZE :: 54
SM_CYMENUSIZE :: 55
SM_ARRANGE :: 56
SM_CXMINIMIZED :: 57
SM_CYMINIMIZED :: 58
SM_CXMAXTRACK :: 59
SM_CYMAXTRACK :: 60
SM_CXMAXIMIZED :: 61
SM_CYMAXIMIZED :: 62
SM_NETWORK :: 63
SM_CLEANBOOT :: 67
SM_CXDRAG :: 68
SM_CYDRAG :: 69
SM_SHOWSOUNDS :: 70
SM_CXMENUCHECK :: 71 // Use instead of GetMenuCheckMarkDimensions()!
SM_CYMENUCHECK :: 72
SM_SLOWMACHINE :: 73
SM_MIDEASTENABLED :: 74
SM_MOUSEWHEELPRESENT :: 75
SM_XVIRTUALSCREEN :: 76
SM_YVIRTUALSCREEN :: 77
SM_CXVIRTUALSCREEN :: 78
SM_CYVIRTUALSCREEN :: 79
SM_CMONITORS :: 80
SM_SAMEDISPLAYFORMAT :: 81
SM_IMMENABLED :: 82
SM_CXFOCUSBORDER :: 83
SM_CYFOCUSBORDER :: 84
SM_TABLETPC :: 86
SM_MEDIACENTER :: 87
SM_STARTER :: 88
SM_SERVERR2 :: 89
SM_MOUSEHORIZONTALWHEELPRESENT :: 91
SM_CXPADDEDBORDER :: 92
SM_DIGITIZER :: 94
SM_MAXIMUMTOUCHES :: 95
SM_CMETRICS :: 97
SM_REMOTESESSION :: 0x1000
SM_SHUTTINGDOWN :: 0x2000
SM_REMOTECONTROL :: 0x2001
SM_CARETBLINKINGENABLED :: 0x2002
SM_CONVERTIBLESLATEMODE :: 0x2003
SM_SYSTEMDOCKED :: 0x2004
// System Menu Command Values
SC_SIZE :: 0xF000
SC_MOVE :: 0xF010
SC_MINIMIZE :: 0xF020
SC_MAXIMIZE :: 0xF030
SC_NEXTWINDOW :: 0xF040
SC_PREVWINDOW :: 0xF050
SC_CLOSE :: 0xF060
SC_VSCROLL :: 0xF070
SC_HSCROLL :: 0xF080
SC_MOUSEMENU :: 0xF090
SC_KEYMENU :: 0xF100
SC_ARRANGE :: 0xF110
SC_RESTORE :: 0xF120
SC_TASKLIST :: 0xF130
SC_SCREENSAVE :: 0xF140
SC_HOTKEY :: 0xF150
SC_DEFAULT :: 0xF160
SC_MONITORPOWER :: 0xF170
SC_CONTEXTHELP :: 0xF180
SC_SEPARATOR :: 0xF00F
SCF_ISSECURE :: 0x00000001
SC_ICON :: SC_MINIMIZE
SC_ZOOM :: SC_MAXIMIZE
CW_USEDEFAULT : c_int : -2147483648
SIZE_RESTORED :: 0
SIZE_MINIMIZED :: 1
SIZE_MAXIMIZED :: 2
SIZE_MAXSHOW :: 3
SIZE_MAXHIDE :: 4
WMSZ_LEFT :: 1
WMSZ_RIGHT :: 2
WMSZ_TOP :: 3
WMSZ_TOPLEFT :: 4
WMSZ_TOPRIGHT :: 5
WMSZ_BOTTOM :: 6
WMSZ_BOTTOMLEFT :: 7
WMSZ_BOTTOMRIGHT :: 8
// Key State Masks for Mouse Messages
MK_LBUTTON :: 0x0001
MK_RBUTTON :: 0x0002
MK_SHIFT :: 0x0004
MK_CONTROL :: 0x0008
MK_MBUTTON :: 0x0010
MK_XBUTTON1 :: 0x0020
MK_XBUTTON2 :: 0x0040
// Value for rolling one detent
WHEEL_DELTA :: 120
// Setting to scroll one page for SPI_GET/SETWHEELSCROLLLINES
WHEEL_PAGESCROLL :: max(UINT)
// XButton values are WORD flags
XBUTTON1 :: 0x0001
XBUTTON2 :: 0x0002
// Were there to be an XBUTTON3, its value would be 0x0004
MAPVK_VK_TO_VSC :: 0
MAPVK_VSC_TO_VK :: 1
MAPVK_VK_TO_CHAR :: 2
MAPVK_VSC_TO_VK_EX :: 3
MAPVK_VK_TO_VSC_EX :: 4
TME_HOVER :: 0x00000001
TME_LEAVE :: 0x00000002
TME_NONCLIENT :: 0x00000010
TME_QUERY :: 0x40000000
TME_CANCEL :: 0x80000000
HOVER_DEFAULT :: 0xFFFFFFFF
USER_TIMER_MAXIMUM :: 0x7FFFFFFF
USER_TIMER_MINIMUM :: 0x0000000A
// SetWindowsHook() codes
WH_MIN :: -1
WH_MSGFILTER :: -1
WH_JOURNALRECORD :: 0
WH_JOURNALPLAYBACK :: 1
WH_KEYBOARD :: 2
WH_GETMESSAGE :: 3
WH_CALLWNDPROC :: 4
WH_CBT :: 5
WH_SYSMSGFILTER :: 6
WH_MOUSE :: 7
WH_HARDWARE :: 8
WH_DEBUG :: 9
WH_SHELL :: 10
WH_FOREGROUNDIDLE :: 11
WH_CALLWNDPROCRET :: 12
WH_KEYBOARD_LL :: 13
WH_MOUSE_LL :: 14
WH_MAX :: 14
WH_MINHOOK :: WH_MIN
WH_MAXHOOK :: WH_MAX
// Hook Codes
HC_ACTION :: 0
HC_GETNEXT :: 1
HC_SKIP :: 2
HC_NOREMOVE :: 3
HC_NOREM :: HC_NOREMOVE
HC_SYSMODALON :: 4
HC_SYSMODALOFF :: 5
// CBT Hook Codes
HCBT_MOVESIZE :: 0
HCBT_MINMAX :: 1
HCBT_QS :: 2
HCBT_CREATEWND :: 3
HCBT_DESTROYWND :: 4
HCBT_ACTIVATE :: 5
HCBT_CLICKSKIPPED :: 6
HCBT_KEYSKIPPED :: 7
HCBT_SYSCOMMAND :: 8
HCBT_SETFOCUS :: 9
_IDC_APPSTARTING := rawptr(uintptr(32650))
_IDC_ARROW := rawptr(uintptr(32512))
_IDC_CROSS := rawptr(uintptr(32515))
_IDC_HAND := rawptr(uintptr(32649))
_IDC_HELP := rawptr(uintptr(32651))
_IDC_IBEAM := rawptr(uintptr(32513))
_IDC_ICON := rawptr(uintptr(32641))
_IDC_NO := rawptr(uintptr(32648))
_IDC_SIZE := rawptr(uintptr(32640))
_IDC_SIZEALL := rawptr(uintptr(32646))
_IDC_SIZENESW := rawptr(uintptr(32643))
_IDC_SIZENS := rawptr(uintptr(32645))
_IDC_SIZENWSE := rawptr(uintptr(32642))
_IDC_SIZEWE := rawptr(uintptr(32644))
_IDC_UPARROW := rawptr(uintptr(32516))
_IDC_WAIT := rawptr(uintptr(32514))
IDC_APPSTARTING := cstring(_IDC_APPSTARTING)
IDC_ARROW := cstring(_IDC_ARROW)
IDC_CROSS := cstring(_IDC_CROSS)
IDC_HAND := cstring(_IDC_HAND)
IDC_HELP := cstring(_IDC_HELP)
IDC_IBEAM := cstring(_IDC_IBEAM)
IDC_ICON := cstring(_IDC_ICON)
IDC_NO := cstring(_IDC_NO)
IDC_SIZE := cstring(_IDC_SIZE)
IDC_SIZEALL := cstring(_IDC_SIZEALL)
IDC_SIZENESW := cstring(_IDC_SIZENESW)
IDC_SIZENS := cstring(_IDC_SIZENS)
IDC_SIZENWSE := cstring(_IDC_SIZENWSE)
IDC_SIZEWE := cstring(_IDC_SIZEWE)
IDC_UPARROW := cstring(_IDC_UPARROW)
IDC_WAIT := cstring(_IDC_WAIT)
_IDI_APPLICATION := rawptr(uintptr(32512))
_IDI_HAND := rawptr(uintptr(32513))
_IDI_QUESTION := rawptr(uintptr(32514))
_IDI_EXCLAMATION := rawptr(uintptr(32515))
_IDI_ASTERISK := rawptr(uintptr(32516))
_IDI_WINLOGO := rawptr(uintptr(32517))
_IDI_SHIELD := rawptr(uintptr(32518))
IDI_APPLICATION := cstring(_IDI_APPLICATION)
IDI_HAND := cstring(_IDI_HAND)
IDI_QUESTION := cstring(_IDI_QUESTION)
IDI_EXCLAMATION := cstring(_IDI_EXCLAMATION)
IDI_ASTERISK := cstring(_IDI_ASTERISK)
IDI_WINLOGO := cstring(_IDI_WINLOGO)
IDI_SHIELD := cstring(_IDI_SHIELD)
IDI_WARNING := IDI_EXCLAMATION
IDI_ERROR := IDI_HAND
IDI_INFORMATION := IDI_ASTERISK
// DIB color table identifiers
DIB_RGB_COLORS :: 0
DIB_PAL_COLORS :: 1
// constants for CreateDIBitmap
CBM_INIT :: 0x04 // initialize bitmap
// Region Flags
ERROR :: 0
NULLREGION :: 1
SIMPLEREGION :: 2
COMPLEXREGION :: 3
RGN_ERROR :: ERROR
// StretchBlt() Modes
BLACKONWHITE :: 1
WHITEONBLACK :: 2
COLORONCOLOR :: 3
HALFTONE :: 4
MAXSTRETCHBLTMODE :: 4
// Binary raster ops
R2_BLACK :: 1 // 0
R2_NOTMERGEPEN :: 2 // DPon
R2_MASKNOTPEN :: 3 // DPna
R2_NOTCOPYPEN :: 4 // PN
R2_MASKPENNOT :: 5 // PDna
R2_NOT :: 6 // Dn
R2_XORPEN :: 7 // DPx
R2_NOTMASKPEN :: 8 // DPan
R2_MASKPEN :: 9 // DPa
R2_NOTXORPEN :: 10 // DPxn
R2_NOP :: 11 // D
R2_MERGENOTPEN :: 12 // DPno
R2_COPYPEN :: 13 // P
R2_MERGEPENNOT :: 14 // PDno
R2_MERGEPEN :: 15 // DPo
R2_WHITE :: 16 // 1
R2_LAST :: 16
// Ternary raster operations
SRCCOPY : DWORD : 0x00CC0020 // dest = source
SRCPAINT : DWORD : 0x00EE0086 // dest = source OR dest
SRCAND : DWORD : 0x008800C6 // dest = source AND dest
SRCINVERT : DWORD : 0x00660046 // dest = source XOR dest
SRCERASE : DWORD : 0x00440328 // dest = source AND (NOT dest)
NOTSRCCOPY : DWORD : 0x00330008 // dest = (NOT source)
NOTSRCERASE : DWORD : 0x001100A6 // dest = (NOT src) AND (NOT dest)
MERGECOPY : DWORD : 0x00C000CA // dest = (source AND pattern
MERGEPAINT : DWORD : 0x00BB0226 // dest = (NOT source) OR dest
PATCOPY : DWORD : 0x00F00021 // dest = pattern
PATPAINT : DWORD : 0x00FB0A09 // dest = DPSnoo
PATINVERT : DWORD : 0x005A0049 // dest = pattern XOR dest
DSTINVERT : DWORD : 0x00550009 // dest = (NOT dest)
BLACKNESS : DWORD : 0x00000042 // dest = BLACK
WHITENESS : DWORD : 0x00FF0062 // dest = WHITE
NOMIRRORBITMAP : DWORD : 0x80000000 // Do not Mirror the bitmap in this call
CAPTUREBLT : DWORD : 0x40000000 // Include layered windows
// Stock Logical Objects
WHITE_BRUSH :: 0
LTGRAY_BRUSH :: 1
GRAY_BRUSH :: 2
DKGRAY_BRUSH :: 3
BLACK_BRUSH :: 4
NULL_BRUSH :: 5
HOLLOW_BRUSH :: NULL_BRUSH
WHITE_PEN :: 6
BLACK_PEN :: 7
NULL_PEN :: 8
OEM_FIXED_FONT :: 10
ANSI_FIXED_FONT :: 11
ANSI_VAR_FONT :: 12
SYSTEM_FONT :: 13
DEVICE_DEFAULT_FONT :: 14
DEFAULT_PALETTE :: 15
SYSTEM_FIXED_FONT :: 16
DEFAULT_GUI_FONT :: 17
DC_BRUSH :: 18
DC_PEN :: 19
STOCK_LAST :: 19
CLR_INVALID :: 0xFFFFFFFF
RGBQUAD :: struct {
rgbBlue: BYTE,
rgbGreen: BYTE,
rgbRed: BYTE,
rgbReserved: BYTE,
}
PIXELFORMATDESCRIPTOR :: struct {
nSize: WORD,
nVersion: WORD,
dwFlags: DWORD,
iPixelType: BYTE,
cColorBits: BYTE,
cRedBits: BYTE,
cRedShift: BYTE,
cGreenBits: BYTE,
cGreenShift: BYTE,
cBlueBits: BYTE,
cBlueShift: BYTE,
cAlphaBits: BYTE,
cAlphaShift: BYTE,
cAccumBits: BYTE,
cAccumRedBits: BYTE,
cAccumGreenBits: BYTE,
cAccumBlueBits: BYTE,
cAccumAlphaBits: BYTE,
cDepthBits: BYTE,
cStencilBits: BYTE,
cAuxBuffers: BYTE,
iLayerType: BYTE,
bReserved: BYTE,
dwLayerMask: DWORD,
dwVisibleMask: DWORD,
dwDamageMask: DWORD,
}
BITMAPINFOHEADER :: struct {
biSize: DWORD,
biWidth: LONG,
biHeight: LONG,
biPlanes: WORD,
biBitCount: WORD,
biCompression: DWORD,
biSizeImage: DWORD,
biXPelsPerMeter: LONG,
biYPelsPerMeter: LONG,
biClrUsed: DWORD,
biClrImportant: DWORD,
}
BITMAPINFO :: struct {
bmiHeader: BITMAPINFOHEADER,
bmiColors: [1]RGBQUAD,
}
// pixel types
PFD_TYPE_RGBA :: 0
PFD_TYPE_COLORINDEX :: 1
// layer types
PFD_MAIN_PLANE :: 0
PFD_OVERLAY_PLANE :: 1
PFD_UNDERLAY_PLANE :: -1
// PIXELFORMATDESCRIPTOR flags
PFD_DOUBLEBUFFER :: 0x00000001
PFD_STEREO :: 0x00000002
PFD_DRAW_TO_WINDOW :: 0x00000004
PFD_DRAW_TO_BITMAP :: 0x00000008
PFD_SUPPORT_GDI :: 0x00000010
PFD_SUPPORT_OPENGL :: 0x00000020
PFD_GENERIC_FORMAT :: 0x00000040
PFD_NEED_PALETTE :: 0x00000080
PFD_NEED_SYSTEM_PALETTE :: 0x00000100
PFD_SWAP_EXCHANGE :: 0x00000200
PFD_SWAP_COPY :: 0x00000400
PFD_SWAP_LAYER_BUFFERS :: 0x00000800
PFD_GENERIC_ACCELERATED :: 0x00001000
PFD_SUPPORT_DIRECTDRAW :: 0x00002000
PFD_DIRECT3D_ACCELERATED :: 0x00004000
PFD_SUPPORT_COMPOSITION :: 0x00008000
// PIXELFORMATDESCRIPTOR flags for use in ChoosePixelFormat only
PFD_DEPTH_DONTCARE :: 0x20000000
PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000
PFD_STEREO_DONTCARE :: 0x80000000
// constants for the biCompression field
BI_RGB :: 0
BI_RLE8 :: 1
BI_RLE4 :: 2
BI_BITFIELDS :: 3
BI_JPEG :: 4
BI_PNG :: 5
WSA_FLAG_OVERLAPPED: DWORD : 0x01
WSA_FLAG_NO_HANDLE_INHERIT: DWORD : 0x80
@@ -379,6 +1199,15 @@ FILE_TYPE_PIPE :: 0x0003
RECT :: struct {left, top, right, bottom: LONG}
POINT :: struct {x, y: LONG}
WINDOWPOS :: struct {
hwnd: HWND,
hwndInsertAfter: HWND,
x: c_int,
y: c_int,
cx: c_int,
cy: c_int,
flags: UINT,
}
when size_of(uintptr) == 4 {
WSADATA :: struct {
@@ -578,7 +1407,7 @@ PROCESS_INFORMATION :: struct {
}
// FYI: This is STARTUPINFOW, not STARTUPINFOA
STARTUPINFO :: struct #packed {
STARTUPINFO :: struct {
cb: DWORD,
lpReserved: LPWSTR,
lpDesktop: LPWSTR,
@@ -784,17 +1613,17 @@ SYSTEM_INFO :: struct {
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
OSVERSIONINFOEXW :: struct {
dwOSVersionInfoSize: ULONG,
dwMajorVersion: ULONG,
dwMinorVersion: ULONG,
dwBuildNumber: ULONG,
dwPlatformId: ULONG,
szCSDVersion: [128]WCHAR,
wServicePackMajor: USHORT,
wServicePackMinor: USHORT,
wSuiteMask: USHORT,
wProductType: UCHAR,
wReserved: UCHAR,
dwOSVersionInfoSize: ULONG,
dwMajorVersion: ULONG,
dwMinorVersion: ULONG,
dwBuildNumber: ULONG,
dwPlatformId: ULONG,
szCSDVersion: [128]WCHAR,
wServicePackMajor: USHORT,
wServicePackMinor: USHORT,
wSuiteMask: USHORT,
wProductType: UCHAR,
wReserved: UCHAR,
}
// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-quota_limits
@@ -837,24 +1666,24 @@ PROFILEINFOW :: struct {
lpDefaultPath: LPWSTR,
lpServerName: LPWSTR,
lpPolicyPath: LPWSTR,
hProfile: HANDLE,
hProfile: HANDLE,
}
// Used in LookupAccountNameW
SID_NAME_USE :: distinct DWORD
SID_TYPE :: enum SID_NAME_USE {
User = 1,
Group,
Domain,
Alias,
WellKnownGroup,
DeletedAccount,
Invalid,
Unknown,
Computer,
Label,
LogonSession,
User = 1,
Group,
Domain,
Alias,
WellKnownGroup,
DeletedAccount,
Invalid,
Unknown,
Computer,
Label,
LogonSession,
}
SECURITY_MAX_SID_SIZE :: 68
@@ -869,7 +1698,7 @@ SID :: struct #packed {
#assert(size_of(SID) == SECURITY_MAX_SID_SIZE)
SID_IDENTIFIER_AUTHORITY :: struct #packed {
Value: [6]u8,
Value: [6]u8,
}
// For NetAPI32
@@ -901,11 +1730,11 @@ USER_INFO_FLAG :: enum DWORD {
Passwd_Cant_Change = 6, // 1 << 6: 0x0040,
Encrypted_Text_Password_Allowed = 7, // 1 << 7: 0x0080,
Temp_Duplicate_Account = 8, // 1 << 8: 0x0100,
Normal_Account = 9, // 1 << 9: 0x0200,
InterDomain_Trust_Account = 11, // 1 << 11: 0x0800,
Workstation_Trust_Account = 12, // 1 << 12: 0x1000,
Server_Trust_Account = 13, // 1 << 13: 0x2000,
Temp_Duplicate_Account = 8, // 1 << 8: 0x0100,
Normal_Account = 9, // 1 << 9: 0x0200,
InterDomain_Trust_Account = 11, // 1 << 11: 0x0800,
Workstation_Trust_Account = 12, // 1 << 12: 0x1000,
Server_Trust_Account = 13, // 1 << 13: 0x2000,
}
USER_INFO_FLAGS :: distinct bit_set[USER_INFO_FLAG]
@@ -1252,4 +2081,4 @@ SYSTEMTIME :: struct {
minute: WORD,
second: WORD,
milliseconds: WORD,
}
}
+252
View File
@@ -0,0 +1,252 @@
// +build windows
package sys_windows
foreign import user32 "system:User32.lib"
@(default_calling_convention="stdcall")
foreign user32 {
GetClassInfoA :: proc(hInstance: HINSTANCE, lpClassNAme: LPCSTR, lpWndClass: ^WNDCLASSA) -> BOOL ---
GetClassInfoW :: proc(hInstance: HINSTANCE, lpClassNAme: LPCWSTR, lpWndClass: ^WNDCLASSW) -> BOOL ---
GetClassInfoExA :: proc(hInsatnce: HINSTANCE, lpszClass: LPCSTR, lpwcx: ^WNDCLASSEXA) -> BOOL ---
GetClassInfoExW :: proc(hInsatnce: HINSTANCE, lpszClass: LPCWSTR, lpwcx: ^WNDCLASSEXW) -> BOOL ---
GetClassLongA :: proc(hWnd: HWND, nIndex: c_int) -> DWORD ---
GetClassLongW :: proc(hWnd: HWND, nIndex: c_int) -> DWORD ---
SetClassLongA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> DWORD ---
SetClassLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> DWORD ---
GetWindowLongA :: proc(hWnd: HWND, nIndex: c_int) -> LONG ---
GetWindowLongW :: proc(hWnd: HWND, nIndex: c_int) -> LONG ---
SetWindowLongA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> LONG ---
SetWindowLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> LONG ---
GetClassNameA :: proc(hWnd: HWND, lpClassName: LPSTR, nMaxCount: c_int) -> c_int ---
GetClassNameW :: proc(hWnd: HWND, lpClassName: LPWSTR, nMaxCount: c_int) -> c_int ---
RegisterClassA :: proc(lpWndClass: ^WNDCLASSA) -> ATOM ---
RegisterClassW :: proc(lpWndClass: ^WNDCLASSW) -> ATOM ---
RegisterClassExA :: proc(^WNDCLASSEXA) -> ATOM ---
RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
CreateWindowExA :: proc(
dwExStyle: DWORD,
lpClassName: LPCSTR,
lpWindowName: LPCSTR,
dwStyle: DWORD,
X: c_int,
Y: c_int,
nWidth: c_int,
nHeight: c_int,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
lpParam: LPVOID,
) -> HWND ---
CreateWindowExW :: proc(
dwExStyle: DWORD,
lpClassName: LPCWSTR,
lpWindowName: LPCWSTR,
dwStyle: DWORD,
X: c_int,
Y: c_int,
nWidth: c_int,
nHeight: c_int,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
lpParam: LPVOID,
) -> HWND ---
DestroyWindow :: proc(hWnd: HWND) -> BOOL ---
ShowWindow :: proc(hWnd: HWND, nCmdShow: c_int) -> BOOL ---
GetMessageA :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
TranslateMessage :: proc(lpMsg: ^MSG) -> BOOL ---
DispatchMessageA :: proc(lpMsg: ^MSG) -> LRESULT ---
DispatchMessageW :: proc(lpMsg: ^MSG) -> LRESULT ---
PeekMessageA :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL ---
PeekMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL ---
PostMessageA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
PostMessageW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
SendMessageA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
SendMessageW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
PostThreadMessageA :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
PostThreadMessageW :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
PostQuitMessage :: proc(nExitCode: c_int) ---
GetQueueStatus :: proc(flags: UINT) -> DWORD ---
DefWindowProcA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
DefWindowProcW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
FindWindowA :: proc(lpClassName: LPCSTR, lpWindowName: LPCSTR) -> HWND ---
FindWindowW :: proc(lpClassName: LPCWSTR, lpWindowName: LPCWSTR) -> HWND ---
FindWindowExA :: proc(hWndParent: HWND, hWndChildAfter: HWND, lpszClass: LPCSTR, lpszWindow: LPCSTR) -> HWND ---
FindWindowExW :: proc(hWndParent: HWND, hWndChildAfter: HWND, lpszClass: LPCWSTR, lpszWindow: LPCWSTR) -> HWND ---
LoadIconA :: proc(hInstance: HINSTANCE, lpIconName: LPCSTR) -> HICON ---
LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON ---
LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR ---
LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR ---
GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
ClientToScreen :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL ---
SetWindowPos :: proc(
hWnd: HWND,
hWndInsertAfter: HWND,
X: c_int,
Y: c_int,
cx: c_int,
cy: c_int,
uFlags: UINT,
) -> BOOL ---
GetSystemMetrics :: proc(nIndex: c_int) -> c_int ---
AdjustWindowRect :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL) -> BOOL ---
AdjustWindowRectEx :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD) -> BOOL ---
GetWindowDC :: proc(hWnd: HWND) -> HDC ---
GetDC :: proc(hWnd: HWND) -> HDC ---
ReleaseDC :: proc(hWnd: HWND, hDC: HDC) -> c_int ---
GetUpdateRect :: proc(hWnd: HWND, lpRect: LPRECT, bErase: BOOL) -> BOOL ---
ValidateRect :: proc(hWnd: HWND, lpRect: ^RECT) -> BOOL ---
InvalidateRect :: proc(hWnd: HWND, lpRect: ^RECT, bErase: BOOL) -> BOOL ---
BeginPaint :: proc(hWnd: HWND, lpPaint: ^PAINTSTRUCT) -> HDC ---
EndPaint :: proc(hWnd: HWND, lpPaint: ^PAINTSTRUCT) -> BOOL ---
GetCapture :: proc() -> HWND ---
SetCapture :: proc(hWnd: HWND) -> HWND ---
ReleaseCapture :: proc() -> BOOL ---
TrackMouseEvent :: proc(lpEventTrack: LPTRACKMOUSEEVENT) -> BOOL ---
GetKeyState :: proc(nVirtKey: c_int) -> SHORT ---
GetAsyncKeyState :: proc(vKey: c_int) -> SHORT ---
MapVirtualKeyA :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
MapVirtualKeyW :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
SetWindowsHookExA :: proc(idHook: c_int, lpfn: HOOKPROC, hmod: HINSTANCE, dwThreadId: DWORD) -> HHOOK ---
SetWindowsHookExW :: proc(idHook: c_int, lpfn: HOOKPROC, hmod: HINSTANCE, dwThreadId: DWORD) -> HHOOK ---
UnhookWindowsHookEx :: proc(hhk: HHOOK) -> BOOL ---
CallNextHookEx :: proc(hhk: HHOOK, nCode: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
SetTimer :: proc(hWnd: HWND, nIDEvent: UINT_PTR, uElapse: UINT, lpTimerFunc: TIMERPROC) -> UINT_PTR ---
KillTimer :: proc(hWnd: HWND, uIDEvent: UINT_PTR) -> BOOL ---
MessageBoxA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT) -> c_int ---
MessageBoxW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> c_int ---
MessageBoxExA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT, wLanguageId: WORD) -> c_int ---
MessageBoxExW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT, wLanguageId: WORD) -> c_int ---
}
CreateWindowA :: #force_inline proc "stdcall" (
lpClassName: LPCSTR,
lpWindowName: LPCSTR,
dwStyle: DWORD,
X: c_int,
Y: c_int,
nWidth: c_int,
nHeight: c_int,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
lpParam: LPVOID,
) -> HWND {
return CreateWindowExA(
0,
lpClassName,
lpWindowName,
dwStyle,
X,
Y,
nWidth,
nHeight,
hWndParent,
hMenu,
hInstance,
lpParam,
)
}
CreateWindowW :: #force_inline proc "stdcall" (
lpClassName: LPCTSTR,
lpWindowName: LPCTSTR,
dwStyle: DWORD,
X: c_int,
Y: c_int,
nWidth: c_int,
nHeight: c_int,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
lpParam: LPVOID,
) -> HWND {
return CreateWindowExW(
0,
lpClassName,
lpWindowName,
dwStyle,
X,
Y,
nWidth,
nHeight,
hWndParent,
hMenu,
hInstance,
lpParam,
)
}
when ODIN_ARCH == .amd64 {
@(default_calling_convention="stdcall")
foreign user32 {
GetClassLongPtrA :: proc(hWnd: HWND, nIndex: c_int) -> ULONG_PTR ---
GetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> ULONG_PTR ---
SetClassLongPtrA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR ---
SetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR ---
GetWindowLongPtrA :: proc(hWnd: HWND, nIndex: c_int) -> LONG_PTR ---
GetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> LONG_PTR ---
SetWindowLongPtrA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> LONG_PTR ---
SetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> LONG_PTR ---
}
} else when ODIN_ARCH == .i386 {
GetClassLongPtrA :: GetClassLongA
GetClassLongPtrW :: GetClassLongW
SetClassLongPtrA :: SetClassLongA
SetClassLongPtrW :: SetClassLongW
GetWindowLongPtrA :: GetWindowLongA
GetWindowLongPtrW :: GetWindowLongW
SetWindowLongPtrA :: GetWindowLongA
SetWindowLongPtrW :: GetWindowLongW
}
GET_SC_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_int {
return c_int(wParam) & 0xFFF0
}
GET_WHEEL_DELTA_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_short {
return cast(c_short)HIWORD(cast(DWORD)wParam)
}
GET_KEYSTATE_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> WORD {
return LOWORD(cast(DWORD)wParam)
}
GET_NCHITTEST_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_short {
return cast(c_short)LOWORD(cast(DWORD)wParam)
}
GET_XBUTTON_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> WORD {
return HIWORD(cast(DWORD)wParam)
}
+8 -4
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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)
}
+38 -79
View File
@@ -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, &params)
assert(res == 0)
thread.procedure = procedure
if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
free(thread, thread.creation_allocator)
sync.condition_destroy(&thread.start_gate)
sync.mutex_destroy(&thread.start_mutex)
return nil
}
thread.procedure = procedure
return thread
}
_start :: proc(t: ^Thread) {
if intrinsics.atomic_xchg(&t.started, true) {
return
}
sync.condition_signal(&t.start_gate)
sync.guard(&t.mutex)
t.flags += { .Started }
sync.signal(&t.cond)
}
_is_done :: proc(t: ^Thread) -> bool {
return intrinsics.atomic_load(&t.done)
return .Done in intrinsics.atomic_load(&t.flags)
}
_join :: proc(t: ^Thread) {
if unix.pthread_equal(unix.pthread_self(), t.unix_thread) {
return
}
// if unix.pthread_self().x == t.unix_thread.x do return;
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)
}
+30 -13
View File
@@ -3,13 +3,21 @@
package thread
import "core:runtime"
import sync "core:sync/sync2"
import "core:intrinsics"
import "core:sync"
import win32 "core:sys/windows"
Thread_State :: enum u8 {
Started,
Joined,
Done,
}
Thread_Os_Specific :: struct {
win32_thread: win32.HANDLE,
win32_thread_id: win32.DWORD,
done: bool, // see note in `is_done`
mutex: sync.Mutex,
flags: bit_set[Thread_State; u8],
}
_thread_priority_map := [Thread_Priority]i32{
@@ -26,15 +34,16 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
context = t.init_context.? or_else runtime.default_context()
t.id = sync.current_thread_id()
t.procedure(t)
intrinsics.atomic_store(&t.flags, t.flags + {.Done})
if t.init_context == nil {
if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
}
}
sync.atomic_store(&t.done, true)
return 0
}
@@ -61,23 +70,31 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
return thread
}
_start :: proc(thread: ^Thread) {
win32.ResumeThread(thread.win32_thread)
_start :: proc(t: ^Thread) {
sync.guard(&t.mutex)
t.flags += {.Started}
win32.ResumeThread(t.win32_thread)
}
_is_done :: proc(using thread: ^Thread) -> bool {
_is_done :: proc(t: ^Thread) -> bool {
// NOTE(tetra, 2019-10-31): Apparently using wait_for_single_object and
// checking if it didn't time out immediately, is not good enough,
// so we do it this way instead.
return sync.atomic_load(&done)
return .Done in sync.atomic_load(&t.flags)
}
_join :: proc(using thread: ^Thread) {
if win32_thread != win32.INVALID_HANDLE {
win32.WaitForSingleObject(win32_thread, win32.INFINITE)
win32.CloseHandle(win32_thread)
win32_thread = win32.INVALID_HANDLE
_join :: proc(t: ^Thread) {
sync.guard(&t.mutex)
if .Joined in t.flags || t.win32_thread == win32.INVALID_HANDLE {
return
}
win32.WaitForSingleObject(t.win32_thread, win32.INFINITE)
win32.CloseHandle(t.win32_thread)
t.win32_thread = win32.INVALID_HANDLE
t.flags += {.Joined}
}
_join_multiple :: proc(threads: ..^Thread) {
+60 -60
View File
@@ -16,140 +16,140 @@ String :: struct {
}
@(private)
_len :: builtin.len; // helper procedure
_len :: builtin.len // helper procedure
init :: proc(s: ^String, contents: string) -> ^String {
s.contents = contents;
s.byte_pos = 0;
s.rune_pos = 0;
s.contents = contents
s.byte_pos = 0
s.rune_pos = 0
for i in 0..<_len(contents) {
if contents[i] >= utf8.RUNE_SELF {
s.rune_count = utf8.rune_count_in_string(contents);
_, s.width = utf8.decode_rune_in_string(contents);
s.non_ascii = i;
return s;
s.rune_count = utf8.rune_count_in_string(contents)
_, s.width = utf8.decode_rune_in_string(contents)
s.non_ascii = i
return s
}
}
s.rune_count = _len(contents);
s.width = 0;
s.non_ascii = _len(contents);
return s;
s.rune_count = _len(contents)
s.width = 0
s.non_ascii = _len(contents)
return s
}
to_string :: proc(s: ^String) -> string {
return s.contents;
return s.contents
}
len :: proc(s: ^String) -> int {
return s.rune_count;
return s.rune_count
}
is_ascii :: proc(s: ^String) -> bool {
return s.width == 0;
return s.width == 0
}
at :: proc(s: ^String, i: int, loc := #caller_location) -> (r: rune) {
runtime.bounds_check_error_loc(loc, i, s.rune_count);
runtime.bounds_check_error_loc(loc, i, s.rune_count)
if i < s.non_ascii {
return rune(s.contents[i]);
return rune(s.contents[i])
}
switch i {
case 0:
r, s.width = utf8.decode_rune_in_string(s.contents);
s.rune_pos = 0;
s.byte_pos = 0;
return;
r, s.width = utf8.decode_rune_in_string(s.contents)
s.rune_pos = 0
s.byte_pos = 0
return
case s.rune_count-1:
r, s.width = utf8.decode_rune_in_string(s.contents);
s.rune_pos = i;
s.byte_pos = _len(s.contents) - s.width;
return;
r, s.width = utf8.decode_rune_in_string(s.contents)
s.rune_pos = i
s.byte_pos = _len(s.contents) - s.width
return
case s.rune_pos-1:
r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos]);
s.rune_pos = i;
s.byte_pos -= s.width;
return;
r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos])
s.rune_pos = i
s.byte_pos -= s.width
return
case s.rune_pos+1:
s.rune_pos = i;
s.byte_pos += s.width;
fallthrough;
s.rune_pos = i
s.byte_pos += s.width
fallthrough
case s.rune_pos:
r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
return;
r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:])
return
}
// Linear scan
scan_forward := true;
scan_forward := true
if i < s.rune_pos {
if i < (s.rune_pos-s.non_ascii)/2 {
s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii;
s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii
} else {
scan_forward = false;
scan_forward = false
}
} else if i-s.rune_pos < (s.rune_count-s.rune_pos)/2 {
// scan_forward = true;
// scan_forward = true
} else {
s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count;
scan_forward = false;
s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count
scan_forward = false
}
if scan_forward {
for {
r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:])
if s.rune_pos == i {
return;
return
}
s.rune_pos += 1;
s.byte_pos += s.width;
s.rune_pos += 1
s.byte_pos += s.width
}
} else {
for {
r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos]);
s.rune_pos -= 1;
s.byte_pos -= s.width;
r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos])
s.rune_pos -= 1
s.byte_pos -= s.width
if s.rune_pos == i {
return;
return
}
}
}
}
slice :: proc(s: ^String, i, j: int, loc := #caller_location) -> string {
runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count);
runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count)
if j < s.non_ascii {
return s.contents[i:j];
return s.contents[i:j]
}
if i == j {
return "";
return ""
}
lo, hi: int;
lo, hi: int
if i < s.non_ascii {
lo = i;
lo = i
} else if i == s.rune_count {
lo = _len(s.contents);
lo = _len(s.contents)
} else {
at(s, i, loc);
lo = s.byte_pos;
at(s, i, loc)
lo = s.byte_pos
}
if j == s.rune_count {
hi = _len(s.contents);
hi = _len(s.contents)
} else {
at(s, j, loc);
hi = s.byte_pos;
at(s, j, loc)
hi = s.byte_pos
}
return s.contents[lo:hi];
return s.contents[lo:hi]
}
+2 -2
View File
@@ -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
View File
@@ -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()
-7
View File
@@ -1,7 +0,0 @@
package basic
import "core:fmt"
main :: proc() {
fmt.println("Hellope!");
}
-67
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -4872,25 +4872,16 @@ bool is_expr_constant_zero(Ast *expr) {
return false;
}
CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
ast_node(ce, CallExpr, call);
GB_ASSERT(is_type_proc(proc_type));
proc_type = base_type(proc_type);
TypeProc *pt = &proc_type->Proc;
isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_) {
GB_ASSERT(pt != nullptr);
GB_ASSERT(pt->kind == Type_Proc);
isize param_count = 0;
isize param_count_excluding_defaults = 0;
bool variadic = pt->variadic;
bool vari_expand = (ce->ellipsis.pos.line != 0);
i64 score = 0;
bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
bool variadic = pt->Proc.variadic;
TypeTuple *param_tuple = nullptr;
if (pt->params != nullptr) {
param_tuple = &pt->params->Tuple;
if (pt->Proc.params != nullptr) {
param_tuple = &pt->Proc.params->Tuple;
param_count = param_tuple->variables.count;
if (variadic) {
@@ -4930,6 +4921,31 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
}
}
if (param_count_) *param_count_ = param_count;
return param_count_excluding_defaults;
}
CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
ast_node(ce, CallExpr, call);
GB_ASSERT(is_type_proc(proc_type));
proc_type = base_type(proc_type);
TypeProc *pt = &proc_type->Proc;
isize param_count = 0;
isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, &param_count);
bool variadic = pt->variadic;
bool vari_expand = (ce->ellipsis.pos.line != 0);
i64 score = 0;
bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
TypeTuple *param_tuple = nullptr;
if (pt->params != nullptr) {
param_tuple = &pt->params->Tuple;
}
CallArgumentError err = CallArgumentError_None;
Type *final_proc_type = proc_type;
Entity *gen_entity = nullptr;
@@ -5602,7 +5618,37 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
if (operand->mode == Addressing_ProcGroup) {
check_entity_decl(c, operand->proc_group, nullptr, nullptr);
Array<Entity *> procs = proc_group_entities(c, *operand);
auto procs = proc_group_entities_cloned(c, *operand);
if (procs.count > 1) {
isize max_arg_count = args.count;
for_array(i, args) {
// NOTE(bill): The only thing that may have multiple values
// will be a call expression (assuming `or_return` and `()` will be stripped)
Ast *arg = strip_or_return_expr(args[i]);
if (arg && arg->kind == Ast_CallExpr) {
max_arg_count = ISIZE_MAX;
break;
}
}
for (isize proc_index = 0; proc_index < procs.count; /**/) {
Entity *proc = procs[proc_index];
Type *pt = base_type(proc->type);
if (!(pt != nullptr && is_type_proc(pt))) {
continue;
}
isize param_count = 0;
isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, &param_count);
if (param_count_excluding_defaults > max_arg_count) {
array_unordered_remove(&procs, proc_index);
} else {
proc_index++;
}
}
}
if (procs.count == 1) {
Ast *ident = operand->expr;
@@ -5632,6 +5678,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
return data;
}
Entity **lhs = nullptr;
isize lhs_count = -1;
@@ -8953,6 +9000,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
o->mode = Addressing_Invalid;
o->type = t_invalid;
o->value = {ExactValue_Invalid};
switch (node->kind) {
default:
@@ -9999,8 +10047,11 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
str = write_expr_to_string(str, st->polymorphic_params, shorthand);
str = gb_string_appendc(str, ") ");
}
if (st->no_nil) str = gb_string_appendc(str, "#no_nil ");
if (st->maybe) str = gb_string_appendc(str, "#maybe ");
switch (st->kind) {
case UnionType_maybe: str = gb_string_appendc(str, "#maybe "); break;
case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break;
case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break;
}
if (st->align) {
str = gb_string_appendc(str, "#align ");
str = write_expr_to_string(str, st->align, shorthand);

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