mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-18 20:02:22 -07:00
Merge branch 'master' into extend_win32_api_types
This commit is contained in:
@@ -6,7 +6,7 @@ jobs:
|
||||
name: NetBSD Build, Check, and Test
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PKGSRC_BRANCH: 2024Q2
|
||||
PKGSRC_BRANCH: 2024Q3
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build, Check, and Test
|
||||
|
||||
@@ -38,6 +38,7 @@ jobs:
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
include-hidden-files: true
|
||||
name: windows_artifacts
|
||||
path: dist
|
||||
build_linux:
|
||||
|
||||
@@ -171,14 +171,6 @@ Type_Info_Simd_Vector :: struct {
|
||||
elem_size: int,
|
||||
count: int,
|
||||
}
|
||||
Type_Info_Relative_Pointer :: struct {
|
||||
pointer: ^Type_Info, // ^T
|
||||
base_integer: ^Type_Info,
|
||||
}
|
||||
Type_Info_Relative_Multi_Pointer :: struct {
|
||||
pointer: ^Type_Info, // [^]T
|
||||
base_integer: ^Type_Info,
|
||||
}
|
||||
Type_Info_Matrix :: struct {
|
||||
elem: ^Type_Info,
|
||||
elem_size: int,
|
||||
@@ -241,8 +233,6 @@ Type_Info :: struct {
|
||||
Type_Info_Map,
|
||||
Type_Info_Bit_Set,
|
||||
Type_Info_Simd_Vector,
|
||||
Type_Info_Relative_Pointer,
|
||||
Type_Info_Relative_Multi_Pointer,
|
||||
Type_Info_Matrix,
|
||||
Type_Info_Soa_Pointer,
|
||||
Type_Info_Bit_Field,
|
||||
@@ -275,8 +265,6 @@ Typeid_Kind :: enum u8 {
|
||||
Map,
|
||||
Bit_Set,
|
||||
Simd_Vector,
|
||||
Relative_Pointer,
|
||||
Relative_Multi_Pointer,
|
||||
Matrix,
|
||||
Soa_Pointer,
|
||||
Bit_Field,
|
||||
|
||||
@@ -388,7 +388,7 @@ _make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, ali
|
||||
//
|
||||
// Note: Prefer using the procedure group `make`.
|
||||
@(builtin, require_results)
|
||||
make_map :: proc($T: typeid/map[$K]$E, allocator := context.allocator) -> (m: T) {
|
||||
make_map :: proc($T: typeid/map[$K]$E, allocator := context.allocator, loc := #caller_location) -> (m: T) {
|
||||
m.allocator = allocator
|
||||
return m
|
||||
}
|
||||
@@ -399,7 +399,7 @@ make_map :: proc($T: typeid/map[$K]$E, allocator := context.allocator) -> (m: T)
|
||||
//
|
||||
// Note: Prefer using the procedure group `make`.
|
||||
@(builtin, require_results)
|
||||
make_map_cap :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error {
|
||||
make_map_cap :: proc($T: typeid/map[$K]$E, #any_int capacity: int, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error {
|
||||
make_map_expr_error_loc(loc, capacity)
|
||||
context.allocator = allocator
|
||||
|
||||
@@ -939,19 +939,7 @@ map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location)
|
||||
|
||||
@builtin
|
||||
card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
|
||||
when size_of(S) == 1 {
|
||||
return int(intrinsics.count_ones(transmute(u8)s))
|
||||
} else when size_of(S) == 2 {
|
||||
return int(intrinsics.count_ones(transmute(u16)s))
|
||||
} else when size_of(S) == 4 {
|
||||
return int(intrinsics.count_ones(transmute(u32)s))
|
||||
} else when size_of(S) == 8 {
|
||||
return int(intrinsics.count_ones(transmute(u64)s))
|
||||
} else when size_of(S) == 16 {
|
||||
return int(intrinsics.count_ones(transmute(u128)s))
|
||||
} else {
|
||||
#panic("Unhandled card bit_set size")
|
||||
}
|
||||
return int(intrinsics.count_ones(transmute(intrinsics.type_bit_set_underlying_type(S))s))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -142,6 +142,7 @@ make_soa_slice :: proc($T: typeid/#soa[]$E, #any_int length: int, allocator := c
|
||||
@(builtin, require_results)
|
||||
make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
context.allocator = allocator
|
||||
array.allocator = allocator
|
||||
reserve_soa(&array, 0, loc) or_return
|
||||
return array, nil
|
||||
}
|
||||
@@ -149,6 +150,7 @@ make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.
|
||||
@(builtin, require_results)
|
||||
make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
|
||||
context.allocator = allocator
|
||||
array.allocator = allocator
|
||||
resize_soa(&array, length, loc) or_return
|
||||
return array, nil
|
||||
}
|
||||
|
||||
@@ -486,18 +486,6 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
|
||||
print_u64(u64(info.count))
|
||||
print_byte(']')
|
||||
print_type(info.elem)
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
print_string("#relative(")
|
||||
print_type(info.base_integer)
|
||||
print_string(") ")
|
||||
print_type(info.pointer)
|
||||
|
||||
case Type_Info_Relative_Multi_Pointer:
|
||||
print_string("#relative(")
|
||||
print_type(info.base_integer)
|
||||
print_string(") ")
|
||||
print_type(info.pointer)
|
||||
|
||||
case Type_Info_Matrix:
|
||||
print_string("matrix[")
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
Copyright (c) 2024 Epic Games Tools
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
Binary file not shown.
@@ -19,7 +19,11 @@ if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
|
||||
)
|
||||
)
|
||||
|
||||
for /f %%i in ('powershell get-date -format "{yyyyMMdd}"') do (
|
||||
pushd misc
|
||||
cl /nologo get-date.c
|
||||
popd
|
||||
|
||||
for /f %%i in ('misc\get-date') do (
|
||||
set CURR_DATE_TIME=%%i
|
||||
)
|
||||
set curr_year=%CURR_DATE_TIME:~0,4%
|
||||
@@ -58,7 +62,6 @@ set V4=0
|
||||
set odin_version_full="%V1%.%V2%.%V3%.%V4%"
|
||||
set odin_version_raw="dev-%V1%-%V2%"
|
||||
|
||||
|
||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
||||
rem Parse source code as utf-8 even on shift-jis and other codepages
|
||||
rem See https://learn.microsoft.com/en-us/cpp/build/reference/utf-8-set-source-and-executable-character-sets-to-utf-8?view=msvc-170
|
||||
|
||||
+17
-10
@@ -25,7 +25,8 @@ error() {
|
||||
|
||||
# Brew advises people not to add llvm to their $PATH, so try and use brew to find it.
|
||||
if [ -z "$LLVM_CONFIG" ] && [ -n "$(command -v brew)" ]; then
|
||||
if [ -n "$(command -v $(brew --prefix llvm@18)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@18)/bin/llvm-config"
|
||||
if [ -n "$(command -v $(brew --prefix llvm@19)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@19)/bin/llvm-config"
|
||||
elif [ -n "$(command -v $(brew --prefix llvm@18)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@18)/bin/llvm-config"
|
||||
elif [ -n "$(command -v $(brew --prefix llvm@17)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@17)/bin/llvm-config"
|
||||
elif [ -n "$(command -v $(brew --prefix llvm@14)/bin/llvm-config)" ]; then LLVM_CONFIG="$(brew --prefix llvm@14)/bin/llvm-config"
|
||||
fi
|
||||
@@ -33,13 +34,15 @@ fi
|
||||
|
||||
if [ -z "$LLVM_CONFIG" ]; then
|
||||
# darwin, linux, openbsd
|
||||
if [ -n "$(command -v llvm-config-18)" ]; then LLVM_CONFIG="llvm-config-18"
|
||||
if [ -n "$(command -v llvm-config-19)" ]; then LLVM_CONFIG="llvm-config-19"
|
||||
elif [ -n "$(command -v llvm-config-18)" ]; then LLVM_CONFIG="llvm-config-18"
|
||||
elif [ -n "$(command -v llvm-config-17)" ]; then LLVM_CONFIG="llvm-config-17"
|
||||
elif [ -n "$(command -v llvm-config-14)" ]; then LLVM_CONFIG="llvm-config-14"
|
||||
elif [ -n "$(command -v llvm-config-13)" ]; then LLVM_CONFIG="llvm-config-13"
|
||||
elif [ -n "$(command -v llvm-config-12)" ]; then LLVM_CONFIG="llvm-config-12"
|
||||
elif [ -n "$(command -v llvm-config-11)" ]; then LLVM_CONFIG="llvm-config-11"
|
||||
# freebsd
|
||||
elif [ -n "$(command -v llvm-config19)" ]; then LLVM_CONFIG="llvm-config19"
|
||||
elif [ -n "$(command -v llvm-config18)" ]; then LLVM_CONFIG="llvm-config18"
|
||||
elif [ -n "$(command -v llvm-config17)" ]; then LLVM_CONFIG="llvm-config17"
|
||||
elif [ -n "$(command -v llvm-config14)" ]; then LLVM_CONFIG="llvm-config14"
|
||||
@@ -66,15 +69,15 @@ LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
|
||||
LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
|
||||
LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"
|
||||
|
||||
if [ $LLVM_VERSION_MAJOR -lt 11 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 18 ]; then
|
||||
error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14, 17 or 18"
|
||||
if [ $LLVM_VERSION_MAJOR -lt 11 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 19 ]; then
|
||||
error "Invalid LLVM version $LLVM_VERSION: must be 11, 12, 13, 14, 17, 18 or 19"
|
||||
fi
|
||||
|
||||
case "$OS_NAME" in
|
||||
Darwin)
|
||||
if [ "$OS_ARCH" = "arm64" ]; then
|
||||
if [ $LLVM_VERSION_MAJOR -lt 13 ]; then
|
||||
error "Invalid LLVM version $LLVM_VERSION: Darwin Arm64 requires LLVM 13, 14, 17 or 18"
|
||||
error "Invalid LLVM version $LLVM_VERSION: Darwin Arm64 requires LLVM 13, 14, 17, 18 or 19"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -152,9 +155,7 @@ build_odin() {
|
||||
}
|
||||
|
||||
run_demo() {
|
||||
if [ $# -eq 0 ] || [ "$1" = "debug" ]; then
|
||||
./odin run examples/demo -vet -strict-style -- Hellope World
|
||||
fi
|
||||
./odin run examples/demo -vet -strict-style -- Hellope World
|
||||
}
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
@@ -166,14 +167,20 @@ if [ $# -eq 0 ]; then
|
||||
elif [ $# -eq 1 ]; then
|
||||
case $1 in
|
||||
report)
|
||||
[ ! -f "./odin" ] && build_odin debug
|
||||
if [ ! -f "./odin" ]; then
|
||||
build_odin debug
|
||||
run_demo
|
||||
fi
|
||||
./odin report
|
||||
;;
|
||||
debug)
|
||||
build_odin debug
|
||||
run_demo
|
||||
;;
|
||||
*)
|
||||
build_odin $1
|
||||
;;
|
||||
esac
|
||||
run_demo
|
||||
else
|
||||
error "Too many arguments!"
|
||||
fi
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
package c_frontend_preprocess
|
||||
|
||||
import "core:c/frontend/tokenizer"
|
||||
|
||||
const_expr :: proc(rest: ^^Token, tok: ^Token) -> i64 {
|
||||
// TODO(bill): Handle const_expr correctly
|
||||
// This is effectively a mini-parser
|
||||
|
||||
assert(rest != nil)
|
||||
assert(tok != nil)
|
||||
rest^ = tokenizer.new_eof(tok)
|
||||
switch v in tok.val {
|
||||
case i64:
|
||||
return v
|
||||
case f64:
|
||||
return i64(v)
|
||||
case string:
|
||||
return 0
|
||||
case []u16:
|
||||
// TODO
|
||||
case []u32:
|
||||
// TODO
|
||||
}
|
||||
return 0
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,154 +0,0 @@
|
||||
package c_frontend_preprocess
|
||||
|
||||
import "core:unicode/utf8"
|
||||
|
||||
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
|
||||
hex_to_int :: proc(c: byte) -> int {
|
||||
switch c {
|
||||
case '0'..='9': return int(c-'0')
|
||||
case 'a'..='f': return int(c-'a')+10
|
||||
case 'A'..='F': return int(c-'A')+10
|
||||
}
|
||||
return -1
|
||||
}
|
||||
w: int
|
||||
|
||||
if str[0] == quote && quote == '"' {
|
||||
return
|
||||
} else if str[0] >= 0x80 {
|
||||
r, w = utf8.decode_rune_in_string(str)
|
||||
return r, true, str[w:], true
|
||||
} else if str[0] != '\\' {
|
||||
return rune(str[0]), false, str[1:], true
|
||||
}
|
||||
|
||||
if len(str) <= 1 {
|
||||
return
|
||||
}
|
||||
s := str
|
||||
c := s[1]
|
||||
s = s[2:]
|
||||
|
||||
switch c {
|
||||
case: r = rune(c)
|
||||
|
||||
case 'a': r = '\a'
|
||||
case 'b': r = '\b'
|
||||
case 'e': r = '\e'
|
||||
case 'f': r = '\f'
|
||||
case 'n': r = '\n'
|
||||
case 'r': r = '\r'
|
||||
case 't': r = '\t'
|
||||
case 'v': r = '\v'
|
||||
case '\\': r = '\\'
|
||||
|
||||
case '"': r = '"'
|
||||
case '\'': r = '\''
|
||||
|
||||
case '0'..='7':
|
||||
v := int(c-'0')
|
||||
if len(s) < 2 {
|
||||
return
|
||||
}
|
||||
for i in 0..<len(s) {
|
||||
d := int(s[i]-'0')
|
||||
if d < 0 || d > 7 {
|
||||
return
|
||||
}
|
||||
v = (v<<3) | d
|
||||
}
|
||||
s = s[2:]
|
||||
if v > 0xff {
|
||||
return
|
||||
}
|
||||
r = rune(v)
|
||||
|
||||
case 'x', 'u', 'U':
|
||||
count: int
|
||||
switch c {
|
||||
case 'x': count = 2
|
||||
case 'u': count = 4
|
||||
case 'U': count = 8
|
||||
}
|
||||
|
||||
if len(s) < count {
|
||||
return
|
||||
}
|
||||
|
||||
for i in 0..<count {
|
||||
d := hex_to_int(s[i])
|
||||
if d < 0 {
|
||||
return
|
||||
}
|
||||
r = (r<<4) | rune(d)
|
||||
}
|
||||
s = s[count:]
|
||||
if c == 'x' {
|
||||
break
|
||||
}
|
||||
if r > utf8.MAX_RUNE {
|
||||
return
|
||||
}
|
||||
multiple_bytes = true
|
||||
}
|
||||
|
||||
success = true
|
||||
tail_string = s
|
||||
return
|
||||
}
|
||||
|
||||
unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: string, allocated, success: bool) {
|
||||
contains_rune :: proc(s: string, r: rune) -> int {
|
||||
for c, offset in s {
|
||||
if c == r {
|
||||
return offset
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
assert(len(lit) >= 2)
|
||||
|
||||
s := lit
|
||||
quote := '"'
|
||||
|
||||
if s == `""` {
|
||||
return "", false, true
|
||||
}
|
||||
|
||||
if contains_rune(s, '\n') >= 0 {
|
||||
return s, false, false
|
||||
}
|
||||
|
||||
if contains_rune(s, '\\') < 0 && contains_rune(s, quote) < 0 {
|
||||
if quote == '"' {
|
||||
return s, false, true
|
||||
}
|
||||
}
|
||||
s = s[1:len(s)-1]
|
||||
|
||||
|
||||
buf_len := 3*len(s) / 2
|
||||
buf := make([]byte, buf_len, allocator)
|
||||
offset := 0
|
||||
for len(s) > 0 {
|
||||
r, multiple_bytes, tail_string, ok := unquote_char(s, byte(quote))
|
||||
if !ok {
|
||||
delete(buf)
|
||||
return s, false, false
|
||||
}
|
||||
s = tail_string
|
||||
if r < 0x80 || !multiple_bytes {
|
||||
buf[offset] = byte(r)
|
||||
offset += 1
|
||||
} else {
|
||||
b, w := utf8.encode_rune(r)
|
||||
copy(buf[offset:], b[:w])
|
||||
offset += w
|
||||
}
|
||||
}
|
||||
|
||||
new_string := string(buf[:offset])
|
||||
|
||||
return new_string, true, true
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
Example:
|
||||
package demo
|
||||
|
||||
import tokenizer "core:c/frontend/tokenizer"
|
||||
import preprocessor "core:c/frontend/preprocessor"
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
t := &tokenizer.Tokenizer{};
|
||||
tokenizer.init_defaults(t);
|
||||
|
||||
cpp := &preprocessor.Preprocessor{};
|
||||
cpp.warn, cpp.err = t.warn, t.err;
|
||||
preprocessor.init_lookup_tables(cpp);
|
||||
preprocessor.init_default_macros(cpp);
|
||||
cpp.include_paths = {"my/path/to/include"};
|
||||
|
||||
tok := tokenizer.tokenize_file(t, "the/source/file.c", 1);
|
||||
|
||||
tok = preprocessor.preprocess(cpp, tok);
|
||||
if tok != nil {
|
||||
for t := tok; t.kind != .EOF; t = t.next {
|
||||
fmt.println(t.lit);
|
||||
}
|
||||
}
|
||||
|
||||
fmt.println("[Done]");
|
||||
}
|
||||
*/
|
||||
package c_frontend_tokenizer
|
||||
@@ -1,68 +0,0 @@
|
||||
package c_frontend_tokenizer
|
||||
|
||||
// NOTE(bill): This is a really dumb approach for a hide set,
|
||||
// but it's really simple and probably fast enough in practice
|
||||
|
||||
|
||||
Hide_Set :: struct {
|
||||
next: ^Hide_Set,
|
||||
name: string,
|
||||
}
|
||||
|
||||
|
||||
new_hide_set :: proc(name: string) -> ^Hide_Set {
|
||||
hs := new(Hide_Set)
|
||||
hs.name = name
|
||||
return hs
|
||||
}
|
||||
|
||||
hide_set_contains :: proc(hs: ^Hide_Set, name: string) -> bool {
|
||||
for h := hs; h != nil; h = h.next {
|
||||
if h.name == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
hide_set_union :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
|
||||
head: Hide_Set
|
||||
curr := &head
|
||||
|
||||
for h := a; h != nil; h = h.next {
|
||||
curr.next = new_hide_set(h.name)
|
||||
curr = curr.next
|
||||
}
|
||||
curr.next = b
|
||||
return head.next
|
||||
}
|
||||
|
||||
|
||||
hide_set_intersection :: proc(a, b: ^Hide_Set) -> ^Hide_Set {
|
||||
head: Hide_Set
|
||||
curr := &head
|
||||
|
||||
for h := a; h != nil; h = h.next {
|
||||
if hide_set_contains(b, h.name) {
|
||||
curr.next = new_hide_set(h.name)
|
||||
curr = curr.next
|
||||
}
|
||||
}
|
||||
return head.next
|
||||
}
|
||||
|
||||
|
||||
add_hide_set :: proc(tok: ^Token, hs: ^Hide_Set) -> ^Token {
|
||||
head: Token
|
||||
curr := &head
|
||||
|
||||
tok := tok
|
||||
for ; tok != nil; tok = tok.next {
|
||||
t := copy_token(tok)
|
||||
t.hide_set = hide_set_union(t.hide_set, hs)
|
||||
curr.next = t
|
||||
curr = curr.next
|
||||
}
|
||||
return head.next
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
package c_frontend_tokenizer
|
||||
|
||||
|
||||
Pos :: struct {
|
||||
file: string,
|
||||
line: int,
|
||||
column: int,
|
||||
offset: int,
|
||||
}
|
||||
|
||||
Token_Kind :: enum {
|
||||
Invalid,
|
||||
Ident,
|
||||
Punct,
|
||||
Keyword,
|
||||
Char,
|
||||
String,
|
||||
Number,
|
||||
PP_Number,
|
||||
Comment,
|
||||
EOF,
|
||||
}
|
||||
|
||||
File :: struct {
|
||||
name: string,
|
||||
id: int,
|
||||
src: []byte,
|
||||
|
||||
display_name: string,
|
||||
line_delta: int,
|
||||
}
|
||||
|
||||
|
||||
Token_Type_Hint :: enum u8 {
|
||||
None,
|
||||
|
||||
Int,
|
||||
Long,
|
||||
Long_Long,
|
||||
|
||||
Unsigned_Int,
|
||||
Unsigned_Long,
|
||||
Unsigned_Long_Long,
|
||||
|
||||
Float,
|
||||
Double,
|
||||
Long_Double,
|
||||
|
||||
UTF_8,
|
||||
UTF_16,
|
||||
UTF_32,
|
||||
UTF_Wide,
|
||||
}
|
||||
|
||||
Token_Value :: union {
|
||||
i64,
|
||||
f64,
|
||||
string,
|
||||
[]u16,
|
||||
[]u32,
|
||||
}
|
||||
|
||||
Token :: struct {
|
||||
kind: Token_Kind,
|
||||
next: ^Token,
|
||||
lit: string,
|
||||
|
||||
pos: Pos,
|
||||
file: ^File,
|
||||
line_delta: int,
|
||||
at_bol: bool,
|
||||
has_space: bool,
|
||||
|
||||
type_hint: Token_Type_Hint,
|
||||
val: Token_Value,
|
||||
prefix: string,
|
||||
|
||||
// Preprocessor values
|
||||
hide_set: ^Hide_Set,
|
||||
origin: ^Token,
|
||||
}
|
||||
|
||||
Is_Keyword_Proc :: #type proc(tok: ^Token) -> bool
|
||||
|
||||
copy_token :: proc(tok: ^Token) -> ^Token {
|
||||
t, _ := new_clone(tok^)
|
||||
t.next = nil
|
||||
return t
|
||||
}
|
||||
|
||||
new_eof :: proc(tok: ^Token) -> ^Token {
|
||||
t, _ := new_clone(tok^)
|
||||
t.kind = .EOF
|
||||
t.lit = ""
|
||||
return t
|
||||
}
|
||||
|
||||
default_is_keyword :: proc(tok: ^Token) -> bool {
|
||||
if tok.kind == .Keyword {
|
||||
return true
|
||||
}
|
||||
if len(tok.lit) > 0 {
|
||||
return default_keyword_set[tok.lit]
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
token_name := [Token_Kind]string {
|
||||
.Invalid = "invalid",
|
||||
.Ident = "ident",
|
||||
.Punct = "punct",
|
||||
.Keyword = "keyword",
|
||||
.Char = "char",
|
||||
.String = "string",
|
||||
.Number = "number",
|
||||
.PP_Number = "preprocessor number",
|
||||
.Comment = "comment",
|
||||
.EOF = "eof",
|
||||
}
|
||||
|
||||
default_keyword_set := map[string]bool{
|
||||
"auto" = true,
|
||||
"break" = true,
|
||||
"case" = true,
|
||||
"char" = true,
|
||||
"const" = true,
|
||||
"continue" = true,
|
||||
"default" = true,
|
||||
"do" = true,
|
||||
"double" = true,
|
||||
"else" = true,
|
||||
"enum" = true,
|
||||
"extern" = true,
|
||||
"float" = true,
|
||||
"for" = true,
|
||||
"goto" = true,
|
||||
"if" = true,
|
||||
"int" = true,
|
||||
"long" = true,
|
||||
"register" = true,
|
||||
"restrict" = true,
|
||||
"return" = true,
|
||||
"short" = true,
|
||||
"signed" = true,
|
||||
"sizeof" = true,
|
||||
"static" = true,
|
||||
"struct" = true,
|
||||
"switch" = true,
|
||||
"typedef" = true,
|
||||
"union" = true,
|
||||
"unsigned" = true,
|
||||
"void" = true,
|
||||
"volatile" = true,
|
||||
"while" = true,
|
||||
"_Alignas" = true,
|
||||
"_Alignof" = true,
|
||||
"_Atomic" = true,
|
||||
"_Bool" = true,
|
||||
"_Generic" = true,
|
||||
"_Noreturn" = true,
|
||||
"_Thread_local" = true,
|
||||
"__restrict" = true,
|
||||
"typeof" = true,
|
||||
"asm" = true,
|
||||
"__restrict__" = true,
|
||||
"__thread" = true,
|
||||
"__attribute__" = true,
|
||||
}
|
||||
@@ -1,667 +0,0 @@
|
||||
package c_frontend_tokenizer
|
||||
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
|
||||
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any)
|
||||
|
||||
|
||||
Tokenizer :: struct {
|
||||
// Immutable data
|
||||
path: string,
|
||||
src: []byte,
|
||||
|
||||
|
||||
// Tokenizing state
|
||||
ch: rune,
|
||||
offset: int,
|
||||
read_offset: int,
|
||||
line_offset: int,
|
||||
line_count: int,
|
||||
|
||||
// Extra information for tokens
|
||||
at_bol: bool,
|
||||
has_space: bool,
|
||||
|
||||
// Mutable data
|
||||
err: Error_Handler,
|
||||
warn: Error_Handler,
|
||||
error_count: int,
|
||||
warning_count: int,
|
||||
}
|
||||
|
||||
init_defaults :: proc(t: ^Tokenizer, err: Error_Handler = default_error_handler, warn: Error_Handler = default_warn_handler) {
|
||||
t.err = err
|
||||
t.warn = warn
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> (pos: Pos) {
|
||||
pos.file = t.path
|
||||
pos.offset = offset
|
||||
pos.line = t.line_count
|
||||
pos.column = offset - t.line_offset + 1
|
||||
return
|
||||
}
|
||||
|
||||
default_error_handler :: proc(pos: Pos, msg: string, args: ..any) {
|
||||
fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column)
|
||||
fmt.eprintf(msg, ..args)
|
||||
fmt.eprintf("\n")
|
||||
}
|
||||
|
||||
default_warn_handler :: proc(pos: Pos, msg: string, args: ..any) {
|
||||
fmt.eprintf("%s(%d:%d) warning: ", pos.file, pos.line, pos.column)
|
||||
fmt.eprintf(msg, ..args)
|
||||
fmt.eprintf("\n")
|
||||
}
|
||||
|
||||
error_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
||||
pos := offset_to_pos(t, offset)
|
||||
if t.err != nil {
|
||||
t.err(pos, msg, ..args)
|
||||
}
|
||||
t.error_count += 1
|
||||
}
|
||||
|
||||
warn_offset :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
||||
pos := offset_to_pos(t, offset)
|
||||
if t.warn != nil {
|
||||
t.warn(pos, msg, ..args)
|
||||
}
|
||||
t.warning_count += 1
|
||||
}
|
||||
|
||||
error :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
|
||||
pos := tok.pos
|
||||
if t.err != nil {
|
||||
t.err(pos, msg, ..args)
|
||||
}
|
||||
t.error_count += 1
|
||||
}
|
||||
|
||||
warn :: proc(t: ^Tokenizer, tok: ^Token, msg: string, args: ..any) {
|
||||
pos := tok.pos
|
||||
if t.warn != nil {
|
||||
t.warn(pos, msg, ..args)
|
||||
}
|
||||
t.warning_count += 1
|
||||
}
|
||||
|
||||
|
||||
advance_rune :: proc(t: ^Tokenizer) {
|
||||
if t.read_offset < len(t.src) {
|
||||
t.offset = t.read_offset
|
||||
if t.ch == '\n' {
|
||||
t.at_bol = true
|
||||
t.line_offset = t.offset
|
||||
t.line_count += 1
|
||||
}
|
||||
r, w := rune(t.src[t.read_offset]), 1
|
||||
switch {
|
||||
case r == 0:
|
||||
error_offset(t, t.offset, "illegal character NUL")
|
||||
case r >= utf8.RUNE_SELF:
|
||||
r, w = utf8.decode_rune(t.src[t.read_offset:])
|
||||
if r == utf8.RUNE_ERROR && w == 1 {
|
||||
error_offset(t, t.offset, "illegal UTF-8 encoding")
|
||||
} else if r == utf8.RUNE_BOM && t.offset > 0 {
|
||||
error_offset(t, t.offset, "illegal byte order mark")
|
||||
}
|
||||
}
|
||||
t.read_offset += w
|
||||
t.ch = r
|
||||
} else {
|
||||
t.offset = len(t.src)
|
||||
if t.ch == '\n' {
|
||||
t.at_bol = true
|
||||
t.line_offset = t.offset
|
||||
t.line_count += 1
|
||||
}
|
||||
t.ch = -1
|
||||
}
|
||||
}
|
||||
|
||||
advance_rune_n :: proc(t: ^Tokenizer, n: int) {
|
||||
for _ in 0..<n {
|
||||
advance_rune(t)
|
||||
}
|
||||
}
|
||||
|
||||
is_digit :: proc(r: rune) -> bool {
|
||||
return '0' <= r && r <= '9'
|
||||
}
|
||||
|
||||
skip_whitespace :: proc(t: ^Tokenizer) {
|
||||
for {
|
||||
switch t.ch {
|
||||
case ' ', '\t', '\r', '\v', '\f', '\n':
|
||||
t.has_space = true
|
||||
advance_rune(t)
|
||||
case:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scan_comment :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset-1
|
||||
next := -1
|
||||
general: {
|
||||
if t.ch == '/'{ // line comments
|
||||
advance_rune(t)
|
||||
for t.ch != '\n' && t.ch >= 0 {
|
||||
advance_rune(t)
|
||||
}
|
||||
|
||||
next = t.offset
|
||||
if t.ch == '\n' {
|
||||
next += 1
|
||||
}
|
||||
break general
|
||||
}
|
||||
|
||||
/* style comment */
|
||||
advance_rune(t)
|
||||
for t.ch >= 0 {
|
||||
ch := t.ch
|
||||
advance_rune(t)
|
||||
if ch == '*' && t.ch == '/' {
|
||||
advance_rune(t)
|
||||
next = t.offset
|
||||
break general
|
||||
}
|
||||
}
|
||||
|
||||
error_offset(t, offset, "comment not terminated")
|
||||
}
|
||||
|
||||
lit := t.src[offset : t.offset]
|
||||
|
||||
// NOTE(bill): Strip CR for line comments
|
||||
for len(lit) > 2 && lit[1] == '/' && lit[len(lit)-1] == '\r' {
|
||||
lit = lit[:len(lit)-1]
|
||||
}
|
||||
|
||||
|
||||
return string(lit)
|
||||
}
|
||||
|
||||
scan_identifier :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset
|
||||
|
||||
for is_ident1(t.ch) {
|
||||
advance_rune(t)
|
||||
}
|
||||
|
||||
return string(t.src[offset : t.offset])
|
||||
}
|
||||
|
||||
scan_string :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset-1
|
||||
|
||||
for {
|
||||
ch := t.ch
|
||||
if ch == '\n' || ch < 0 {
|
||||
error_offset(t, offset, "string literal was not terminated")
|
||||
break
|
||||
}
|
||||
advance_rune(t)
|
||||
if ch == '"' {
|
||||
break
|
||||
}
|
||||
if ch == '\\' {
|
||||
scan_escape(t)
|
||||
}
|
||||
}
|
||||
|
||||
return string(t.src[offset : t.offset])
|
||||
}
|
||||
|
||||
digit_val :: proc(r: rune) -> int {
|
||||
switch r {
|
||||
case '0'..='9':
|
||||
return int(r-'0')
|
||||
case 'A'..='F':
|
||||
return int(r-'A' + 10)
|
||||
case 'a'..='f':
|
||||
return int(r-'a' + 10)
|
||||
}
|
||||
return 16
|
||||
}
|
||||
|
||||
scan_escape :: proc(t: ^Tokenizer) -> bool {
|
||||
offset := t.offset
|
||||
|
||||
esc := t.ch
|
||||
n: int
|
||||
base, max: u32
|
||||
switch esc {
|
||||
case 'a', 'b', 'e', 'f', 'n', 't', 'v', 'r', '\\', '\'', '"':
|
||||
advance_rune(t)
|
||||
return true
|
||||
|
||||
case '0'..='7':
|
||||
for digit_val(t.ch) < 8 {
|
||||
advance_rune(t)
|
||||
}
|
||||
return true
|
||||
case 'x':
|
||||
advance_rune(t)
|
||||
for digit_val(t.ch) < 16 {
|
||||
advance_rune(t)
|
||||
}
|
||||
return true
|
||||
case 'u':
|
||||
advance_rune(t)
|
||||
n, base, max = 4, 16, utf8.MAX_RUNE
|
||||
case 'U':
|
||||
advance_rune(t)
|
||||
n, base, max = 8, 16, utf8.MAX_RUNE
|
||||
case:
|
||||
if t.ch < 0 {
|
||||
error_offset(t, offset, "escape sequence was not terminated")
|
||||
} else {
|
||||
break
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
x: u32
|
||||
main_loop: for n > 0 {
|
||||
d := u32(digit_val(t.ch))
|
||||
if d >= base {
|
||||
if t.ch == '"' || t.ch == '\'' {
|
||||
break main_loop
|
||||
}
|
||||
if t.ch < 0 {
|
||||
error_offset(t, t.offset, "escape sequence was not terminated")
|
||||
} else {
|
||||
error_offset(t, t.offset, "illegal character '%r' : %d in escape sequence", t.ch, t.ch)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
x = x*base + d
|
||||
advance_rune(t)
|
||||
n -= 1
|
||||
}
|
||||
|
||||
if x > max || 0xd800 <= x && x <= 0xe000 {
|
||||
error_offset(t, offset, "escape sequence is an invalid Unicode code point")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
scan_rune :: proc(t: ^Tokenizer) -> string {
|
||||
offset := t.offset-1
|
||||
valid := true
|
||||
n := 0
|
||||
for {
|
||||
ch := t.ch
|
||||
if ch == '\n' || ch < 0 {
|
||||
if valid {
|
||||
error_offset(t, offset, "rune literal not terminated")
|
||||
valid = false
|
||||
}
|
||||
break
|
||||
}
|
||||
advance_rune(t)
|
||||
if ch == '\'' {
|
||||
break
|
||||
}
|
||||
n += 1
|
||||
if ch == '\\' {
|
||||
if !scan_escape(t) {
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if valid && n != 1 {
|
||||
error_offset(t, offset, "illegal rune literal")
|
||||
}
|
||||
|
||||
return string(t.src[offset : t.offset])
|
||||
}
|
||||
|
||||
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, string) {
|
||||
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
|
||||
for digit_val(t.ch) < base {
|
||||
advance_rune(t)
|
||||
}
|
||||
}
|
||||
scan_exponent :: proc(t: ^Tokenizer) {
|
||||
if t.ch == 'e' || t.ch == 'E' || t.ch == 'p' || t.ch == 'P' {
|
||||
advance_rune(t)
|
||||
if t.ch == '-' || t.ch == '+' {
|
||||
advance_rune(t)
|
||||
}
|
||||
if digit_val(t.ch) < 10 {
|
||||
scan_mantissa(t, 10)
|
||||
} else {
|
||||
error_offset(t, t.offset, "illegal floating-point exponent")
|
||||
}
|
||||
}
|
||||
}
|
||||
scan_fraction :: proc(t: ^Tokenizer) -> (early_exit: bool) {
|
||||
if t.ch == '.' && peek(t) == '.' {
|
||||
return true
|
||||
}
|
||||
if t.ch == '.' {
|
||||
advance_rune(t)
|
||||
scan_mantissa(t, 10)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
check_end := true
|
||||
|
||||
|
||||
offset := t.offset
|
||||
seen_point := seen_decimal_point
|
||||
|
||||
if seen_point {
|
||||
offset -= 1
|
||||
scan_mantissa(t, 10)
|
||||
scan_exponent(t)
|
||||
} else {
|
||||
if t.ch == '0' {
|
||||
int_base :: proc(t: ^Tokenizer, base: int, msg: string) {
|
||||
prev := t.offset
|
||||
advance_rune(t)
|
||||
scan_mantissa(t, base)
|
||||
if t.offset - prev <= 1 {
|
||||
error_offset(t, t.offset, msg)
|
||||
}
|
||||
}
|
||||
|
||||
advance_rune(t)
|
||||
switch t.ch {
|
||||
case 'b', 'B':
|
||||
int_base(t, 2, "illegal binary integer")
|
||||
case 'x', 'X':
|
||||
int_base(t, 16, "illegal hexadecimal integer")
|
||||
case:
|
||||
seen_point = false
|
||||
scan_mantissa(t, 10)
|
||||
if t.ch == '.' {
|
||||
seen_point = true
|
||||
if scan_fraction(t) {
|
||||
check_end = false
|
||||
}
|
||||
}
|
||||
if check_end {
|
||||
scan_exponent(t)
|
||||
check_end = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if check_end {
|
||||
scan_mantissa(t, 10)
|
||||
|
||||
if !scan_fraction(t) {
|
||||
scan_exponent(t)
|
||||
}
|
||||
}
|
||||
|
||||
return .Number, string(t.src[offset : t.offset])
|
||||
}
|
||||
|
||||
scan_punct :: proc(t: ^Tokenizer, ch: rune) -> (kind: Token_Kind) {
|
||||
kind = .Punct
|
||||
switch ch {
|
||||
case:
|
||||
kind = .Invalid
|
||||
|
||||
case '<', '>':
|
||||
if t.ch == ch {
|
||||
advance_rune(t)
|
||||
}
|
||||
if t.ch == '=' {
|
||||
advance_rune(t)
|
||||
}
|
||||
case '!', '+', '-', '*', '/', '%', '^', '=':
|
||||
if t.ch == '=' {
|
||||
advance_rune(t)
|
||||
}
|
||||
case '#':
|
||||
if t.ch == '#' {
|
||||
advance_rune(t)
|
||||
}
|
||||
case '&':
|
||||
if t.ch == '=' || t.ch == '&' {
|
||||
advance_rune(t)
|
||||
}
|
||||
case '|':
|
||||
if t.ch == '=' || t.ch == '|' {
|
||||
advance_rune(t)
|
||||
}
|
||||
case '(', ')', '[', ']', '{', '}':
|
||||
// okay
|
||||
case '~', ',', ':', ';', '?':
|
||||
// okay
|
||||
case '`':
|
||||
// okay
|
||||
case '.':
|
||||
if t.ch == '.' && peek(t) == '.' {
|
||||
advance_rune(t)
|
||||
advance_rune(t) // consume last '.'
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
peek :: proc(t: ^Tokenizer) -> byte {
|
||||
if t.read_offset < len(t.src) {
|
||||
return t.src[t.read_offset]
|
||||
}
|
||||
return 0
|
||||
}
|
||||
peek_str :: proc(t: ^Tokenizer, str: string) -> bool {
|
||||
if t.read_offset < len(t.src) {
|
||||
return strings.has_prefix(string(t.src[t.offset:]), str)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
scan_literal_prefix :: proc(t: ^Tokenizer, str: string, prefix: ^string) -> bool {
|
||||
if peek_str(t, str) {
|
||||
offset := t.offset
|
||||
for _ in str {
|
||||
advance_rune(t)
|
||||
}
|
||||
prefix^ = string(t.src[offset:][:len(str)-1])
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
allow_next_to_be_newline :: proc(t: ^Tokenizer) -> bool {
|
||||
if t.ch == '\n' {
|
||||
advance_rune(t)
|
||||
return true
|
||||
} else if t.ch == '\r' && peek(t) == '\n' { // allow for MS-DOS style line endings
|
||||
advance_rune(t) // \r
|
||||
advance_rune(t) // \n
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
scan :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
|
||||
skip_whitespace(t)
|
||||
|
||||
offset := t.offset
|
||||
|
||||
kind: Token_Kind
|
||||
lit: string
|
||||
prefix: string
|
||||
|
||||
switch ch := t.ch; {
|
||||
case scan_literal_prefix(t, `u8"`, &prefix):
|
||||
kind = .String
|
||||
lit = scan_string(t)
|
||||
case scan_literal_prefix(t, `u"`, &prefix):
|
||||
kind = .String
|
||||
lit = scan_string(t)
|
||||
case scan_literal_prefix(t, `L"`, &prefix):
|
||||
kind = .String
|
||||
lit = scan_string(t)
|
||||
case scan_literal_prefix(t, `U"`, &prefix):
|
||||
kind = .String
|
||||
lit = scan_string(t)
|
||||
case scan_literal_prefix(t, `u'`, &prefix):
|
||||
kind = .Char
|
||||
lit = scan_rune(t)
|
||||
case scan_literal_prefix(t, `L'`, &prefix):
|
||||
kind = .Char
|
||||
lit = scan_rune(t)
|
||||
case scan_literal_prefix(t, `U'`, &prefix):
|
||||
kind = .Char
|
||||
lit = scan_rune(t)
|
||||
|
||||
case is_ident0(ch):
|
||||
lit = scan_identifier(t)
|
||||
kind = .Ident
|
||||
case '0' <= ch && ch <= '9':
|
||||
kind, lit = scan_number(t, false)
|
||||
case:
|
||||
advance_rune(t)
|
||||
switch ch {
|
||||
case -1:
|
||||
kind = .EOF
|
||||
case '\\':
|
||||
kind = .Punct
|
||||
if allow_next_to_be_newline(t) {
|
||||
t.at_bol = true
|
||||
t.has_space = false
|
||||
return scan(t, f)
|
||||
}
|
||||
|
||||
case '.':
|
||||
if is_digit(t.ch) {
|
||||
kind, lit = scan_number(t, true)
|
||||
} else {
|
||||
kind = scan_punct(t, ch)
|
||||
}
|
||||
case '"':
|
||||
kind = .String
|
||||
lit = scan_string(t)
|
||||
case '\'':
|
||||
kind = .Char
|
||||
lit = scan_rune(t)
|
||||
case '/':
|
||||
if t.ch == '/' || t.ch == '*' {
|
||||
kind = .Comment
|
||||
lit = scan_comment(t)
|
||||
t.has_space = true
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case:
|
||||
kind = scan_punct(t, ch)
|
||||
if kind == .Invalid && ch != utf8.RUNE_BOM {
|
||||
error_offset(t, t.offset, "illegal character '%r': %d", ch, ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if lit == "" {
|
||||
lit = string(t.src[offset : t.offset])
|
||||
}
|
||||
|
||||
if kind == .Comment {
|
||||
return scan(t, f)
|
||||
}
|
||||
|
||||
tok := new(Token)
|
||||
tok.kind = kind
|
||||
tok.lit = lit
|
||||
tok.pos = offset_to_pos(t, offset)
|
||||
tok.file = f
|
||||
tok.prefix = prefix
|
||||
tok.at_bol = t.at_bol
|
||||
tok.has_space = t.has_space
|
||||
|
||||
t.at_bol, t.has_space = false, false
|
||||
|
||||
return tok
|
||||
}
|
||||
|
||||
tokenize :: proc(t: ^Tokenizer, f: ^File) -> ^Token {
|
||||
setup_tokenizer: {
|
||||
t.src = f.src
|
||||
t.ch = ' '
|
||||
t.offset = 0
|
||||
t.read_offset = 0
|
||||
t.line_offset = 0
|
||||
t.line_count = len(t.src) > 0 ? 1 : 0
|
||||
t.error_count = 0
|
||||
t.path = f.name
|
||||
|
||||
|
||||
advance_rune(t)
|
||||
if t.ch == utf8.RUNE_BOM {
|
||||
advance_rune(t)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
t.at_bol = true
|
||||
t.has_space = false
|
||||
|
||||
head: Token
|
||||
curr := &head
|
||||
for {
|
||||
tok := scan(t, f)
|
||||
if tok == nil {
|
||||
break
|
||||
}
|
||||
curr.next = tok
|
||||
curr = curr.next
|
||||
if tok.kind == .EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return head.next
|
||||
}
|
||||
|
||||
add_new_file :: proc(t: ^Tokenizer, name: string, src: []byte, id: int) -> ^File {
|
||||
file := new(File)
|
||||
file.id = id
|
||||
file.src = src
|
||||
file.name = name
|
||||
file.display_name = name
|
||||
return file
|
||||
}
|
||||
|
||||
tokenize_file :: proc(t: ^Tokenizer, path: string, id: int, loc := #caller_location) -> ^Token {
|
||||
src, ok := os.read_entire_file(path)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return tokenize(t, add_new_file(t, path, src, id))
|
||||
}
|
||||
|
||||
|
||||
inline_tokenize :: proc(t: ^Tokenizer, tok: ^Token, src: []byte) -> ^Token {
|
||||
file := new(File)
|
||||
file.src = src
|
||||
if tok.file != nil {
|
||||
file.id = tok.file.id
|
||||
file.name = tok.file.name
|
||||
file.display_name = tok.file.name
|
||||
}
|
||||
|
||||
return tokenize(t, file)
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
package c_frontend_tokenizer
|
||||
|
||||
|
||||
in_range :: proc(range: []rune, c: rune) -> bool #no_bounds_check {
|
||||
for i := 0; range[i] != -1; i += 2 {
|
||||
if range[i] <= c && c <= range[i+1] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
// [https://www.sigbus.info/n1570#D] C11 allows ASCII and some multibyte characters in certan Unicode ranges to be used in an identifier.
|
||||
//
|
||||
// is_ident0 returns true if a given character is acceptable as the first character of an identifier.
|
||||
is_ident0 :: proc(c: rune) -> bool {
|
||||
return in_range(_range_ident0, c)
|
||||
}
|
||||
// is_ident0 returns true if a given character is acceptable as a non-first character of an identifier.
|
||||
is_ident1 :: proc(c: rune) -> bool {
|
||||
return is_ident0(c) || in_range(_range_ident1, c)
|
||||
}
|
||||
|
||||
// Returns the number of columns needed to display a given character in a fixed-width font.
|
||||
// Based on https://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
|
||||
char_width :: proc(c: rune) -> int {
|
||||
switch {
|
||||
case in_range(_range_width0, c):
|
||||
return 0
|
||||
case in_range(_range_width2, c):
|
||||
return 2
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
display_width :: proc(str: string) -> (w: int) {
|
||||
for c in str {
|
||||
w += char_width(c)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
_range_ident0 := []rune{
|
||||
'_', '_', 'a', 'z', 'A', 'Z', '$', '$',
|
||||
0x00A8, 0x00A8, 0x00AA, 0x00AA, 0x00AD, 0x00AD, 0x00AF, 0x00AF,
|
||||
0x00B2, 0x00B5, 0x00B7, 0x00BA, 0x00BC, 0x00BE, 0x00C0, 0x00D6,
|
||||
0x00D8, 0x00F6, 0x00F8, 0x00FF, 0x0100, 0x02FF, 0x0370, 0x167F,
|
||||
0x1681, 0x180D, 0x180F, 0x1DBF, 0x1E00, 0x1FFF, 0x200B, 0x200D,
|
||||
0x202A, 0x202E, 0x203F, 0x2040, 0x2054, 0x2054, 0x2060, 0x206F,
|
||||
0x2070, 0x20CF, 0x2100, 0x218F, 0x2460, 0x24FF, 0x2776, 0x2793,
|
||||
0x2C00, 0x2DFF, 0x2E80, 0x2FFF, 0x3004, 0x3007, 0x3021, 0x302F,
|
||||
0x3031, 0x303F, 0x3040, 0xD7FF, 0xF900, 0xFD3D, 0xFD40, 0xFDCF,
|
||||
0xFDF0, 0xFE1F, 0xFE30, 0xFE44, 0xFE47, 0xFFFD,
|
||||
0x10000, 0x1FFFD, 0x20000, 0x2FFFD, 0x30000, 0x3FFFD, 0x40000, 0x4FFFD,
|
||||
0x50000, 0x5FFFD, 0x60000, 0x6FFFD, 0x70000, 0x7FFFD, 0x80000, 0x8FFFD,
|
||||
0x90000, 0x9FFFD, 0xA0000, 0xAFFFD, 0xB0000, 0xBFFFD, 0xC0000, 0xCFFFD,
|
||||
0xD0000, 0xDFFFD, 0xE0000, 0xEFFFD,
|
||||
-1,
|
||||
}
|
||||
|
||||
_range_ident1 := []rune{
|
||||
'0', '9', '$', '$', 0x0300, 0x036F, 0x1DC0, 0x1DFF, 0x20D0, 0x20FF, 0xFE20, 0xFE2F,
|
||||
-1,
|
||||
}
|
||||
|
||||
|
||||
_range_width0 := []rune{
|
||||
0x0000, 0x001F, 0x007f, 0x00a0, 0x0300, 0x036F, 0x0483, 0x0486,
|
||||
0x0488, 0x0489, 0x0591, 0x05BD, 0x05BF, 0x05BF, 0x05C1, 0x05C2,
|
||||
0x05C4, 0x05C5, 0x05C7, 0x05C7, 0x0600, 0x0603, 0x0610, 0x0615,
|
||||
0x064B, 0x065E, 0x0670, 0x0670, 0x06D6, 0x06E4, 0x06E7, 0x06E8,
|
||||
0x06EA, 0x06ED, 0x070F, 0x070F, 0x0711, 0x0711, 0x0730, 0x074A,
|
||||
0x07A6, 0x07B0, 0x07EB, 0x07F3, 0x0901, 0x0902, 0x093C, 0x093C,
|
||||
0x0941, 0x0948, 0x094D, 0x094D, 0x0951, 0x0954, 0x0962, 0x0963,
|
||||
0x0981, 0x0981, 0x09BC, 0x09BC, 0x09C1, 0x09C4, 0x09CD, 0x09CD,
|
||||
0x09E2, 0x09E3, 0x0A01, 0x0A02, 0x0A3C, 0x0A3C, 0x0A41, 0x0A42,
|
||||
0x0A47, 0x0A48, 0x0A4B, 0x0A4D, 0x0A70, 0x0A71, 0x0A81, 0x0A82,
|
||||
0x0ABC, 0x0ABC, 0x0AC1, 0x0AC5, 0x0AC7, 0x0AC8, 0x0ACD, 0x0ACD,
|
||||
0x0AE2, 0x0AE3, 0x0B01, 0x0B01, 0x0B3C, 0x0B3C, 0x0B3F, 0x0B3F,
|
||||
0x0B41, 0x0B43, 0x0B4D, 0x0B4D, 0x0B56, 0x0B56, 0x0B82, 0x0B82,
|
||||
0x0BC0, 0x0BC0, 0x0BCD, 0x0BCD, 0x0C3E, 0x0C40, 0x0C46, 0x0C48,
|
||||
0x0C4A, 0x0C4D, 0x0C55, 0x0C56, 0x0CBC, 0x0CBC, 0x0CBF, 0x0CBF,
|
||||
0x0CC6, 0x0CC6, 0x0CCC, 0x0CCD, 0x0CE2, 0x0CE3, 0x0D41, 0x0D43,
|
||||
0x0D4D, 0x0D4D, 0x0DCA, 0x0DCA, 0x0DD2, 0x0DD4, 0x0DD6, 0x0DD6,
|
||||
0x0E31, 0x0E31, 0x0E34, 0x0E3A, 0x0E47, 0x0E4E, 0x0EB1, 0x0EB1,
|
||||
0x0EB4, 0x0EB9, 0x0EBB, 0x0EBC, 0x0EC8, 0x0ECD, 0x0F18, 0x0F19,
|
||||
0x0F35, 0x0F35, 0x0F37, 0x0F37, 0x0F39, 0x0F39, 0x0F71, 0x0F7E,
|
||||
0x0F80, 0x0F84, 0x0F86, 0x0F87, 0x0F90, 0x0F97, 0x0F99, 0x0FBC,
|
||||
0x0FC6, 0x0FC6, 0x102D, 0x1030, 0x1032, 0x1032, 0x1036, 0x1037,
|
||||
0x1039, 0x1039, 0x1058, 0x1059, 0x1160, 0x11FF, 0x135F, 0x135F,
|
||||
0x1712, 0x1714, 0x1732, 0x1734, 0x1752, 0x1753, 0x1772, 0x1773,
|
||||
0x17B4, 0x17B5, 0x17B7, 0x17BD, 0x17C6, 0x17C6, 0x17C9, 0x17D3,
|
||||
0x17DD, 0x17DD, 0x180B, 0x180D, 0x18A9, 0x18A9, 0x1920, 0x1922,
|
||||
0x1927, 0x1928, 0x1932, 0x1932, 0x1939, 0x193B, 0x1A17, 0x1A18,
|
||||
0x1B00, 0x1B03, 0x1B34, 0x1B34, 0x1B36, 0x1B3A, 0x1B3C, 0x1B3C,
|
||||
0x1B42, 0x1B42, 0x1B6B, 0x1B73, 0x1DC0, 0x1DCA, 0x1DFE, 0x1DFF,
|
||||
0x200B, 0x200F, 0x202A, 0x202E, 0x2060, 0x2063, 0x206A, 0x206F,
|
||||
0x20D0, 0x20EF, 0x302A, 0x302F, 0x3099, 0x309A, 0xA806, 0xA806,
|
||||
0xA80B, 0xA80B, 0xA825, 0xA826, 0xFB1E, 0xFB1E, 0xFE00, 0xFE0F,
|
||||
0xFE20, 0xFE23, 0xFEFF, 0xFEFF, 0xFFF9, 0xFFFB, 0x10A01, 0x10A03,
|
||||
0x10A05, 0x10A06, 0x10A0C, 0x10A0F, 0x10A38, 0x10A3A, 0x10A3F, 0x10A3F,
|
||||
0x1D167, 0x1D169, 0x1D173, 0x1D182, 0x1D185, 0x1D18B, 0x1D1AA, 0x1D1AD,
|
||||
0x1D242, 0x1D244, 0xE0001, 0xE0001, 0xE0020, 0xE007F, 0xE0100, 0xE01EF,
|
||||
-1,
|
||||
}
|
||||
|
||||
_range_width2 := []rune{
|
||||
0x1100, 0x115F, 0x2329, 0x2329, 0x232A, 0x232A, 0x2E80, 0x303E,
|
||||
0x3040, 0xA4CF, 0xAC00, 0xD7A3, 0xF900, 0xFAFF, 0xFE10, 0xFE19,
|
||||
0xFE30, 0xFE6F, 0xFF00, 0xFF60, 0xFFE0, 0xFFE6, 0x1F000, 0x1F644,
|
||||
0x20000, 0x2FFFD, 0x30000, 0x3FFFD,
|
||||
-1,
|
||||
}
|
||||
@@ -14,7 +14,7 @@ The following is a mostly-complete projection of the C11 standard library as def
|
||||
| `<inttypes.h>` | Fully projected |
|
||||
| `<iso646.h>` | Not applicable, use Odin's operators |
|
||||
| `<limits.h>` | Not projected |
|
||||
| `<locale.h>` | Not projected |
|
||||
| `<locale.h>` | Fully projected |
|
||||
| `<math.h>` | Mostly projected, see [limitations](#Limitations) |
|
||||
| `<setjmp.h>` | Fully projected |
|
||||
| `<signal.h>` | Fully projected |
|
||||
@@ -70,4 +70,4 @@ with the following copyright.
|
||||
|
||||
```
|
||||
Copyright 2021 Dale Weiler <weilercdale@gmail.com>.
|
||||
```
|
||||
```
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
package libc
|
||||
|
||||
import "core:c"
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc "system:libucrt.lib"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
// locale.h - category macros
|
||||
|
||||
foreign libc {
|
||||
/*
|
||||
Sets the components of an object with the type lconv with the values appropriate for the
|
||||
formatting of numeric quantities (monetary and otherwise) according to the rules of the current
|
||||
locale.
|
||||
|
||||
Returns: a pointer to the lconv structure, might be invalidated by subsequent calls to localeconv() and setlocale()
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/localeconv.html ]]
|
||||
*/
|
||||
localeconv :: proc() -> ^lconv ---
|
||||
|
||||
/*
|
||||
Selects the appropriate piece of the global locale, as specified by the category and locale arguments,
|
||||
and can be used to change or query the entire global locale or portions thereof.
|
||||
|
||||
Returns: the current locale if `locale` is `nil`, the set locale otherwise
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setlocale.html ]]
|
||||
*/
|
||||
@(link_name=LSETLOCALE)
|
||||
setlocale :: proc(category: Locale_Category, locale: cstring) -> cstring ---
|
||||
}
|
||||
|
||||
Locale_Category :: enum c.int {
|
||||
ALL = LC_ALL,
|
||||
COLLATE = LC_COLLATE,
|
||||
CTYPE = LC_CTYPE,
|
||||
MESSAGES = LC_MESSAGES,
|
||||
MONETARY = LC_MONETARY,
|
||||
NUMERIC = LC_NUMERIC,
|
||||
TIME = LC_TIME,
|
||||
}
|
||||
|
||||
when ODIN_OS == .NetBSD {
|
||||
@(private) LSETLOCALE :: "__setlocale50"
|
||||
} else {
|
||||
@(private) LSETLOCALE :: "setlocale"
|
||||
}
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
lconv :: struct {
|
||||
decimal_point: cstring,
|
||||
thousand_sep: cstring,
|
||||
grouping: cstring,
|
||||
int_curr_symbol: cstring,
|
||||
currency_symbol: cstring,
|
||||
mon_decimal_points: cstring,
|
||||
mon_thousands_sep: cstring,
|
||||
mon_grouping: cstring,
|
||||
positive_sign: cstring,
|
||||
negative_sign: cstring,
|
||||
int_frac_digits: c.char,
|
||||
frac_digits: c.char,
|
||||
p_cs_precedes: c.char,
|
||||
p_sep_by_space: c.char,
|
||||
n_cs_precedes: c.char,
|
||||
n_sep_by_space: c.char,
|
||||
p_sign_posn: c.char,
|
||||
n_sign_posn: c.char,
|
||||
_W_decimal_point: [^]u16 `fmt:"s,0"`,
|
||||
_W_thousands_sep: [^]u16 `fmt:"s,0"`,
|
||||
_W_int_curr_symbol: [^]u16 `fmt:"s,0"`,
|
||||
_W_currency_symbol: [^]u16 `fmt:"s,0"`,
|
||||
_W_mon_decimal_point: [^]u16 `fmt:"s,0"`,
|
||||
_W_mon_thousands_sep: [^]u16 `fmt:"s,0"`,
|
||||
_W_positive_sign: [^]u16 `fmt:"s,0"`,
|
||||
_W_negative_sign: [^]u16 `fmt:"s,0"`,
|
||||
}
|
||||
} else {
|
||||
lconv :: struct {
|
||||
decimal_point: cstring,
|
||||
thousand_sep: cstring,
|
||||
grouping: cstring,
|
||||
int_curr_symbol: cstring,
|
||||
currency_symbol: cstring,
|
||||
mon_decimal_points: cstring,
|
||||
mon_thousands_sep: cstring,
|
||||
mon_grouping: cstring,
|
||||
positive_sign: cstring,
|
||||
negative_sign: cstring,
|
||||
int_frac_digits: c.char,
|
||||
frac_digits: c.char,
|
||||
p_cs_precedes: c.char,
|
||||
p_sep_by_space: c.char,
|
||||
n_cs_precedes: c.char,
|
||||
n_sep_by_space: c.char,
|
||||
p_sign_posn: c.char,
|
||||
n_sign_posn: c.char,
|
||||
_int_p_cs_precedes: c.char,
|
||||
_int_n_cs_precedes: c.char,
|
||||
_int_p_sep_by_space: c.char,
|
||||
_int_n_sep_by_space: c.char,
|
||||
_int_p_sign_posn: c.char,
|
||||
_int_n_sign_posn: c.char,
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Windows {
|
||||
|
||||
LC_ALL :: 0
|
||||
LC_COLLATE :: 1
|
||||
LC_CTYPE :: 2
|
||||
LC_MESSAGES :: 6
|
||||
LC_MONETARY :: 3
|
||||
LC_NUMERIC :: 4
|
||||
LC_TIME :: 5
|
||||
|
||||
} else when ODIN_OS == .Linux {
|
||||
|
||||
LC_CTYPE :: 0
|
||||
LC_NUMERIC :: 1
|
||||
LC_TIME :: 2
|
||||
LC_COLLATE :: 3
|
||||
LC_MONETARY :: 4
|
||||
LC_MESSAGES :: 5
|
||||
LC_ALL :: 6
|
||||
|
||||
}
|
||||
@@ -13,6 +13,7 @@ Example:
|
||||
package main
|
||||
|
||||
import "core:bytes"
|
||||
import "core:compress/zlib"
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
@@ -36,7 +37,7 @@ Example:
|
||||
buf: bytes.Buffer
|
||||
|
||||
// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
|
||||
err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
|
||||
err := zlib.inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
|
||||
defer bytes.buffer_destroy(&buf)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -21,12 +21,14 @@ Symbols :: struct {
|
||||
main :: proc() {
|
||||
sym: Symbols
|
||||
|
||||
LIB_PATH :: "lib." + dynlib.LIBRARY_FILE_EXTENSION
|
||||
|
||||
// Load symbols from `lib.dll` into Symbols struct.
|
||||
// Each struct field is prefixed with `foo_` before lookup in the DLL's symbol table.
|
||||
// The library's Handle (to unload) will be stored in `sym._my_lib_handle`. This way you can load multiple DLLs in one struct.
|
||||
count, ok := dynlib.initialize_symbols(&sym, "lib.dll", "foo_", "_my_lib_handle")
|
||||
count, ok := dynlib.initialize_symbols(&sym, LIB_PATH, "foo_", "_my_lib_handle")
|
||||
defer dynlib.unload_library(sym._my_lib_handle)
|
||||
fmt.printf("(Initial DLL Load) ok: %v. %v symbols loaded from lib.dll (%p).\n", ok, count, sym._my_lib_handle)
|
||||
fmt.printf("(Initial DLL Load) ok: %v. %v symbols loaded from " + LIB_PATH + " (%p).\n", ok, count, sym._my_lib_handle)
|
||||
|
||||
if count > 0 {
|
||||
fmt.println("42 + 42 =", sym.add(42, 42))
|
||||
@@ -34,12 +36,12 @@ main :: proc() {
|
||||
fmt.println("hellope =", sym.hellope^)
|
||||
}
|
||||
|
||||
count, ok = dynlib.initialize_symbols(&sym, "lib.dll", "foo_", "_my_lib_handle")
|
||||
fmt.printf("(DLL Reload) ok: %v. %v symbols loaded from lib.dll (%p).\n", ok, count, sym._my_lib_handle)
|
||||
count, ok = dynlib.initialize_symbols(&sym, LIB_PATH, "foo_", "_my_lib_handle")
|
||||
fmt.printf("(DLL Reload) ok: %v. %v symbols loaded from " + LIB_PATH + " (%p).\n", ok, count, sym._my_lib_handle)
|
||||
|
||||
if count > 0 {
|
||||
fmt.println("42 + 42 =", sym.add(42, 42))
|
||||
fmt.println("84 - 13 =", sym.sub(84, 13))
|
||||
fmt.println("hellope =", sym.hellope^)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-21
@@ -12,6 +12,11 @@ A handle to a dynamically loaded library.
|
||||
*/
|
||||
Library :: distinct rawptr
|
||||
|
||||
/*
|
||||
The file extension for dynamic libraries on the target OS.
|
||||
*/
|
||||
LIBRARY_FILE_EXTENSION :: _LIBRARY_FILE_EXTENSION
|
||||
|
||||
/*
|
||||
Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded
|
||||
library available to resolve references in subsequently loaded libraries.
|
||||
@@ -37,8 +42,8 @@ Example:
|
||||
fmt.println("The library %q was successfully loaded", LIBRARY_PATH)
|
||||
}
|
||||
*/
|
||||
load_library :: proc(path: string, global_symbols := false) -> (library: Library, did_load: bool) {
|
||||
return _load_library(path, global_symbols)
|
||||
load_library :: proc(path: string, global_symbols := false, allocator := context.temp_allocator) -> (library: Library, did_load: bool) {
|
||||
return _load_library(path, global_symbols, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -98,8 +103,8 @@ Example:
|
||||
}
|
||||
}
|
||||
*/
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
|
||||
return _symbol_address(library, symbol)
|
||||
symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) #optional_ok {
|
||||
return _symbol_address(library, symbol, allocator)
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -123,31 +128,36 @@ initialize_symbols :: proc(
|
||||
) -> (count: int = -1, ok: bool = false) where intrinsics.type_is_struct(T) {
|
||||
assert(symbol_table != nil)
|
||||
|
||||
handle := load_library(library_path) or_return
|
||||
// First, (re)load the library.
|
||||
handle: Library
|
||||
for field in reflect.struct_fields_zipped(T) {
|
||||
if field.name == handle_field_name {
|
||||
field_ptr := rawptr(uintptr(symbol_table) + field.offset)
|
||||
|
||||
// We appear to be hot reloading. Unload previous incarnation of the library.
|
||||
if old_handle := (^Library)(field_ptr)^; old_handle != nil {
|
||||
unload_library(old_handle) or_return
|
||||
}
|
||||
|
||||
handle = load_library(library_path) or_return
|
||||
(^Library)(field_ptr)^ = handle
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Buffer to concatenate the prefix + symbol name.
|
||||
prefixed_symbol_buf: [2048]u8 = ---
|
||||
|
||||
count = 0
|
||||
for field in reflect.struct_fields_zipped(T) {
|
||||
// If we're not the library handle, the field needs to be a pointer type, be it a procedure pointer or an exported global.
|
||||
if field.name == handle_field_name || !(reflect.is_procedure(field.type) || reflect.is_pointer(field.type)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Calculate address of struct member
|
||||
field_ptr := rawptr(uintptr(symbol_table) + field.offset)
|
||||
|
||||
// If we've come across the struct member for the handle, store it and continue scanning for other symbols.
|
||||
if field.name == handle_field_name {
|
||||
// We appear to be hot reloading. Unload previous incarnation of the library.
|
||||
if old_handle := (^Library)(field_ptr)^; old_handle != nil {
|
||||
unload_library(old_handle) or_return
|
||||
}
|
||||
(^Library)(field_ptr)^ = handle
|
||||
continue
|
||||
}
|
||||
|
||||
// We're not the library handle, so the field needs to be a pointer type, be it a procedure pointer or an exported global.
|
||||
if !(reflect.is_procedure(field.type) || reflect.is_pointer(field.type)) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Let's look up or construct the symbol name to find in the library
|
||||
prefixed_name: string
|
||||
|
||||
@@ -174,4 +184,4 @@ initialize_symbols :: proc(
|
||||
// Returns an error message for the last failed procedure call.
|
||||
last_error :: proc() -> string {
|
||||
return _last_error()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,11 @@
|
||||
#+private
|
||||
package dynlib
|
||||
|
||||
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
import "base:runtime"
|
||||
|
||||
_LIBRARY_FILE_EXTENSION :: ""
|
||||
|
||||
_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@@ -10,10 +14,10 @@ _unload_library :: proc(library: Library) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
_last_error :: proc() -> string {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
+22
-10
@@ -2,28 +2,40 @@
|
||||
#+private
|
||||
package dynlib
|
||||
|
||||
import "core:os"
|
||||
import "base:runtime"
|
||||
|
||||
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
flags := os.RTLD_NOW
|
||||
import "core:strings"
|
||||
import "core:sys/posix"
|
||||
|
||||
_LIBRARY_FILE_EXTENSION :: "dylib" when ODIN_OS == .Darwin else "so"
|
||||
|
||||
_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
|
||||
flags := posix.RTLD_Flags{.NOW}
|
||||
if global_symbols {
|
||||
flags |= os.RTLD_GLOBAL
|
||||
flags += {.GLOBAL}
|
||||
}
|
||||
lib := os.dlopen(path, flags)
|
||||
|
||||
cpath := strings.clone_to_cstring(path, allocator)
|
||||
defer delete(cpath, allocator)
|
||||
|
||||
lib := posix.dlopen(cpath, flags)
|
||||
return Library(lib), lib != nil
|
||||
}
|
||||
|
||||
_unload_library :: proc(library: Library) -> bool {
|
||||
return os.dlclose(rawptr(library))
|
||||
return posix.dlclose(posix.Symbol_Table(library)) == 0
|
||||
}
|
||||
|
||||
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
ptr = os.dlsym(rawptr(library), symbol)
|
||||
_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) {
|
||||
csymbol := strings.clone_to_cstring(symbol, allocator)
|
||||
defer delete(csymbol, allocator)
|
||||
|
||||
ptr = posix.dlsym(posix.Symbol_Table(library), csymbol)
|
||||
found = ptr != nil
|
||||
return
|
||||
}
|
||||
|
||||
_last_error :: proc() -> string {
|
||||
err := os.dlerror()
|
||||
err := string(posix.dlerror())
|
||||
return "unknown" if err == "" else err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
#+private
|
||||
package dynlib
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
|
||||
_load_library :: proc(path: string, global_symbols := false, allocator := context.temp_allocator) -> (Library, bool) {
|
||||
_LIBRARY_FILE_EXTENSION :: "dll"
|
||||
|
||||
_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
|
||||
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
|
||||
wide_path := win32.utf8_to_wstring(path, allocator)
|
||||
defer free(wide_path, allocator)
|
||||
@@ -19,7 +23,7 @@ _unload_library :: proc(library: Library) -> bool {
|
||||
return bool(ok)
|
||||
}
|
||||
|
||||
_symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) {
|
||||
_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) {
|
||||
c_str := strings.clone_to_cstring(symbol, allocator)
|
||||
defer delete(c_str, allocator)
|
||||
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
||||
@@ -31,4 +35,4 @@ _last_error :: proc() -> string {
|
||||
err := win32.System_Error(win32.GetLastError())
|
||||
err_msg := reflect.enum_string(err)
|
||||
return "unknown" if err_msg == "" else err_msg
|
||||
}
|
||||
}
|
||||
|
||||
@@ -563,7 +563,7 @@ to_json :: proc(val: Value, allocator := context.allocator) -> (json.Value, mem.
|
||||
case: return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
if keys_all_strings(v) {
|
||||
|
||||
@@ -442,9 +442,6 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
loc := #caller_location,
|
||||
) -> (out_of_space: bool, err: Unmarshal_Error) {
|
||||
for idx: uintptr = 0; length == -1 || idx < uintptr(length); idx += 1 {
|
||||
elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size))
|
||||
elem := any{elem_ptr, elemt.id}
|
||||
|
||||
hdr := _decode_header(d.reader) or_return
|
||||
|
||||
// Double size if out of capacity.
|
||||
@@ -459,6 +456,10 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
if !ok { return false, .Out_Of_Memory }
|
||||
}
|
||||
|
||||
// Set ptr after potential resizes to avoid invalidation.
|
||||
elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size))
|
||||
elem := any{elem_ptr, elemt.id}
|
||||
|
||||
err = _unmarshal_value(d, elem, hdr, allocator=allocator, loc=loc)
|
||||
if length == -1 && err == .Break { break }
|
||||
if err != nil { return }
|
||||
@@ -509,7 +510,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
|
||||
raw := (^mem.Raw_Dynamic_Array)(v.data)
|
||||
raw.data = raw_data(data)
|
||||
raw.len = 0
|
||||
raw.cap = length
|
||||
raw.cap = scap
|
||||
raw.allocator = context.allocator
|
||||
|
||||
_ = assign_array(d, raw, t.elem, length) or_return
|
||||
@@ -627,7 +628,8 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
|
||||
unknown := length == -1
|
||||
fields := reflect.struct_fields_zipped(ti.id)
|
||||
|
||||
for idx := 0; idx < len(fields) && (unknown || idx < length); idx += 1 {
|
||||
idx := 0
|
||||
for ; idx < len(fields) && (unknown || idx < length); idx += 1 {
|
||||
// Decode key, keys can only be strings.
|
||||
key: string
|
||||
if keyv, kerr := decode_key(d, v, context.temp_allocator); unknown && kerr == .Break {
|
||||
@@ -662,6 +664,8 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
|
||||
|
||||
// Skips unused map entries.
|
||||
if use_field_idx < 0 {
|
||||
val := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return
|
||||
destroy(val, context.temp_allocator)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -672,6 +676,17 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
|
||||
fany := any{ptr, field.type.id}
|
||||
_unmarshal_value(d, fany, _decode_header(r) or_return) or_return
|
||||
}
|
||||
|
||||
// If there are fields left in the map that did not get decoded into the struct, decode and discard them.
|
||||
if !unknown {
|
||||
for _ in idx..<length {
|
||||
key := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return
|
||||
destroy(key, context.temp_allocator)
|
||||
val := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return
|
||||
destroy(val, context.temp_allocator)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
case reflect.Type_Info_Map:
|
||||
|
||||
@@ -117,7 +117,7 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
|
||||
layer.name = read_name(r) or_return
|
||||
layer.components = read_value(r, u8) or_return
|
||||
type := read_value(r, Layer_Data_Type) or_return
|
||||
if type > max(type) {
|
||||
if type > max(Layer_Data_Type) {
|
||||
if r.print_error {
|
||||
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)))
|
||||
|
||||
@@ -192,12 +192,6 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
|
||||
case runtime.Type_Info_Simd_Vector:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Relative_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Relative_Multi_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Matrix:
|
||||
return .Unsupported_Type
|
||||
|
||||
@@ -259,6 +259,7 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
skip_digits(t)
|
||||
}
|
||||
if t.r == 'e' || t.r == 'E' {
|
||||
token.kind = .Float
|
||||
switch r := next_rune(t); r {
|
||||
case '+', '-':
|
||||
next_rune(t)
|
||||
@@ -485,7 +486,7 @@ is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
|
||||
case '"':
|
||||
// okay
|
||||
case '\'':
|
||||
if spec != .JSON {
|
||||
if spec == .JSON {
|
||||
return false
|
||||
}
|
||||
// okay
|
||||
|
||||
@@ -172,20 +172,33 @@ assign_float :: proc(val: any, f: $T) -> bool {
|
||||
|
||||
|
||||
@(private)
|
||||
unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool {
|
||||
unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> (ok: bool, err: Error) {
|
||||
val := val
|
||||
switch &dst in val {
|
||||
case string:
|
||||
dst = str
|
||||
return true
|
||||
return true, nil
|
||||
case cstring:
|
||||
if str == "" {
|
||||
dst = strings.clone_to_cstring("", p.allocator)
|
||||
a_err: runtime.Allocator_Error
|
||||
dst, a_err = strings.clone_to_cstring("", p.allocator)
|
||||
#partial switch a_err {
|
||||
case nil:
|
||||
// okay
|
||||
case .Out_Of_Memory:
|
||||
err = .Out_Of_Memory
|
||||
case:
|
||||
err = .Invalid_Allocator
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// NOTE: This is valid because 'clone_string' appends a NUL terminator
|
||||
dst = cstring(raw_data(str))
|
||||
}
|
||||
return true
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
#partial switch variant in ti.variant {
|
||||
@@ -193,31 +206,37 @@ unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.T
|
||||
for name, i in variant.names {
|
||||
if name == str {
|
||||
assign_int(val, variant.values[i])
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
// TODO(bill): should this be an error or not?
|
||||
return true
|
||||
return true, nil
|
||||
|
||||
case reflect.Type_Info_Integer:
|
||||
i := strconv.parse_i128(str) or_return
|
||||
i, pok := strconv.parse_i128(str)
|
||||
if !pok {
|
||||
return false, nil
|
||||
}
|
||||
if assign_int(val, i) {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
if assign_float(val, i) {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
case reflect.Type_Info_Float:
|
||||
f := strconv.parse_f64(str) or_return
|
||||
f, pok := strconv.parse_f64(str)
|
||||
if !pok {
|
||||
return false, nil
|
||||
}
|
||||
if assign_int(val, f) {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
if assign_float(val, f) {
|
||||
return true
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -304,7 +323,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
|
||||
case .Ident:
|
||||
advance_token(p)
|
||||
if p.spec == .MJSON {
|
||||
if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) {
|
||||
if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) or_return {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -312,13 +331,18 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
|
||||
|
||||
case .String:
|
||||
advance_token(p)
|
||||
str := unquote_string(token, p.spec, p.allocator) or_return
|
||||
if unmarshal_string_token(p, any{v.data, ti.id}, str, ti) {
|
||||
return nil
|
||||
str := unquote_string(token, p.spec, p.allocator) or_return
|
||||
dest := any{v.data, ti.id}
|
||||
if !(unmarshal_string_token(p, dest, str, ti) or_return) {
|
||||
delete(str, p.allocator)
|
||||
return UNSUPPORTED_TYPE
|
||||
}
|
||||
delete(str, p.allocator)
|
||||
return UNSUPPORTED_TYPE
|
||||
|
||||
switch destv in dest {
|
||||
case string, cstring:
|
||||
case: delete(str, p.allocator)
|
||||
}
|
||||
return nil
|
||||
|
||||
case .Open_Brace:
|
||||
return unmarshal_object(p, v, .Close_Brace)
|
||||
@@ -393,15 +417,15 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
if .raw_union in t.flags {
|
||||
return UNSUPPORTED_TYPE
|
||||
}
|
||||
|
||||
|
||||
fields := reflect.struct_fields_zipped(ti.id)
|
||||
|
||||
struct_loop: for p.curr_token.kind != end_token {
|
||||
key, _ := parse_object_key(p, p.allocator)
|
||||
key := parse_object_key(p, p.allocator) or_return
|
||||
defer delete(key, p.allocator)
|
||||
|
||||
unmarshal_expect_token(p, .Colon)
|
||||
|
||||
fields := reflect.struct_fields_zipped(ti.id)
|
||||
|
||||
field_test :: #force_inline proc "contextless" (field_used: [^]byte, offset: uintptr) -> bool {
|
||||
prev_set := field_used[offset/8] & byte(offset&7) != 0
|
||||
field_used[offset/8] |= byte(offset&7)
|
||||
@@ -409,7 +433,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
}
|
||||
|
||||
field_used_bytes := (reflect.size_of_typeid(ti.id)+7)/8
|
||||
field_used := intrinsics.alloca(field_used_bytes, 1)
|
||||
field_used := intrinsics.alloca(field_used_bytes + 1, 1) // + 1 to not overflow on size_of 0 types.
|
||||
intrinsics.mem_zero(field_used, field_used_bytes)
|
||||
|
||||
use_field_idx := -1
|
||||
|
||||
+1
-13
@@ -1531,7 +1531,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
|
||||
case 'o': _fmt_int(fi, u, 8, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
case 'i', 'd': _fmt_int(fi, u, 10, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
case 'z': _fmt_int(fi, u, 12, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
case 'x': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
case 'x': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_LOWER)
|
||||
case 'X': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
|
||||
|
||||
case:
|
||||
@@ -3002,18 +3002,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
case runtime.Type_Info_Bit_Set:
|
||||
fmt_bit_set(fi, v, verb = verb)
|
||||
|
||||
case runtime.Type_Info_Relative_Pointer:
|
||||
ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id)
|
||||
absolute_ptr := any{ptr, info.pointer.id}
|
||||
|
||||
fmt_value(fi, absolute_ptr, verb)
|
||||
|
||||
case runtime.Type_Info_Relative_Multi_Pointer:
|
||||
ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id)
|
||||
absolute_ptr := any{ptr, info.pointer.id}
|
||||
|
||||
fmt_value(fi, absolute_ptr, verb)
|
||||
|
||||
case runtime.Type_Info_Matrix:
|
||||
fmt_matrix(fi, v, verb, info)
|
||||
|
||||
|
||||
+10
-2
@@ -23,7 +23,15 @@ register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_
|
||||
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
loader := _internal_loaders[which(data)]
|
||||
if loader == nil {
|
||||
return nil, .Unsupported_Format
|
||||
|
||||
// Check if there is at least one loader, otherwise panic to let the user know about misuse.
|
||||
for a_loader in _internal_loaders {
|
||||
if a_loader != nil {
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
}
|
||||
|
||||
panic("image.load called when no image loaders are registered. Register a loader by first importing a subpackage (eg: `import \"core:image/png\"`), or with image.register")
|
||||
}
|
||||
return loader(data, options, allocator)
|
||||
}
|
||||
@@ -185,7 +193,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
|
||||
return .HDR
|
||||
case s[:4] == "\x38\x42\x50\x53":
|
||||
return .PSD
|
||||
case s[:4] != "\x53\x80\xF6\x34" && s[88:92] == "PICT":
|
||||
case s[:4] == "\x53\x80\xF6\x34" && s[88:92] == "PICT":
|
||||
return .PIC
|
||||
case s[:4] == "\x69\x63\x6e\x73":
|
||||
return .ICNS
|
||||
|
||||
+1
-129
@@ -396,132 +396,4 @@ exif :: proc(c: image.PNG_Chunk) -> (res: Exif, ok: bool) {
|
||||
General helper functions
|
||||
*/
|
||||
|
||||
compute_buffer_size :: image.compute_buffer_size
|
||||
|
||||
/*
|
||||
PNG save helpers
|
||||
*/
|
||||
|
||||
when false {
|
||||
|
||||
make_chunk :: proc(c: any, t: Chunk_Type) -> (res: Chunk) {
|
||||
|
||||
data: []u8
|
||||
if v, ok := c.([]u8); ok {
|
||||
data = v
|
||||
} else {
|
||||
data = mem.any_to_bytes(c)
|
||||
}
|
||||
|
||||
res.header.length = u32be(len(data))
|
||||
res.header.type = t
|
||||
res.data = data
|
||||
|
||||
// CRC the type
|
||||
crc := hash.crc32(mem.any_to_bytes(res.header.type))
|
||||
// Extend the CRC with the data
|
||||
res.crc = u32be(hash.crc32(data, crc))
|
||||
return
|
||||
}
|
||||
|
||||
write_chunk :: proc(fd: os.Handle, chunk: Chunk) {
|
||||
c := chunk
|
||||
// Write length + type
|
||||
os.write_ptr(fd, &c.header, 8)
|
||||
// Write data
|
||||
os.write_ptr(fd, mem.raw_data(c.data), int(c.header.length))
|
||||
// Write CRC32
|
||||
os.write_ptr(fd, &c.crc, 4)
|
||||
}
|
||||
|
||||
write_image_as_png :: proc(filename: string, image: Image) -> (err: Error) {
|
||||
profiler.timed_proc()
|
||||
using image
|
||||
using os
|
||||
flags: int = O_WRONLY|O_CREATE|O_TRUNC
|
||||
|
||||
if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) {
|
||||
return .Invalid_Image_Dimensions
|
||||
}
|
||||
|
||||
mode: int = 0
|
||||
when ODIN_OS == .Linux || ODIN_OS == .Darwin {
|
||||
// NOTE(justasd): 644 (owner read, write; group read; others read)
|
||||
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
|
||||
}
|
||||
|
||||
fd, fderr := open(filename, flags, mode)
|
||||
if fderr != nil {
|
||||
return .Cannot_Open_File
|
||||
}
|
||||
defer close(fd)
|
||||
|
||||
magic := Signature
|
||||
|
||||
write_ptr(fd, &magic, 8)
|
||||
|
||||
ihdr := IHDR{
|
||||
width = u32be(width),
|
||||
height = u32be(height),
|
||||
bit_depth = depth,
|
||||
compression_method = 0,
|
||||
filter_method = 0,
|
||||
interlace_method = .None,
|
||||
}
|
||||
|
||||
switch channels {
|
||||
case 1: ihdr.color_type = Color_Type{}
|
||||
case 2: ihdr.color_type = Color_Type{.Alpha}
|
||||
case 3: ihdr.color_type = Color_Type{.Color}
|
||||
case 4: ihdr.color_type = Color_Type{.Color, .Alpha}
|
||||
case:// Unhandled
|
||||
return .Unknown_Color_Type
|
||||
}
|
||||
h := make_chunk(ihdr, .IHDR)
|
||||
write_chunk(fd, h)
|
||||
|
||||
bytes_needed := width * height * int(channels) + height
|
||||
filter_bytes := mem.make_dynamic_array_len_cap([dynamic]u8, bytes_needed, bytes_needed, context.allocator)
|
||||
defer delete(filter_bytes)
|
||||
|
||||
i := 0; j := 0
|
||||
// Add a filter byte 0 per pixel row
|
||||
for y := 0; y < height; y += 1 {
|
||||
filter_bytes[j] = 0; j += 1
|
||||
for x := 0; x < width; x += 1 {
|
||||
for z := 0; z < channels; z += 1 {
|
||||
filter_bytes[j+z] = image.pixels[i+z]
|
||||
}
|
||||
i += channels; j += channels
|
||||
}
|
||||
}
|
||||
assert(j == bytes_needed)
|
||||
|
||||
a: []u8 = filter_bytes[:]
|
||||
|
||||
out_buf: ^[dynamic]u8
|
||||
defer free(out_buf)
|
||||
|
||||
ctx := zlib.ZLIB_Context{
|
||||
in_buf = &a,
|
||||
out_buf = out_buf,
|
||||
}
|
||||
err = zlib.write_zlib_stream_from_memory(&ctx)
|
||||
|
||||
b: []u8
|
||||
if err == nil {
|
||||
b = ctx.out_buf[:]
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
|
||||
idat := make_chunk(b, .IDAT)
|
||||
|
||||
write_chunk(fd, idat)
|
||||
|
||||
iend := make_chunk([]u8{}, .IEND)
|
||||
write_chunk(fd, iend)
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
compute_buffer_size :: image.compute_buffer_size
|
||||
+8
-4
@@ -132,9 +132,13 @@ write_encoded_rune :: proc(w: Writer, r: rune, write_quote := true, n_written: ^
|
||||
buf: [2]byte
|
||||
s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil)
|
||||
switch len(s) {
|
||||
case 0: write_string(w, "00", &n) or_return
|
||||
case 1: write_byte(w, '0', &n) or_return
|
||||
case 2: write_string(w, s, &n) or_return
|
||||
case 0:
|
||||
write_string(w, "00", &n) or_return
|
||||
case 1:
|
||||
write_byte(w, '0', &n) or_return
|
||||
fallthrough
|
||||
case 2:
|
||||
write_string(w, s, &n) or_return
|
||||
}
|
||||
} else {
|
||||
write_rune(w, r, &n) or_return
|
||||
@@ -225,7 +229,7 @@ write_escaped_rune :: proc(w: Writer, r: rune, quote: byte, html_safe := false,
|
||||
} else {
|
||||
write_byte(w, '\\', &n) or_return
|
||||
write_byte(w, 'U', &n) or_return
|
||||
for s := 24; s >= 0; s -= 4 {
|
||||
for s := 28; s >= 0; s -= 4 {
|
||||
write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf], &n) or_return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,30 +37,30 @@ File_Console_Logger_Data :: struct {
|
||||
ident: string,
|
||||
}
|
||||
|
||||
create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "") -> Logger {
|
||||
data := new(File_Console_Logger_Data)
|
||||
create_file_logger :: proc(h: os.Handle, lowest := Level.Debug, opt := Default_File_Logger_Opts, ident := "", allocator := context.allocator) -> Logger {
|
||||
data := new(File_Console_Logger_Data, allocator)
|
||||
data.file_handle = h
|
||||
data.ident = ident
|
||||
return Logger{file_console_logger_proc, data, lowest, opt}
|
||||
}
|
||||
|
||||
destroy_file_logger :: proc(log: Logger) {
|
||||
destroy_file_logger :: proc(log: Logger, allocator := context.allocator) {
|
||||
data := cast(^File_Console_Logger_Data)log.data
|
||||
if data.file_handle != os.INVALID_HANDLE {
|
||||
os.close(data.file_handle)
|
||||
}
|
||||
free(data)
|
||||
free(data, allocator)
|
||||
}
|
||||
|
||||
create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "") -> Logger {
|
||||
data := new(File_Console_Logger_Data)
|
||||
create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logger_Opts, ident := "", allocator := context.allocator) -> Logger {
|
||||
data := new(File_Console_Logger_Data, allocator)
|
||||
data.file_handle = os.INVALID_HANDLE
|
||||
data.ident = ident
|
||||
return Logger{file_console_logger_proc, data, lowest, opt}
|
||||
}
|
||||
|
||||
destroy_console_logger :: proc(log: Logger) {
|
||||
free(log.data)
|
||||
destroy_console_logger :: proc(log: Logger, allocator := context.allocator) {
|
||||
free(log.data, allocator)
|
||||
}
|
||||
|
||||
file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
||||
|
||||
@@ -5,17 +5,17 @@ Multi_Logger_Data :: struct {
|
||||
loggers: []Logger,
|
||||
}
|
||||
|
||||
create_multi_logger :: proc(logs: ..Logger) -> Logger {
|
||||
data := new(Multi_Logger_Data)
|
||||
data.loggers = make([]Logger, len(logs))
|
||||
create_multi_logger :: proc(logs: ..Logger, allocator := context.allocator) -> Logger {
|
||||
data := new(Multi_Logger_Data, allocator)
|
||||
data.loggers = make([]Logger, len(logs), allocator)
|
||||
copy(data.loggers, logs)
|
||||
return Logger{multi_logger_proc, data, Level.Debug, nil}
|
||||
}
|
||||
|
||||
destroy_multi_logger :: proc(log: Logger) {
|
||||
destroy_multi_logger :: proc(log: Logger, allocator := context.allocator) {
|
||||
data := (^Multi_Logger_Data)(log.data)
|
||||
delete(data.loggers)
|
||||
free(data)
|
||||
delete(data.loggers, allocator)
|
||||
free(data, allocator)
|
||||
}
|
||||
|
||||
multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string,
|
||||
|
||||
@@ -53,15 +53,15 @@ vector_dot :: proc "contextless" (a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E)
|
||||
}
|
||||
@(require_results)
|
||||
quaternion64_dot :: proc "contextless" (a, b: $T/quaternion64) -> (c: f16) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
|
||||
return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z
|
||||
}
|
||||
@(require_results)
|
||||
quaternion128_dot :: proc "contextless" (a, b: $T/quaternion128) -> (c: f32) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
|
||||
return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z
|
||||
}
|
||||
@(require_results)
|
||||
quaternion256_dot :: proc "contextless" (a, b: $T/quaternion256) -> (c: f64) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
|
||||
return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z
|
||||
}
|
||||
|
||||
dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot}
|
||||
@@ -167,6 +167,18 @@ vector_triple_product :: proc "contextless" (a, b, c: $T/[$N]$E) -> T where IS_N
|
||||
length :: proc{vector_length, quaternion_length}
|
||||
length2 :: proc{vector_length2, quaternion_length2}
|
||||
|
||||
|
||||
@(require_results)
|
||||
clamp_length :: proc "contextless" (v: $T/[$N]$E, a: E) -> T where IS_FLOAT(E) {
|
||||
if a <= 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
m2 := length2(v)
|
||||
return v if (m2 <= a*a) else (v / sqrt(m2) * a) // returns original when m2 is 0
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
projection :: proc "contextless" (x, normal: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
return dot(x, normal) / dot(normal, normal) * normal
|
||||
|
||||
@@ -473,6 +473,22 @@ floor :: proc{
|
||||
@(require_results) floor_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {floor(x.x), floor(x.y), floor(x.z)} }
|
||||
@(require_results) floor_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
|
||||
|
||||
trunc :: proc{
|
||||
trunc_f32,
|
||||
trunc_f64,
|
||||
trunc_vec2,
|
||||
trunc_vec3,
|
||||
trunc_vec4,
|
||||
trunc_dvec2,
|
||||
trunc_dvec3,
|
||||
trunc_dvec4,
|
||||
}
|
||||
@(require_results) trunc_vec2 :: proc "c" (x: vec2) -> vec2 { return {trunc(x.x), trunc(x.y)} }
|
||||
@(require_results) trunc_vec3 :: proc "c" (x: vec3) -> vec3 { return {trunc(x.x), trunc(x.y), trunc(x.z)} }
|
||||
@(require_results) trunc_vec4 :: proc "c" (x: vec4) -> vec4 { return {trunc(x.x), trunc(x.y), trunc(x.z), trunc(x.w)} }
|
||||
@(require_results) trunc_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {trunc(x.x), trunc(x.y)} }
|
||||
@(require_results) trunc_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {trunc(x.x), trunc(x.y), trunc(x.z)} }
|
||||
@(require_results) trunc_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {trunc(x.x), trunc(x.y), trunc(x.z), trunc(x.w)} }
|
||||
|
||||
|
||||
round :: proc{
|
||||
|
||||
@@ -23,6 +23,7 @@ import "core:math"
|
||||
@(require_results) exp2_f32 :: proc "c" (x: f32) -> f32 { return math.pow(f32(2), x) }
|
||||
@(require_results) sign_f32 :: proc "c" (x: f32) -> f32 { return math.sign(x) }
|
||||
@(require_results) floor_f32 :: proc "c" (x: f32) -> f32 { return math.floor(x) }
|
||||
@(require_results) trunc_f32 :: proc "c" (x: f32) -> f32 { return math.trunc(x) }
|
||||
@(require_results) round_f32 :: proc "c" (x: f32) -> f32 { return math.round(x) }
|
||||
@(require_results) ceil_f32 :: proc "c" (x: f32) -> f32 { return math.ceil(x) }
|
||||
@(require_results) mod_f32 :: proc "c" (x, y: f32) -> f32 { return math.mod(x, y) }
|
||||
@@ -55,6 +56,7 @@ fract_f32 :: proc "c" (x: f32) -> f32 {
|
||||
@(require_results) exp2_f64 :: proc "c" (x: f64) -> f64 { return math.pow(f64(2), x) }
|
||||
@(require_results) sign_f64 :: proc "c" (x: f64) -> f64 { return math.sign(x) }
|
||||
@(require_results) floor_f64 :: proc "c" (x: f64) -> f64 { return math.floor(x) }
|
||||
@(require_results) trunc_f64 :: proc "c" (x: f64) -> f64 { return math.trunc(x) }
|
||||
@(require_results) round_f64 :: proc "c" (x: f64) -> f64 { return math.round(x) }
|
||||
@(require_results) ceil_f64 :: proc "c" (x: f64) -> f64 { return math.ceil(x) }
|
||||
@(require_results) mod_f64 :: proc "c" (x, y: f64) -> f64 { return math.mod(x, y) }
|
||||
|
||||
+1
-1
@@ -1271,7 +1271,7 @@ binomial :: proc "contextless" (n, k: int) -> int {
|
||||
}
|
||||
|
||||
b := n
|
||||
for i in 2..<k {
|
||||
for i in 2..=k {
|
||||
b = (b * (n+1-i))/i
|
||||
}
|
||||
return b
|
||||
|
||||
@@ -189,12 +189,12 @@ sincos_f64 :: proc "contextless" (x: f64) -> (sin, cos: f64) #no_bounds_check {
|
||||
// sin coefficients
|
||||
@(private="file")
|
||||
_sin := [?]f64{
|
||||
0h3de5d8fd1fd19ccd, // 1.58962301576546568060e-10
|
||||
0hbe5ae5e5a9291f5d, // -2.50507477628578072866e-8
|
||||
0h3ec71de3567d48a1, // 2.75573136213857245213e-6
|
||||
0hbf2a01a019bfdf03, // -1.98412698295895385996e-4
|
||||
0h3f8111111110f7d0, // 8.33333333332211858878e-3
|
||||
0hbfc5555555555548, // -1.66666666666666307295e-1
|
||||
0h3de5d8fd1fd19ccd, // 1.58962301576546568060e-10
|
||||
0hbe5ae5e5a9291f5d, // -2.50507477628578072866e-8
|
||||
0h3ec71de3567d48a1, // 2.75573136213857245213e-6
|
||||
0hbf2a01a019bfdf03, // -1.98412698295895385996e-4
|
||||
0h3f8111111110f7d0, // 8.33333333332211858878e-3
|
||||
0hbfc5555555555548, // -1.66666666666666307295e-1
|
||||
}
|
||||
|
||||
// cos coefficients
|
||||
|
||||
+64
-39
@@ -29,30 +29,6 @@ Reset the seed used by the context.random_generator.
|
||||
Inputs:
|
||||
- seed: The seed value
|
||||
|
||||
Example:
|
||||
import "core:math/rand"
|
||||
import "core:fmt"
|
||||
|
||||
set_global_seed_example :: proc() {
|
||||
rand.set_global_seed(1)
|
||||
fmt.println(rand.uint64())
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
|
||||
10
|
||||
*/
|
||||
@(deprecated="Prefer `rand.reset`")
|
||||
set_global_seed :: proc(seed: u64) {
|
||||
runtime.random_generator_reset_u64(context.random_generator, seed)
|
||||
}
|
||||
|
||||
/*
|
||||
Reset the seed used by the context.random_generator.
|
||||
|
||||
Inputs:
|
||||
- seed: The seed value
|
||||
|
||||
Example:
|
||||
import "core:math/rand"
|
||||
import "core:fmt"
|
||||
@@ -670,20 +646,69 @@ choice :: proc(array: $T/[]$E, gen := context.random_generator) -> (res: E) {
|
||||
|
||||
|
||||
@(require_results)
|
||||
choice_enum :: proc($T: typeid, gen := context.random_generator) -> T
|
||||
where
|
||||
intrinsics.type_is_enum(T),
|
||||
size_of(T) <= 8,
|
||||
len(T) == cap(T) /* Only allow contiguous enum types */ \
|
||||
{
|
||||
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
|
||||
u64(max(T)) > u64(max(i64)) {
|
||||
i := uint64(gen) % u64(len(T))
|
||||
i += u64(min(T))
|
||||
return T(i)
|
||||
choice_enum :: proc($T: typeid, gen := context.random_generator) -> T where intrinsics.type_is_enum(T) {
|
||||
when size_of(T) <= 8 && len(T) == cap(T) {
|
||||
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
|
||||
u64(max(T)) > u64(max(i64)) {
|
||||
i := uint64(gen) % u64(len(T))
|
||||
i += u64(min(T))
|
||||
return T(i)
|
||||
} else {
|
||||
i := int63_max(i64(len(T)), gen)
|
||||
i += i64(min(T))
|
||||
return T(i)
|
||||
}
|
||||
} else {
|
||||
i := int63_max(i64(len(T)), gen)
|
||||
i += i64(min(T))
|
||||
return T(i)
|
||||
values := runtime.type_info_base(type_info_of(T)).variant.(runtime.Type_Info_Enum).values
|
||||
return T(choice(values))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Returns a random *set* bit from the provided `bit_set`.
|
||||
|
||||
Inputs:
|
||||
- set: The `bit_set` to choose a random set bit from
|
||||
|
||||
Returns:
|
||||
- res: The randomly selected bit, or the zero value if `ok` is `false`
|
||||
- ok: Whether the bit_set was not empty and thus `res` is actually a random set bit
|
||||
|
||||
Example:
|
||||
import "core:math/rand"
|
||||
import "core:fmt"
|
||||
|
||||
choice_bit_set_example :: proc() {
|
||||
Flags :: enum {
|
||||
A,
|
||||
B = 10,
|
||||
C,
|
||||
}
|
||||
|
||||
fmt.println(rand.choice_bit_set(bit_set[Flags]{}))
|
||||
fmt.println(rand.choice_bit_set(bit_set[Flags]{.B}))
|
||||
fmt.println(rand.choice_bit_set(bit_set[Flags]{.B, .C}))
|
||||
fmt.println(rand.choice_bit_set(bit_set[0..<15]{5, 1, 4}))
|
||||
}
|
||||
|
||||
Possible Output:
|
||||
A false
|
||||
B true
|
||||
C true
|
||||
5 true
|
||||
*/
|
||||
@(require_results)
|
||||
choice_bit_set :: proc(set: $T/bit_set[$E], gen := context.random_generator) -> (res: E, ok: bool) {
|
||||
total_set := card(set)
|
||||
if total_set == 0 {
|
||||
return {}, false
|
||||
}
|
||||
|
||||
core_set := transmute(intrinsics.type_bit_set_underlying_type(T))set
|
||||
|
||||
for target := int_max(total_set, gen); target > 0; target -= 1 {
|
||||
core_set &= core_set - 1
|
||||
}
|
||||
|
||||
return E(intrinsics.count_trailing_zeros(core_set)), true
|
||||
}
|
||||
|
||||
@@ -140,14 +140,6 @@ arena_init :: proc(a: ^Arena, data: []byte) {
|
||||
a.temp_count = 0
|
||||
}
|
||||
|
||||
@(deprecated="prefer 'mem.arena_init'")
|
||||
init_arena :: proc(a: ^Arena, data: []byte) {
|
||||
a.data = data
|
||||
a.offset = 0
|
||||
a.peak_used = 0
|
||||
a.temp_count = 0
|
||||
}
|
||||
|
||||
/*
|
||||
Allocate memory from an arena.
|
||||
|
||||
@@ -786,14 +778,6 @@ stack_init :: proc(s: ^Stack, data: []byte) {
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
@(deprecated="prefer 'mem.stack_init'")
|
||||
init_stack :: proc(s: ^Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.prev_offset = 0
|
||||
s.curr_offset = 0
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
/*
|
||||
Allocate memory from stack.
|
||||
|
||||
@@ -1162,13 +1146,6 @@ small_stack_init :: proc(s: ^Small_Stack, data: []byte) {
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
@(deprecated="prefer 'small_stack_init'")
|
||||
init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.offset = 0
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
/*
|
||||
Small stack allocator.
|
||||
|
||||
|
||||
+4
-9
@@ -461,10 +461,12 @@ Check if a pointer is aligned.
|
||||
|
||||
This procedure checks whether a pointer `x` is aligned to a boundary specified
|
||||
by `align`, and returns `true` if the pointer is aligned, and false otherwise.
|
||||
|
||||
The specified alignment must be a power of 2.
|
||||
*/
|
||||
is_aligned :: proc "contextless" (x: rawptr, align: int) -> bool {
|
||||
p := uintptr(x)
|
||||
return (p & (1<<uintptr(align) - 1)) == 0
|
||||
return (p & (uintptr(align) - 1)) == 0
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -683,11 +685,4 @@ calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, he
|
||||
}
|
||||
}
|
||||
return int(padding)
|
||||
}
|
||||
|
||||
@(require_results, deprecated="prefer 'slice.clone'")
|
||||
clone_slice :: proc(slice: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> (new_slice: T) {
|
||||
new_slice, _ = make(T, len(slice), allocator, loc)
|
||||
runtime.copy(new_slice, slice)
|
||||
return new_slice
|
||||
}
|
||||
}
|
||||
@@ -6,17 +6,13 @@ import "core:sys/posix"
|
||||
|
||||
// Define non-posix needed flags:
|
||||
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
|
||||
MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
|
||||
|
||||
MADV_FREE :: 5 /* pages unneeded, discard contents */
|
||||
MADV_FREE :: 5 /* pages unneeded, discard contents */
|
||||
} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
||||
MAP_ANONYMOUS :: 0x1000
|
||||
|
||||
MADV_FREE :: 6
|
||||
MADV_FREE :: 6
|
||||
}
|
||||
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
flags := posix.Map_Flags{ .PRIVATE } + transmute(posix.Map_Flags)i32(MAP_ANONYMOUS)
|
||||
flags := posix.Map_Flags{ .ANONYMOUS, .PRIVATE }
|
||||
result := posix.mmap(nil, size, {}, flags)
|
||||
if result == posix.MAP_FAILED {
|
||||
return nil, .Out_Of_Memory
|
||||
|
||||
@@ -17,6 +17,9 @@ Build_Kind :: struct {
|
||||
arch: runtime.Odin_Arch_Types,
|
||||
}
|
||||
|
||||
// empty build kind acts as a marker for separating multiple lines with build tags
|
||||
BUILD_KIND_NEWLINE_MARKER :: Build_Kind{}
|
||||
|
||||
File_Tags :: struct {
|
||||
build_project_name: [][]string,
|
||||
build: []Build_Kind,
|
||||
@@ -147,6 +150,11 @@ parse_file_tags :: proc(file: ast.File, allocator := context.allocator) -> (tags
|
||||
append(build_project_names, build_project_name_strings[index_start:])
|
||||
}
|
||||
case "build":
|
||||
|
||||
if len(build_kinds) > 0 {
|
||||
append(build_kinds, BUILD_KIND_NEWLINE_MARKER)
|
||||
}
|
||||
|
||||
kinds_loop: for {
|
||||
os_positive: runtime.Odin_OS_Types
|
||||
os_negative: runtime.Odin_OS_Types
|
||||
@@ -248,10 +256,20 @@ match_build_tags :: proc(file_tags: File_Tags, target: Build_Target) -> bool {
|
||||
project_name_correct ||= group_correct
|
||||
}
|
||||
|
||||
os_and_arch_correct := len(file_tags.build) == 0
|
||||
os_and_arch_correct := true
|
||||
|
||||
for kind in file_tags.build {
|
||||
os_and_arch_correct ||= target.os in kind.os && target.arch in kind.arch
|
||||
if len(file_tags.build) > 0 {
|
||||
os_and_arch_correct_line := false
|
||||
|
||||
for kind in file_tags.build {
|
||||
if kind == BUILD_KIND_NEWLINE_MARKER {
|
||||
os_and_arch_correct &&= os_and_arch_correct_line
|
||||
os_and_arch_correct_line = false
|
||||
} else {
|
||||
os_and_arch_correct_line ||= target.os in kind.os && target.arch in kind.arch
|
||||
}
|
||||
}
|
||||
os_and_arch_correct &&= os_and_arch_correct_line
|
||||
}
|
||||
|
||||
return !file_tags.ignore && project_name_correct && os_and_arch_correct
|
||||
|
||||
@@ -3696,6 +3696,8 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou
|
||||
}
|
||||
}
|
||||
|
||||
end := p.prev_tok
|
||||
|
||||
if p.expr_level >= 0 {
|
||||
end: ^ast.Expr
|
||||
if !is_mutable && len(values) > 0 {
|
||||
@@ -3715,7 +3717,7 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou
|
||||
}
|
||||
}
|
||||
|
||||
decl := ast.new(ast.Value_Decl, names[0].pos, end_pos(p.prev_tok))
|
||||
decl := ast.new(ast.Value_Decl, names[0].pos, end_pos(end))
|
||||
decl.docs = docs
|
||||
decl.names = names
|
||||
decl.type = type
|
||||
|
||||
@@ -331,7 +331,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool {
|
||||
n -= 1
|
||||
}
|
||||
|
||||
if x > max || 0xd800 <= x && x <= 0xe000 {
|
||||
if x > max || 0xd800 <= x && x <= 0xdfff {
|
||||
error(t, offset, "escape sequence is an invalid Unicode code point")
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,584 +0,0 @@
|
||||
package os
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:unicode/utf16"
|
||||
|
||||
@(require_results)
|
||||
is_path_separator :: proc(c: byte) -> bool {
|
||||
return c == '/' || c == '\\'
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) {
|
||||
if len(path) == 0 {
|
||||
return INVALID_HANDLE, General_Error.Not_Exist
|
||||
}
|
||||
|
||||
access: u32
|
||||
switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
|
||||
case O_RDONLY: access = win32.FILE_GENERIC_READ
|
||||
case O_WRONLY: access = win32.FILE_GENERIC_WRITE
|
||||
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
|
||||
}
|
||||
|
||||
share_mode := win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE
|
||||
sa: ^win32.SECURITY_ATTRIBUTES = nil
|
||||
sa_inherit := win32.SECURITY_ATTRIBUTES{nLength = size_of(win32.SECURITY_ATTRIBUTES), bInheritHandle = true}
|
||||
if mode&O_CLOEXEC == 0 {
|
||||
sa = &sa_inherit
|
||||
}
|
||||
|
||||
create_mode: u32
|
||||
switch {
|
||||
case mode&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL):
|
||||
create_mode = win32.CREATE_NEW
|
||||
case mode&(O_CREATE|O_TRUNC) == (O_CREATE | O_TRUNC):
|
||||
create_mode = win32.CREATE_ALWAYS
|
||||
case mode&O_CREATE == O_CREATE:
|
||||
create_mode = win32.OPEN_ALWAYS
|
||||
case mode&O_TRUNC == O_TRUNC:
|
||||
create_mode = win32.TRUNCATE_EXISTING
|
||||
case:
|
||||
create_mode = win32.OPEN_EXISTING
|
||||
}
|
||||
wide_path := win32.utf8_to_wstring(path)
|
||||
handle := Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil))
|
||||
if handle != INVALID_HANDLE {
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
return INVALID_HANDLE, get_last_error()
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) -> Error {
|
||||
if !win32.CloseHandle(win32.HANDLE(fd)) {
|
||||
return get_last_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
flush :: proc(fd: Handle) -> (err: Error) {
|
||||
if !win32.FlushFileBuffers(win32.HANDLE(fd)) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Error) {
|
||||
if len(data) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
single_write_length: win32.DWORD
|
||||
total_write: i64
|
||||
length := i64(len(data))
|
||||
|
||||
for total_write < length {
|
||||
remaining := length - total_write
|
||||
to_write := win32.DWORD(min(i32(remaining), MAX_RW))
|
||||
|
||||
e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil)
|
||||
if single_write_length <= 0 || !e {
|
||||
return int(total_write), get_last_error()
|
||||
}
|
||||
total_write += i64(single_write_length)
|
||||
}
|
||||
return int(total_write), nil
|
||||
}
|
||||
|
||||
@(private="file", require_results)
|
||||
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
BUF_SIZE :: 386
|
||||
buf16: [BUF_SIZE]u16
|
||||
buf8: [4*BUF_SIZE]u8
|
||||
|
||||
for n < len(b) && err == nil {
|
||||
min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
|
||||
max_read := u32(min(BUF_SIZE, min_read))
|
||||
if max_read == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
single_read_length: u32
|
||||
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
|
||||
if !ok {
|
||||
err = get_last_error()
|
||||
}
|
||||
|
||||
buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
|
||||
src := buf8[:buf8_len]
|
||||
|
||||
ctrl_z := false
|
||||
for i := 0; i < len(src) && n < len(b); i += 1 {
|
||||
x := src[i]
|
||||
if x == 0x1a { // ctrl-z
|
||||
ctrl_z = true
|
||||
break
|
||||
}
|
||||
b[n] = x
|
||||
n += 1
|
||||
}
|
||||
if ctrl_z || single_read_length < max_read {
|
||||
break
|
||||
}
|
||||
|
||||
// NOTE(bill): if the last two values were a newline, then it is expected that
|
||||
// this is the end of the input
|
||||
if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Error) {
|
||||
if len(data) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
handle := win32.HANDLE(fd)
|
||||
|
||||
m: u32
|
||||
is_console := win32.GetConsoleMode(handle, &m)
|
||||
length := len(data)
|
||||
|
||||
// NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that.
|
||||
to_read := min(i64(length), MAX_RW)
|
||||
|
||||
if is_console {
|
||||
total_read, err = read_console(handle, data[total_read:][:to_read])
|
||||
if err != nil {
|
||||
return total_read, err
|
||||
}
|
||||
} else {
|
||||
// NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB)
|
||||
bytes_read: win32.DWORD
|
||||
if e := win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &bytes_read, nil); e {
|
||||
// Successful read can mean two things, including EOF, see:
|
||||
// https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
|
||||
if bytes_read == 0 {
|
||||
return 0, .EOF
|
||||
} else {
|
||||
return int(bytes_read), nil
|
||||
}
|
||||
} else {
|
||||
return 0, get_last_error()
|
||||
}
|
||||
}
|
||||
return total_read, nil
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
|
||||
w: u32
|
||||
switch whence {
|
||||
case 0: w = win32.FILE_BEGIN
|
||||
case 1: w = win32.FILE_CURRENT
|
||||
case 2: w = win32.FILE_END
|
||||
case:
|
||||
return 0, .Invalid_Whence
|
||||
}
|
||||
hi := i32(offset>>32)
|
||||
lo := i32(offset)
|
||||
ft := win32.GetFileType(win32.HANDLE(fd))
|
||||
if ft == win32.FILE_TYPE_PIPE {
|
||||
return 0, .File_Is_Pipe
|
||||
}
|
||||
|
||||
dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w)
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
err := get_last_error()
|
||||
return 0, err
|
||||
}
|
||||
return i64(hi)<<32 + i64(dw_ptr), nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
file_size :: proc(fd: Handle) -> (i64, Error) {
|
||||
length: win32.LARGE_INTEGER
|
||||
err: Error
|
||||
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return i64(length), err
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
MAX_RW :: 1<<30
|
||||
|
||||
@(private)
|
||||
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
curr_off := seek(fd, 0, 1) or_return
|
||||
defer seek(fd, curr_off, 0)
|
||||
|
||||
buf := data
|
||||
if len(buf) > MAX_RW {
|
||||
buf = buf[:MAX_RW]
|
||||
}
|
||||
|
||||
o := win32.OVERLAPPED{
|
||||
OffsetHigh = u32(offset>>32),
|
||||
Offset = u32(offset),
|
||||
}
|
||||
|
||||
// TODO(bill): Determine the correct behaviour for consoles
|
||||
|
||||
h := win32.HANDLE(fd)
|
||||
done: win32.DWORD
|
||||
e: Error
|
||||
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
e = get_last_error()
|
||||
done = 0
|
||||
}
|
||||
return int(done), e
|
||||
}
|
||||
@(private)
|
||||
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
curr_off := seek(fd, 0, 1) or_return
|
||||
defer seek(fd, curr_off, 0)
|
||||
|
||||
buf := data
|
||||
if len(buf) > MAX_RW {
|
||||
buf = buf[:MAX_RW]
|
||||
}
|
||||
|
||||
o := win32.OVERLAPPED{
|
||||
OffsetHigh = u32(offset>>32),
|
||||
Offset = u32(offset),
|
||||
}
|
||||
|
||||
h := win32.HANDLE(fd)
|
||||
done: win32.DWORD
|
||||
e: Error
|
||||
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
e = get_last_error()
|
||||
done = 0
|
||||
}
|
||||
return int(done), e
|
||||
}
|
||||
|
||||
/*
|
||||
read_at returns n: 0, err: 0 on EOF
|
||||
*/
|
||||
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := data, offset
|
||||
for len(b) > 0 {
|
||||
m, e := pread(fd, b, offset)
|
||||
if e == ERROR_EOF {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
if e != nil {
|
||||
err = e
|
||||
break
|
||||
}
|
||||
n += m
|
||||
b = b[m:]
|
||||
offset += i64(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := data, offset
|
||||
for len(b) > 0 {
|
||||
m := pwrite(fd, b, offset) or_return
|
||||
n += m
|
||||
b = b[m:]
|
||||
offset += i64(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE))
|
||||
stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE))
|
||||
stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
|
||||
|
||||
|
||||
@(require_results)
|
||||
get_std_handle :: proc "contextless" (h: uint) -> Handle {
|
||||
fd := win32.GetStdHandle(win32.DWORD(h))
|
||||
return Handle(fd)
|
||||
}
|
||||
|
||||
|
||||
exists :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
|
||||
return attribs != win32.INVALID_FILE_ATTRIBUTES
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_file :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
|
||||
if attribs != win32.INVALID_FILE_ATTRIBUTES {
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_dir :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
|
||||
if attribs != win32.INVALID_FILE_ATTRIBUTES {
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
|
||||
@private cwd_lock := win32.SRWLOCK{} // zero is initialized
|
||||
|
||||
@(require_results)
|
||||
get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
win32.AcquireSRWLockExclusive(&cwd_lock)
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
|
||||
sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
|
||||
dir_buf_wstr, _ := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL.
|
||||
|
||||
sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr))
|
||||
assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL.
|
||||
|
||||
win32.ReleaseSRWLockExclusive(&cwd_lock)
|
||||
|
||||
return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wstr := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
win32.AcquireSRWLockExclusive(&cwd_lock)
|
||||
|
||||
if !win32.SetCurrentDirectoryW(wstr) {
|
||||
err = get_last_error()
|
||||
}
|
||||
|
||||
win32.ReleaseSRWLockExclusive(&cwd_lock)
|
||||
|
||||
return
|
||||
}
|
||||
change_directory :: set_current_directory
|
||||
|
||||
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
// Mode is unused on Windows, but is needed on *nix
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.CreateDirectoryW(wpath, nil) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
remove_directory :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.RemoveDirectoryW(wpath) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private, require_results)
|
||||
is_abs :: proc(path: string) -> bool {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
return true
|
||||
}
|
||||
when ODIN_OS == .Windows {
|
||||
if len(path) > 2 {
|
||||
switch path[0] {
|
||||
case 'A'..='Z', 'a'..='z':
|
||||
return path[1] == ':' && is_path_separator(path[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
fix_long_path :: proc(path: string) -> string {
|
||||
if len(path) < 248 {
|
||||
return path
|
||||
}
|
||||
|
||||
if len(path) >= 2 && path[:2] == `\\` {
|
||||
return path
|
||||
}
|
||||
if !is_abs(path) {
|
||||
return path
|
||||
}
|
||||
|
||||
prefix :: `\\?`
|
||||
|
||||
path_buf, _ := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator)
|
||||
copy(path_buf, prefix)
|
||||
n := len(path)
|
||||
r, w := 0, len(prefix)
|
||||
for r < n {
|
||||
switch {
|
||||
case is_path_separator(path[r]):
|
||||
r += 1
|
||||
case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])):
|
||||
r += 1
|
||||
case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])):
|
||||
return path
|
||||
case:
|
||||
path_buf[w] = '\\'
|
||||
w += 1
|
||||
for ; r < n && !is_path_separator(path[r]); r += 1 {
|
||||
path_buf[w] = path[r]
|
||||
w += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if w == len(`\\?\c:`) {
|
||||
path_buf[w] = '\\'
|
||||
w += 1
|
||||
}
|
||||
return string(path_buf[:w])
|
||||
}
|
||||
|
||||
|
||||
link :: proc(old_name, new_name: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
n := win32.utf8_to_wstring(fix_long_path(new_name))
|
||||
o := win32.utf8_to_wstring(fix_long_path(old_name))
|
||||
return Platform_Error(win32.CreateHardLinkW(n, o, nil))
|
||||
}
|
||||
|
||||
unlink :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.DeleteFileW(wpath) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
rename :: proc(old_path, new_path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
|
||||
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
|
||||
|
||||
if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) {
|
||||
curr_off := seek(fd, 0, 1) or_return
|
||||
defer seek(fd, curr_off, 0)
|
||||
_= seek(fd, length, 0) or_return
|
||||
ok := win32.SetEndOfFile(win32.HANDLE(fd))
|
||||
if !ok {
|
||||
return get_last_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
truncate :: proc(path: string, length: i64) -> (err: Error) {
|
||||
fd := open(path, O_WRONLY|O_CREATE, 0o666) or_return
|
||||
defer close(fd)
|
||||
return ftruncate(fd, length)
|
||||
}
|
||||
|
||||
|
||||
remove :: proc(name: string) -> Error {
|
||||
p := win32.utf8_to_wstring(fix_long_path(name))
|
||||
err, err1: win32.DWORD
|
||||
if !win32.DeleteFileW(p) {
|
||||
err = win32.GetLastError()
|
||||
}
|
||||
if err == 0 {
|
||||
return nil
|
||||
}
|
||||
if !win32.RemoveDirectoryW(p) {
|
||||
err1 = win32.GetLastError()
|
||||
}
|
||||
if err1 == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != err1 {
|
||||
a := win32.GetFileAttributesW(p)
|
||||
if a == ~u32(0) {
|
||||
err = win32.GetLastError()
|
||||
} else {
|
||||
if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
err = err1
|
||||
} else if a & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) {
|
||||
err = 0
|
||||
if !win32.DeleteFileW(p) {
|
||||
err = win32.GetLastError()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Platform_Error(err)
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
pipe :: proc() -> (r, w: Handle, err: Error) {
|
||||
sa: win32.SECURITY_ATTRIBUTES
|
||||
sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
|
||||
sa.bInheritHandle = true
|
||||
if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
|
||||
return {}, nil
|
||||
return {}, .Unsupported
|
||||
}
|
||||
|
||||
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
|
||||
@@ -16,28 +16,25 @@ find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, al
|
||||
}
|
||||
path := concatenate({base_path, `\`, win32_utf16_to_utf8(d.cFileName[:], temp_allocator()) or_else ""}, allocator) or_return
|
||||
|
||||
handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
|
||||
defer win32.CloseHandle(handle)
|
||||
|
||||
fi.fullpath = path
|
||||
fi.name = basename(path)
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
|
||||
fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, d.dwReserved0)
|
||||
fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, handle, d.dwReserved0)
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
|
||||
|
||||
handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
|
||||
defer win32.CloseHandle(handle)
|
||||
|
||||
if file_id_info: win32.FILE_ID_INFO; handle != nil && win32.GetFileInformationByHandleEx(handle, .FileIdInfo, &file_id_info, size_of(file_id_info)) {
|
||||
#assert(size_of(fi.inode) == size_of(file_id_info.FileId))
|
||||
#assert(size_of(fi.inode) == 16)
|
||||
runtime.mem_copy_non_overlapping(&fi.inode, &file_id_info.FileId, 16)
|
||||
}
|
||||
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -137,5 +134,6 @@ _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
return
|
||||
}
|
||||
file_info_delete(it.impl.prev_fi, file_allocator())
|
||||
delete(it.impl.path, file_allocator())
|
||||
win32.FindClose(it.impl.find_handle)
|
||||
}
|
||||
|
||||
@@ -272,28 +272,12 @@ _truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
}
|
||||
|
||||
_remove :: proc(name: string) -> Error {
|
||||
is_dir_fd :: proc(fd: linux.Fd) -> bool {
|
||||
s: linux.Stat
|
||||
if linux.fstat(fd, &s) != .NONE {
|
||||
return false
|
||||
}
|
||||
return linux.S_ISDIR(s.mode)
|
||||
}
|
||||
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr := temp_cstring(name) or_return
|
||||
|
||||
fd, errno := linux.open(name_cstr, {.NOFOLLOW})
|
||||
#partial switch (errno) {
|
||||
case .ELOOP:
|
||||
/* symlink */
|
||||
case .NONE:
|
||||
defer linux.close(fd)
|
||||
if is_dir_fd(fd) {
|
||||
return _get_platform_error(linux.rmdir(name_cstr))
|
||||
}
|
||||
case:
|
||||
return _get_platform_error(errno)
|
||||
if fd, errno := linux.open(name_cstr, _OPENDIR_FLAGS); errno == .NONE {
|
||||
linux.close(fd)
|
||||
return _get_platform_error(linux.rmdir(name_cstr))
|
||||
}
|
||||
|
||||
return _get_platform_error(linux.unlink(name_cstr))
|
||||
|
||||
@@ -50,9 +50,9 @@ init_std_files :: proc() {
|
||||
}
|
||||
@(fini)
|
||||
fini_std_files :: proc() {
|
||||
close(stdin)
|
||||
close(stdout)
|
||||
close(stderr)
|
||||
_destroy((^File_Impl)(stdin.impl))
|
||||
_destroy((^File_Impl)(stdout.impl))
|
||||
_destroy((^File_Impl)(stderr.impl))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -415,7 +415,7 @@ _region_resize :: proc(alloc: ^Allocation_Header, new_size: int, alloc_is_free_l
|
||||
back_idx := -1
|
||||
idx: u16
|
||||
infinite: for {
|
||||
for i := 0; i < len(region_iter.hdr.free_list); i += 1 {
|
||||
for i := 0; i < int(region_iter.hdr.free_list_len); i += 1 {
|
||||
idx = region_iter.hdr.free_list[i]
|
||||
if _get_block_count(region_iter.memory[idx]) >= new_block_count {
|
||||
break infinite
|
||||
|
||||
@@ -290,12 +290,21 @@ process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Erro
|
||||
return _process_open(pid, flags)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
OS-specific process attributes.
|
||||
*/
|
||||
Process_Attributes :: struct {
|
||||
sys_attr: _Sys_Process_Attributes,
|
||||
}
|
||||
|
||||
/*
|
||||
The description of how a process should be created.
|
||||
*/
|
||||
Process_Desc :: struct {
|
||||
// OS-specific attributes.
|
||||
sys_attr: _Sys_Process_Attributes,
|
||||
sys_attr: Process_Attributes,
|
||||
|
||||
// The working directory of the process. If the string has length 0, the
|
||||
// working directory is assumed to be the current working directory of the
|
||||
// current process.
|
||||
|
||||
@@ -384,14 +384,6 @@ _Sys_Process_Attributes :: struct {}
|
||||
|
||||
@(private="package")
|
||||
_process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
has_executable_permissions :: proc(fd: linux.Fd) -> bool {
|
||||
backing: [48]u8
|
||||
b := strings.builder_from_bytes(backing[:])
|
||||
strings.write_string(&b, "/proc/self/fd/")
|
||||
strings.write_int(&b, int(fd))
|
||||
return linux.access(strings.to_cstring(&b), linux.X_OK) == .NONE
|
||||
}
|
||||
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
if len(desc.command) == 0 {
|
||||
@@ -411,7 +403,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
}
|
||||
|
||||
// search PATH if just a plain name is provided
|
||||
exe_fd: linux.Fd
|
||||
exe_path: cstring
|
||||
executable_name := desc.command[0]
|
||||
if strings.index_byte(executable_name, '/') < 0 {
|
||||
path_env := get_env("PATH", temp_allocator())
|
||||
@@ -426,16 +418,11 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
strings.write_byte(&exe_builder, '/')
|
||||
strings.write_string(&exe_builder, executable_name)
|
||||
|
||||
exe_path := strings.to_cstring(&exe_builder)
|
||||
if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
|
||||
continue
|
||||
exe_path = strings.to_cstring(&exe_builder)
|
||||
if linux.access(exe_path, linux.X_OK) == .NONE {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
if !has_executable_permissions(exe_fd) {
|
||||
linux.close(exe_fd)
|
||||
continue
|
||||
}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
if !found {
|
||||
// check in cwd to match windows behavior
|
||||
@@ -443,29 +430,18 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
strings.write_string(&exe_builder, "./")
|
||||
strings.write_string(&exe_builder, executable_name)
|
||||
|
||||
exe_path := strings.to_cstring(&exe_builder)
|
||||
if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
|
||||
exe_path = strings.to_cstring(&exe_builder)
|
||||
if linux.access(exe_path, linux.X_OK) != .NONE {
|
||||
return process, .Not_Exist
|
||||
}
|
||||
if !has_executable_permissions(exe_fd) {
|
||||
linux.close(exe_fd)
|
||||
return process, .Permission_Denied
|
||||
}
|
||||
}
|
||||
} else {
|
||||
exe_path := temp_cstring(executable_name) or_return
|
||||
if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
|
||||
return process, _get_platform_error(errno)
|
||||
}
|
||||
if !has_executable_permissions(exe_fd) {
|
||||
linux.close(exe_fd)
|
||||
return process, .Permission_Denied
|
||||
exe_path = temp_cstring(executable_name) or_return
|
||||
if linux.access(exe_path, linux.X_OK) != .NONE {
|
||||
return process, .Not_Exist
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, we have an executable.
|
||||
defer linux.close(exe_fd)
|
||||
|
||||
// args and environment need to be a list of cstrings
|
||||
// that are terminated by a nil pointer.
|
||||
cargs := make([]cstring, len(desc.command) + 1, temp_allocator()) or_return
|
||||
@@ -492,7 +468,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
}
|
||||
defer linux.close(child_pipe_fds[READ])
|
||||
|
||||
|
||||
// TODO: This is the traditional textbook implementation with fork.
|
||||
// A more efficient implementation with vfork:
|
||||
//
|
||||
@@ -573,7 +548,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
|
||||
}
|
||||
|
||||
errno = linux.execveat(exe_fd, "", &cargs[0], env, {.AT_EMPTY_PATH})
|
||||
errno = linux.execveat(dir_fd, exe_path, &cargs[0], env)
|
||||
assert(errno != nil)
|
||||
write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
|
||||
}
|
||||
|
||||
@@ -442,7 +442,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
stderr_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
|
||||
}
|
||||
if desc.stdin != nil {
|
||||
stdin_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
|
||||
stdin_handle = win32.HANDLE((^File_Impl)(desc.stdin.impl).fd)
|
||||
}
|
||||
|
||||
working_dir_w := (win32_utf8_to_wstring(desc.working_dir, temp_allocator()) or_else nil) if len(desc.working_dir) > 0 else nil
|
||||
|
||||
@@ -72,7 +72,11 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runt
|
||||
ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa)
|
||||
if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||
// Not a symlink
|
||||
return _file_info_from_win32_file_attribute_data(&fa, name, allocator)
|
||||
fi = _file_info_from_win32_file_attribute_data(&fa, name, allocator) or_return
|
||||
if fi.type == .Undetermined {
|
||||
fi.type = _file_type_from_create_file(wname, create_file_attributes)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
err := 0 if ok else win32.GetLastError()
|
||||
@@ -86,7 +90,11 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runt
|
||||
}
|
||||
win32.FindClose(sh)
|
||||
|
||||
return _file_info_from_win32_find_data(&fd, name, allocator)
|
||||
fi = _file_info_from_win32_find_data(&fd, name, allocator) or_return
|
||||
if fi.type == .Undetermined {
|
||||
fi.type = _file_type_from_create_file(wname, create_file_attributes)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
|
||||
@@ -194,28 +202,36 @@ file_type :: proc(h: win32.HANDLE) -> File_Type {
|
||||
return .Undetermined
|
||||
}
|
||||
|
||||
_file_type_from_create_file :: proc(wname: win32.wstring, create_file_attributes: u32) -> File_Type {
|
||||
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
|
||||
if h == win32.INVALID_HANDLE_VALUE {
|
||||
return .Undetermined
|
||||
}
|
||||
defer win32.CloseHandle(h)
|
||||
return file_type(h)
|
||||
}
|
||||
|
||||
_file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (type: File_Type, mode: int) {
|
||||
if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
mode |= 0o444
|
||||
} else {
|
||||
mode |= 0o666
|
||||
}
|
||||
|
||||
is_sym := false
|
||||
if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||
is_sym = false
|
||||
} else {
|
||||
is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT
|
||||
}
|
||||
|
||||
if is_sym {
|
||||
type = .Symlink
|
||||
} else {
|
||||
if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
type = .Directory
|
||||
mode |= 0o111
|
||||
}
|
||||
if h != nil {
|
||||
type = file_type(h)
|
||||
}
|
||||
} else if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
type = .Directory
|
||||
mode |= 0o111
|
||||
} else if h != nil {
|
||||
type = file_type(h)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -267,7 +283,7 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA
|
||||
fi.name = basename(path)
|
||||
fi.inode = u128(u64(d.nFileIndexHigh)<<32 + u64(d.nFileIndexLow))
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||
type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, h, 0)
|
||||
fi.type = type
|
||||
fi.mode |= mode
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
|
||||
@@ -679,7 +679,7 @@ get_last_error_string :: proc() -> string {
|
||||
|
||||
|
||||
@(require_results)
|
||||
open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (handle: Handle, err: Error) {
|
||||
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (handle: Handle, err: Error) {
|
||||
isDir := is_dir_path(path)
|
||||
flags := flags
|
||||
if isDir {
|
||||
@@ -1026,7 +1026,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -1041,9 +1041,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> bool {
|
||||
|
||||
@@ -789,7 +789,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -804,10 +804,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
@@ -431,7 +431,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -447,9 +447,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
defer _unix_free(path_ptr)
|
||||
|
||||
path_cstr := cstring(path_ptr)
|
||||
path = strings.clone(string(path_cstr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_cstr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
@@ -490,7 +490,7 @@ foreign libc {
|
||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
|
||||
|
||||
@(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> int ---
|
||||
@(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> c.int ---
|
||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
|
||||
@(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int ---
|
||||
@(link_name="setenv") _unix_setenv :: proc(key: cstring, value: cstring, overwrite: c.int) -> c.int ---
|
||||
@@ -917,7 +917,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -932,9 +932,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
@@ -844,7 +844,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -859,9 +859,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
@@ -758,7 +758,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -773,9 +773,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
|
||||
path = strings.clone(string(path_ptr))
|
||||
|
||||
return path, nil
|
||||
return strings.clone(string(path_ptr), allocator)
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Error) {
|
||||
|
||||
+578
-3
@@ -4,15 +4,13 @@ package os
|
||||
import win32 "core:sys/windows"
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
import "core:unicode/utf16"
|
||||
|
||||
Handle :: distinct uintptr
|
||||
File_Time :: distinct u64
|
||||
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0)
|
||||
|
||||
|
||||
|
||||
O_RDONLY :: 0x00000
|
||||
O_WRONLY :: 0x00001
|
||||
O_RDWR :: 0x00002
|
||||
@@ -278,3 +276,580 @@ is_windows_11 :: proc "contextless" () -> bool {
|
||||
osvi := get_windows_version_w()
|
||||
return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= WINDOWS_11_BUILD_CUTOFF)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_path_separator :: proc(c: byte) -> bool {
|
||||
return c == '/' || c == '\\'
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) {
|
||||
if len(path) == 0 {
|
||||
return INVALID_HANDLE, General_Error.Not_Exist
|
||||
}
|
||||
|
||||
access: u32
|
||||
switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
|
||||
case O_RDONLY: access = win32.FILE_GENERIC_READ
|
||||
case O_WRONLY: access = win32.FILE_GENERIC_WRITE
|
||||
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
|
||||
}
|
||||
|
||||
share_mode := win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE
|
||||
sa: ^win32.SECURITY_ATTRIBUTES = nil
|
||||
sa_inherit := win32.SECURITY_ATTRIBUTES{nLength = size_of(win32.SECURITY_ATTRIBUTES), bInheritHandle = true}
|
||||
if mode&O_CLOEXEC == 0 {
|
||||
sa = &sa_inherit
|
||||
}
|
||||
|
||||
create_mode: u32
|
||||
switch {
|
||||
case mode&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL):
|
||||
create_mode = win32.CREATE_NEW
|
||||
case mode&(O_CREATE|O_TRUNC) == (O_CREATE | O_TRUNC):
|
||||
create_mode = win32.CREATE_ALWAYS
|
||||
case mode&O_CREATE == O_CREATE:
|
||||
create_mode = win32.OPEN_ALWAYS
|
||||
case mode&O_TRUNC == O_TRUNC:
|
||||
create_mode = win32.TRUNCATE_EXISTING
|
||||
case:
|
||||
create_mode = win32.OPEN_EXISTING
|
||||
}
|
||||
wide_path := win32.utf8_to_wstring(path)
|
||||
handle := Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil))
|
||||
if handle != INVALID_HANDLE {
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
return INVALID_HANDLE, get_last_error()
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) -> Error {
|
||||
if !win32.CloseHandle(win32.HANDLE(fd)) {
|
||||
return get_last_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
flush :: proc(fd: Handle) -> (err: Error) {
|
||||
if !win32.FlushFileBuffers(win32.HANDLE(fd)) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Error) {
|
||||
if len(data) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
single_write_length: win32.DWORD
|
||||
total_write: i64
|
||||
length := i64(len(data))
|
||||
|
||||
for total_write < length {
|
||||
remaining := length - total_write
|
||||
to_write := win32.DWORD(min(i32(remaining), MAX_RW))
|
||||
|
||||
e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil)
|
||||
if single_write_length <= 0 || !e {
|
||||
return int(total_write), get_last_error()
|
||||
}
|
||||
total_write += i64(single_write_length)
|
||||
}
|
||||
return int(total_write), nil
|
||||
}
|
||||
|
||||
@(private="file", require_results)
|
||||
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
BUF_SIZE :: 386
|
||||
buf16: [BUF_SIZE]u16
|
||||
buf8: [4*BUF_SIZE]u8
|
||||
|
||||
for n < len(b) && err == nil {
|
||||
min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
|
||||
max_read := u32(min(BUF_SIZE, min_read))
|
||||
if max_read == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
single_read_length: u32
|
||||
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
|
||||
if !ok {
|
||||
err = get_last_error()
|
||||
}
|
||||
|
||||
buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
|
||||
src := buf8[:buf8_len]
|
||||
|
||||
ctrl_z := false
|
||||
for i := 0; i < len(src) && n < len(b); i += 1 {
|
||||
x := src[i]
|
||||
if x == 0x1a { // ctrl-z
|
||||
ctrl_z = true
|
||||
break
|
||||
}
|
||||
b[n] = x
|
||||
n += 1
|
||||
}
|
||||
if ctrl_z || single_read_length < max_read {
|
||||
break
|
||||
}
|
||||
|
||||
// NOTE(bill): if the last two values were a newline, then it is expected that
|
||||
// this is the end of the input
|
||||
if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" {
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Error) {
|
||||
if len(data) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
handle := win32.HANDLE(fd)
|
||||
|
||||
m: u32
|
||||
is_console := win32.GetConsoleMode(handle, &m)
|
||||
length := len(data)
|
||||
|
||||
// NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that.
|
||||
to_read := min(i64(length), MAX_RW)
|
||||
|
||||
if is_console {
|
||||
total_read, err = read_console(handle, data[total_read:][:to_read])
|
||||
if err != nil {
|
||||
return total_read, err
|
||||
}
|
||||
} else {
|
||||
// NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB)
|
||||
bytes_read: win32.DWORD
|
||||
if e := win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &bytes_read, nil); e {
|
||||
// Successful read can mean two things, including EOF, see:
|
||||
// https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
|
||||
if bytes_read == 0 {
|
||||
return 0, .EOF
|
||||
} else {
|
||||
return int(bytes_read), nil
|
||||
}
|
||||
} else {
|
||||
return 0, get_last_error()
|
||||
}
|
||||
}
|
||||
return total_read, nil
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
|
||||
w: u32
|
||||
switch whence {
|
||||
case 0: w = win32.FILE_BEGIN
|
||||
case 1: w = win32.FILE_CURRENT
|
||||
case 2: w = win32.FILE_END
|
||||
case:
|
||||
return 0, .Invalid_Whence
|
||||
}
|
||||
hi := i32(offset>>32)
|
||||
lo := i32(offset)
|
||||
ft := win32.GetFileType(win32.HANDLE(fd))
|
||||
if ft == win32.FILE_TYPE_PIPE {
|
||||
return 0, .File_Is_Pipe
|
||||
}
|
||||
|
||||
dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w)
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
err := get_last_error()
|
||||
return 0, err
|
||||
}
|
||||
return i64(hi)<<32 + i64(dw_ptr), nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
file_size :: proc(fd: Handle) -> (i64, Error) {
|
||||
length: win32.LARGE_INTEGER
|
||||
err: Error
|
||||
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return i64(length), err
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
MAX_RW :: 1<<30
|
||||
|
||||
@(private)
|
||||
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
curr_off := seek(fd, 0, 1) or_return
|
||||
defer seek(fd, curr_off, 0)
|
||||
|
||||
buf := data
|
||||
if len(buf) > MAX_RW {
|
||||
buf = buf[:MAX_RW]
|
||||
}
|
||||
|
||||
o := win32.OVERLAPPED{
|
||||
OffsetHigh = u32(offset>>32),
|
||||
Offset = u32(offset),
|
||||
}
|
||||
|
||||
// TODO(bill): Determine the correct behaviour for consoles
|
||||
|
||||
h := win32.HANDLE(fd)
|
||||
done: win32.DWORD
|
||||
e: Error
|
||||
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
e = get_last_error()
|
||||
done = 0
|
||||
}
|
||||
return int(done), e
|
||||
}
|
||||
@(private)
|
||||
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
curr_off := seek(fd, 0, 1) or_return
|
||||
defer seek(fd, curr_off, 0)
|
||||
|
||||
buf := data
|
||||
if len(buf) > MAX_RW {
|
||||
buf = buf[:MAX_RW]
|
||||
}
|
||||
|
||||
o := win32.OVERLAPPED{
|
||||
OffsetHigh = u32(offset>>32),
|
||||
Offset = u32(offset),
|
||||
}
|
||||
|
||||
h := win32.HANDLE(fd)
|
||||
done: win32.DWORD
|
||||
e: Error
|
||||
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
e = get_last_error()
|
||||
done = 0
|
||||
}
|
||||
return int(done), e
|
||||
}
|
||||
|
||||
/*
|
||||
read_at returns n: 0, err: 0 on EOF
|
||||
*/
|
||||
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := data, offset
|
||||
for len(b) > 0 {
|
||||
m, e := pread(fd, b, offset)
|
||||
if e == ERROR_EOF {
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
if e != nil {
|
||||
err = e
|
||||
break
|
||||
}
|
||||
n += m
|
||||
b = b[m:]
|
||||
offset += i64(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := data, offset
|
||||
for len(b) > 0 {
|
||||
m := pwrite(fd, b, offset) or_return
|
||||
n += m
|
||||
b = b[m:]
|
||||
offset += i64(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE))
|
||||
stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE))
|
||||
stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
|
||||
|
||||
|
||||
@(require_results)
|
||||
get_std_handle :: proc "contextless" (h: uint) -> Handle {
|
||||
fd := win32.GetStdHandle(win32.DWORD(h))
|
||||
return Handle(fd)
|
||||
}
|
||||
|
||||
|
||||
exists :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
|
||||
return attribs != win32.INVALID_FILE_ATTRIBUTES
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_file :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
|
||||
if attribs != win32.INVALID_FILE_ATTRIBUTES {
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_dir :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
|
||||
if attribs != win32.INVALID_FILE_ATTRIBUTES {
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
|
||||
@private cwd_lock := win32.SRWLOCK{} // zero is initialized
|
||||
|
||||
@(require_results)
|
||||
get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
win32.AcquireSRWLockExclusive(&cwd_lock)
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
|
||||
sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
|
||||
dir_buf_wstr, _ := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL.
|
||||
|
||||
sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr))
|
||||
assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL.
|
||||
|
||||
win32.ReleaseSRWLockExclusive(&cwd_lock)
|
||||
|
||||
return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wstr := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
win32.AcquireSRWLockExclusive(&cwd_lock)
|
||||
|
||||
if !win32.SetCurrentDirectoryW(wstr) {
|
||||
err = get_last_error()
|
||||
}
|
||||
|
||||
win32.ReleaseSRWLockExclusive(&cwd_lock)
|
||||
|
||||
return
|
||||
}
|
||||
change_directory :: set_current_directory
|
||||
|
||||
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
// Mode is unused on Windows, but is needed on *nix
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.CreateDirectoryW(wpath, nil) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
remove_directory :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.RemoveDirectoryW(wpath) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private, require_results)
|
||||
is_abs :: proc(path: string) -> bool {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
return true
|
||||
}
|
||||
when ODIN_OS == .Windows {
|
||||
if len(path) > 2 {
|
||||
switch path[0] {
|
||||
case 'A'..='Z', 'a'..='z':
|
||||
return path[1] == ':' && is_path_separator(path[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@(private, require_results)
|
||||
fix_long_path :: proc(path: string) -> string {
|
||||
if len(path) < 248 {
|
||||
return path
|
||||
}
|
||||
|
||||
if len(path) >= 2 && path[:2] == `\\` {
|
||||
return path
|
||||
}
|
||||
if !is_abs(path) {
|
||||
return path
|
||||
}
|
||||
|
||||
prefix :: `\\?`
|
||||
|
||||
path_buf, _ := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator)
|
||||
copy(path_buf, prefix)
|
||||
n := len(path)
|
||||
r, w := 0, len(prefix)
|
||||
for r < n {
|
||||
switch {
|
||||
case is_path_separator(path[r]):
|
||||
r += 1
|
||||
case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])):
|
||||
r += 1
|
||||
case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])):
|
||||
return path
|
||||
case:
|
||||
path_buf[w] = '\\'
|
||||
w += 1
|
||||
for ; r < n && !is_path_separator(path[r]); r += 1 {
|
||||
path_buf[w] = path[r]
|
||||
w += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if w == len(`\\?\c:`) {
|
||||
path_buf[w] = '\\'
|
||||
w += 1
|
||||
}
|
||||
return string(path_buf[:w])
|
||||
}
|
||||
|
||||
|
||||
link :: proc(old_name, new_name: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
n := win32.utf8_to_wstring(fix_long_path(new_name))
|
||||
o := win32.utf8_to_wstring(fix_long_path(old_name))
|
||||
return Platform_Error(win32.CreateHardLinkW(n, o, nil))
|
||||
}
|
||||
|
||||
unlink :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.DeleteFileW(wpath) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
rename :: proc(old_path, new_path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
|
||||
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
|
||||
|
||||
if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) {
|
||||
curr_off := seek(fd, 0, 1) or_return
|
||||
defer seek(fd, curr_off, 0)
|
||||
_= seek(fd, length, 0) or_return
|
||||
ok := win32.SetEndOfFile(win32.HANDLE(fd))
|
||||
if !ok {
|
||||
return get_last_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
truncate :: proc(path: string, length: i64) -> (err: Error) {
|
||||
fd := open(path, O_WRONLY|O_CREATE, 0o666) or_return
|
||||
defer close(fd)
|
||||
return ftruncate(fd, length)
|
||||
}
|
||||
|
||||
|
||||
remove :: proc(name: string) -> Error {
|
||||
p := win32.utf8_to_wstring(fix_long_path(name))
|
||||
err, err1: win32.DWORD
|
||||
if !win32.DeleteFileW(p) {
|
||||
err = win32.GetLastError()
|
||||
}
|
||||
if err == 0 {
|
||||
return nil
|
||||
}
|
||||
if !win32.RemoveDirectoryW(p) {
|
||||
err1 = win32.GetLastError()
|
||||
}
|
||||
if err1 == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err != err1 {
|
||||
a := win32.GetFileAttributesW(p)
|
||||
if a == ~u32(0) {
|
||||
err = win32.GetLastError()
|
||||
} else {
|
||||
if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
err = err1
|
||||
} else if a & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) {
|
||||
err = 0
|
||||
if !win32.DeleteFileW(p) {
|
||||
err = win32.GetLastError()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Platform_Error(err)
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
pipe :: proc() -> (r, w: Handle, err: Error) {
|
||||
sa: win32.SECURITY_ATTRIBUTES
|
||||
sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
|
||||
sa.bInheritHandle = true
|
||||
if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -246,6 +246,13 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
|
||||
if err != .None {
|
||||
return
|
||||
}
|
||||
defer {
|
||||
for s in m {
|
||||
delete(s)
|
||||
}
|
||||
delete(m)
|
||||
}
|
||||
|
||||
dmatches := make([dynamic]string, 0, 0)
|
||||
for d in m {
|
||||
dmatches, err = _glob(d, file, &dmatches)
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
#+build linux, darwin, freebsd, openbsd, netbsd
|
||||
package filepath
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
foreign import libc "system:System.framework"
|
||||
} else {
|
||||
foreign import libc "system:c"
|
||||
}
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
import "core:strings"
|
||||
import "core:sys/posix"
|
||||
|
||||
SEPARATOR :: '/'
|
||||
SEPARATOR_STRING :: `/`
|
||||
@@ -28,11 +24,11 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
|
||||
rel = "."
|
||||
}
|
||||
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
|
||||
path_ptr := realpath(rel_cstr, nil)
|
||||
path_ptr := posix.realpath(rel_cstr, nil)
|
||||
if path_ptr == nil {
|
||||
return "", __error()^ == 0
|
||||
return "", posix.errno() == nil
|
||||
}
|
||||
defer _unix_free(rawptr(path_ptr))
|
||||
defer posix.free(path_ptr)
|
||||
|
||||
path_str := strings.clone(string(path_ptr), allocator)
|
||||
return path_str, true
|
||||
@@ -48,26 +44,3 @@ join :: proc(elems: []string, allocator := context.allocator) -> (joined: string
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@(private)
|
||||
foreign libc {
|
||||
realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
|
||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
|
||||
|
||||
}
|
||||
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
|
||||
@(private)
|
||||
foreign libc {
|
||||
@(link_name="__error") __error :: proc() -> ^i32 ---
|
||||
}
|
||||
} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
||||
@(private)
|
||||
foreign libc {
|
||||
@(link_name="__errno") __error :: proc() -> ^i32 ---
|
||||
}
|
||||
} else {
|
||||
@(private)
|
||||
foreign libc {
|
||||
@(link_name="__errno_location") __error :: proc() -> ^i32 ---
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ Example:
|
||||
defer spall.context_destroy(&spall_ctx)
|
||||
|
||||
buffer_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE)
|
||||
defer delete(buffer_backing)
|
||||
|
||||
spall_buffer = spall.buffer_create(buffer_backing, u32(sync.current_thread_id()))
|
||||
defer spall.buffer_destroy(&spall_ctx, &spall_buffer)
|
||||
|
||||
|
||||
+48
-54
@@ -31,8 +31,6 @@ Type_Info_Enum :: runtime.Type_Info_Enum
|
||||
Type_Info_Map :: runtime.Type_Info_Map
|
||||
Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set
|
||||
Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector
|
||||
Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer
|
||||
Type_Info_Relative_Multi_Pointer :: runtime.Type_Info_Relative_Multi_Pointer
|
||||
Type_Info_Matrix :: runtime.Type_Info_Matrix
|
||||
Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer
|
||||
Type_Info_Bit_Field :: runtime.Type_Info_Bit_Field
|
||||
@@ -67,8 +65,6 @@ Type_Kind :: enum {
|
||||
Map,
|
||||
Bit_Set,
|
||||
Simd_Vector,
|
||||
Relative_Pointer,
|
||||
Relative_Multi_Pointer,
|
||||
Matrix,
|
||||
Soa_Pointer,
|
||||
Bit_Field,
|
||||
@@ -80,35 +76,33 @@ type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
ti := type_info_of(T)
|
||||
if ti != nil {
|
||||
switch _ in ti.variant {
|
||||
case Type_Info_Named: return .Named
|
||||
case Type_Info_Integer: return .Integer
|
||||
case Type_Info_Rune: return .Rune
|
||||
case Type_Info_Float: return .Float
|
||||
case Type_Info_Complex: return .Complex
|
||||
case Type_Info_Quaternion: return .Quaternion
|
||||
case Type_Info_String: return .String
|
||||
case Type_Info_Boolean: return .Boolean
|
||||
case Type_Info_Any: return .Any
|
||||
case Type_Info_Type_Id: return .Type_Id
|
||||
case Type_Info_Pointer: return .Pointer
|
||||
case Type_Info_Multi_Pointer: return .Multi_Pointer
|
||||
case Type_Info_Procedure: return .Procedure
|
||||
case Type_Info_Array: return .Array
|
||||
case Type_Info_Enumerated_Array: return .Enumerated_Array
|
||||
case Type_Info_Dynamic_Array: return .Dynamic_Array
|
||||
case Type_Info_Slice: return .Slice
|
||||
case Type_Info_Parameters: return .Tuple
|
||||
case Type_Info_Struct: return .Struct
|
||||
case Type_Info_Union: return .Union
|
||||
case Type_Info_Enum: return .Enum
|
||||
case Type_Info_Map: return .Map
|
||||
case Type_Info_Bit_Set: return .Bit_Set
|
||||
case Type_Info_Simd_Vector: return .Simd_Vector
|
||||
case Type_Info_Relative_Pointer: return .Relative_Pointer
|
||||
case Type_Info_Relative_Multi_Pointer: return .Relative_Multi_Pointer
|
||||
case Type_Info_Matrix: return .Matrix
|
||||
case Type_Info_Soa_Pointer: return .Soa_Pointer
|
||||
case Type_Info_Bit_Field: return .Bit_Field
|
||||
case Type_Info_Named: return .Named
|
||||
case Type_Info_Integer: return .Integer
|
||||
case Type_Info_Rune: return .Rune
|
||||
case Type_Info_Float: return .Float
|
||||
case Type_Info_Complex: return .Complex
|
||||
case Type_Info_Quaternion: return .Quaternion
|
||||
case Type_Info_String: return .String
|
||||
case Type_Info_Boolean: return .Boolean
|
||||
case Type_Info_Any: return .Any
|
||||
case Type_Info_Type_Id: return .Type_Id
|
||||
case Type_Info_Pointer: return .Pointer
|
||||
case Type_Info_Multi_Pointer: return .Multi_Pointer
|
||||
case Type_Info_Procedure: return .Procedure
|
||||
case Type_Info_Array: return .Array
|
||||
case Type_Info_Enumerated_Array: return .Enumerated_Array
|
||||
case Type_Info_Dynamic_Array: return .Dynamic_Array
|
||||
case Type_Info_Slice: return .Slice
|
||||
case Type_Info_Parameters: return .Tuple
|
||||
case Type_Info_Struct: return .Struct
|
||||
case Type_Info_Union: return .Union
|
||||
case Type_Info_Enum: return .Enum
|
||||
case Type_Info_Map: return .Map
|
||||
case Type_Info_Bit_Set: return .Bit_Set
|
||||
case Type_Info_Simd_Vector: return .Simd_Vector
|
||||
case Type_Info_Matrix: return .Matrix
|
||||
case Type_Info_Soa_Pointer: return .Soa_Pointer
|
||||
case Type_Info_Bit_Field: return .Bit_Field
|
||||
}
|
||||
|
||||
}
|
||||
@@ -723,6 +717,27 @@ enum_name_from_value_any :: proc(value: any) -> (name: string, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Returns whether the value given has a defined name in the enum type.
|
||||
*/
|
||||
@(require_results)
|
||||
enum_value_has_name :: proc(value: $T) -> bool where intrinsics.type_is_enum(T) {
|
||||
when len(T) == cap(T) {
|
||||
return value >= min(T) && value <= max(T)
|
||||
} else {
|
||||
if value < min(T) || value > max(T) {
|
||||
return false
|
||||
}
|
||||
|
||||
for valid_value in T {
|
||||
if valid_value == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1467,21 +1482,6 @@ as_string :: proc(a: any) -> (value: string, valid: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
relative_pointer_to_absolute :: proc(a: any) -> rawptr {
|
||||
if a == nil { return nil }
|
||||
a := a
|
||||
ti := runtime.type_info_core(type_info_of(a.id))
|
||||
a.id = ti.id
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_Relative_Pointer:
|
||||
return relative_pointer_to_absolute_raw(a.data, info.base_integer.id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
|
||||
_handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
|
||||
@@ -1543,10 +1543,6 @@ as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
|
||||
case cstring: value = rawptr(v)
|
||||
case: valid = false
|
||||
}
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
valid = true
|
||||
value = relative_pointer_to_absolute_raw(a.data, info.base_integer.id)
|
||||
}
|
||||
|
||||
return
|
||||
@@ -1656,8 +1652,6 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
Type_Info_Bit_Set,
|
||||
Type_Info_Enum,
|
||||
Type_Info_Simd_Vector,
|
||||
Type_Info_Relative_Pointer,
|
||||
Type_Info_Relative_Multi_Pointer,
|
||||
Type_Info_Soa_Pointer,
|
||||
Type_Info_Matrix:
|
||||
return runtime.memory_compare(a.data, b.data, t.size) == 0
|
||||
|
||||
@@ -158,14 +158,6 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
||||
case Type_Info_Simd_Vector:
|
||||
y := b.variant.(Type_Info_Simd_Vector) or_return
|
||||
return x.count == y.count && x.elem == y.elem
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
y := b.variant.(Type_Info_Relative_Pointer) or_return
|
||||
return x.base_integer == y.base_integer && x.pointer == y.pointer
|
||||
|
||||
case Type_Info_Relative_Multi_Pointer:
|
||||
y := b.variant.(Type_Info_Relative_Multi_Pointer) or_return
|
||||
return x.base_integer == y.base_integer && x.pointer == y.pointer
|
||||
|
||||
case Type_Info_Matrix:
|
||||
y := b.variant.(Type_Info_Matrix) or_return
|
||||
@@ -392,18 +384,6 @@ is_simd_vector :: proc(info: ^Type_Info) -> bool {
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Simd_Vector)
|
||||
return ok
|
||||
}
|
||||
@(require_results)
|
||||
is_relative_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Relative_Pointer)
|
||||
return ok
|
||||
}
|
||||
@(require_results)
|
||||
is_relative_multi_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Relative_Multi_Pointer)
|
||||
return ok
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
@@ -736,18 +716,6 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt
|
||||
io.write_i64(w, i64(info.count), 10, &n) or_return
|
||||
io.write_byte(w, ']', &n) or_return
|
||||
write_type(w, info.elem, &n) or_return
|
||||
|
||||
case Type_Info_Relative_Pointer:
|
||||
io.write_string(w, "#relative(", &n) or_return
|
||||
write_type(w, info.base_integer, &n) or_return
|
||||
io.write_string(w, ") ", &n) or_return
|
||||
write_type(w, info.pointer, &n) or_return
|
||||
|
||||
case Type_Info_Relative_Multi_Pointer:
|
||||
io.write_string(w, "#relative(", &n) or_return
|
||||
write_type(w, info.base_integer, &n) or_return
|
||||
io.write_string(w, ") ", &n) or_return
|
||||
write_type(w, info.pointer, &n) or_return
|
||||
|
||||
case Type_Info_Matrix:
|
||||
if info.layout == .Row_Major {
|
||||
|
||||
+39
-14
@@ -48,22 +48,41 @@ to_type :: proc(buf: []u8, $T: typeid) -> (T, bool) #optional_ok {
|
||||
}
|
||||
|
||||
/*
|
||||
Turn a slice of one type, into a slice of another type.
|
||||
Turn a slice of one type, into a slice of another type.
|
||||
|
||||
Only converts the type and length of the slice itself.
|
||||
The length is rounded down to the nearest whole number of items.
|
||||
Only converts the type and length of the slice itself.
|
||||
The length is rounded down to the nearest whole number of items.
|
||||
|
||||
Example:
|
||||
|
||||
import "core:fmt"
|
||||
import "core:slice"
|
||||
|
||||
i64s_as_i32s :: proc() {
|
||||
large_items := []i64{1, 2, 3, 4}
|
||||
small_items := slice.reinterpret([]i32, large_items)
|
||||
assert(len(small_items) == 8)
|
||||
fmt.println(large_items, "->", small_items)
|
||||
}
|
||||
|
||||
bytes_as_i64s :: proc() {
|
||||
small_items := [12]byte{}
|
||||
small_items[0] = 1
|
||||
small_items[8] = 2
|
||||
large_items := slice.reinterpret([]i64, small_items[:])
|
||||
assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes.
|
||||
fmt.println(small_items, "->", large_items)
|
||||
}
|
||||
|
||||
reinterpret_example :: proc() {
|
||||
i64s_as_i32s()
|
||||
bytes_as_i64s()
|
||||
}
|
||||
|
||||
Output:
|
||||
[1, 2, 3, 4] -> [1, 0, 2, 0, 3, 0, 4, 0]
|
||||
[1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0] -> [1]
|
||||
|
||||
```
|
||||
large_items := []i64{1, 2, 3, 4}
|
||||
small_items := slice.reinterpret([]i32, large_items)
|
||||
assert(len(small_items) == 8)
|
||||
```
|
||||
```
|
||||
small_items := []byte{1, 0, 0, 0, 0, 0, 0, 0,
|
||||
2, 0, 0, 0}
|
||||
large_items := slice.reinterpret([]i64, small_items)
|
||||
assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes.
|
||||
```
|
||||
*/
|
||||
@(require_results)
|
||||
reinterpret :: proc "contextless" ($T: typeid/[]$U, s: []$V) -> []U {
|
||||
@@ -471,6 +490,12 @@ is_empty :: proc(a: $T/[]$E) -> bool {
|
||||
return len(a) == 0
|
||||
}
|
||||
|
||||
// Gets the byte size of the backing data
|
||||
@(require_results)
|
||||
size :: proc "contextless" (a: $T/[]$E) -> int {
|
||||
return len(a) * size_of(E)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
|
||||
@@ -1121,6 +1121,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
|
||||
break trunc_block
|
||||
}
|
||||
f := f64(mantissa)
|
||||
f_abs := f
|
||||
if neg {
|
||||
f = -f
|
||||
}
|
||||
@@ -1132,7 +1133,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
|
||||
f *= pow10[exp-22]
|
||||
exp = 22
|
||||
}
|
||||
if f > 1e15 || f < 1e-15 {
|
||||
if f_abs > 1e15 || f_abs < 1e-15 {
|
||||
break trunc_block
|
||||
}
|
||||
return f * pow10[exp], nr, true
|
||||
|
||||
@@ -1872,7 +1872,8 @@ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) {
|
||||
lowest_index := len(s)
|
||||
found := false
|
||||
for substr in substrs {
|
||||
if i := index(s, substr); i >= 0 {
|
||||
haystack := s[:min(len(s), lowest_index + len(substr))]
|
||||
if i := index(haystack, substr); i >= 0 {
|
||||
if i < lowest_index {
|
||||
lowest_index = i
|
||||
width = len(substr)
|
||||
|
||||
@@ -12,8 +12,8 @@ _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
|
||||
when !intrinsics.has_target_feature("atomics") {
|
||||
panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
|
||||
} else {
|
||||
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
|
||||
return s != 0
|
||||
_ = intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati
|
||||
panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
|
||||
} else {
|
||||
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration))
|
||||
return s != 0
|
||||
return s != 2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +30,7 @@ _futex_signal :: proc "contextless" (f: ^Futex) {
|
||||
when !intrinsics.has_target_feature("atomics") {
|
||||
panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
|
||||
} else {
|
||||
loop: for {
|
||||
s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
|
||||
if s >= 1 {
|
||||
return
|
||||
}
|
||||
}
|
||||
_ = intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,12 +38,7 @@ _futex_broadcast :: proc "contextless" (f: ^Futex) {
|
||||
when !intrinsics.has_target_feature("atomics") {
|
||||
panic_contextless("usage of `core:sync` requires the `-target-feature:\"atomics\"` or a `-microarch` that supports it")
|
||||
} else {
|
||||
loop: for {
|
||||
s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0))
|
||||
if s >= 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
_ = intrinsics.wasm_memory_atomic_notify32((^u32)(f), max(u32))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ atomic_mutex_unlock :: proc "contextless" (m: ^Atomic_Mutex) {
|
||||
|
||||
switch atomic_exchange_explicit(&m.state, .Unlocked, .Release) {
|
||||
case .Unlocked:
|
||||
unreachable()
|
||||
// Kind of okay - unlocking while already unlocked.
|
||||
case .Locked:
|
||||
// Okay
|
||||
case .Waiting:
|
||||
|
||||
@@ -108,6 +108,16 @@ Application_setMainMenu :: proc "c" (self: ^Application, menu: ^Menu) {
|
||||
msgSend(nil, self, "setMainMenu:", menu)
|
||||
}
|
||||
|
||||
@(objc_type=Application, objc_name="mainWindow")
|
||||
Application_mainWindow :: proc "c" (self: ^Application) -> ^Window {
|
||||
return msgSend(^Window, self, "mainWindow")
|
||||
}
|
||||
|
||||
@(objc_type=Application, objc_name="keyWindow")
|
||||
Application_keyWindow :: proc "c" (self: ^Application) -> ^Window {
|
||||
return msgSend(^Window, self, "keyWindow")
|
||||
}
|
||||
|
||||
@(objc_type=Application, objc_name="windows")
|
||||
Application_windows :: proc "c" (self: ^Application) -> ^Array {
|
||||
return msgSend(^Array, self, "windows")
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package objc_Foundation
|
||||
|
||||
@(objc_class="NSObjectProtocol")
|
||||
ObjectProtocol :: struct {using _: Object}
|
||||
// TODO: implement NSObjectProtocol
|
||||
@@ -0,0 +1,203 @@
|
||||
package objc_Foundation
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:c"
|
||||
|
||||
@(objc_class="NSProcessInfo")
|
||||
ProcessInfo :: struct {using _: Object}
|
||||
|
||||
// Getting the Process Information Agent
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="processInfo", objc_is_class_method=true)
|
||||
ProcessInfo_processInfo :: proc "c" () -> ^ProcessInfo {
|
||||
return msgSend(^ProcessInfo, ProcessInfo, "processInfo")
|
||||
}
|
||||
|
||||
// Accessing Process Information
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="arguments")
|
||||
ProcessInfo_arguments :: proc "c" (self: ^ProcessInfo) -> ^Array {
|
||||
return msgSend(^Array, self, "arguments")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="environment")
|
||||
ProcessInfo_environment :: proc "c" (self: ^ProcessInfo) -> ^Dictionary {
|
||||
return msgSend(^Dictionary, self, "environment")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="globallyUniqueString")
|
||||
ProcessInfo_globallyUniqueString :: proc "c" (self: ^ProcessInfo) -> ^String {
|
||||
return msgSend(^String, self, "globallyUniqueString")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="isMacCatalystApp")
|
||||
ProcessInfo_isMacCatalystApp :: proc "c" (self: ^ProcessInfo) -> bool {
|
||||
return msgSend(bool, self, "isMacCatalystApp")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="isiOSAppOnMac")
|
||||
ProcessInfo_isiOSAppOnMac :: proc "c" (self: ^ProcessInfo) -> bool {
|
||||
return msgSend(bool, self, "isiOSAppOnMac")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="processIdentifier")
|
||||
ProcessInfo_processIdentifier :: proc "c" (self: ^ProcessInfo) -> c.int {
|
||||
return msgSend(c.int, self, "processIdentifier")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="processName")
|
||||
ProcessInfo_processName :: proc "c" (self: ^ProcessInfo) -> ^String {
|
||||
return msgSend(^String, self, "processName")
|
||||
}
|
||||
|
||||
// Accessing User Information
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="userName")
|
||||
ProcessInfo_userName :: proc "c" (self: ^ProcessInfo) -> ^String {
|
||||
return msgSend(^String, self, "userName")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="fullUserName")
|
||||
ProcessInfo_fullUserName :: proc "c" (self: ^ProcessInfo) -> ^String {
|
||||
return msgSend(^String, self, "fullUserName")
|
||||
}
|
||||
|
||||
// Sudden Application Termination
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="disableSuddenTermination")
|
||||
ProcessInfo_disableSuddenTermination :: proc "c" (self: ^ProcessInfo) {
|
||||
msgSend(nil, self, "disableSuddenTermination")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="enableSuddenTermination")
|
||||
ProcessInfo_enableSuddenTermination :: proc "c" (self: ^ProcessInfo) {
|
||||
msgSend(nil, self, "enableSuddenTermination")
|
||||
}
|
||||
|
||||
// Controlling Automatic Termination
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="disableAutomaticTermination")
|
||||
ProcessInfo_disableAutomaticTermination :: proc "c" (self: ^ProcessInfo, reason: ^String) {
|
||||
msgSend(nil, self, "disableAutomaticTermination:", reason)
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="enableAutomaticTermination")
|
||||
ProcessInfo_enableAutomaticTermination :: proc "c" (self: ^ProcessInfo, reason: ^String) {
|
||||
msgSend(nil, self, "enableAutomaticTermination:", reason)
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="automaticTerminationSupportEnabled")
|
||||
ProcessInfo_automaticTerminationSupportEnabled :: proc "c" (self: ^ProcessInfo) -> bool {
|
||||
return msgSend(bool, self, "automaticTerminationSupportEnabled")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="setAutomaticTerminationSupportEnabled")
|
||||
ProcessInfo_setAutomaticTerminationSupportEnabled :: proc "c" (self: ^ProcessInfo, automaticTerminationSupportEnabled: bool) {
|
||||
msgSend(nil, self, "setAutomaticTerminationSupportEnabled:", automaticTerminationSupportEnabled)
|
||||
}
|
||||
|
||||
// Getting Host Information
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="hostName")
|
||||
ProcessInfo_hostName :: proc "c" (self: ^ProcessInfo) -> ^String {
|
||||
return msgSend(^String, self, "hostName")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="operatingSystemVersionString")
|
||||
ProcessInfo_operatingSystemVersionString :: proc "c" (self: ^ProcessInfo) -> ^String {
|
||||
return msgSend(^String, self, "operatingSystemVersionString")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="operatingSystemVersion")
|
||||
ProcessInfo_operatingSystemVersion :: proc "c" (self: ^ProcessInfo) -> OperatingSystemVersion {
|
||||
return msgSend(OperatingSystemVersion, self, "operatingSystemVersion")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="isOperatingSystemAtLeastVersion")
|
||||
ProcessInfo_isOperatingSystemAtLeastVersion :: proc "c" (self: ^ProcessInfo, version: OperatingSystemVersion) -> bool {
|
||||
return msgSend(bool, self, "isOperatingSystemAtLeastVersion:", version)
|
||||
}
|
||||
|
||||
// Getting Computer Information
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="processorCount")
|
||||
ProcessInfo_processorCount :: proc "c" (self: ^ProcessInfo) -> UInteger {
|
||||
return msgSend(UInteger, self, "processorCount")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="activeProcessorCount")
|
||||
ProcessInfo_activeProcessorCount :: proc "c" (self: ^ProcessInfo) -> UInteger {
|
||||
return msgSend(UInteger, self, "activeProcessorCount")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="physicalMemory")
|
||||
ProcessInfo_physicalMemory :: proc "c" (self: ^ProcessInfo) -> c.ulonglong {
|
||||
return msgSend(c.ulonglong, self, "physicalMemory")
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="systemUptime")
|
||||
ProcessInfo_systemUptime :: proc "c" (self: ^ProcessInfo) -> TimeInterval {
|
||||
return msgSend(TimeInterval, self, "systemUptime")
|
||||
}
|
||||
|
||||
// Managing Activities
|
||||
|
||||
@(private)
|
||||
log2 :: intrinsics.constant_log2
|
||||
|
||||
ActivityOptionsBits :: enum u64 {
|
||||
IdleDisplaySleepDisabled = log2(1099511627776), // Require the screen to stay powered on.
|
||||
IdleSystemSleepDisabled = log2(1048576), // Prevent idle sleep.
|
||||
SuddenTerminationDisabled = log2(16384), // Prevent sudden termination.
|
||||
AutomaticTerminationDisabled = log2(32768), // Prevent automatic termination.
|
||||
AnimationTrackingEnabled = log2(35184372088832), // Track activity with an animation signpost interval.
|
||||
TrackingEnabled = log2(70368744177664), // Track activity with a signpost interval.
|
||||
UserInitiated = log2(16777215), // Performing a user-requested action.
|
||||
UserInitiatedAllowingIdleSystemSleep = log2(15728639), // Performing a user-requested action, but the system can sleep on idle.
|
||||
Background = log2(255), // Initiated some kind of work, but not as the direct result of a user request.
|
||||
LatencyCritical = log2(1095216660480), // Requires the highest amount of timer and I/O precision available.
|
||||
UserInteractive = log2(1095233437695), // Responding to user interaction.
|
||||
}
|
||||
ActivityOptions :: bit_set[ActivityOptionsBits; u64]
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="beginActivityWithOptions")
|
||||
ProcessInfo_beginActivityWithOptions :: proc "c" (self: ^ProcessInfo, options: ActivityOptions, reason: ^String) -> ^ObjectProtocol {
|
||||
return msgSend(^ObjectProtocol, self, "beginActivityWithOptions:reason:", options, reason)
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="endActivity")
|
||||
ProcessInfo_endActivity :: proc "c" (self: ^ProcessInfo, activity: ^ObjectProtocol) {
|
||||
msgSend(nil, self, "endActivity:", activity)
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="performActivityWithOptions")
|
||||
ProcessInfo_performActivityWithOptions :: proc "c" (self: ^ProcessInfo, options: ActivityOptions, reason: ^String, block: proc "c" ()) {
|
||||
msgSend(nil, self, "performActivityWithOptions:reason:usingBlock:", options, reason, block)
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="performExpiringActivityWithReason")
|
||||
ProcessInfo_performExpiringActivityWithReason :: proc "c" (self: ^ProcessInfo, reason: ^String, block: proc "c" (expired: bool)) {
|
||||
msgSend(nil, self, "performExpiringActivityWithReason:usingBlock:", reason, block)
|
||||
}
|
||||
|
||||
// Getting the Thermal State
|
||||
|
||||
ProcessInfoThermalState :: enum c.long {
|
||||
Nominal,
|
||||
Fair,
|
||||
Serious,
|
||||
Critical,
|
||||
}
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="thermalState")
|
||||
ProcessInfo_thermalState :: proc "c" (self: ^ProcessInfo) -> ProcessInfoThermalState {
|
||||
return msgSend(ProcessInfoThermalState, self, "thermalState")
|
||||
}
|
||||
|
||||
// Determining Whether Low Power Mode is Enabled
|
||||
|
||||
@(objc_type=ProcessInfo, objc_name="isLowPowerModeEnabled")
|
||||
ProcessInfo_isLowPowerModeEnabled :: proc "c" (self: ^ProcessInfo) -> bool {
|
||||
return msgSend(bool, self, "isLowPowerModeEnabled")
|
||||
}
|
||||
@@ -20,7 +20,7 @@ BOOL :: bool // TODO(bill): should this be `distinct`?
|
||||
YES :: true
|
||||
NO :: false
|
||||
|
||||
OperatingSystemVersion :: struct #packed {
|
||||
OperatingSystemVersion :: struct #align(8) {
|
||||
majorVersion: Integer,
|
||||
minorVersion: Integer,
|
||||
patchVersion: Integer,
|
||||
@@ -58,4 +58,4 @@ when size_of(Float) == 8 {
|
||||
} else {
|
||||
_POINT_ENCODING :: "{NSPoint=ff}"
|
||||
_SIZE_ENCODING :: "{NSSize=ff}"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,23 @@ CPU_Feature :: enum u64 {
|
||||
ssse3, // Supplemental streaming SIMD extension 3
|
||||
sse41, // Streaming SIMD extension 4 and 4.1
|
||||
sse42, // Streaming SIMD extension 4 and 4.2
|
||||
|
||||
avx512bf16, // Vector Neural Network Instructions supporting bfloat16
|
||||
avx512bitalg, // Bit Algorithms
|
||||
avx512bw, // Byte and Word instructions
|
||||
avx512cd, // Conflict Detection instructions
|
||||
avx512dq, // Doubleword and Quadword instructions
|
||||
avx512er, // Exponential and Reciprocal instructions
|
||||
avx512f, // Foundation
|
||||
avx512fp16, // Vector 16-bit float instructions
|
||||
avx512ifma, // Integer Fused Multiply Add
|
||||
avx512pf, // Prefetch instructions
|
||||
avx512vbmi, // Vector Byte Manipulation Instructions
|
||||
avx512vbmi2, // Vector Byte Manipulation Instructions 2
|
||||
avx512vl, // Vector Length extensions
|
||||
avx512vnni, // Vector Neural Network Instructions
|
||||
avx512vp2intersect, // Vector Pair Intersection to a Pair of Mask Registers
|
||||
avx512vpopcntdq, // Vector Population Count for Doubleword and Quadword
|
||||
}
|
||||
|
||||
CPU_Features :: distinct bit_set[CPU_Feature; u64]
|
||||
@@ -82,9 +99,11 @@ init_cpu_features :: proc "c" () {
|
||||
//
|
||||
// See: crbug.com/375968
|
||||
os_supports_avx := false
|
||||
os_supports_avx512 := false
|
||||
if .os_xsave in set && is_set(26, ecx1) {
|
||||
eax, _ := xgetbv(0)
|
||||
os_supports_avx = is_set(1, eax) && is_set(2, eax)
|
||||
os_supports_avx512 = is_set(5, eax) && is_set(6, eax) && is_set(7, eax)
|
||||
}
|
||||
if os_supports_avx {
|
||||
try_set(&set, .avx, 28, ecx1)
|
||||
@@ -94,11 +113,37 @@ init_cpu_features :: proc "c" () {
|
||||
return
|
||||
}
|
||||
|
||||
_, ebx7, _, _ := cpuid(7, 0)
|
||||
_, ebx7, ecx7, edx7 := cpuid(7, 0)
|
||||
try_set(&set, .bmi1, 3, ebx7)
|
||||
if os_supports_avx {
|
||||
try_set(&set, .avx2, 5, ebx7)
|
||||
}
|
||||
if os_supports_avx512 {
|
||||
try_set(&set, .avx512f, 16, ebx7)
|
||||
try_set(&set, .avx512dq, 17, ebx7)
|
||||
try_set(&set, .avx512ifma, 21, ebx7)
|
||||
try_set(&set, .avx512pf, 26, ebx7)
|
||||
try_set(&set, .avx512er, 27, ebx7)
|
||||
try_set(&set, .avx512cd, 28, ebx7)
|
||||
try_set(&set, .avx512bw, 30, ebx7)
|
||||
|
||||
// XMM/YMM are also required for 128/256-bit instructions
|
||||
if os_supports_avx {
|
||||
try_set(&set, .avx512vl, 31, ebx7)
|
||||
}
|
||||
|
||||
try_set(&set, .avx512vbmi, 1, ecx7)
|
||||
try_set(&set, .avx512vbmi2, 6, ecx7)
|
||||
try_set(&set, .avx512vnni, 11, ecx7)
|
||||
try_set(&set, .avx512bitalg, 12, ecx7)
|
||||
try_set(&set, .avx512vpopcntdq, 14, ecx7)
|
||||
|
||||
try_set(&set, .avx512vp2intersect, 8, edx7)
|
||||
try_set(&set, .avx512fp16, 23, edx7)
|
||||
|
||||
eax7_1, _, _, _ := cpuid(7, 1)
|
||||
try_set(&set, .avx512bf16, 5, eax7_1)
|
||||
}
|
||||
try_set(&set, .bmi2, 8, ebx7)
|
||||
try_set(&set, .erms, 9, ebx7)
|
||||
try_set(&set, .rdseed, 18, ebx7)
|
||||
|
||||
@@ -4,7 +4,7 @@ Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Laytan: ARM and RISC-V CPU feature detection.
|
||||
Laytan: ARM and RISC-V CPU feature detection, iOS/macOS platform overhaul.
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,581 +1,101 @@
|
||||
package sysinfo
|
||||
|
||||
import sys "core:sys/unix"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "base:runtime"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:sys/unix"
|
||||
import NS "core:sys/darwin/Foundation"
|
||||
|
||||
@(private)
|
||||
version_string_buf: [1024]u8
|
||||
|
||||
@(init, private)
|
||||
init_os_version :: proc () {
|
||||
os_version.platform = .MacOS
|
||||
init_platform :: proc() {
|
||||
ws :: strings.write_string
|
||||
wi :: strings.write_int
|
||||
|
||||
// Start building display version
|
||||
b := strings.builder_from_bytes(version_string_buf[:])
|
||||
|
||||
mib := []i32{sys.CTL_KERN, sys.KERN_OSVERSION}
|
||||
build_buf: [12]u8
|
||||
version: NS.OperatingSystemVersion
|
||||
{
|
||||
NS.scoped_autoreleasepool()
|
||||
|
||||
ok := sys.sysctl(mib, &build_buf)
|
||||
if !ok {
|
||||
strings.write_string(&b, "macOS Unknown")
|
||||
os_version.as_string = strings.to_string(b)
|
||||
return
|
||||
info := NS.ProcessInfo.processInfo()
|
||||
version = info->operatingSystemVersion()
|
||||
mem := info->physicalMemory()
|
||||
|
||||
ram.total_ram = int(mem)
|
||||
}
|
||||
|
||||
build := string(cstring(&build_buf[0]))
|
||||
macos_version = {int(version.majorVersion), int(version.minorVersion), int(version.patchVersion)}
|
||||
|
||||
// Do we have an exact match?
|
||||
match: Darwin_Match
|
||||
rel, exact := macos_release_map[build]
|
||||
|
||||
if exact {
|
||||
match = .Exact
|
||||
when ODIN_PLATFORM_SUBTARGET == .iOS {
|
||||
os_version.platform = .iOS
|
||||
ws(&b, "iOS")
|
||||
} else {
|
||||
os_version.platform = .MacOS
|
||||
switch version.majorVersion {
|
||||
case 15: ws(&b, "macOS Sequoia")
|
||||
case 14: ws(&b, "macOS Sonoma")
|
||||
case 13: ws(&b, "macOS Ventura")
|
||||
case 12: ws(&b, "macOS Monterey")
|
||||
case 11: ws(&b, "macOS Big Sur")
|
||||
case 10:
|
||||
switch version.minorVersion {
|
||||
case 15: ws(&b, "macOS Catalina")
|
||||
case 14: ws(&b, "macOS Mojave")
|
||||
case 13: ws(&b, "macOS High Sierra")
|
||||
case 12: ws(&b, "macOS Sierra")
|
||||
case 11: ws(&b, "OS X El Capitan")
|
||||
case 10: ws(&b, "OS X Yosemite")
|
||||
case:
|
||||
// `ProcessInfo.operatingSystemVersion` is 10.10 and up.
|
||||
unreachable()
|
||||
}
|
||||
case:
|
||||
// New version not yet added here.
|
||||
assert(version.majorVersion > 15)
|
||||
ws(&b, "macOS Unknown")
|
||||
}
|
||||
}
|
||||
|
||||
ws(&b, " ")
|
||||
wi(&b, int(version.majorVersion))
|
||||
ws(&b, ".")
|
||||
wi(&b, int(version.minorVersion))
|
||||
ws(&b, ".")
|
||||
wi(&b, int(version.patchVersion))
|
||||
|
||||
{
|
||||
build_buf: [12]u8
|
||||
mib := []i32{unix.CTL_KERN, unix.KERN_OSVERSION}
|
||||
ok := unix.sysctl(mib, &build_buf)
|
||||
build := string(cstring(raw_data(build_buf[:]))) if ok else "Unknown"
|
||||
|
||||
ws(&b, " (build ")
|
||||
|
||||
build_start := len(b.buf)
|
||||
ws(&b, build)
|
||||
os_version.version = string(b.buf[build_start:][:len(build)])
|
||||
}
|
||||
|
||||
{
|
||||
// Match on XNU kernel version
|
||||
mib = []i32{sys.CTL_KERN, sys.KERN_OSRELEASE}
|
||||
version_bits: [12]u8 // enough for 999.999.999\x00
|
||||
have_kernel_version := sys.sysctl(mib, &version_bits)
|
||||
mib := []i32{unix.CTL_KERN, unix.KERN_OSRELEASE}
|
||||
ok := unix.sysctl(mib, &version_bits)
|
||||
kernel := string(cstring(raw_data(version_bits[:]))) if ok else "Unknown"
|
||||
|
||||
major_ok, minor_ok, patch_ok: bool
|
||||
major, _, tail := strings.partition(kernel, ".")
|
||||
minor, _, patch := strings.partition(tail, ".")
|
||||
|
||||
tmp := runtime.default_temp_allocator_temp_begin()
|
||||
os_version.major, _ = strconv.parse_int(major, 10)
|
||||
os_version.minor, _ = strconv.parse_int(minor, 10)
|
||||
os_version.patch, _ = strconv.parse_int(patch, 10)
|
||||
|
||||
triplet := strings.split(string(cstring(&version_bits[0])), ".", context.temp_allocator)
|
||||
if len(triplet) != 3 {
|
||||
have_kernel_version = false
|
||||
} else {
|
||||
rel.darwin.x, major_ok = strconv.parse_int(triplet[0])
|
||||
rel.darwin.y, minor_ok = strconv.parse_int(triplet[1])
|
||||
rel.darwin.z, patch_ok = strconv.parse_int(triplet[2])
|
||||
|
||||
if !(major_ok && minor_ok && patch_ok) {
|
||||
have_kernel_version = false
|
||||
}
|
||||
}
|
||||
|
||||
runtime.default_temp_allocator_temp_end(tmp)
|
||||
|
||||
if !have_kernel_version {
|
||||
// We don't know the kernel version, but we do know the build
|
||||
strings.write_string(&b, "macOS Unknown (build ")
|
||||
l := strings.builder_len(b)
|
||||
strings.write_string(&b, build)
|
||||
os_version.version = strings.to_string(b)[l:]
|
||||
strings.write_rune(&b, ')')
|
||||
os_version.as_string = strings.to_string(b)
|
||||
return
|
||||
}
|
||||
rel, match = map_darwin_kernel_version_to_macos_release(build, rel.darwin)
|
||||
ws(&b, ", kernel ")
|
||||
ws(&b, kernel)
|
||||
ws(&b, ")")
|
||||
}
|
||||
|
||||
os_version.major = rel.darwin.x
|
||||
os_version.minor = rel.darwin.y
|
||||
os_version.patch = rel.darwin.z
|
||||
|
||||
macos_version = transmute(Version)rel.release.version
|
||||
|
||||
strings.write_string(&b, rel.os_name)
|
||||
if match == .Exact || match == .Nearest {
|
||||
strings.write_rune(&b, ' ')
|
||||
strings.write_string(&b, rel.release.name)
|
||||
strings.write_rune(&b, ' ')
|
||||
strings.write_int(&b, rel.release.version.x)
|
||||
if rel.release.version.y > 0 || rel.release.version.z > 0 {
|
||||
strings.write_rune(&b, '.')
|
||||
strings.write_int(&b, rel.release.version.y)
|
||||
}
|
||||
if rel.release.version.z > 0 {
|
||||
strings.write_rune(&b, '.')
|
||||
strings.write_int(&b, rel.release.version.z)
|
||||
}
|
||||
if match == .Nearest {
|
||||
strings.write_rune(&b, '?')
|
||||
}
|
||||
} else {
|
||||
strings.write_string(&b, " Unknown")
|
||||
}
|
||||
|
||||
strings.write_string(&b, " (build ")
|
||||
l := strings.builder_len(b)
|
||||
strings.write_string(&b, build)
|
||||
os_version.version = strings.to_string(b)[l:]
|
||||
|
||||
strings.write_string(&b, ", kernel ")
|
||||
strings.write_int(&b, rel.darwin.x)
|
||||
strings.write_rune(&b, '.')
|
||||
strings.write_int(&b, rel.darwin.y)
|
||||
strings.write_rune(&b, '.')
|
||||
strings.write_int(&b, rel.darwin.z)
|
||||
strings.write_rune(&b, ')')
|
||||
|
||||
os_version.as_string = strings.to_string(b)
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
init_ram :: proc() {
|
||||
// Retrieve RAM info using `sysctl`
|
||||
|
||||
mib := []i32{sys.CTL_HW, sys.HW_MEMSIZE}
|
||||
mem_size: u64
|
||||
if sys.sysctl(mib, &mem_size) {
|
||||
ram.total_ram = int(mem_size)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
Darwin_To_Release :: struct {
|
||||
darwin: [3]int, // Darwin kernel triplet
|
||||
os_name: string, // OS X, MacOS
|
||||
release: struct {
|
||||
name: string, // Monterey, Mojave, etc.
|
||||
version: [3]int, // 12.4, etc.
|
||||
},
|
||||
}
|
||||
|
||||
// Important: Order from lowest to highest kernel version
|
||||
@(private)
|
||||
macos_release_map: map[string]Darwin_To_Release = {
|
||||
// MacOS Tiger
|
||||
"8A428" = {{8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}},
|
||||
"8A432" = {{8, 0, 0}, "macOS", {"Tiger", {10, 4, 0}}},
|
||||
"8B15" = {{8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}},
|
||||
"8B17" = {{8, 1, 0}, "macOS", {"Tiger", {10, 4, 1}}},
|
||||
"8C46" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
|
||||
"8C47" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
|
||||
"8E102" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
|
||||
"8E45" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
|
||||
"8E90" = {{8, 2, 0}, "macOS", {"Tiger", {10, 4, 2}}},
|
||||
"8F46" = {{8, 3, 0}, "macOS", {"Tiger", {10, 4, 3}}},
|
||||
"8G32" = {{8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}},
|
||||
"8G1165" = {{8, 4, 0}, "macOS", {"Tiger", {10, 4, 4}}},
|
||||
"8H14" = {{8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}},
|
||||
"8G1454" = {{8, 5, 0}, "macOS", {"Tiger", {10, 4, 5}}},
|
||||
"8I127" = {{8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}},
|
||||
"8I1119" = {{8, 6, 0}, "macOS", {"Tiger", {10, 4, 6}}},
|
||||
"8J135" = {{8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
|
||||
"8J2135a" = {{8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
|
||||
"8K1079" = {{8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
|
||||
"8N5107" = {{8, 7, 0}, "macOS", {"Tiger", {10, 4, 7}}},
|
||||
"8L127" = {{8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}},
|
||||
"8L2127" = {{8, 8, 0}, "macOS", {"Tiger", {10, 4, 8}}},
|
||||
"8P135" = {{8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}},
|
||||
"8P2137" = {{8, 9, 0}, "macOS", {"Tiger", {10, 4, 9}}},
|
||||
"8R218" = {{8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
|
||||
"8R2218" = {{8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
|
||||
"8R2232" = {{8, 10, 0}, "macOS", {"Tiger", {10, 4, 10}}},
|
||||
"8S165" = {{8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}},
|
||||
"8S2167" = {{8, 11, 0}, "macOS", {"Tiger", {10, 4, 11}}},
|
||||
|
||||
// MacOS Leopard
|
||||
"9A581" = {{9, 0, 0}, "macOS", {"Leopard", {10, 5, 0}}},
|
||||
"9B18" = {{9, 1, 0}, "macOS", {"Leopard", {10, 5, 1}}},
|
||||
"9B2117" = {{9, 1, 1}, "macOS", {"Leopard", {10, 5, 1}}},
|
||||
"9C31" = {{9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}},
|
||||
"9C7010" = {{9, 2, 0}, "macOS", {"Leopard", {10, 5, 2}}},
|
||||
"9D34" = {{9, 3, 0}, "macOS", {"Leopard", {10, 5, 3}}},
|
||||
"9E17" = {{9, 4, 0}, "macOS", {"Leopard", {10, 5, 4}}},
|
||||
"9F33" = {{9, 5, 0}, "macOS", {"Leopard", {10, 5, 5}}},
|
||||
"9G55" = {{9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
|
||||
"9G66" = {{9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
|
||||
"9G71" = {{9, 6, 0}, "macOS", {"Leopard", {10, 5, 6}}},
|
||||
"9J61" = {{9, 7, 0}, "macOS", {"Leopard", {10, 5, 7}}},
|
||||
"9L30" = {{9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}},
|
||||
"9L34" = {{9, 8, 0}, "macOS", {"Leopard", {10, 5, 8}}},
|
||||
|
||||
// MacOS Snow Leopard
|
||||
"10A432" = {{10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}},
|
||||
"10A433" = {{10, 0, 0}, "macOS", {"Snow Leopard", {10, 6, 0}}},
|
||||
"10B504" = {{10, 1, 0}, "macOS", {"Snow Leopard", {10, 6, 1}}},
|
||||
"10C540" = {{10, 2, 0}, "macOS", {"Snow Leopard", {10, 6, 2}}},
|
||||
"10D573" = {{10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
|
||||
"10D575" = {{10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
|
||||
"10D578" = {{10, 3, 0}, "macOS", {"Snow Leopard", {10, 6, 3}}},
|
||||
"10F569" = {{10, 4, 0}, "macOS", {"Snow Leopard", {10, 6, 4}}},
|
||||
"10H574" = {{10, 5, 0}, "macOS", {"Snow Leopard", {10, 6, 5}}},
|
||||
"10J567" = {{10, 6, 0}, "macOS", {"Snow Leopard", {10, 6, 6}}},
|
||||
"10J869" = {{10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
|
||||
"10J3250" = {{10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
|
||||
"10J4138" = {{10, 7, 0}, "macOS", {"Snow Leopard", {10, 6, 7}}},
|
||||
"10K540" = {{10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}},
|
||||
"10K549" = {{10, 8, 0}, "macOS", {"Snow Leopard", {10, 6, 8}}},
|
||||
|
||||
// MacOS Lion
|
||||
"11A511" = {{11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}},
|
||||
"11A511s" = {{11, 0, 0}, "macOS", {"Lion", {10, 7, 0}}},
|
||||
"11A2061" = {{11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}},
|
||||
"11A2063" = {{11, 0, 2}, "macOS", {"Lion", {10, 7, 0}}},
|
||||
"11B26" = {{11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}},
|
||||
"11B2118" = {{11, 1, 0}, "macOS", {"Lion", {10, 7, 1}}},
|
||||
"11C74" = {{11, 2, 0}, "macOS", {"Lion", {10, 7, 2}}},
|
||||
"11D50" = {{11, 3, 0}, "macOS", {"Lion", {10, 7, 3}}},
|
||||
"11E53" = {{11, 4, 0}, "macOS", {"Lion", {10, 7, 4}}},
|
||||
"11G56" = {{11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}},
|
||||
"11G63" = {{11, 4, 2}, "macOS", {"Lion", {10, 7, 5}}},
|
||||
|
||||
// MacOS Mountain Lion
|
||||
"12A269" = {{12, 0, 0}, "macOS", {"Mountain Lion", {10, 8, 0}}},
|
||||
"12B19" = {{12, 1, 0}, "macOS", {"Mountain Lion", {10, 8, 1}}},
|
||||
"12C54" = {{12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
|
||||
"12C60" = {{12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
|
||||
"12C2034" = {{12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
|
||||
"12C3104" = {{12, 2, 0}, "macOS", {"Mountain Lion", {10, 8, 2}}},
|
||||
"12D78" = {{12, 3, 0}, "macOS", {"Mountain Lion", {10, 8, 3}}},
|
||||
"12E55" = {{12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
|
||||
"12E3067" = {{12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
|
||||
"12E4022" = {{12, 4, 0}, "macOS", {"Mountain Lion", {10, 8, 4}}},
|
||||
"12F37" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
|
||||
"12F45" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
|
||||
"12F2501" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
|
||||
"12F2518" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
|
||||
"12F2542" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
|
||||
"12F2560" = {{12, 5, 0}, "macOS", {"Mountain Lion", {10, 8, 5}}},
|
||||
|
||||
// MacOS Mavericks
|
||||
"13A603" = {{13, 0, 0}, "macOS", {"Mavericks", {10, 9, 0}}},
|
||||
"13B42" = {{13, 0, 0}, "macOS", {"Mavericks", {10, 9, 1}}},
|
||||
"13C64" = {{13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}},
|
||||
"13C1021" = {{13, 1, 0}, "macOS", {"Mavericks", {10, 9, 2}}},
|
||||
"13D65" = {{13, 2, 0}, "macOS", {"Mavericks", {10, 9, 3}}},
|
||||
"13E28" = {{13, 3, 0}, "macOS", {"Mavericks", {10, 9, 4}}},
|
||||
"13F34" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
|
||||
"13F1066" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
|
||||
"13F1077" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
|
||||
"13F1096" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
|
||||
"13F1112" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
|
||||
"13F1134" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
|
||||
"13F1507" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
|
||||
"13F1603" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
|
||||
"13F1712" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
|
||||
"13F1808" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
|
||||
"13F1911" = {{13, 4, 0}, "macOS", {"Mavericks", {10, 9, 5}}},
|
||||
|
||||
// MacOS Yosemite
|
||||
"14A389" = {{14, 0, 0}, "macOS", {"Yosemite", {10, 10, 0}}},
|
||||
"14B25" = {{14, 0, 0}, "macOS", {"Yosemite", {10, 10, 1}}},
|
||||
"14C109" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
|
||||
"14C1510" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
|
||||
"14C2043" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
|
||||
"14C1514" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
|
||||
"14C2513" = {{14, 1, 0}, "macOS", {"Yosemite", {10, 10, 2}}},
|
||||
"14D131" = {{14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}},
|
||||
"14D136" = {{14, 3, 0}, "macOS", {"Yosemite", {10, 10, 3}}},
|
||||
"14E46" = {{14, 4, 0}, "macOS", {"Yosemite", {10, 10, 4}}},
|
||||
"14F27" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
"14F1021" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
"14F1505" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
"14F1509" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
"14F1605" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
"14F1713" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
"14F1808" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
"14F1909" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
"14F1912" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
"14F2009" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
"14F2109" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
"14F2315" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
"14F2411" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
"14F2511" = {{14, 5, 0}, "macOS", {"Yosemite", {10, 10, 5}}},
|
||||
|
||||
// MacOS El Capitan
|
||||
"15A284" = {{15, 0, 0}, "macOS", {"El Capitan", {10, 11, 0}}},
|
||||
"15B42" = {{15, 0, 0}, "macOS", {"El Capitan", {10, 11, 1}}},
|
||||
"15C50" = {{15, 2, 0}, "macOS", {"El Capitan", {10, 11, 2}}},
|
||||
"15D21" = {{15, 3, 0}, "macOS", {"El Capitan", {10, 11, 3}}},
|
||||
"15E65" = {{15, 4, 0}, "macOS", {"El Capitan", {10, 11, 4}}},
|
||||
"15F34" = {{15, 5, 0}, "macOS", {"El Capitan", {10, 11, 5}}},
|
||||
"15G31" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G1004" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G1011" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G1108" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G1212" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G1217" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G1421" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G1510" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G1611" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G17023" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G18013" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G19009" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G20015" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G21013" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
"15G22010" = {{15, 6, 0}, "macOS", {"El Capitan", {10, 11, 6}}},
|
||||
|
||||
// MacOS Sierra
|
||||
"16A323" = {{16, 0, 0}, "macOS", {"Sierra", {10, 12, 0}}},
|
||||
"16B2555" = {{16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}},
|
||||
"16B2657" = {{16, 1, 0}, "macOS", {"Sierra", {10, 12, 1}}},
|
||||
"16C67" = {{16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}},
|
||||
"16C68" = {{16, 3, 0}, "macOS", {"Sierra", {10, 12, 2}}},
|
||||
"16D32" = {{16, 4, 0}, "macOS", {"Sierra", {10, 12, 3}}},
|
||||
"16E195" = {{16, 5, 0}, "macOS", {"Sierra", {10, 12, 4}}},
|
||||
"16F73" = {{16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}},
|
||||
"16F2073" = {{16, 6, 0}, "macOS", {"Sierra", {10, 12, 5}}},
|
||||
"16G29" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G1036" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G1114" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G1212" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G1314" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G1408" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G1510" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G1618" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G1710" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G1815" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G1917" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G1918" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G2016" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G2127" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G2128" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
"16G2136" = {{16, 7, 0}, "macOS", {"Sierra", {10, 12, 6}}},
|
||||
|
||||
// MacOS High Sierra
|
||||
"17A365" = {{17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}},
|
||||
"17A405" = {{17, 0, 0}, "macOS", {"High Sierra", {10, 13, 0}}},
|
||||
"17B48" = {{17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
|
||||
"17B1002" = {{17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
|
||||
"17B1003" = {{17, 2, 0}, "macOS", {"High Sierra", {10, 13, 1}}},
|
||||
"17C88" = {{17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
|
||||
"17C89" = {{17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
|
||||
"17C205" = {{17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
|
||||
"17C2205" = {{17, 3, 0}, "macOS", {"High Sierra", {10, 13, 2}}},
|
||||
"17D47" = {{17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
|
||||
"17D2047" = {{17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
|
||||
"17D102" = {{17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
|
||||
"17D2102" = {{17, 4, 0}, "macOS", {"High Sierra", {10, 13, 3}}},
|
||||
"17E199" = {{17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}},
|
||||
"17E202" = {{17, 5, 0}, "macOS", {"High Sierra", {10, 13, 4}}},
|
||||
"17F77" = {{17, 6, 0}, "macOS", {"High Sierra", {10, 13, 5}}},
|
||||
"17G65" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G2208" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G2307" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G3025" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G4015" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G5019" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G6029" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G6030" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G7024" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G8029" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G8030" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G8037" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G9016" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G10021" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G11023" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G12034" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G13033" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G13035" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G14019" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G14033" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
"17G14042" = {{17, 7, 0}, "macOS", {"High Sierra", {10, 13, 6}}},
|
||||
|
||||
// MacOS Mojave
|
||||
"18A391" = {{18, 0, 0}, "macOS", {"Mojave", {10, 14, 0}}},
|
||||
"18B75" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
|
||||
"18B2107" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
|
||||
"18B3094" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 1}}},
|
||||
"18C54" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 2}}},
|
||||
"18D42" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
|
||||
"18D43" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
|
||||
"18D109" = {{18, 2, 0}, "macOS", {"Mojave", {10, 14, 3}}},
|
||||
"18E226" = {{18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}},
|
||||
"18E227" = {{18, 5, 0}, "macOS", {"Mojave", {10, 14, 4}}},
|
||||
"18F132" = {{18, 6, 0}, "macOS", {"Mojave", {10, 14, 5}}},
|
||||
"18G84" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G87" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G95" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G103" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G1012" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G2022" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G3020" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G4032" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G5033" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G6020" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G6032" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G6042" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G7016" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G8012" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G8022" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G9028" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G9216" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
"18G9323" = {{18, 7, 0}, "macOS", {"Mojave", {10, 14, 6}}},
|
||||
|
||||
// MacOS Catalina
|
||||
"19A583" = {{19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
|
||||
"19A602" = {{19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
|
||||
"19A603" = {{19, 0, 0}, "macOS", {"Catalina", {10, 15, 0}}},
|
||||
"19B88" = {{19, 0, 0}, "macOS", {"Catalina", {10, 15, 1}}},
|
||||
"19C57" = {{19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}},
|
||||
"19C58" = {{19, 2, 0}, "macOS", {"Catalina", {10, 15, 2}}},
|
||||
"19D76" = {{19, 3, 0}, "macOS", {"Catalina", {10, 15, 3}}},
|
||||
"19E266" = {{19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}},
|
||||
"19E287" = {{19, 4, 0}, "macOS", {"Catalina", {10, 15, 4}}},
|
||||
"19F96" = {{19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}},
|
||||
"19F101" = {{19, 5, 0}, "macOS", {"Catalina", {10, 15, 5}}},
|
||||
"19G73" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}},
|
||||
"19G2021" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 6}}},
|
||||
"19H2" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H4" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H15" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H114" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H512" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H524" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H1030" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H1217" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H1323" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H1417" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H1419" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H1519" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H1615" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H1713" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H1715" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H1824" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H1922" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
"19H2026" = {{19, 6, 0}, "macOS", {"Catalina", {10, 15, 7}}},
|
||||
|
||||
// MacOS Big Sur
|
||||
"20A2411" = {{20, 1, 0}, "macOS", {"Big Sur", {11, 0, 0}}},
|
||||
"20B29" = {{20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}},
|
||||
"20B50" = {{20, 1, 0}, "macOS", {"Big Sur", {11, 0, 1}}},
|
||||
"20C69" = {{20, 2, 0}, "macOS", {"Big Sur", {11, 1, 0}}},
|
||||
"20D64" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 0}}},
|
||||
"20D74" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}},
|
||||
"20D75" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 1}}},
|
||||
"20D80" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 2}}},
|
||||
"20D91" = {{20, 3, 0}, "macOS", {"Big Sur", {11, 2, 3}}},
|
||||
"20E232" = {{20, 4, 0}, "macOS", {"Big Sur", {11, 3, 0}}},
|
||||
"20E241" = {{20, 4, 0}, "macOS", {"Big Sur", {11, 3, 1}}},
|
||||
"20F71" = {{20, 5, 0}, "macOS", {"Big Sur", {11, 4, 0}}},
|
||||
"20G71" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 5, 0}}},
|
||||
"20G80" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 5, 1}}},
|
||||
"20G95" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 5, 2}}},
|
||||
"20G165" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 0}}},
|
||||
"20G224" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 1}}},
|
||||
"20G314" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 2}}},
|
||||
"20G415" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 3}}},
|
||||
"20G417" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 4}}},
|
||||
"20G527" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 5}}},
|
||||
"20G624" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 6}}},
|
||||
"20G630" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 7}}},
|
||||
"20G730" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 6, 8}}},
|
||||
"20G817" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 0}}},
|
||||
"20G918" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 1}}},
|
||||
"20G1020" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 2}}},
|
||||
"20G1116" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 3}}},
|
||||
"20G1120" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 4}}},
|
||||
"20G1225" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 5}}},
|
||||
"20G1231" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 6}}},
|
||||
"20G1345" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 7}}},
|
||||
"20G1351" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 8}}},
|
||||
"20G1426" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 9}}},
|
||||
"20G1427" = {{20, 6, 0}, "macOS", {"Big Sur", {11, 7, 10}}},
|
||||
|
||||
// MacOS Monterey
|
||||
"21A344" = {{21, 0, 1}, "macOS", {"Monterey", {12, 0, 0}}},
|
||||
"21A559" = {{21, 1, 0}, "macOS", {"Monterey", {12, 0, 1}}},
|
||||
"21C52" = {{21, 2, 0}, "macOS", {"Monterey", {12, 1, 0}}},
|
||||
"21D49" = {{21, 3, 0}, "macOS", {"Monterey", {12, 2, 0}}},
|
||||
"21D62" = {{21, 3, 0}, "macOS", {"Monterey", {12, 2, 1}}},
|
||||
"21E230" = {{21, 4, 0}, "macOS", {"Monterey", {12, 3, 0}}},
|
||||
"21E258" = {{21, 4, 0}, "macOS", {"Monterey", {12, 3, 1}}},
|
||||
"21F79" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
|
||||
"21F2081" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
|
||||
"21F2092" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
|
||||
"21G72" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}},
|
||||
"21G83" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}},
|
||||
"21G115" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 0}}},
|
||||
"21G217" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 1}}},
|
||||
"21G320" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 2}}},
|
||||
"21G419" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 3}}},
|
||||
"21G526" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 4}}},
|
||||
"21G531" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 5}}},
|
||||
"21G646" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 6}}},
|
||||
"21G651" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 7}}},
|
||||
"21G725" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 8}}},
|
||||
"21G726" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 9}}},
|
||||
"21G816" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 0}}},
|
||||
"21G920" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 1}}},
|
||||
"21G1974" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 2}}},
|
||||
|
||||
// MacOS Ventura
|
||||
"22A380" = {{22, 1, 0}, "macOS", {"Ventura", {13, 0, 0}}},
|
||||
"22A400" = {{22, 1, 0}, "macOS", {"Ventura", {13, 0, 1}}},
|
||||
"22C65" = {{22, 2, 0}, "macOS", {"Ventura", {13, 1, 0}}},
|
||||
"22D49" = {{22, 3, 0}, "macOS", {"Ventura", {13, 2, 0}}},
|
||||
"22D68" = {{22, 3, 0}, "macOS", {"Ventura", {13, 2, 1}}},
|
||||
"22E252" = {{22, 4, 0}, "macOS", {"Ventura", {13, 3, 0}}},
|
||||
"22E261" = {{22, 4, 0}, "macOS", {"Ventura", {13, 3, 1}}},
|
||||
"22F66" = {{22, 5, 0}, "macOS", {"Ventura", {13, 4, 0}}},
|
||||
"22F82" = {{22, 5, 0}, "macOS", {"Ventura", {13, 4, 1}}},
|
||||
"22E772610a" = {{22, 5, 0}, "macOS", {"Ventura", {13, 4, 1}}},
|
||||
"22F770820d" = {{22, 5, 0}, "macOS", {"Ventura", {13, 4, 1}}},
|
||||
"22G74" = {{22, 6, 0}, "macOS", {"Ventura", {13, 5, 0}}},
|
||||
"22G90" = {{22, 6, 0}, "macOS", {"Ventura", {13, 5, 1}}},
|
||||
"22G91" = {{22, 6, 0}, "macOS", {"Ventura", {13, 5, 2}}},
|
||||
"22G120" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 0}}},
|
||||
"22G313" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 1}}},
|
||||
"22G320" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 2}}},
|
||||
|
||||
// MacOS Sonoma
|
||||
"23A344" = {{23, 0, 0}, "macOS", {"Sonoma", {14, 0, 0}}},
|
||||
"23B74" = {{23, 1, 0}, "macOS", {"Sonoma", {14, 1, 0}}},
|
||||
"23B81" = {{23, 1, 0}, "macOS", {"Sonoma", {14, 1, 1}}},
|
||||
"23B2082" = {{23, 1, 0}, "macOS", {"Sonoma", {14, 1, 1}}},
|
||||
"23B92" = {{23, 1, 0}, "macOS", {"Sonoma", {14, 1, 2}}},
|
||||
"23B2091" = {{23, 1, 0}, "macOS", {"Sonoma", {14, 1, 2}}},
|
||||
"23C64" = {{23, 2, 0}, "macOS", {"Sonoma", {14, 2, 0}}},
|
||||
"23C71" = {{23, 2, 0}, "macOS", {"Sonoma", {14, 2, 1}}},
|
||||
"23D56" = {{23, 3, 0}, "macOS", {"Sonoma", {14, 3, 0}}},
|
||||
"23D60" = {{23, 3, 0}, "macOS", {"Sonoma", {14, 3, 1}}},
|
||||
"23E214" = {{23, 4, 0}, "macOS", {"Sonoma", {14, 4, 0}}},
|
||||
"23E224" = {{23, 4, 0}, "macOS", {"Sonoma", {14, 4, 1}}},
|
||||
"23F79" = {{23, 5, 0}, "macOS", {"Sonoma", {14, 5, 0}}},
|
||||
"23G80" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 0}}},
|
||||
"23G93" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 1}}},
|
||||
"23H124" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 7, 0}}},
|
||||
|
||||
// MacOS Sequoia
|
||||
"24A335" = {{24, 0, 0}, "macOS", {"Sequoia", {15, 0, 0}}},
|
||||
"24A348" = {{24, 0, 0}, "macOS", {"Sequoia", {15, 0, 1}}},
|
||||
}
|
||||
|
||||
@(private)
|
||||
Darwin_Match :: enum {
|
||||
Unknown,
|
||||
Exact,
|
||||
Nearest,
|
||||
}
|
||||
|
||||
@(private)
|
||||
map_darwin_kernel_version_to_macos_release :: proc(build: string, darwin: [3]int) -> (res: Darwin_To_Release, match: Darwin_Match) {
|
||||
// Find exact release match if possible.
|
||||
if v, v_ok := macos_release_map[build]; v_ok {
|
||||
return v, .Exact
|
||||
}
|
||||
|
||||
nearest: Darwin_To_Release
|
||||
for _, v in macos_release_map {
|
||||
// Try an exact match on XNU version first.
|
||||
if darwin == v.darwin {
|
||||
return v, .Exact
|
||||
}
|
||||
|
||||
// Major kernel version needs to match exactly,
|
||||
// otherwise the release is considered .Unknown
|
||||
if darwin.x == v.darwin.x {
|
||||
if nearest == {} {
|
||||
nearest = v
|
||||
}
|
||||
if darwin.y >= v.darwin.y && v.darwin != nearest.darwin {
|
||||
nearest = v
|
||||
if darwin.z >= v.darwin.z && v.darwin != nearest.darwin {
|
||||
nearest = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if nearest == {} {
|
||||
return {darwin, "macOS", {"Unknown", {}}}, .Unknown
|
||||
} else {
|
||||
return nearest, .Nearest
|
||||
}
|
||||
os_version.as_string = string(b.buf[:])
|
||||
}
|
||||
|
||||
+88
-36
@@ -152,43 +152,65 @@ Errno :: enum i32 {
|
||||
RDONLY flag is not present, because it has the value of 0, i.e. it is the
|
||||
default, unless WRONLY or RDWR is specified.
|
||||
*/
|
||||
Open_Flags_Bits :: enum {
|
||||
WRONLY = 0,
|
||||
RDWR = 1,
|
||||
CREAT = 6,
|
||||
EXCL = 7,
|
||||
NOCTTY = 8,
|
||||
TRUNC = 9,
|
||||
APPEND = 10,
|
||||
NONBLOCK = 11,
|
||||
DSYNC = 12,
|
||||
ASYNC = 13,
|
||||
DIRECT = 14,
|
||||
LARGEFILE = 15,
|
||||
DIRECTORY = 16,
|
||||
NOFOLLOW = 17,
|
||||
NOATIME = 18,
|
||||
CLOEXEC = 19,
|
||||
PATH = 21,
|
||||
when ODIN_ARCH != .arm64 && ODIN_ARCH != .arm32 {
|
||||
Open_Flags_Bits :: enum {
|
||||
WRONLY = 0,
|
||||
RDWR = 1,
|
||||
CREAT = 6,
|
||||
EXCL = 7,
|
||||
NOCTTY = 8,
|
||||
TRUNC = 9,
|
||||
APPEND = 10,
|
||||
NONBLOCK = 11,
|
||||
DSYNC = 12,
|
||||
ASYNC = 13,
|
||||
DIRECT = 14,
|
||||
LARGEFILE = 15,
|
||||
DIRECTORY = 16,
|
||||
NOFOLLOW = 17,
|
||||
NOATIME = 18,
|
||||
CLOEXEC = 19,
|
||||
PATH = 21,
|
||||
}
|
||||
// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
|
||||
#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
|
||||
#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
|
||||
#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
|
||||
#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
|
||||
#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
|
||||
#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
|
||||
#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
|
||||
#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
|
||||
#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
|
||||
#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
|
||||
} else {
|
||||
Open_Flags_Bits :: enum {
|
||||
WRONLY = 0,
|
||||
RDWR = 1,
|
||||
CREAT = 6,
|
||||
EXCL = 7,
|
||||
NOCTTY = 8,
|
||||
TRUNC = 9,
|
||||
APPEND = 10,
|
||||
NONBLOCK = 11,
|
||||
DSYNC = 12,
|
||||
ASYNC = 13,
|
||||
DIRECTORY = 14,
|
||||
NOFOLLOW = 15,
|
||||
DIRECT = 16,
|
||||
LARGEFILE = 17,
|
||||
NOATIME = 18,
|
||||
CLOEXEC = 19,
|
||||
PATH = 21,
|
||||
}
|
||||
}
|
||||
// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
|
||||
#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
|
||||
#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
|
||||
#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
|
||||
#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
|
||||
#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
|
||||
#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
|
||||
#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
|
||||
#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
|
||||
#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
|
||||
#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
|
||||
|
||||
/*
|
||||
Bits for FD_Flags bitset
|
||||
@@ -519,6 +541,36 @@ Fd_Poll_Events_Bits :: enum {
|
||||
RDHUP = 13,
|
||||
}
|
||||
|
||||
Inotify_Init_Bits :: enum {
|
||||
NONBLOCK = 11,
|
||||
CLOEXEC = 19,
|
||||
}
|
||||
|
||||
Inotify_Event_Bits :: enum u32 {
|
||||
ACCESS = 0,
|
||||
MODIFY = 1,
|
||||
ATTRIB = 2,
|
||||
CLOSE_WRITE = 3,
|
||||
CLOSE_NOWRITE = 4,
|
||||
OPEN = 5,
|
||||
MOVED_FROM = 6,
|
||||
MOVED_TO = 7,
|
||||
CREATE = 8,
|
||||
DELETE = 9,
|
||||
DELETE_SELF = 10,
|
||||
MOVE_SELF = 11,
|
||||
UNMOUNT = 13,
|
||||
Q_OVERFLOW = 14,
|
||||
IGNORED = 15,
|
||||
ONLYDIR = 24,
|
||||
DONT_FOLLOW = 25,
|
||||
EXCL_UNLINK = 26,
|
||||
MASK_CREATE = 28,
|
||||
MASK_ADD = 29,
|
||||
ISDIR = 30,
|
||||
ONESHOT = 31,
|
||||
}
|
||||
|
||||
/*
|
||||
Bits for Mem_Protection bitfield
|
||||
*/
|
||||
|
||||
@@ -135,6 +135,31 @@ STATX_BASIC_STATS :: Statx_Mask {
|
||||
.BLOCKS,
|
||||
}
|
||||
|
||||
IN_ALL_EVENTS :: Inotify_Event_Mask {
|
||||
.ACCESS,
|
||||
.MODIFY,
|
||||
.ATTRIB,
|
||||
.CLOSE_WRITE,
|
||||
.CLOSE_NOWRITE,
|
||||
.OPEN,
|
||||
.MOVED_FROM,
|
||||
.MOVED_TO,
|
||||
.CREATE,
|
||||
.DELETE,
|
||||
.DELETE_SELF,
|
||||
.MOVE_SELF,
|
||||
}
|
||||
|
||||
IN_CLOSE :: Inotify_Event_Mask {
|
||||
.CLOSE_WRITE,
|
||||
.CLOSE_NOWRITE,
|
||||
}
|
||||
|
||||
IN_MOVE :: Inotify_Event_Mask {
|
||||
.MOVED_FROM,
|
||||
.MOVED_TO,
|
||||
}
|
||||
|
||||
/*
|
||||
Tell `shmget` to create a new key
|
||||
*/
|
||||
|
||||
+22
-3
@@ -2536,11 +2536,30 @@ waitid :: proc "contextless" (id_type: Id_Type, id: Id, sig_info: ^Sig_Info, opt
|
||||
|
||||
// TODO(flysand): ioprio_get
|
||||
|
||||
// TODO(flysand): inotify_init
|
||||
inotify_init :: proc "contextless" () -> (Fd, Errno) {
|
||||
when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
|
||||
ret := syscall(SYS_inotify_init1, 0)
|
||||
return errno_unwrap(ret, Fd)
|
||||
} else {
|
||||
ret := syscall(SYS_inotify_init)
|
||||
return errno_unwrap(ret, Fd)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(flysand): inotify_add_watch
|
||||
inotify_init1 :: proc "contextless" (flags: Inotify_Init_Flags) -> (Fd, Errno) {
|
||||
ret := syscall(SYS_inotify_init1, transmute(i32)flags)
|
||||
return errno_unwrap(ret, Fd)
|
||||
}
|
||||
|
||||
// TODO(flysand): inotify_rm_watch
|
||||
inotify_add_watch :: proc "contextless" (fd: Fd, pathname: cstring, mask: Inotify_Event_Mask) -> (Wd, Errno) {
|
||||
ret := syscall(SYS_inotify_add_watch, fd, transmute(uintptr) pathname, transmute(u32) mask)
|
||||
return errno_unwrap(ret, Wd)
|
||||
}
|
||||
|
||||
inotify_rm_watch :: proc "contextless" (fd: Fd, wd: Wd) -> (Errno) {
|
||||
ret := syscall(SYS_inotify_rm_watch, fd, wd)
|
||||
return Errno(-ret)
|
||||
}
|
||||
|
||||
// TODO(flysand): migrate_pages
|
||||
|
||||
|
||||
@@ -30,6 +30,11 @@ Id :: distinct uint
|
||||
*/
|
||||
Fd :: distinct i32
|
||||
|
||||
/*
|
||||
Represents a watch descriptor.
|
||||
*/
|
||||
Wd :: distinct i32
|
||||
|
||||
/*
|
||||
Type for PID file descriptors.
|
||||
*/
|
||||
@@ -343,6 +348,18 @@ Poll_Fd :: struct {
|
||||
revents: Fd_Poll_Events,
|
||||
}
|
||||
|
||||
Inotify_Init_Flags :: bit_set[Inotify_Init_Bits; i32]
|
||||
|
||||
Inotify_Event :: struct {
|
||||
wd: Wd,
|
||||
mask: Inotify_Event_Mask,
|
||||
cookie: u32,
|
||||
len: u32,
|
||||
name: [0]u8,
|
||||
}
|
||||
|
||||
Inotify_Event_Mask :: bit_set[Inotify_Event_Bits; u32]
|
||||
|
||||
/*
|
||||
Specifies protection for memory pages.
|
||||
*/
|
||||
@@ -667,6 +684,14 @@ Address_Family :: distinct Protocol_Family
|
||||
*/
|
||||
Socket_Msg :: bit_set[Socket_Msg_Bits; i32]
|
||||
|
||||
/*
|
||||
Struct representing a generic socket address.
|
||||
*/
|
||||
Sock_Addr :: struct #packed {
|
||||
sa_family: Address_Family,
|
||||
sa_data: [14]u8,
|
||||
}
|
||||
|
||||
/*
|
||||
Struct representing IPv4 socket address.
|
||||
*/
|
||||
@@ -674,6 +699,7 @@ Sock_Addr_In :: struct #packed {
|
||||
sin_family: Address_Family,
|
||||
sin_port: u16be,
|
||||
sin_addr: [4]u8,
|
||||
sin_zero: [size_of(Sock_Addr) - size_of(Address_Family) - size_of(u16be) - size_of([4]u8)]u8,
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -703,6 +729,7 @@ Sock_Addr_Any :: struct #raw_union {
|
||||
family: Address_Family,
|
||||
port: u16be,
|
||||
},
|
||||
using generic: Sock_Addr,
|
||||
using ipv4: Sock_Addr_In,
|
||||
using ipv6: Sock_Addr_In6,
|
||||
using uds: Sock_Addr_Un,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build darwin, linux, freebsd, openbsd, netbsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
|
||||
+41
-26
@@ -1,3 +1,4 @@
|
||||
#+build darwin, linux, freebsd, openbsd, netbsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -29,12 +30,12 @@ foreign lib {
|
||||
panic(string(posix.strerror(posix.errno())))
|
||||
}
|
||||
defer posix.free(list)
|
||||
|
||||
|
||||
entries := list[:ret]
|
||||
for entry in entries {
|
||||
log.info(entry)
|
||||
posix.free(entry)
|
||||
}
|
||||
for entry in entries {
|
||||
log.info(entry)
|
||||
posix.free(entry)
|
||||
}
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/scandir.html ]]
|
||||
*/
|
||||
@@ -53,15 +54,6 @@ foreign lib {
|
||||
*/
|
||||
closedir :: proc(dirp: DIR) -> result ---
|
||||
|
||||
/*
|
||||
Return a file descriptor referring to the same directory as the dirp argument.
|
||||
|
||||
// TODO: this is a macro on NetBSD?
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]]
|
||||
*/
|
||||
dirfd :: proc(dirp: DIR) -> FD ---
|
||||
|
||||
/*
|
||||
Equivalent to the opendir() function except that the directory is specified by a file descriptor
|
||||
rather than by a name.
|
||||
@@ -89,16 +81,16 @@ foreign lib {
|
||||
Returns nil when the end is reached or an error occurred (which sets errno).
|
||||
|
||||
Example:
|
||||
posix.set_errno(.NONE)
|
||||
entry := posix.readdir(dirp)
|
||||
if entry == nil {
|
||||
if errno := posix.errno(); errno != .NONE {
|
||||
panic(string(posix.strerror(errno)))
|
||||
} else {
|
||||
fmt.println("end of directory stream")
|
||||
}
|
||||
} else {
|
||||
fmt.println(entry)
|
||||
posix.set_errno(.NONE)
|
||||
entry := posix.readdir(dirp)
|
||||
if entry == nil {
|
||||
if errno := posix.errno(); errno != .NONE {
|
||||
panic(string(posix.strerror(errno)))
|
||||
} else {
|
||||
fmt.println("end of directory stream")
|
||||
}
|
||||
} else {
|
||||
fmt.println(entry)
|
||||
}
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html ]]
|
||||
@@ -160,11 +152,36 @@ when ODIN_OS == .NetBSD {
|
||||
@(private) LSCANDIR :: "__scandir30"
|
||||
@(private) LOPENDIR :: "__opendir30"
|
||||
@(private) LREADDIR :: "__readdir30"
|
||||
|
||||
/*
|
||||
Return a file descriptor referring to the same directory as the dirp argument.
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]]
|
||||
*/
|
||||
dirfd :: proc "c" (dirp: DIR) -> FD {
|
||||
_dirdesc :: struct {
|
||||
dd_fd: FD,
|
||||
|
||||
// more stuff...
|
||||
}
|
||||
|
||||
return (^_dirdesc)(dirp).dd_fd
|
||||
}
|
||||
|
||||
} else {
|
||||
@(private) LALPHASORT :: "alphasort" + INODE_SUFFIX
|
||||
@(private) LSCANDIR :: "scandir" + INODE_SUFFIX
|
||||
@(private) LOPENDIR :: "opendir" + INODE_SUFFIX
|
||||
@(private) LREADDIR :: "readdir" + INODE_SUFFIX
|
||||
|
||||
foreign lib {
|
||||
/*
|
||||
Return a file descriptor referring to the same directory as the dirp argument.
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]]
|
||||
*/
|
||||
dirfd :: proc(dirp: DIR) -> FD ---
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
@@ -210,6 +227,4 @@ when ODIN_OS == .Darwin {
|
||||
d_name: [256]c.char `fmt:"s,0"`, /* [PSX] entry name */
|
||||
}
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build darwin, linux, freebsd, openbsd, netbsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -120,7 +121,5 @@ when ODIN_OS == .Darwin {
|
||||
_RTLD_LOCAL :: 0
|
||||
RTLD_LOCAL :: RTLD_Flags{}
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build windows, darwin, linux, freebsd, openbsd, netbsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -456,7 +457,84 @@ when ODIN_OS == .Darwin {
|
||||
|
||||
EOWNERDEAD :: 130
|
||||
ENOTRECOVERABLE :: 131
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
} else when ODIN_OS == .Windows {
|
||||
E2BIG :: 7
|
||||
EACCES :: 13
|
||||
EADDRINUSE :: 100
|
||||
EADDRNOTAVAIL :: 101
|
||||
EAFNOSUPPORT :: 102
|
||||
EAGAIN :: 11
|
||||
EALREADY :: 103
|
||||
EBADF :: 9
|
||||
EBADMSG :: 104
|
||||
EBUSY :: 16
|
||||
ECANCELED :: 105
|
||||
ECHILD :: 10
|
||||
ECONNABORTED :: 106
|
||||
ECONNREFUSED :: 107
|
||||
ECONNRESET :: 108
|
||||
EDEADLK :: 36
|
||||
EDESTADDRREQ :: 109
|
||||
EDQUOT :: -1 // NOTE: not defined
|
||||
EEXIST :: 17
|
||||
EFAULT :: 14
|
||||
EFBIG :: 27
|
||||
EHOSTUNREACH :: 110
|
||||
EIDRM :: 111
|
||||
EINPROGRESS :: 112
|
||||
EINTR :: 4
|
||||
EINVAL :: 22
|
||||
EIO :: 5
|
||||
EISCONN :: 113
|
||||
EISDIR :: 21
|
||||
ELOOP :: 114
|
||||
EMFILE :: 24
|
||||
EMLINK :: 31
|
||||
EMSGSIZE :: 115
|
||||
EMULTIHOP :: -1 // NOTE: not defined
|
||||
ENAMETOOLONG :: 38
|
||||
ENETDOWN :: 116
|
||||
ENETRESET :: 117
|
||||
ENETUNREACH :: 118
|
||||
ENFILE :: 23
|
||||
ENOBUFS :: 119
|
||||
ENODATA :: 120
|
||||
ENODEV :: 19
|
||||
ENOENT :: 2
|
||||
ENOEXEC :: 8
|
||||
ENOLCK :: 39
|
||||
ENOLINK :: 121
|
||||
ENOMEM :: 12
|
||||
ENOMSG :: 122
|
||||
ENOPROTOOPT :: 123
|
||||
ENOSPC :: 28
|
||||
ENOSR :: 124
|
||||
ENOSTR :: 125
|
||||
ENOSYS :: 40
|
||||
ENOTCONN :: 126
|
||||
ENOTDIR :: 20
|
||||
ENOTEMPTY :: 41
|
||||
ENOTRECOVERABLE :: 127
|
||||
ENOTSOCK :: 128
|
||||
ENOTSUP :: 129
|
||||
ENOTTY :: 25
|
||||
ENXIO :: 6
|
||||
EOPNOTSUPP :: 130
|
||||
EOVERFLOW :: 132
|
||||
EOWNERDEAD :: 133
|
||||
EPERM :: 1
|
||||
EPIPE :: 32
|
||||
EPROTO :: 134
|
||||
EPROTONOSUPPORT :: 135
|
||||
EPROTOTYPE :: 136
|
||||
EROFS :: 30
|
||||
ESPIPE :: 29
|
||||
ESRCH :: 3
|
||||
ESTALE :: -1 // NOTE: not defined
|
||||
ETIME :: 137
|
||||
ETIMEDOUT :: 138
|
||||
ETXTBSY :: 139
|
||||
EWOULDBLOCK :: 140
|
||||
EXDEV :: 18
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build linux, darwin, openbsd, freebsd, netbsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -466,13 +467,11 @@ when ODIN_OS == .Darwin {
|
||||
AT_REMOVEDIR :: 0x200
|
||||
|
||||
flock :: struct {
|
||||
l_type: Lock_Type, /* [PSX] type of lock. */
|
||||
l_whence: c.short, /* [PSX] flag (Whence) of starting offset. */
|
||||
l_start: off_t, /* [PSX] relative offset in bytes. */
|
||||
l_len: off_t, /* [PSX] size; if 0 then until EOF. */
|
||||
l_pid: pid_t, /* [PSX] process ID of the process holding the lock. */
|
||||
l_type: Lock_Type, /* [PSX] type of lock. */
|
||||
l_whence: c.short, /* [PSX] flag (Whence) of starting offset. */
|
||||
}
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build darwin, linux, openbsd, freebsd, netbsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -61,6 +62,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
|
||||
FNM_NOESCAPE :: 0x02
|
||||
FNM_PERIOD :: 0x04
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build linux, darwin, netbsd, openbsd, freebsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -203,6 +204,4 @@ when ODIN_OS == .Darwin {
|
||||
GLOB_ABORTED :: 2
|
||||
GLOB_NOMATCH :: 3
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build linux, darwin, netbsd, openbsd, freebsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
@@ -125,6 +126,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
|
||||
gr_mem: [^]cstring, /* [PSX] group members */
|
||||
}
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build linux, darwin, netbsd, openbsd, freebsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build linux, darwin, netbsd, openbsd, freebsd
|
||||
package posix
|
||||
|
||||
import "core:c"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build linux, darwin, netbsd, openbsd, freebsd
|
||||
package posix
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
@@ -56,6 +57,7 @@ foreign lib {
|
||||
|
||||
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/basename.html ]]
|
||||
*/
|
||||
@(link_name=LBASENAME)
|
||||
basename :: proc(path: cstring) -> cstring ---
|
||||
|
||||
/*
|
||||
@@ -72,3 +74,9 @@ foreign lib {
|
||||
*/
|
||||
dirname :: proc(path: cstring) -> cstring ---
|
||||
}
|
||||
|
||||
when ODIN_OS == .Linux {
|
||||
@(private) LBASENAME :: "__xpg_basename"
|
||||
} else {
|
||||
@(private) LBASENAME :: "basename"
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#+build linux, darwin, netbsd, openbsd, freebsd
|
||||
package posix
|
||||
|
||||
// limits.h - implementation-defined constants
|
||||
@@ -549,6 +550,4 @@ when ODIN_OS == .Darwin {
|
||||
NL_TEXTMAX :: 2048 // 255 on glibc, 2048 on musl
|
||||
NZERO :: 20
|
||||
|
||||
} else {
|
||||
#panic("posix is unimplemented for the current target")
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user