Merge branch 'master' into extend_win32_api_types

This commit is contained in:
VladPavliuk
2025-01-03 18:53:48 +02:00
277 changed files with 10923 additions and 10179 deletions
+1 -1
View File
@@ -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
+1
View File
@@ -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:
-12
View File
@@ -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,
+3 -15
View File
@@ -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))
}
+2
View File
@@ -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
}
-12
View File
@@ -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[")
+19
View File
@@ -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.
BIN
View File
Binary file not shown.
+5 -2
View File
@@ -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
View File
@@ -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
-154
View File
@@ -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
}
-31
View File
@@ -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
-68
View File
@@ -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
}
-169
View File
@@ -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,
}
-667
View File
@@ -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)
}
-116
View 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,
}
+2 -2
View File
@@ -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>.
```
```
+133
View File
@@ -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
}
+2 -1
View File
@@ -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 {
+7 -5
View File
@@ -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
View File
@@ -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()
}
}
+7 -3
View File
@@ -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
View File
@@ -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
}
}
+7 -3
View File
@@ -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
}
}
+1 -1
View File
@@ -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) {
+20 -5
View File
@@ -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:
+1 -1
View File
@@ -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)))
-6
View File
@@ -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
+2 -1
View File
@@ -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
+48 -24
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
}
+8 -8
View File
@@ -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) {
+6 -6
View File
@@ -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,
+15 -3
View File
@@ -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
+16
View File
@@ -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
View File
@@ -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
+6 -6
View File
@@ -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
View File
@@ -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
}
-23
View File
@@ -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
View File
@@ -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
}
}
+3 -7
View File
@@ -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
+21 -3
View File
@@ -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
+3 -1
View File
@@ -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
+1 -1
View File
@@ -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
}
-584
View File
@@ -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
}
+1 -1
View File
@@ -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) {
+4 -6
View File
@@ -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)
}
+3 -19
View File
@@ -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))
+3 -3
View File
@@ -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))
}
+1 -1
View File
@@ -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
+10 -1
View File
@@ -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.
+11 -36
View File
@@ -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)
}
+1 -1
View File
@@ -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
+27 -11
View File
@@ -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))
+3 -5
View File
@@ -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 {
+2 -5
View File
@@ -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) {
+2 -4
View File
@@ -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) {
+3 -5
View File
@@ -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) {
+2 -4
View File
@@ -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) {
+2 -4
View File
@@ -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
View File
@@ -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
}
+7
View File
@@ -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)
+5 -32
View File
@@ -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 ---
}
}
+2
View File
@@ -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
View File
@@ -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
-32
View File
@@ -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
View File
@@ -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)
+2 -1
View File
@@ -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
+2 -1
View File
@@ -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)
+5 -15
View File
@@ -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))
}
}
+1 -1
View File
@@ -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")
}
+2 -2
View File
@@ -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}"
}
}
+46 -1
View File
@@ -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)
+1 -1
View File
@@ -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.
*/
/*
+78 -558
View File
@@ -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
View File
@@ -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
*/
+25
View File
@@ -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
View File
@@ -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
+27
View File
@@ -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
View File
@@ -1,3 +1,4 @@
#+build darwin, linux, freebsd, openbsd, netbsd
package posix
import "core:c"
+41 -26
View File
@@ -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 -2
View File
@@ -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")
}
+80 -2
View File
@@ -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
}
+3 -4
View File
@@ -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 -2
View File
@@ -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 -2
View File
@@ -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 -2
View File
@@ -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
View File
@@ -1,3 +1,4 @@
#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
+1
View File
@@ -1,3 +1,4 @@
#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
+8
View File
@@ -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 -2
View File
@@ -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