Merge tag 'dev-2024-12'

# Conflicts:
#	vendor/raylib/windows/raylib.dll
#	vendor/raylib/windows/raylibdll.lib
This commit is contained in:
2024-12-13 09:36:58 -05:00
129 changed files with 2705 additions and 5792 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
-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,
+2 -2
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
+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 <= 0xdfff {
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 -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^)
}
}
}
+26 -16
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.
@@ -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
+2
View File
@@ -4,6 +4,8 @@ package dynlib
import "base:runtime"
_LIBRARY_FILE_EXTENSION :: ""
_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
return nil, false
}
+2
View File
@@ -7,6 +7,8 @@ import "base:runtime"
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 {
+2
View File
@@ -8,6 +8,8 @@ import win32 "core:sys/windows"
import "core:strings"
import "core:reflect"
_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)
+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
+38 -19
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
}
}
@@ -314,7 +333,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
advance_token(p)
str := unquote_string(token, p.spec, p.allocator) or_return
dest := any{v.data, ti.id}
if !unmarshal_string_token(p, dest, str, ti) {
if !(unmarshal_string_token(p, dest, str, ti) or_return) {
delete(str, p.allocator)
return UNSUPPORTED_TYPE
}
@@ -398,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)
-12
View File
@@ -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)
+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
+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
+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
-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)
}
+7 -8
View File
@@ -200,22 +200,21 @@ _file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: wi
} 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
}
+1 -1
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 {
+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
}
+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 {
+6
View File
@@ -471,6 +471,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
@@ -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))
}
}
@@ -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}"
}
}
+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[:])
}
+30
View File
@@ -519,6 +519,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
+17
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.
*/
+25 -9
View File
@@ -54,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.
@@ -161,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 {
+13 -9
View File
@@ -116,12 +116,14 @@ Prot_Flag_Bits :: enum c.int {
Prot_Flags :: bit_set[Prot_Flag_Bits; c.int]
Map_Flag_Bits :: enum c.int {
// Map anonymous memory.
ANONYMOUS = log2(MAP_ANONYMOUS),
// Interpret addr exactly.
FIXED = log2(MAP_FIXED),
FIXED = log2(MAP_FIXED),
// Changes are private.
PRIVATE = log2(MAP_PRIVATE),
PRIVATE = log2(MAP_PRIVATE),
// Changes are shared.
SHARED = log2(MAP_SHARED),
SHARED = log2(MAP_SHARED),
}
Map_Flags :: bit_set[Map_Flag_Bits; c.int]
@@ -171,9 +173,10 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS
PROT_READ :: 0x01
PROT_WRITE :: 0x02
MAP_FIXED :: 0x0010
MAP_PRIVATE :: 0x0002
MAP_SHARED :: 0x0001
MAP_FIXED :: 0x0010
MAP_PRIVATE :: 0x0002
MAP_SHARED :: 0x0001
MAP_ANONYMOUS :: 0x0020 when ODIN_OS == .Linux else 0x1000
when ODIN_OS == .Darwin || ODIN_OS == .Linux {
MS_INVALIDATE :: 0x0002
@@ -207,9 +210,10 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS
PROT_READ :: 0x01
PROT_WRITE :: 0x02
MAP_FIXED :: 0x0010
MAP_PRIVATE :: 0x0002
MAP_SHARED :: 0x0001
MAP_FIXED :: 0x0010
MAP_PRIVATE :: 0x0002
MAP_SHARED :: 0x0001
MAP_ANONYMOUS :: 0x1000
MS_ASYNC :: 0x0001
MS_INVALIDATE :: 0x0002
+6
View File
@@ -48,6 +48,12 @@ foreign libc {
addr.sun_family = .UNIX
copy(addr.sun_path[:], "/somepath\x00")
/*
unlink the socket before binding in case
of previous runs not cleaning up the socket
*/
posix.unlink("/somepath")
if posix.bind(sfd, (^posix.sockaddr)(&addr), size_of(addr)) != .OK {
/* Handle error */
}
+2 -2
View File
@@ -186,8 +186,8 @@ Key_Location :: enum u8 {
Numpad = 3,
}
KEYBOARD_MAX_KEY_SIZE :: 16
KEYBOARD_MAX_CODE_SIZE :: 16
KEYBOARD_MAX_KEY_SIZE :: 32
KEYBOARD_MAX_CODE_SIZE :: 32
GAMEPAD_MAX_ID_SIZE :: 64
GAMEPAD_MAX_MAPPING_SIZE :: 64
+2 -2
View File
@@ -1548,8 +1548,8 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
wmi.storeInt(off(W, W), e.key.length)
wmi.storeInt(off(W, W), e.code.length)
wmi.storeString(off(16, 1), e.key);
wmi.storeString(off(16, 1), e.code);
wmi.storeString(off(32, 1), e.key);
wmi.storeString(off(32, 1), e.code);
} else if (e.type === 'scroll') {
wmi.storeF64(off(8, 8), window.scrollX);
wmi.storeF64(off(8, 8), window.scrollY);
+1 -1
View File
@@ -4519,7 +4519,7 @@ DNS_RECORD :: struct { // aka DNS_RECORDA
Flags: DWORD,
dwTtl: DWORD,
_: DWORD,
Data: struct #raw_union {
Data: struct #raw_union #align(4) {
CNAME: DNS_PTR_DATAA,
A: u32be, // Ipv4 Address
AAAA: u128be, // Ipv6 Address
+61
View File
@@ -781,3 +781,64 @@ CF_GDIOBJLAST :: 0x03FF
CF_OWNERDISPLAY :: 0x0080
CF_PRIVATEFIRST :: 0x0200
CF_PRIVATELAST :: 0x02FF
STICKYKEYS :: struct {
cbSize: UINT,
dwFlags: DWORD,
}
LPSTICKYKEYS :: ^STICKYKEYS
SKF_STICKYKEYSON :: 0x1
SKF_AVAILABLE :: 0x2
SKF_HOTKEYACTIVE :: 0x4
SKF_CONFIRMHOTKEY :: 0x8
SKF_HOTKEYSOUND :: 0x10
SKF_INDICATOR :: 0x20
SKF_AUDIBLEFEEDBACK :: 0x40
SKF_TRISTATE :: 0x80
SKF_TWOKEYSOFF :: 0x100
SKF_LSHIFTLOCKED :: 0x10000
SKF_RSHIFTLOCKED :: 0x20000
SKF_LCTLLOCKED :: 0x40000
SKF_RCTLLOCKED :: 0x80000
SKF_LALTLOCKED :: 0x100000
SKF_RALTLOCKED :: 0x200000
SKF_LWINLOCKED :: 0x400000
SKF_RWINLOCKED :: 0x800000
SKF_LSHIFTLATCHED :: 0x1000000
SKF_RSHIFTLATCHED :: 0x2000000
SKF_LCTLLATCHED :: 0x4000000
SKF_RCTLLATCHED :: 0x8000000
SKF_LALTLATCHED :: 0x10000000
SKF_RALTLATCHED :: 0x20000000
TOGGLEKEYS :: struct {
cbSize: UINT,
dwFlags: DWORD,
}
LPTOGGLEKEYS :: ^TOGGLEKEYS
TKF_TOGGLEKEYSON :: 0x1
TKF_AVAILABLE :: 0x2
TKF_HOTKEYACTIVE :: 0x4
TKF_CONFIRMHOTKEY :: 0x8
TKF_HOTKEYSOUND :: 0x10
TKF_INDICATOR :: 0x20
FILTERKEYS :: struct {
cbSize: UINT,
dwFlags: DWORD,
iWaitMSec: DWORD,
iDelayMSec: DWORD,
iRepeatMSec: DWORD,
iBounceMSec: DWORD,
}
LPFILTERKEYS :: ^FILTERKEYS
FKF_FILTERKEYSON :: 0x1
FKF_AVAILABLE :: 0x2
FKF_HOTKEYACTIVE :: 0x4
FKF_CONFIRMHOTKEY :: 0x8
FKF_HOTKEYSOUND :: 0x10
FKF_INDICATOR :: 0x20
FKF_CLICKON :: 0x40
+1 -1
View File
@@ -3,7 +3,7 @@ package sys_windows
foreign import uxtheme "system:UxTheme.lib"
MARGINS :: distinct [4]int
MARGINS :: distinct [4]i32
PMARGINS :: ^MARGINS
@(default_calling_convention="system")
+1
View File
@@ -381,6 +381,7 @@ match_with_preallocated_capture :: proc(
capture.pos[n] = {a, b}
n += 1
}
num_groups = n
}
return
+1 -1
View File
@@ -2,7 +2,7 @@
// It takes a string providing the source, which then can be tokenized through
// repeated calls to the scan procedure.
// For compatibility with existing tooling and languages, the NUL character is not allowed.
// If an UTF-8 encoded byte order mark (BOM) is the first character in the first character in the source, it will be discarded.
// If an UTF-8 encoded byte order mark (BOM) is the first character in the source, it will be discarded.
//
// By default, a Scanner skips white space and Odin comments and recognizes all literals defined by the Odin programming language specification.
// A Scanner may be customized to recognize only a subset of those literals and to recognize different identifiers and white space characters.
+9 -8
View File
@@ -9,6 +9,7 @@ package thread
import "base:intrinsics"
import "core:sync"
import "core:mem"
import "core:container/queue"
Task_Proc :: #type proc(task: Task)
@@ -40,7 +41,7 @@ Pool :: struct {
threads: []^Thread,
tasks: [dynamic]Task,
tasks: queue.Queue(Task),
tasks_done: [dynamic]Task,
}
@@ -69,13 +70,13 @@ pool_thread_runner :: proc(t: ^Thread) {
}
// Once initialized, the pool's memory address is not allowed to change until
// it is destroyed.
// it is destroyed.
//
// The thread pool requires an allocator which it either owns, or which is thread safe.
pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) {
context.allocator = allocator
pool.allocator = allocator
pool.tasks = make([dynamic]Task)
queue.init(&pool.tasks)
pool.tasks_done = make([dynamic]Task)
pool.threads = make([]^Thread, max(thread_count, 1))
@@ -92,7 +93,7 @@ pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) {
}
pool_destroy :: proc(pool: ^Pool) {
delete(pool.tasks)
queue.destroy(&pool.tasks)
delete(pool.tasks_done)
for &t in pool.threads {
@@ -140,11 +141,11 @@ pool_join :: proc(pool: ^Pool) {
// the thread pool. You can even add tasks from inside other tasks.
//
// Each task also needs an allocator which it either owns, or which is thread
// safe.
// safe.
pool_add_task :: proc(pool: ^Pool, allocator: mem.Allocator, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
sync.guard(&pool.mutex)
append(&pool.tasks, Task{
queue.push_back(&pool.tasks, Task{
procedure = procedure,
data = data,
user_index = user_index,
@@ -288,10 +289,10 @@ pool_is_empty :: #force_inline proc(pool: ^Pool) -> bool {
pool_pop_waiting :: proc(pool: ^Pool) -> (task: Task, got_task: bool) {
sync.guard(&pool.mutex)
if len(pool.tasks) != 0 {
if queue.len(pool.tasks) != 0 {
intrinsics.atomic_sub(&pool.num_waiting, 1)
intrinsics.atomic_add(&pool.num_in_processing, 1)
task = pop_front(&pool.tasks)
task = queue.pop_front(&pool.tasks)
got_task = true
}
+16
View File
@@ -203,6 +203,22 @@ generate_rrule_from_tzi :: proc(tzi: ^REG_TZI_FORMAT, abbrevs: TZ_Abbrev, alloca
if err != nil { return }
defer if err != nil { delete(std_name, allocator) }
if (tzi.std_date.month == 0) {
return datetime.TZ_RRule{
has_dst = false,
std_name = std_name,
std_offset = -(i64(tzi.bias) + i64(tzi.std_bias)) * 60,
dst_date = datetime.TZ_Transition_Date{
type = .Month_Week_Day,
month = u8(tzi.std_date.month),
week = u8(tzi.std_date.day),
day = tzi.std_date.day_of_week,
time = (i64(tzi.std_date.hour) * 60 * 60) + (i64(tzi.std_date.minute) * 60) + i64(tzi.std_date.second),
},
}, true
}
dst_name: string
dst_name, err = strings.clone(abbrevs.dst, allocator)
if err != nil { return }
+2
View File
@@ -93,6 +93,8 @@ trans_date_to_seconds :: proc(year: i64, td: datetime.TZ_Transition_Date) -> (se
switch td.type {
case .Month_Week_Day:
if td.month < 1 { return }
t += month_to_seconds(int(td.month) - 1, is_leap)
weekday := ((t + (4 * DAY_SEC)) %% (7 * DAY_SEC)) / DAY_SEC
+8 -11
View File
@@ -536,24 +536,21 @@ parse_tzif :: proc(_buffer: []u8, region_name: string, allocator := context.allo
buffer = buffer[(int(real_hdr.leapcnt) * size_of(Leapsecond_Record)):]
standard_wall_tags := buffer[:int(real_hdr.isstdcnt)]
buffer = buffer[int(real_hdr.isstdcnt):]
ut_tags := buffer[:int(real_hdr.isutcnt)]
for stdwall_tag, idx in standard_wall_tags {
ut_tag := ut_tags[idx]
for stdwall_tag, _ in standard_wall_tags {
if (stdwall_tag != 0 && stdwall_tag != 1) {
return
}
}
buffer = buffer[int(real_hdr.isstdcnt):]
ut_tags := buffer[:int(real_hdr.isutcnt)]
for ut_tag, _ in ut_tags {
if (ut_tag != 0 && ut_tag != 1) {
return
}
if ut_tag == 1 && stdwall_tag != 1 {
return
}
}
buffer = buffer[int(real_hdr.isutcnt):]
// Start of footer
+2 -2
View File
@@ -2,8 +2,8 @@
The `example` directory contains two packages:
A [demo](examples/demo) illustrating the basics of Odin.
A [demo](demo) illustrating the basics of Odin.
It further contains [all](examples/all), which imports all [core](core) and [vendor](vendor) packages so we can conveniently run `odin check` on everything at once.
It further contains [all](all), which imports all [core](/core) and [vendor](/vendor) packages so we can conveniently run `odin check` on everything at once.
For additional example code, see the [examples](https://github.com/odin-lang/examples) repository.
-6
View File
@@ -1,8 +1,2 @@
#+build windows
package all
import c_tokenizer "core:c/frontend/tokenizer"
import c_preprocessor "core:c/frontend/preprocessor"
_ :: c_tokenizer
_ :: c_preprocessor
+1 -18
View File
@@ -2052,22 +2052,6 @@ explicit_context_definition :: proc "c" () {
dummy_procedure()
}
relative_data_types :: proc() {
fmt.println("\n#relative data types")
x: int = 123
ptr: #relative(i16) ^int
ptr = &x
fmt.println(ptr^)
arr := [3]int{1, 2, 3}
multi_ptr: #relative(i16) [^]int
multi_ptr = &arr[0]
fmt.println(multi_ptr)
fmt.println(multi_ptr[:3])
fmt.println(multi_ptr[1])
}
or_else_operator :: proc() {
fmt.println("\n#'or_else'")
{
@@ -2151,7 +2135,7 @@ or_return_operator :: proc() {
return .None
}
foo_2 :: proc() -> (n: int, err: Error) {
// It is more common that your procedure turns multiple values
// It is more common that your procedure returns multiple values
// If 'or_return' is used within a procedure multiple parameters (2+),
// then all the parameters must be named so that the remaining parameters
// so that a bare 'return' statement can be used
@@ -2634,7 +2618,6 @@ main :: proc() {
constant_literal_expressions()
union_maybe()
explicit_context_definition()
relative_data_types()
or_else_operator()
or_return_operator()
or_break_and_or_continue_operators()
+13
View File
@@ -0,0 +1,13 @@
/*
Prints the current date as YYYYMMDD
e.g. 2024-12-25
*/
#include <stdio.h>
#include <time.h>
int main(int arg_count, char const **arg_ptr) {
time_t t = time(NULL);
struct tm* now = localtime(&t);
printf("%04d%02d%02d", now->tm_year + 1900, now->tm_mon + 1, now->tm_mday);
}
+2 -2
View File
@@ -55,12 +55,12 @@ BEGIN
BLOCK "0409FDE9"
BEGIN
VALUE "CompanyName", "https://odin-lang.org/"
VALUE "FileDescription", "Odin general-purpose programming language." // note this is shown in the task manager
VALUE "FileDescription", "Odin" // note this is shown in the task manager
VALUE "FileVersion", QUOTE(VF)
VALUE "InternalName", "odin.exe"
VALUE "LegalCopyright", "Copyright (c) 2016-2024 Ginger Bill. All rights reserved."
VALUE "OriginalFilename", "odin.exe"
VALUE "ProductName", "The Odin Programming Language"
VALUE "ProductName", "Odin Programming Language"
VALUE "ProductVersion", QUOTE(VP)
VALUE "Comments", QUOTE(git-sha: GIT_SHA)
// custom values
+1
View File
@@ -3,6 +3,7 @@ pkgs.mkShell {
name = "odin";
nativeBuildInputs = with pkgs; [
git
which
clang_17
llvmPackages_17.llvm
llvmPackages_17.bintools
+43 -461
View File
@@ -2,12 +2,6 @@
Gather and print platform and version info to help with reporting Odin bugs.
*/
#if !defined(GB_COMPILER_MSVC)
#if defined(GB_CPU_X86)
#include <cpuid.h>
#endif
#endif
#if defined(GB_SYSTEM_LINUX)
#include <sys/utsname.h>
#include <sys/sysinfo.h>
@@ -154,21 +148,6 @@ gb_internal void report_windows_product_type(DWORD ProductType) {
}
#endif
gb_internal void odin_cpuid(int leaf, int result[]) {
#if defined(GB_CPU_ARM) || defined(GB_CPU_RISCV)
return;
#elif defined(GB_CPU_X86)
#if defined(GB_COMPILER_MSVC)
__cpuid(result, leaf);
#else
__get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]);
#endif
#endif
}
gb_internal void report_cpu_info() {
gb_printf("\tCPU: ");
@@ -547,387 +526,46 @@ gb_internal void report_os_info() {
gb_printf(", %s %s\n", info->sysname, info->release);
#elif defined(GB_SYSTEM_OSX)
struct Darwin_To_Release {
const char* build; // 21G83
int darwin[3]; // Darwin kernel triplet
const char* os_name; // OS X, MacOS
struct {
const char* name; // Monterey, Mojave, etc.
int version[3]; // 12.4, etc.
} release;
};
Darwin_To_Release macos_release_map[] = {
{"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}}},
{"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}}},
{"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}}},
{"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}}},
{"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}}},
{"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}}},
{"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}}},
{"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}}},
{"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}}},
{"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}}},
{"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}}},
{"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}}},
{"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}}},
{"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}}},
{"22A380", {13, 0, 0}, "macOS", {"Ventura", {22, 1, 0}}},
{"22A400", {13, 0, 1}, "macOS", {"Ventura", {22, 1, 0}}},
{"22C65", {13, 1, 0}, "macOS", {"Ventura", {22, 2, 0}}},
{"22D49", {13, 2, 0}, "macOS", {"Ventura", {22, 3, 0}}},
{"22D68", {13, 2, 1}, "macOS", {"Ventura", {22, 3, 0}}},
{"22E252", {13, 3, 0}, "macOS", {"Ventura", {22, 4, 0}}},
{"22E261", {13, 3, 1}, "macOS", {"Ventura", {22, 4, 0}}},
{"22F66", {13, 4, 0}, "macOS", {"Ventura", {22, 5, 0}}},
{"22F82", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
{"22E772610a", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
{"22F770820d", {13, 4, 1}, "macOS", {"Ventura", {22, 5, 0}}},
{"22G74", {13, 5, 0}, "macOS", {"Ventura", {22, 6, 0}}},
{"22G90", {13, 5, 1}, "macOS", {"Ventura", {22, 6, 0}}},
{"22G91", {13, 5, 2}, "macOS", {"Ventura", {22, 6, 0}}},
{"22G120", {13, 6, 0}, "macOS", {"Ventura", {22, 6, 0}}},
{"22G313", {13, 6, 1}, "macOS", {"Ventura", {22, 6, 0}}},
{"22G320", {13, 6, 2}, "macOS", {"Ventura", {22, 6, 0}}},
{"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}}},
{"24A335", {24, 0, 0}, "macOS", {"Sequoia", {15, 0, 0}}},
{"24A348", {24, 0, 0}, "macOS", {"Sequoia", {15, 0, 1}}},
gbString sw_vers = gb_string_make(heap_allocator(), "");
if (!system_exec_command_line_app_output("sw_vers --productVersion", &sw_vers)) {
gb_printf("macOS Unknown\n");
return;
}
uint32_t major, minor, patch;
if (sscanf(cast(const char *)sw_vers, "%u.%u.%u", &major, &minor, &patch) != 3) {
gb_printf("macOS Unknown\n");
return;
}
switch (major) {
case 15: gb_printf("macOS Sequoia"); break;
case 14: gb_printf("macOS Sonoma"); break;
case 13: gb_printf("macOS Ventura"); break;
case 12: gb_printf("macOS Monterey"); break;
case 11: gb_printf("macOS Big Sur"); break;
case 10:
{
switch (minor) {
case 15: gb_printf("macOS Catalina"); break;
case 14: gb_printf("macOS Mojave"); break;
case 13: gb_printf("macOS High Sierra"); break;
case 12: gb_printf("macOS Sierra"); break;
case 11: gb_printf("OS X El Capitan"); break;
case 10: gb_printf("OS X Yosemite"); break;
default: gb_printf("macOS Unknown");
};
break;
}
default:
gb_printf("macOS Unknown");
};
gb_printf(" %d.%d.%d (build ", major, minor, patch);
b32 build_found = 1;
b32 darwin_found = 1;
uint32_t major, minor, patch;
#define MACOS_VERSION_BUFFER_SIZE 100
char build_buffer[MACOS_VERSION_BUFFER_SIZE];
@@ -943,81 +581,25 @@ gb_internal void report_os_info() {
int darwin_mibs[] = { CTL_KERN, KERN_OSRELEASE };
if (sysctl(darwin_mibs, 2, darwin_buffer, &darwin_buffer_size, NULL, 0) == -1) {
gb_printf("macOS Unknown\n");
return;
darwin_found = 0;
} else {
if (sscanf(darwin_buffer, "%u.%u.%u", &major, &minor, &patch) != 3) {
darwin_found = 0;
}
}
// Scan table for match on BUILD
int macos_release_count = sizeof(macos_release_map) / sizeof(macos_release_map[0]);
Darwin_To_Release build_match = {};
Darwin_To_Release kernel_match = {};
for (int build = 0; build < macos_release_count; build++) {
Darwin_To_Release rel = macos_release_map[build];
// Do we have an exact match on the BUILD?
if (gb_strcmp(rel.build, (const char *)build_buffer) == 0) {
build_match = rel;
break;
}
// Do we have an exact Darwin match?
if (rel.darwin[0] == major && rel.darwin[1] == minor && rel.darwin[2] == patch) {
kernel_match = rel;
}
// Major kernel version needs to match exactly,
if (rel.darwin[0] == major) {
// No major version match yet.
if (!kernel_match.os_name) {
kernel_match = rel;
}
if (minor >= rel.darwin[1]) {
kernel_match = rel;
if (patch >= rel.darwin[2]) {
kernel_match = rel;
}
}
}
}
Darwin_To_Release match = {};
if(!build_match.build) {
match = kernel_match;
if (build_found) {
gb_printf("%s, kernel ", build_buffer);
} else {
match = build_match;
gb_printf("Unknown, kernel ");
}
if (match.os_name) {
gb_printf("%s %s %d", match.os_name, match.release.name, match.release.version[0]);
if (match.release.version[1] > 0 || match.release.version[2] > 0) {
gb_printf(".%d", match.release.version[1]);
}
if (match.release.version[2] > 0) {
gb_printf(".%d", match.release.version[2]);
}
if (build_found) {
gb_printf(" (build: %s, kernel: %d.%d.%d)\n", build_buffer, match.darwin[0], match.darwin[1], match.darwin[2]);
} else {
gb_printf(" (build: %s?, kernel: %d.%d.%d)\n", match.build, match.darwin[0], match.darwin[1], match.darwin[2]);
}
return;
if (darwin_found) {
gb_printf("%s)\n", darwin_buffer);
} else {
gb_printf("Unknown)\n");
}
if (build_found && darwin_found) {
gb_printf("macOS Unknown (build: %s, kernel: %d.%d.%d)\n", build_buffer, major, minor, patch);
return;
} else if (build_found) {
gb_printf("macOS Unknown (build: %s)\n", build_buffer);
return;
} else if (darwin_found) {
gb_printf("macOS Unknown (kernel: %d.%d.%d)\n", major, minor, patch);
return;
}
#elif defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_NETBSD)
struct utsname un;
+35
View File
@@ -0,0 +1,35 @@
#if !defined(GB_COMPILER_MSVC)
#if defined(GB_CPU_X86)
#include <cpuid.h>
#endif
#endif
gb_internal void odin_cpuid(int leaf, int result[]) {
#if defined(GB_CPU_ARM) || defined(GB_CPU_RISCV)
return;
#elif defined(GB_CPU_X86)
#if defined(GB_COMPILER_MSVC)
__cpuid(result, leaf);
#else
__get_cpuid(leaf, (unsigned int*)&result[0], (unsigned int*)&result[1], (unsigned int*)&result[2], (unsigned int*)&result[3]);
#endif
#endif
}
gb_internal bool should_use_march_native() {
#if !defined(GB_CPU_X86)
return false;
#else
int cpu[4];
odin_cpuid(0x1, &cpu[0]); // Get feature information in ECX + EDX
bool have_popcnt = cpu[2] & (1 << 23); // bit 23 in ECX = popcnt
return !have_popcnt;
#endif
}
+21 -2
View File
@@ -2,6 +2,7 @@
#include <sys/types.h>
#include <sys/sysctl.h>
#endif
#include "build_cpuid.cpp"
// #if defined(GB_SYSTEM_WINDOWS)
// #define DEFAULT_TO_THREADED_CHECKER
@@ -343,6 +344,22 @@ struct BuildCacheData {
bool copy_already_done;
};
enum LinkerChoice : i32 {
Linker_Invalid = -1,
Linker_Default = 0,
Linker_lld,
Linker_radlink,
Linker_COUNT,
};
String linker_choices[Linker_COUNT] = {
str_lit("default"),
str_lit("lld"),
str_lit("radlink"),
};
// This stores the information for the specify architecture of this build
struct BuildContext {
// Constants
@@ -418,12 +435,13 @@ struct BuildContext {
bool no_rpath;
bool no_entry_point;
bool no_thread_local;
bool use_lld;
bool cross_compiling;
bool different_os;
bool keep_object_files;
bool disallow_do;
LinkerChoice linker_choice;
StringSet custom_attributes;
bool strict_style;
@@ -449,6 +467,7 @@ struct BuildContext {
BuildCacheData build_cache_data;
bool internal_no_inline;
bool internal_by_value;
bool no_threaded_checker;
@@ -1870,7 +1889,7 @@ gb_internal bool init_build_paths(String init_filename) {
return false;
}
if (!build_context.use_lld && find_result.vs_exe_path.len == 0) {
if (build_context.linker_choice == Linker_Default && find_result.vs_exe_path.len == 0) {
gb_printf_err("link.exe not found.\n");
return false;
}
+47 -1
View File
@@ -2565,6 +2565,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_swizzle: {
// swizzle :: proc(v: [N]T, ..int) -> [M]T
if (!operand->type) {
return false;
}
Type *original_type = operand->type;
Type *type = base_type(original_type);
i64 max_count = 0;
@@ -2922,6 +2926,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// imag :: proc(x: type) -> float_type
Operand *x = operand;
if (!x->type) {
return false;
}
if (is_type_untyped(x->type)) {
if (x->mode == Addressing_Constant) {
if (is_type_numeric(x->type)) {
@@ -2982,6 +2990,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
// kmag :: proc(x: type) -> float_type
Operand *x = operand;
if (!x->type) {
return false;
}
if (is_type_untyped(x->type)) {
if (x->mode == Addressing_Constant) {
if (is_type_numeric(x->type)) {
@@ -3031,6 +3043,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_conj: {
// conj :: proc(x: type) -> type
Operand *x = operand;
if (!x->type) {
return false;
}
Type *t = x->type;
Type *elem = core_array_type(t);
@@ -3071,10 +3087,14 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
}
case BuiltinProc_expand_values: {
if (!operand->type) {
return false;
}
Type *type = base_type(operand->type);
if (!is_type_struct(type) && !is_type_array(type)) {
gbString type_str = type_to_string(operand->type);
error(call, "Expected a struct or array type, got '%s'", type_str);
error(call, "Expected a struct or array type to 'expand_values', got '%s'", type_str);
gb_string_free(type_str);
return false;
}
@@ -3110,8 +3130,13 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
check_multi_expr_or_type(c, operand, ce->args[0]);
if (!operand->type) {
return false;
}
Type *original_type = operand->type;
Type *type = base_type(operand->type);
if (operand->mode == Addressing_Type && is_type_enumerated_array(type)) {
// Okay
} else if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
@@ -3184,6 +3209,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
return false;
}
if (ce->args.count <= 1) {
error(call, "Too few arguments for 'min', two or more are required");
return false;
}
bool all_constant = operand->mode == Addressing_Constant;
@@ -3278,6 +3307,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
check_multi_expr_or_type(c, operand, ce->args[0]);
if (!operand->type) {
return false;
}
Type *original_type = operand->type;
Type *type = base_type(operand->type);
@@ -3357,6 +3390,11 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
gb_string_free(type_str);
return false;
}
if (ce->args.count <= 1) {
error(call, "Too few arguments for 'max', two or more are required");
return false;
}
bool all_constant = operand->mode == Addressing_Constant;
@@ -3448,6 +3486,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_abs: {
// abs :: proc(n: numeric) -> numeric
if (!operand->type) {
return false;
}
if (!(is_type_numeric(operand->type) && !is_type_array(operand->type))) {
gbString type_str = type_to_string(operand->type);
error(call, "Expected a numeric type to 'abs', got '%s'", type_str);
@@ -3503,6 +3545,10 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_clamp: {
// clamp :: proc(a, min, max: ordered) -> ordered
if (!operand->type) {
return false;
}
Type *type = operand->type;
if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
gbString type_str = type_to_string(operand->type);
+20 -68
View File
@@ -897,20 +897,6 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
}
}
if (is_type_relative_pointer(dst)) {
i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type, allow_array_programming);
if (score >= 0) {
return score+2;
}
}
if (is_type_relative_multi_pointer(dst)) {
i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type, allow_array_programming);
if (score >= 0) {
return score+2;
}
}
if (is_type_proc(dst)) {
if (are_types_identical(src, dst)) {
return 3;
@@ -1052,12 +1038,6 @@ gb_internal AstPackage *get_package_of_type(Type *type) {
case Type_DynamicArray:
type = type->DynamicArray.elem;
continue;
case Type_RelativePointer:
type = type->RelativePointer.pointer_type;
continue;
case Type_RelativeMultiPointer:
type = type->RelativeMultiPointer.pointer_type;
continue;
}
return nullptr;
}
@@ -3561,6 +3541,8 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
return false;
}
Operand src = *o;
Type *src_t = o->type;
Type *dst_t = t;
Type *src_bt = base_type(src_t);
@@ -3649,7 +3631,8 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
// identical casts that cannot be foreseen or otherwise
// forbidden, so just skip them.
if (forbid_identical && check_vet_flags(c) & VetFlag_Cast &&
(c->curr_proc_sig == nullptr || !is_type_polymorphic(c->curr_proc_sig))) {
(c->curr_proc_sig == nullptr || !is_type_polymorphic(c->curr_proc_sig)) &&
check_is_castable_to(c, &src, dst_t)) {
if (are_types_identical(src_t, dst_t)) {
gbString oper_str = expr_to_string(o->expr);
gbString to_type = type_to_string(dst_t);
@@ -4471,8 +4454,8 @@ gb_internal ExactValue convert_exact_value_for_type(ExactValue v, Type *type) {
}
gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
GB_ASSERT_NOT_NULL(target_type);
if (operand->mode == Addressing_Invalid ||
// GB_ASSERT_NOT_NULL(target_type);
if (target_type == nullptr || operand->mode == Addressing_Invalid ||
operand->mode == Addressing_Type ||
is_type_typed(operand->type) ||
target_type == t_invalid) {
@@ -4997,8 +4980,12 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
if (success_) *success_ = true;
if (finish_) *finish_ = false;
return tav.value;
} else if (is_type_proc(tav.type)) {
if (success_) *success_ = true;
if (finish_) *finish_ = false;
return tav.value;
} else {
GB_ASSERT(is_type_untyped_nil(tav.type));
GB_ASSERT_MSG(is_type_untyped_nil(tav.type), "%s", type_to_string(tav.type));
if (success_) *success_ = true;
if (finish_) *finish_ = false;
return tav.value;
@@ -8232,17 +8219,6 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
}
return true;
case Type_RelativeMultiPointer:
{
Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
GB_ASSERT(pointer_type->kind == Type_MultiPointer);
o->type = pointer_type->MultiPointer.elem;
if (o->mode != Addressing_Constant) {
o->mode = Addressing_Variable;
}
}
return true;
case Type_DynamicArray:
o->type = t->DynamicArray.elem;
if (o->mode != Addressing_Constant) {
@@ -8481,6 +8457,15 @@ gb_internal ExprKind check_implicit_selector_expr(CheckerContext *c, Operand *o,
error(node, "Undeclared name '%.*s' for type '%s'", LIT(name), typ);
check_did_you_mean_type(name, bt->Enum.fields);
} else if (is_type_bit_set(th) && is_type_enum(th->BitSet.elem)) {
ERROR_BLOCK();
gbString typ = type_to_string(th);
gbString str = expr_to_string(node);
error(node, "Cannot convert enum value to '%s'", typ);
error_line("\tSuggestion: Did you mean '{ %s }'?\n", str);
gb_string_free(typ);
gb_string_free(str);
} else {
gbString typ = type_to_string(th);
gbString str = expr_to_string(node);
@@ -10616,8 +10601,6 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
// Okay
} else if (is_type_string(t)) {
// Okay
} else if (is_type_relative_multi_pointer(t)) {
// Okay
} else if (is_type_matrix(t)) {
// Okay
} else {
@@ -10772,11 +10755,6 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
}
break;
case Type_RelativeMultiPointer:
valid = true;
o->type = type_deref(o->type);
break;
case Type_EnumeratedArray:
{
gbString str = expr_to_string(o->expr);
@@ -10853,16 +10831,6 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
x[i:n] -> []T
*/
o->type = alloc_type_slice(t->MultiPointer.elem);
} else if (t->kind == Type_RelativeMultiPointer && se->high != nullptr) {
/*
x[:] -> [^]T
x[i:] -> [^]T
x[:n] -> []T
x[i:n] -> []T
*/
Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
GB_ASSERT(pointer_type->kind == Type_MultiPointer);
o->type = alloc_type_slice(pointer_type->MultiPointer.elem);
}
@@ -11223,22 +11191,6 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
} else if (t->kind == Type_SoaPointer) {
o->mode = Addressing_SoaVariable;
o->type = type_deref(t);
} else if (t->kind == Type_RelativePointer) {
if (o->mode != Addressing_Variable) {
gbString str = expr_to_string(o->expr);
gbString typ = type_to_string(o->type);
error(o->expr, "Cannot dereference relative pointer '%s' of type '%s' as it does not have a variable addressing mode", str, typ);
gb_string_free(typ);
gb_string_free(str);
}
// NOTE(bill): This is required because when dereferencing, the original type has been lost
add_type_info_type(c, o->type);
Type *ptr_type = base_type(t->RelativePointer.pointer_type);
GB_ASSERT(ptr_type->kind == Type_Pointer);
o->mode = Addressing_Variable;
o->type = ptr_type->Pointer.elem;
} else {
gbString str = expr_to_string(o->expr);
gbString typ = type_to_string(o->type);
+2
View File
@@ -2605,6 +2605,7 @@ gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
if (cond && cond->kind == Ast_BinaryExpr &&
cond->BinaryExpr.left && cond->BinaryExpr.right &&
cond->BinaryExpr.op.kind == Token_GtEq &&
type_of_expr(cond->BinaryExpr.left) != nullptr &&
is_type_unsigned(type_of_expr(cond->BinaryExpr.left)) &&
cond->BinaryExpr.right->tav.value.kind == ExactValue_Integer &&
is_exact_value_zero(cond->BinaryExpr.right->tav.value)) {
@@ -2612,6 +2613,7 @@ gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
} else if (cond && cond->kind == Ast_BinaryExpr &&
cond->BinaryExpr.left && cond->BinaryExpr.right &&
cond->BinaryExpr.op.kind == Token_LtEq &&
type_of_expr(cond->BinaryExpr.right) != nullptr &&
is_type_unsigned(type_of_expr(cond->BinaryExpr.right)) &&
cond->BinaryExpr.left->tav.value.kind == ExactValue_Integer &&
is_exact_value_zero(cond->BinaryExpr.left->tav.value)) {
+3 -37
View File
@@ -2366,8 +2366,7 @@ gb_internal Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_res
}
gb_internal void check_procedure_param_polymorphic_type(CheckerContext *ctx, Type *type, Ast *type_expr) {
GB_ASSERT_NOT_NULL(type_expr);
if (type == nullptr || ctx->in_polymorphic_specialization) { return; }
if (type == nullptr || type_expr == nullptr || ctx->in_polymorphic_specialization) { return; }
if (!is_type_polymorphic_record_unspecialized(type)) { return; }
bool invalid_polymorpic_type_use = false;
@@ -3517,41 +3516,8 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
case_end;
case_ast_node(rt, RelativeType, e);
GB_ASSERT(rt->tag->kind == Ast_CallExpr);
ast_node(ce, CallExpr, rt->tag);
Type *base_integer = nullptr;
if (ce->args.count != 1) {
error(rt->type, "#relative expected 1 type argument, got %td", ce->args.count);
} else {
base_integer = check_type(ctx, ce->args[0]);
if (!is_type_integer(base_integer)) {
error(rt->type, "#relative base types must be an integer");
base_integer = nullptr;
} else if (type_size_of(base_integer) > 64) {
error(rt->type, "#relative base integer types be less than or equal to 64-bits");
base_integer = nullptr;
}
}
Type *relative_type = nullptr;
Type *base_type = check_type(ctx, rt->type);
if (!is_type_pointer(base_type) && !is_type_multi_pointer(base_type)) {
error(rt->type, "#relative types can only be a pointer or multi-pointer");
relative_type = base_type;
} else if (base_integer == nullptr) {
relative_type = base_type;
} else {
if (is_type_pointer(base_type)) {
relative_type = alloc_type_relative_pointer(base_type, base_integer);
} else if (is_type_multi_pointer(base_type)) {
relative_type = alloc_type_relative_multi_pointer(base_type, base_integer);
}
}
GB_ASSERT(relative_type != nullptr);
*type = relative_type;
error(e, "#relative types have been removed from the compiler. Prefer \"core:relative\".");
*type = t_invalid;
set_base_type(named_type, *type);
return true;
case_end;
-24
View File
@@ -2186,16 +2186,6 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->SimdVector.elem);
break;
case Type_RelativePointer:
add_type_info_type_internal(c, bt->RelativePointer.pointer_type);
add_type_info_type_internal(c, bt->RelativePointer.base_integer);
break;
case Type_RelativeMultiPointer:
add_type_info_type_internal(c, bt->RelativeMultiPointer.pointer_type);
add_type_info_type_internal(c, bt->RelativeMultiPointer.base_integer);
break;
case Type_Matrix:
add_type_info_type_internal(c, bt->Matrix.elem);
break;
@@ -2441,16 +2431,6 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
add_min_dep_type_info(c, bt->SimdVector.elem);
break;
case Type_RelativePointer:
add_min_dep_type_info(c, bt->RelativePointer.pointer_type);
add_min_dep_type_info(c, bt->RelativePointer.base_integer);
break;
case Type_RelativeMultiPointer:
add_min_dep_type_info(c, bt->RelativeMultiPointer.pointer_type);
add_min_dep_type_info(c, bt->RelativeMultiPointer.base_integer);
break;
case Type_Matrix:
add_min_dep_type_info(c, bt->Matrix.elem);
break;
@@ -3075,8 +3055,6 @@ gb_internal void init_core_type_info(Checker *c) {
t_type_info_map = find_core_type(c, str_lit("Type_Info_Map"));
t_type_info_bit_set = find_core_type(c, str_lit("Type_Info_Bit_Set"));
t_type_info_simd_vector = find_core_type(c, str_lit("Type_Info_Simd_Vector"));
t_type_info_relative_pointer = find_core_type(c, str_lit("Type_Info_Relative_Pointer"));
t_type_info_relative_multi_pointer = find_core_type(c, str_lit("Type_Info_Relative_Multi_Pointer"));
t_type_info_matrix = find_core_type(c, str_lit("Type_Info_Matrix"));
t_type_info_soa_pointer = find_core_type(c, str_lit("Type_Info_Soa_Pointer"));
t_type_info_bit_field = find_core_type(c, str_lit("Type_Info_Bit_Field"));
@@ -3105,8 +3083,6 @@ gb_internal void init_core_type_info(Checker *c) {
t_type_info_map_ptr = alloc_type_pointer(t_type_info_map);
t_type_info_bit_set_ptr = alloc_type_pointer(t_type_info_bit_set);
t_type_info_simd_vector_ptr = alloc_type_pointer(t_type_info_simd_vector);
t_type_info_relative_pointer_ptr = alloc_type_pointer(t_type_info_relative_pointer);
t_type_info_relative_multi_pointer_ptr = alloc_type_pointer(t_type_info_relative_multi_pointer);
t_type_info_matrix_ptr = alloc_type_pointer(t_type_info_matrix);
t_type_info_soa_pointer_ptr = alloc_type_pointer(t_type_info_soa_pointer);
t_type_info_bit_field_ptr = alloc_type_pointer(t_type_info_bit_field);
+1 -2
View File
@@ -79,8 +79,7 @@ enum OdinDocTypeKind : u32 {
OdinDocType_SOAStructFixed = 17,
OdinDocType_SOAStructSlice = 18,
OdinDocType_SOAStructDynamic = 19,
OdinDocType_RelativePointer = 20,
OdinDocType_RelativeMultiPointer = 21,
OdinDocType_MultiPointer = 22,
OdinDocType_Matrix = 23,
OdinDocType_SoaPointer = 24,
-18
View File
@@ -776,24 +776,6 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
doc_type.types = odin_doc_type_as_slice(w, type->SimdVector.elem);
// TODO(bill):
break;
case Type_RelativePointer:
doc_type.kind = OdinDocType_RelativePointer;
{
OdinDocTypeIndex types[2] = {};
types[0] = odin_doc_type(w, type->RelativePointer.pointer_type);
types[1] = odin_doc_type(w, type->RelativePointer.base_integer);
doc_type.types = odin_write_slice(w, types, gb_count_of(types));
}
break;
case Type_RelativeMultiPointer:
doc_type.kind = OdinDocType_RelativeMultiPointer;
{
OdinDocTypeIndex types[2] = {};
types[0] = odin_doc_type(w, type->RelativeMultiPointer.pointer_type);
types[1] = odin_doc_type(w, type->RelativeMultiPointer.base_integer);
doc_type.types = odin_write_slice(w, types, gb_count_of(types));
}
break;
case Type_Matrix:
doc_type.kind = OdinDocType_Matrix;
+5 -1
View File
@@ -370,7 +370,11 @@ gb_internal ExactValue exact_value_from_basic_literal(TokenKind kind, String con
}
case Token_Rune: {
Rune r = GB_RUNE_INVALID;
utf8_decode(string.text, string.len, &r);
if (string.len == 1) {
r = cast(Rune)string.text[0];
} else {
utf8_decode(string.text, string.len, &r);
}
return exact_value_i64(r);
}
}
+49 -23
View File
@@ -167,8 +167,10 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (is_windows) {
String section_name = str_lit("msvc-link");
if (build_context.use_lld) {
section_name = str_lit("lld-link");
switch (build_context.linker_choice) {
case Linker_Default: break;
case Linker_lld: section_name = str_lit("lld-link"); break;
case Linker_radlink: section_name = str_lit("rad-link"); break;
}
timings_start_section(timings, section_name);
@@ -304,7 +306,48 @@ gb_internal i32 linker_stage(LinkerData *gen) {
String windows_sdk_bin_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Win_SDK_Bin_Path]);
defer (gb_free(heap_allocator(), windows_sdk_bin_path.text));
if (!build_context.use_lld) { // msvc
switch (build_context.linker_choice) {
case Linker_lld:
result = system_exec_command_line_app("msvc-lld-link",
"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%.*s "
"%.*s "
"%.*s "
"%s "
"",
LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
link_settings,
LIT(build_context.ODIN_WINDOWS_SUBSYSTEM),
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
);
if (result) {
return result;
}
break;
case Linker_radlink:
result = system_exec_command_line_app("msvc-rad-link",
"\"%.*s\\bin\\radlink\" %s -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%.*s "
"%.*s "
"%.*s "
"%s "
"",
LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
link_settings,
LIT(build_context.ODIN_WINDOWS_SUBSYSTEM),
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
);
if (result) {
return result;
}
break;
default: { // msvc
String res_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RES]);
String rc_path = quote_path(heap_allocator(), build_context.build_paths[BuildPath_RC]);
defer (gb_free(heap_allocator(), res_path.text));
@@ -365,25 +408,8 @@ gb_internal i32 linker_stage(LinkerData *gen) {
if (result) {
return result;
}
} else { // lld
result = system_exec_command_line_app("msvc-lld-link",
"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%.*s "
"%.*s "
"%.*s "
"%s "
"",
LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
link_settings,
LIT(build_context.ODIN_WINDOWS_SUBSYSTEM),
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
);
if (result) {
return result;
}
break;
}
}
} else {
timings_start_section(timings, str_lit("ld-link"));
@@ -679,7 +705,7 @@ gb_internal i32 linker_stage(LinkerData *gen) {
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
if (build_context.use_lld) {
if (build_context.linker_choice == Linker_lld) {
link_command_line = gb_string_append_fmt(link_command_line, " -fuse-ld=lld");
result = system_exec_command_line_app("lld-link", link_command_line);
} else {
+5 -5
View File
@@ -57,6 +57,10 @@
#define LB_USE_NEW_PASS_SYSTEM 0
#endif
#if LLVM_VERSION_MAJOR >= 19
#define LLVMDIBuilderInsertDeclareAtEnd(...) LLVMDIBuilderInsertDeclareRecordAtEnd(__VA_ARGS__)
#endif
gb_internal bool lb_use_new_pass_system(void) {
return LB_USE_NEW_PASS_SYSTEM;
}
@@ -75,7 +79,6 @@ enum lbAddrKind {
lbAddr_Context,
lbAddr_SoaVariable,
lbAddr_RelativePointer,
lbAddr_Swizzle,
lbAddr_SwizzleLarge,
@@ -103,9 +106,6 @@ struct lbAddr {
lbValue index;
Ast *node;
} index_set;
struct {
bool deref;
} relative;
struct {
Type *type;
u8 count; // 2, 3, or 4 components
@@ -741,4 +741,4 @@ gb_global char const *llvm_linkage_strings[] = {
"linker private weak linkage"
};
#define ODIN_METADATA_IS_PACKED str_lit("odin-is-packed")
#define ODIN_METADATA_IS_PACKED str_lit("odin-is-packed")
-11
View File
@@ -920,17 +920,6 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
elem, subscripts, gb_count_of(subscripts));
}
case Type_RelativePointer: {
LLVMMetadataRef base_integer = lb_debug_type(m, type->RelativePointer.base_integer);
gbString name = type_to_string(type, temporary_allocator());
return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
}
case Type_RelativeMultiPointer: {
LLVMMetadataRef base_integer = lb_debug_type(m, type->RelativeMultiPointer.base_integer);
gbString name = type_to_string(type, temporary_allocator());
return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
}
case Type_Matrix: {
LLVMMetadataRef subscripts[1] = {};
subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
+11 -38
View File
@@ -4200,30 +4200,6 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
return lb_addr(v);
}
case Type_RelativeMultiPointer: {
lbAddr rel_ptr_addr = {};
if (deref) {
lbValue rel_ptr_ptr = lb_build_expr(p, ie->expr);
rel_ptr_addr = lb_addr(rel_ptr_ptr);
} else {
rel_ptr_addr = lb_build_addr(p, ie->expr);
}
lbValue rel_ptr = lb_relative_pointer_to_pointer(p, rel_ptr_addr);
lbValue index = lb_build_expr(p, ie->index);
index = lb_emit_conv(p, index, t_int);
lbValue v = {};
Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
GB_ASSERT(pointer_type->kind == Type_MultiPointer);
Type *elem = pointer_type->MultiPointer.elem;
LLVMValueRef indices[1] = {index.value};
v.value = LLVMBuildGEP2(p->builder, lb_type(p->module, elem), rel_ptr.value, indices, 1, "");
v.type = alloc_type_pointer(elem);
return lb_addr(v);
}
case Type_DynamicArray: {
lbValue dynamic_array = {};
dynamic_array = lb_build_expr(p, ie->expr);
@@ -4333,13 +4309,6 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
return slice;
}
case Type_RelativePointer:
GB_PANIC("TODO(bill): Type_RelativePointer should be handled above already on the lb_addr_load");
break;
case Type_RelativeMultiPointer:
GB_PANIC("TODO(bill): Type_RelativeMultiPointer should be handled above already on the lb_addr_load");
break;
case Type_DynamicArray: {
Type *elem_type = type->DynamicArray.elem;
Type *slice_type = alloc_type_slice(elem_type);
@@ -4605,7 +4574,11 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
auto const &f = fields[i];
LLVMValueRef mask = LLVMConstInt(lit, 1, false);
mask = LLVMConstShl(mask, LLVMConstInt(lit, f.bit_size, false));
#if LLVM_VERSION_MAJOR >= 19
mask = LLVMBuildShl(p->builder, mask, LLVMConstInt(lit, f.bit_size, false), "");
#else
mask = LLVMConstShl(mask, LLVMConstInt(lit, f.bit_size, false));
#endif
mask = LLVMConstSub(mask, LLVMConstInt(lit, 1, false));
LLVMValueRef elem = values[i].value;
@@ -4653,7 +4626,11 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
bits_to_set -= mask_width;
LLVMValueRef mask = LLVMConstInt(vt, 1, false);
mask = LLVMConstShl(mask, LLVMConstInt(vt, mask_width, false));
#if LLVM_VERSION_MAJOR >= 19
mask = LLVMBuildShl(p->builder, mask, LLVMConstInt(vt, mask_width, false), "");
#else
mask = LLVMConstShl(mask, LLVMConstInt(vt, mask_width, false));
#endif
mask = LLVMConstSub(mask, LLVMConstInt(vt, 1, false));
LLVMValueRef to_set = LLVMBuildAnd(p->builder, val, mask, "");
@@ -5343,11 +5320,7 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
case_ast_node(de, DerefExpr, expr);
Type *t = type_of_expr(de->expr);
if (is_type_relative_pointer(t)) {
lbAddr addr = lb_build_addr(p, de->expr);
addr.relative.deref = true;
return addr;
} else if (is_type_soa_pointer(t)) {
if (is_type_soa_pointer(t)) {
lbValue value = lb_build_expr(p, de->expr);
lbValue ptr = lb_emit_struct_ev(p, value, 0);
lbValue idx = lb_emit_struct_ev(p, value, 1);
+1 -140
View File
@@ -409,14 +409,6 @@ gb_internal lbModule *lb_module_of_entity(lbGenerator *gen, Entity *e) {
gb_internal lbAddr lb_addr(lbValue addr) {
lbAddr v = {lbAddr_Default, addr};
if (addr.type != nullptr && is_type_relative_pointer(type_deref(addr.type))) {
GB_ASSERT(is_type_pointer(addr.type));
v.kind = lbAddr_RelativePointer;
} else if (addr.type != nullptr && is_type_relative_multi_pointer(type_deref(addr.type))) {
GB_ASSERT(is_type_pointer(addr.type) ||
is_type_multi_pointer(addr.type));
v.kind = lbAddr_RelativePointer;
}
return v;
}
@@ -501,42 +493,6 @@ gb_internal Type *lb_addr_type(lbAddr const &addr) {
return type_deref(addr.addr.type);
}
gb_internal lbValue lb_relative_pointer_to_pointer(lbProcedure *p, lbAddr const &addr) {
GB_ASSERT(addr.kind == lbAddr_RelativePointer);
Type *t = base_type(lb_addr_type(addr));
GB_ASSERT(is_type_relative_pointer(t) || is_type_relative_multi_pointer(t));
Type *pointer_type = nullptr;
Type *base_integer = nullptr;
if (t->kind == Type_RelativePointer) {
pointer_type = t->RelativePointer.pointer_type;
base_integer = t->RelativePointer.base_integer;
} else if (t->kind == Type_RelativeMultiPointer) {
pointer_type = t->RelativeMultiPointer.pointer_type;
base_integer = t->RelativeMultiPointer.base_integer;
}
lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(base_integer));
offset = lb_emit_load(p, offset);
if (!is_type_unsigned(base_integer)) {
offset = lb_emit_conv(p, offset, t_i64);
}
offset = lb_emit_conv(p, offset, t_uintptr);
lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
absolute_ptr = lb_emit_conv(p, absolute_ptr, pointer_type);
lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, base_integer));
// NOTE(bill): nil check
lbValue nil_ptr = lb_const_nil(p->module, pointer_type);
lbValue final_ptr = lb_emit_select(p, cond, nil_ptr, absolute_ptr);
return final_ptr;
}
gb_internal lbValue lb_make_soa_pointer(lbProcedure *p, Type *type, lbValue const &addr, lbValue const &index) {
lbAddr v = lb_add_local_generated(p, type, false);
lbValue ptr = lb_emit_struct_ep(p, v.addr, 0);
@@ -557,9 +513,6 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
case lbAddr_Map:
return lb_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key);
case lbAddr_RelativePointer:
return lb_relative_pointer_to_pointer(p, addr);
case lbAddr_SoaVariable:
{
Type *soa_ptr_type = alloc_type_soa_pointer(lb_addr_type(addr));
@@ -584,9 +537,6 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
gb_internal lbValue lb_build_addr_ptr(lbProcedure *p, Ast *expr) {
lbAddr addr = lb_build_addr(p, expr);
if (addr.kind == lbAddr_RelativePointer) {
return addr.addr;
}
return lb_addr_get_ptr(p, addr);
}
@@ -819,10 +769,6 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
value.value = LLVMConstNull(lb_type(p->module, t));
}
if (addr.kind == lbAddr_RelativePointer && addr.relative.deref) {
addr = lb_addr(lb_address_from_load(p, lb_addr_load(p, addr)));
}
if (addr.kind == lbAddr_BitField) {
lbValue dst = addr.addr;
if (is_type_endian_big(addr.bitfield.type)) {
@@ -860,44 +806,6 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
lb_emit_runtime_call(p, "__write_bits", args);
}
return;
} else if (addr.kind == lbAddr_RelativePointer) {
Type *rel_ptr = base_type(lb_addr_type(addr));
GB_ASSERT(rel_ptr->kind == Type_RelativePointer ||
rel_ptr->kind == Type_RelativeMultiPointer);
Type *pointer_type = nullptr;
Type *base_integer = nullptr;
if (rel_ptr->kind == Type_RelativePointer) {
pointer_type = rel_ptr->RelativePointer.pointer_type;
base_integer = rel_ptr->RelativePointer.base_integer;
} else if (rel_ptr->kind == Type_RelativeMultiPointer) {
pointer_type = rel_ptr->RelativeMultiPointer.pointer_type;
base_integer = rel_ptr->RelativeMultiPointer.base_integer;
}
value = lb_emit_conv(p, value, pointer_type);
GB_ASSERT(is_type_pointer(addr.addr.type));
lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
lbValue val_ptr = lb_emit_conv(p, value, t_uintptr);
lbValue offset = {};
offset.value = LLVMBuildSub(p->builder, val_ptr.value, ptr.value, "");
offset.type = t_uintptr;
if (!is_type_unsigned(base_integer)) {
offset = lb_emit_conv(p, offset, t_i64);
}
offset = lb_emit_conv(p, offset, base_integer);
lbValue offset_ptr = lb_emit_conv(p, addr.addr, alloc_type_pointer(base_integer));
offset = lb_emit_select(p,
lb_emit_comp(p, Token_CmpEq, val_ptr, lb_const_nil(p->module, t_uintptr)),
lb_const_nil(p->module, base_integer),
offset
);
LLVMBuildStore(p->builder, offset.value, offset_ptr.value);
return;
} else if (addr.kind == lbAddr_Map) {
lb_internal_dynamic_map_set(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
return;
@@ -1246,46 +1154,6 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
}
return r;
} else if (addr.kind == lbAddr_RelativePointer) {
Type *rel_ptr = base_type(lb_addr_type(addr));
Type *base_integer = nullptr;
Type *pointer_type = nullptr;
GB_ASSERT(rel_ptr->kind == Type_RelativePointer ||
rel_ptr->kind == Type_RelativeMultiPointer);
if (rel_ptr->kind == Type_RelativePointer) {
base_integer = rel_ptr->RelativePointer.base_integer;
pointer_type = rel_ptr->RelativePointer.pointer_type;
} else if (rel_ptr->kind == Type_RelativeMultiPointer) {
base_integer = rel_ptr->RelativeMultiPointer.base_integer;
pointer_type = rel_ptr->RelativeMultiPointer.pointer_type;
}
lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(base_integer));
offset = lb_emit_load(p, offset);
if (!is_type_unsigned(base_integer)) {
offset = lb_emit_conv(p, offset, t_i64);
}
offset = lb_emit_conv(p, offset, t_uintptr);
lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
absolute_ptr = lb_emit_conv(p, absolute_ptr, pointer_type);
lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, base_integer));
// NOTE(bill): nil check
lbValue nil_ptr = lb_const_nil(p->module, pointer_type);
lbValue final_ptr = {};
final_ptr.type = absolute_ptr.type;
final_ptr.value = LLVMBuildSelect(p->builder, cond.value, nil_ptr.value, absolute_ptr.value, "");
if (rel_ptr->kind == Type_RelativeMultiPointer) {
return final_ptr;
}
return lb_emit_load(p, final_ptr);
} else if (addr.kind == lbAddr_Map) {
Type *map_type = base_type(type_deref(addr.addr.type));
GB_ASSERT(map_type->kind == Type_Map);
@@ -2378,13 +2246,6 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_SimdVector:
return LLVMVectorType(lb_type(m, type->SimdVector.elem), cast(unsigned)type->SimdVector.count);
case Type_RelativePointer:
return lb_type_internal(m, type->RelativePointer.base_integer);
case Type_RelativeMultiPointer:
return lb_type_internal(m, type->RelativeMultiPointer.base_integer);
case Type_Matrix:
{
@@ -2740,7 +2601,7 @@ general_end:;
GB_ASSERT(p->decl_block != p->curr_block);
i64 max_align = gb_max(lb_alignof(src_type), lb_alignof(dst_type));
max_align = gb_max(max_align, 4);
max_align = gb_max(max_align, 16);
LLVMValueRef ptr = llvm_alloca(p, dst_type, max_align);
+19
View File
@@ -579,6 +579,8 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
p->raw_input_parameters = array_make<LLVMValueRef>(permanent_allocator(), raw_input_parameters_count);
LLVMGetParams(p->value, p->raw_input_parameters.data);
bool is_odin_cc = is_calling_convention_odin(ft->calling_convention);
unsigned param_index = 0;
for_array(i, params->variables) {
Entity *e = params->variables[i];
@@ -613,9 +615,26 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
}
} else if (arg_type->kind == lbArg_Indirect) {
if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
i64 sz = type_size_of(e->type);
bool do_callee_copy = false;
if (is_odin_cc) {
do_callee_copy = sz <= 16;
if (build_context.internal_by_value) {
do_callee_copy = true;
}
}
lbValue ptr = {};
ptr.value = LLVMGetParam(p->value, param_offset+param_index);
ptr.type = alloc_type_pointer(e->type);
if (do_callee_copy) {
lbValue new_ptr = lb_add_local_generated(p, e->type, false).addr;
lb_mem_copy_non_overlapping(p, new_ptr, ptr, lb_const_int(p->module, t_uint, sz));
ptr = new_ptr;
}
lb_add_entity(p->module, e, ptr);
lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1, p->decl_block);
}
+6
View File
@@ -201,6 +201,8 @@ gb_internal void lb_open_scope(lbProcedure *p, Scope *s) {
}
}
GB_ASSERT(s != nullptr);
p->curr_scope = s;
p->scope_index += 1;
array_add(&p->scope_stack, s);
@@ -221,6 +223,10 @@ gb_internal void lb_close_scope(lbProcedure *p, lbDeferExitKind kind, lbBlock *b
}
if (p->curr_scope) {
p->curr_scope = p->curr_scope->parent;
}
p->scope_index -= 1;
array_pop(&p->scope_stack);
}
-26
View File
@@ -61,8 +61,6 @@ gb_internal u64 lb_typeid_kind(lbModule *m, Type *type, u64 id=0) {
case Type_Proc: kind = Typeid_Procedure; break;
case Type_BitSet: kind = Typeid_Bit_Set; break;
case Type_SimdVector: kind = Typeid_Simd_Vector; break;
case Type_RelativePointer: kind = Typeid_Relative_Pointer; break;
case Type_RelativeMultiPointer: kind = Typeid_Relative_Multi_Pointer; break;
case Type_SoaPointer: kind = Typeid_SoaPointer; break;
case Type_BitField: kind = Typeid_Bit_Field; break;
}
@@ -950,30 +948,6 @@ gb_internal void lb_setup_type_info_data_giant_array(lbModule *m, i64 global_typ
}
break;
case Type_RelativePointer:
{
tag_type = t_type_info_relative_pointer;
LLVMValueRef vals[2] = {
get_type_info_ptr(m, t->RelativePointer.pointer_type),
get_type_info_ptr(m, t->RelativePointer.base_integer),
};
variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
}
break;
case Type_RelativeMultiPointer:
{
tag_type = t_type_info_relative_multi_pointer;
LLVMValueRef vals[2] = {
get_type_info_ptr(m, t->RelativeMultiPointer.pointer_type),
get_type_info_ptr(m, t->RelativeMultiPointer.base_integer),
};
variant_value = llvm_const_named_struct(m, tag_type, vals, gb_count_of(vals));
}
break;
case Type_Matrix:
{
tag_type = t_type_info_matrix;
+10 -8
View File
@@ -124,8 +124,16 @@ gb_internal void lb_mem_zero_ptr(lbProcedure *p, LLVMValueRef ptr, Type *type, u
switch (kind) {
case LLVMStructTypeKind:
case LLVMArrayTypeKind:
// NOTE(bill): Enforce zeroing through memset to make sure padding is zeroed too
lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, sz).value, alignment, false);
if (is_type_tuple(type)) {
// NOTE(bill): even though this should be safe, to keep ASAN happy, do not zero the implicit padding at the end
GB_ASSERT(type->kind == Type_Tuple);
i64 n = type->Tuple.variables.count-1;
i64 end_offset = type->Tuple.offsets[n] + type_size_of(type->Tuple.variables[n]->type);
lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, end_offset).value, alignment, false);
} else {
// NOTE(bill): Enforce zeroing through memset to make sure padding is zeroed too
lb_mem_zero_ptr_internal(p, ptr, lb_const_int(p->module, t_int, sz).value, alignment, false);
}
break;
default:
LLVMBuildStore(p->builder, LLVMConstNull(lb_type(p->module, type)), ptr);
@@ -1123,10 +1131,6 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
Type *t = base_type(type_deref(s.type));
Type *result_type = nullptr;
if (is_type_relative_pointer(t)) {
s = lb_addr_get_ptr(p, lb_addr(s));
}
if (is_type_struct(t)) {
result_type = get_struct_field_type(t, index);
} else if (is_type_union(t)) {
@@ -1432,8 +1436,6 @@ gb_internal lbValue lb_emit_deep_field_gep(lbProcedure *p, lbValue e, Selection
e = lb_emit_array_epi(p, e, index);
} else if (type->kind == Type_Map) {
e = lb_emit_struct_ep(p, e, index);
} else if (type->kind == Type_RelativePointer) {
e = lb_emit_struct_ep(p, e, index);
} else {
GB_PANIC("un-gep-able type %s", type_to_string(type));
}
+492 -377
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -2488,6 +2488,7 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
tag = parse_call_expr(f, tag);
}
Ast *type = parse_type(f);
syntax_error(tag, "#relative types have now been removed in favour of \"core:relative\"");
return ast_relative_type(f, tag, type);
} else if (name.string == "force_inline" ||
name.string == "force_no_inline") {
+35 -5
View File
@@ -412,6 +412,32 @@ gb_internal String concatenate4_strings(gbAllocator a, String const &x, String c
return make_string(data, len);
}
#if defined(GB_SYSTEM_WINDOWS)
gb_internal String escape_char(gbAllocator a, String s, char cte) {
isize buf_len = s.len;
isize cte_count = 0;
for (isize j = 0; j < s.len; j++) {
if (s.text[j] == cte) {
cte_count++;
}
}
u8 *buf = gb_alloc_array(a, u8, buf_len+cte_count);
isize i = 0;
for (isize j = 0; j < s.len; j++) {
u8 c = s.text[j];
if (c == cte) {
buf[i++] = '\\';
buf[i++] = c;
} else {
buf[i++] = c;
}
}
return make_string(buf, i);
}
#endif
gb_internal String string_join_and_quote(gbAllocator a, Array<String> strings) {
if (!strings.count) {
return make_string(nullptr, 0);
@@ -427,7 +453,11 @@ gb_internal String string_join_and_quote(gbAllocator a, Array<String> strings) {
if (i > 0) {
s = gb_string_append_fmt(s, " ");
}
#if defined(GB_SYSTEM_WINDOWS)
s = gb_string_append_fmt(s, "\"%.*s\" ", LIT(escape_char(a, strings[i], '\\')));
#else
s = gb_string_append_fmt(s, "\"%.*s\" ", LIT(strings[i]));
#endif
}
return make_string(cast(u8 *) s, gb_string_length(s));
@@ -688,12 +718,12 @@ gb_internal bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_byt
Rune r = -1;
isize size = utf8_decode(s.text, s.len, &r);
*rune = r;
*multiple_bytes = true;
*tail_string = make_string(s.text+size, s.len-size);
if (multiple_bytes) *multiple_bytes = true;
if (tail_string) *tail_string = make_string(s.text+size, s.len-size);
return true;
} else if (s[0] != '\\') {
*rune = s[0];
*tail_string = make_string(s.text+1, s.len-1);
if (tail_string) *tail_string = make_string(s.text+1, s.len-1);
return true;
}
@@ -779,10 +809,10 @@ gb_internal bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_byt
return false;
}
*rune = r;
*multiple_bytes = true;
if (multiple_bytes) *multiple_bytes = true;
} break;
}
*tail_string = s;
if (tail_string) *tail_string = s;
return true;
}
+7 -101
View File
@@ -272,14 +272,6 @@ struct TypeProc {
Type *elem; \
Type *generic_count; \
}) \
TYPE_KIND(RelativePointer, struct { \
Type *pointer_type; \
Type *base_integer; \
}) \
TYPE_KIND(RelativeMultiPointer, struct { \
Type *pointer_type; \
Type *base_integer; \
}) \
TYPE_KIND(Matrix, struct { \
Type *elem; \
i64 row_count; \
@@ -367,8 +359,6 @@ enum Typeid_Kind : u8 {
Typeid_Map,
Typeid_Bit_Set,
Typeid_Simd_Vector,
Typeid_Relative_Pointer,
Typeid_Relative_Multi_Pointer,
Typeid_Matrix,
Typeid_SoaPointer,
Typeid_Bit_Field,
@@ -678,8 +668,6 @@ gb_global Type *t_type_info_enum = nullptr;
gb_global Type *t_type_info_map = nullptr;
gb_global Type *t_type_info_bit_set = nullptr;
gb_global Type *t_type_info_simd_vector = nullptr;
gb_global Type *t_type_info_relative_pointer = nullptr;
gb_global Type *t_type_info_relative_multi_pointer = nullptr;
gb_global Type *t_type_info_matrix = nullptr;
gb_global Type *t_type_info_soa_pointer = nullptr;
gb_global Type *t_type_info_bit_field = nullptr;
@@ -708,8 +696,6 @@ gb_global Type *t_type_info_enum_ptr = nullptr;
gb_global Type *t_type_info_map_ptr = nullptr;
gb_global Type *t_type_info_bit_set_ptr = nullptr;
gb_global Type *t_type_info_simd_vector_ptr = nullptr;
gb_global Type *t_type_info_relative_pointer_ptr = nullptr;
gb_global Type *t_type_info_relative_multi_pointer_ptr = nullptr;
gb_global Type *t_type_info_matrix_ptr = nullptr;
gb_global Type *t_type_info_soa_pointer_ptr = nullptr;
gb_global Type *t_type_info_bit_field_ptr = nullptr;
@@ -1118,24 +1104,6 @@ gb_internal Type *alloc_type_bit_field() {
return t;
}
gb_internal Type *alloc_type_relative_pointer(Type *pointer_type, Type *base_integer) {
GB_ASSERT(is_type_pointer(pointer_type));
GB_ASSERT(is_type_integer(base_integer));
Type *t = alloc_type(Type_RelativePointer);
t->RelativePointer.pointer_type = pointer_type;
t->RelativePointer.base_integer = base_integer;
return t;
}
gb_internal Type *alloc_type_relative_multi_pointer(Type *pointer_type, Type *base_integer) {
GB_ASSERT(is_type_multi_pointer(pointer_type));
GB_ASSERT(is_type_integer(base_integer));
Type *t = alloc_type(Type_RelativeMultiPointer);
t->RelativeMultiPointer.pointer_type = pointer_type;
t->RelativeMultiPointer.base_integer = base_integer;
return t;
}
gb_internal Type *alloc_type_named(String name, Type *base, Entity *type_name) {
Type *t = alloc_type(Type_Named);
t->Named.name = name;
@@ -1227,8 +1195,6 @@ gb_internal Type *type_deref(Type *t, bool allow_multi_pointer) {
switch (bt->kind) {
case Type_Pointer:
return bt->Pointer.elem;
case Type_RelativePointer:
return type_deref(bt->RelativePointer.pointer_type);
case Type_SoaPointer:
{
Type *elem = base_type(bt->SoaPointer.elem);
@@ -1667,15 +1633,6 @@ gb_internal bool is_type_generic(Type *t) {
return t->kind == Type_Generic;
}
gb_internal bool is_type_relative_pointer(Type *t) {
t = base_type(t);
return t->kind == Type_RelativePointer;
}
gb_internal bool is_type_relative_multi_pointer(Type *t) {
t = base_type(t);
return t->kind == Type_RelativeMultiPointer;
}
gb_internal bool is_type_u8_slice(Type *t) {
t = base_type(t);
if (t->kind == Type_Slice) {
@@ -2118,8 +2075,6 @@ gb_internal bool is_type_indexable(Type *t) {
return true;
case Type_EnumeratedArray:
return true;
case Type_RelativeMultiPointer:
return true;
case Type_Matrix:
return true;
}
@@ -2137,8 +2092,6 @@ gb_internal bool is_type_sliceable(Type *t) {
return true;
case Type_EnumeratedArray:
return false;
case Type_RelativeMultiPointer:
return true;
case Type_Matrix:
return false;
}
@@ -2345,27 +2298,7 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) {
return true;
}
break;
case Type_RelativeMultiPointer:
if (is_type_polymorphic(t->RelativeMultiPointer.pointer_type, or_specialized)) {
return true;
}
if (t->RelativeMultiPointer.base_integer != nullptr &&
is_type_polymorphic(t->RelativeMultiPointer.base_integer, or_specialized)) {
return true;
}
break;
case Type_RelativePointer:
if (is_type_polymorphic(t->RelativePointer.pointer_type, or_specialized)) {
return true;
}
if (t->RelativePointer.base_integer != nullptr &&
is_type_polymorphic(t->RelativePointer.base_integer, or_specialized)) {
return true;
}
break;
}
return false;
}
@@ -2407,10 +2340,6 @@ gb_internal bool type_has_nil(Type *t) {
}
}
return false;
case Type_RelativePointer:
case Type_RelativeMultiPointer:
return true;
}
return false;
}
@@ -2579,10 +2508,6 @@ gb_internal bool is_type_load_safe(Type *type) {
}
return true;
case Type_RelativePointer:
case Type_RelativeMultiPointer:
return true;
case Type_Pointer:
case Type_MultiPointer:
case Type_Slice:
@@ -3945,11 +3870,6 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
case Type_Matrix:
return matrix_align_of(t, path);
case Type_RelativePointer:
return type_align_of_internal(t->RelativePointer.base_integer, path);
case Type_RelativeMultiPointer:
return type_align_of_internal(t->RelativeMultiPointer.base_integer, path);
case Type_SoaPointer:
return build_context.int_size;
}
@@ -3968,6 +3888,10 @@ gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_pack
min_field_align = 1;
}
TypePath path{};
type_path_init(&path);
defer (type_path_free(&path));
if (is_raw_union) {
for_array(i, fields) {
offsets[i] = 0;
@@ -3977,7 +3901,7 @@ gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_pack
if (fields[i]->kind != Entity_Variable) {
offsets[i] = -1;
} else {
i64 size = type_size_of(fields[i]->type);
i64 size = type_size_of_internal(fields[i]->type, &path);
offsets[i] = curr_offset;
curr_offset += size;
}
@@ -3988,11 +3912,11 @@ gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_pack
offsets[i] = -1;
} else {
Type *t = fields[i]->type;
i64 align = gb_max(type_align_of(t), min_field_align);
i64 align = gb_max(type_align_of_internal(t, &path), min_field_align);
if (max_field_align > min_field_align) {
align = gb_min(align, max_field_align);
}
i64 size = gb_max(type_size_of( t), 0);
i64 size = gb_max(type_size_of_internal(t, &path), 0);
curr_offset = align_formula(curr_offset, align);
offsets[i] = curr_offset;
curr_offset += size;
@@ -4242,11 +4166,6 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
case Type_BitField:
return type_size_of_internal(t->BitField.backing_type, path);
case Type_RelativePointer:
return type_size_of_internal(t->RelativePointer.base_integer, path);
case Type_RelativeMultiPointer:
return type_size_of_internal(t->RelativeMultiPointer.base_integer, path);
}
// Catch all
@@ -4872,19 +4791,6 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
str = gb_string_append_fmt(str, "#simd[%d]", cast(int)type->SimdVector.count);
str = write_type_to_string(str, type->SimdVector.elem);
break;
case Type_RelativePointer:
str = gb_string_append_fmt(str, "#relative(");
str = write_type_to_string(str, type->RelativePointer.base_integer);
str = gb_string_append_fmt(str, ") ");
str = write_type_to_string(str, type->RelativePointer.pointer_type);
break;
case Type_RelativeMultiPointer:
str = gb_string_append_fmt(str, "#relative(");
str = write_type_to_string(str, type->RelativePointer.base_integer);
str = gb_string_append_fmt(str, ") ");
str = write_type_to_string(str, type->RelativePointer.pointer_type);
break;
case Type_Matrix:
if (type->Matrix.is_row_major) {

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