mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-26 06:51:34 +00:00
Merge branch 'master' into windows-llvm-13.0.0
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -269,6 +269,8 @@ bin/
|
||||
# - Linux/MacOS
|
||||
odin
|
||||
odin.dSYM
|
||||
*.bin
|
||||
demo.bin
|
||||
|
||||
# shared collection
|
||||
shared/
|
||||
|
||||
@@ -1,90 +1,90 @@
|
||||
// This is purely for documentation
|
||||
package builtin
|
||||
|
||||
nil :: nil;
|
||||
false :: 0!=0;
|
||||
true :: 0==0;
|
||||
nil :: nil
|
||||
false :: 0!=0
|
||||
true :: 0==0
|
||||
|
||||
ODIN_OS :: ODIN_OS;
|
||||
ODIN_ARCH :: ODIN_ARCH;
|
||||
ODIN_ENDIAN :: ODIN_ENDIAN;
|
||||
ODIN_VENDOR :: ODIN_VENDOR;
|
||||
ODIN_VERSION :: ODIN_VERSION;
|
||||
ODIN_ROOT :: ODIN_ROOT;
|
||||
ODIN_DEBUG :: ODIN_DEBUG;
|
||||
ODIN_OS :: ODIN_OS
|
||||
ODIN_ARCH :: ODIN_ARCH
|
||||
ODIN_ENDIAN :: ODIN_ENDIAN
|
||||
ODIN_VENDOR :: ODIN_VENDOR
|
||||
ODIN_VERSION :: ODIN_VERSION
|
||||
ODIN_ROOT :: ODIN_ROOT
|
||||
ODIN_DEBUG :: ODIN_DEBUG
|
||||
|
||||
byte :: u8; // alias
|
||||
byte :: u8 // alias
|
||||
|
||||
bool :: bool;
|
||||
b8 :: b8;
|
||||
b16 :: b16;
|
||||
b32 :: b32;
|
||||
b64 :: b64;
|
||||
bool :: bool
|
||||
b8 :: b8
|
||||
b16 :: b16
|
||||
b32 :: b32
|
||||
b64 :: b64
|
||||
|
||||
i8 :: i8;
|
||||
u8 :: u8;
|
||||
i16 :: i16;
|
||||
u16 :: u16;
|
||||
i32 :: i32;
|
||||
u32 :: u32;
|
||||
i64 :: i64;
|
||||
u64 :: u64;
|
||||
i8 :: i8
|
||||
u8 :: u8
|
||||
i16 :: i16
|
||||
u16 :: u16
|
||||
i32 :: i32
|
||||
u32 :: u32
|
||||
i64 :: i64
|
||||
u64 :: u64
|
||||
|
||||
i128 :: i128;
|
||||
u128 :: u128;
|
||||
i128 :: i128
|
||||
u128 :: u128
|
||||
|
||||
rune :: rune;
|
||||
rune :: rune
|
||||
|
||||
f16 :: f16;
|
||||
f32 :: f32;
|
||||
f64 :: f64;
|
||||
f16 :: f16
|
||||
f32 :: f32
|
||||
f64 :: f64
|
||||
|
||||
complex32 :: complex32;
|
||||
complex64 :: complex64;
|
||||
complex128 :: complex128;
|
||||
complex32 :: complex32
|
||||
complex64 :: complex64
|
||||
complex128 :: complex128
|
||||
|
||||
quaternion64 :: quaternion64;
|
||||
quaternion128 :: quaternion128;
|
||||
quaternion256 :: quaternion256;
|
||||
quaternion64 :: quaternion64
|
||||
quaternion128 :: quaternion128
|
||||
quaternion256 :: quaternion256
|
||||
|
||||
int :: int;
|
||||
uint :: uint;
|
||||
uintptr :: uintptr;
|
||||
int :: int
|
||||
uint :: uint
|
||||
uintptr :: uintptr
|
||||
|
||||
rawptr :: rawptr;
|
||||
string :: string;
|
||||
cstring :: cstring;
|
||||
any :: any;
|
||||
rawptr :: rawptr
|
||||
string :: string
|
||||
cstring :: cstring
|
||||
any :: any
|
||||
|
||||
typeid :: typeid;
|
||||
typeid :: typeid
|
||||
|
||||
// Endian Specific Types
|
||||
i16le :: i16le;
|
||||
u16le :: u16le;
|
||||
i32le :: i32le;
|
||||
u32le :: u32le;
|
||||
i64le :: i64le;
|
||||
u64le :: u64le;
|
||||
i128le :: i128le;
|
||||
u128le :: u128le;
|
||||
i16le :: i16le
|
||||
u16le :: u16le
|
||||
i32le :: i32le
|
||||
u32le :: u32le
|
||||
i64le :: i64le
|
||||
u64le :: u64le
|
||||
i128le :: i128le
|
||||
u128le :: u128le
|
||||
|
||||
i16be :: i16be;
|
||||
u16be :: u16be;
|
||||
i32be :: i32be;
|
||||
u32be :: u32be;
|
||||
i64be :: i64be;
|
||||
u64be :: u64be;
|
||||
i128be :: i128be;
|
||||
u128be :: u128be;
|
||||
i16be :: i16be
|
||||
u16be :: u16be
|
||||
i32be :: i32be
|
||||
u32be :: u32be
|
||||
i64be :: i64be
|
||||
u64be :: u64be
|
||||
i128be :: i128be
|
||||
u128be :: u128be
|
||||
|
||||
|
||||
f16le :: f16le;
|
||||
f32le :: f32le;
|
||||
f64le :: f64le;
|
||||
f16le :: f16le
|
||||
f32le :: f32le
|
||||
f64le :: f64le
|
||||
|
||||
f16be :: f16be;
|
||||
f32be :: f32be;
|
||||
f64be :: f64be;
|
||||
f16be :: f16be
|
||||
f32be :: f32be
|
||||
f64be :: f64be
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -10,7 +10,14 @@ clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location
|
||||
return c[:len(s)]
|
||||
}
|
||||
|
||||
ptr_from_slice :: proc(str: []byte) -> ^byte {
|
||||
clone_safe :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
c := make([]byte, len(s), allocator, loc) or_return
|
||||
copy(c, s)
|
||||
return c[:len(s)], nil
|
||||
}
|
||||
|
||||
ptr_from_slice :: ptr_from_bytes
|
||||
ptr_from_bytes :: proc(str: []byte) -> ^byte {
|
||||
d := transmute(mem.Raw_String)str
|
||||
return d.data
|
||||
}
|
||||
@@ -134,6 +141,25 @@ join :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> []byte
|
||||
return b
|
||||
}
|
||||
|
||||
join_safe :: proc(a: [][]byte, sep: []byte, allocator := context.allocator) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
if len(a) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
n := len(sep) * (len(a) - 1)
|
||||
for s in a {
|
||||
n += len(s)
|
||||
}
|
||||
|
||||
b := make([]byte, n, allocator) or_return
|
||||
i := copy(b, a[0])
|
||||
for s in a[1:] {
|
||||
i += copy(b[i:], sep)
|
||||
i += copy(b[i:], s)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
||||
if len(a) == 0 {
|
||||
return nil
|
||||
@@ -151,6 +177,24 @@ concatenate :: proc(a: [][]byte, allocator := context.allocator) -> []byte {
|
||||
return b
|
||||
}
|
||||
|
||||
concatenate_safe :: proc(a: [][]byte, allocator := context.allocator) -> (data: []byte, err: mem.Allocator_Error) {
|
||||
if len(a) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
n := 0
|
||||
for s in a {
|
||||
n += len(s)
|
||||
}
|
||||
b := make([]byte, n, allocator) or_return
|
||||
i := 0
|
||||
for s in a {
|
||||
i += copy(b[i:], s)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
|
||||
@private
|
||||
_split :: proc(s, sep: []byte, sep_save, n: int, allocator := context.allocator) -> [][]byte {
|
||||
s, n := s, n
|
||||
|
||||
@@ -1276,7 +1276,7 @@ preprocess_internal :: proc(cpp: ^Preprocessor, tok: ^Token) -> ^Token {
|
||||
if start.file != nil {
|
||||
dir = filepath.dir(start.file.name)
|
||||
}
|
||||
path := filepath.join(dir, filename)
|
||||
path := filepath.join({dir, filename})
|
||||
if os.exists(path) {
|
||||
tok = include_file(cpp, tok, path, start.next.next)
|
||||
continue
|
||||
|
||||
@@ -211,19 +211,19 @@ _signbitf :: #force_inline proc(x: float) -> int {
|
||||
return int(transmute(uint32_t)x >> 31)
|
||||
}
|
||||
|
||||
isfinite :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
isfinite :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) == FP_INFINITE
|
||||
}
|
||||
|
||||
isinf :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
isinf :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) > FP_INFINITE
|
||||
}
|
||||
|
||||
isnan :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
isnan :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) == FP_NAN
|
||||
}
|
||||
|
||||
isnormal :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
isnormal :: #force_inline proc(x: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return fpclassify(x) == FP_NORMAL
|
||||
}
|
||||
|
||||
@@ -231,27 +231,27 @@ isnormal :: #force_inline proc(x: $T) where intrinsics.type_is_float(T) {
|
||||
// implemented as the relational comparisons, as that would produce an invalid
|
||||
// "sticky" state that propagates and affects maths results. These need
|
||||
// to be implemented natively in Odin assuming isunordered to prevent that.
|
||||
isgreater :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
isgreater :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x > y
|
||||
}
|
||||
|
||||
isgreaterequal :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
isgreaterequal :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x >= y
|
||||
}
|
||||
|
||||
isless :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
isless :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x < y
|
||||
}
|
||||
|
||||
islessequal :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
islessequal :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x <= y
|
||||
}
|
||||
|
||||
islessgreater :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
islessgreater :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
return !isunordered(x, y) && x <= y
|
||||
}
|
||||
|
||||
isunordered :: #force_inline proc(x, y: $T) where intrinsics.type_is_float(T) {
|
||||
isunordered :: #force_inline proc(x, y: $T) -> bool where intrinsics.type_is_float(T) {
|
||||
if isnan(x) {
|
||||
// Force evaluation of y to propagate exceptions for ordering semantics.
|
||||
// To ensure correct semantics of IEEE 754 this cannot be compiled away.
|
||||
|
||||
@@ -45,7 +45,7 @@ main :: proc() {
|
||||
|
||||
if len(args) < 2 {
|
||||
stderr("No input file specified.\n")
|
||||
err := load(slice=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||
err := load(data=TEST, buf=&buf, known_gzip_size=len(TEST))
|
||||
if err == nil {
|
||||
stdout("Displaying test vector: ")
|
||||
stdout(bytes.buffer_to_string(&buf))
|
||||
|
||||
@@ -102,7 +102,7 @@ E_Deflate :: compress.Deflate_Error
|
||||
|
||||
GZIP_MAX_PAYLOAD_SIZE :: i64(max(u32le))
|
||||
|
||||
load :: proc{load_from_slice, load_from_file, load_from_context}
|
||||
load :: proc{load_from_bytes, load_from_file, load_from_context}
|
||||
|
||||
load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
@@ -112,16 +112,16 @@ load_from_file :: proc(filename: string, buf: ^bytes.Buffer, expected_output_siz
|
||||
|
||||
err = E_General.File_Not_Found
|
||||
if ok {
|
||||
err = load_from_slice(data, buf, len(data), expected_output_size)
|
||||
err = load_from_bytes(data, buf, len(data), expected_output_size)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
load_from_slice :: proc(slice: []u8, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
load_from_bytes :: proc(data: []byte, buf: ^bytes.Buffer, known_gzip_size := -1, expected_output_size := -1, allocator := context.allocator) -> (err: Error) {
|
||||
buf := buf
|
||||
|
||||
z := &compress.Context_Memory_Input{
|
||||
input_data = slice,
|
||||
input_data = data,
|
||||
output = buf,
|
||||
}
|
||||
return load_from_context(z, buf, known_gzip_size, expected_output_size, allocator)
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
package dynlib
|
||||
|
||||
Library :: distinct rawptr
|
||||
|
||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
return _load_library(path, global_symbols)
|
||||
}
|
||||
|
||||
unload_library :: proc(library: Library) -> bool {
|
||||
return _unload_library(library)
|
||||
}
|
||||
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
|
||||
return _symbol_address(library, symbol)
|
||||
}
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
// +build linux, darwin, freebsd, openbsd
|
||||
//+build linux, darwin, freebsd, openbsd
|
||||
//+private
|
||||
package dynlib
|
||||
|
||||
import "core:os"
|
||||
|
||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
flags := os.RTLD_NOW
|
||||
if global_symbols {
|
||||
flags |= os.RTLD_GLOBAL
|
||||
}
|
||||
lib := os.dlopen(path, flags)
|
||||
return Library(lib), lib != nil
|
||||
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
flags := os.RTLD_NOW
|
||||
if global_symbols {
|
||||
flags |= os.RTLD_GLOBAL
|
||||
}
|
||||
lib := os.dlopen(path, flags)
|
||||
return Library(lib), lib != nil
|
||||
}
|
||||
|
||||
unload_library :: proc(library: Library) {
|
||||
os.dlclose(rawptr(library))
|
||||
_unload_library :: proc(library: Library) -> bool {
|
||||
return os.dlclose(rawptr(library))
|
||||
}
|
||||
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
ptr = os.dlsym(rawptr(library), symbol)
|
||||
found = ptr != nil
|
||||
return
|
||||
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
ptr = os.dlsym(rawptr(library), symbol)
|
||||
found = ptr != nil
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// +build windows
|
||||
//+build windows
|
||||
//+private
|
||||
package dynlib
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
|
||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
|
||||
|
||||
wide_path := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
@@ -12,12 +13,12 @@ load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
return handle, handle != nil
|
||||
}
|
||||
|
||||
unload_library :: proc(library: Library) -> bool {
|
||||
_unload_library :: proc(library: Library) -> bool {
|
||||
ok := win32.FreeLibrary(cast(win32.HMODULE)library)
|
||||
return bool(ok)
|
||||
}
|
||||
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
c_str := strings.clone_to_cstring(symbol, context.temp_allocator)
|
||||
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
|
||||
found = ptr != nil
|
||||
|
||||
@@ -34,6 +34,10 @@ Reader :: struct {
|
||||
// If lazy_quotes is true, a quote may appear in an unquoted field and a non-doubled quote may appear in a quoted field
|
||||
lazy_quotes: bool,
|
||||
|
||||
// multiline_fields, when set to true, will treat a field starting with a " as a multiline string
|
||||
// therefore, instead of reading until the next \n, it'll read until the next "
|
||||
multiline_fields: bool,
|
||||
|
||||
// reuse_record controls whether calls to 'read' may return a slice using the backing buffer
|
||||
// for performance
|
||||
// By default, each call to 'read' returns a newly allocated slice
|
||||
@@ -194,32 +198,72 @@ is_valid_delim :: proc(r: rune) -> bool {
|
||||
@private
|
||||
_read_record :: proc(r: ^Reader, dst: ^[dynamic]string, allocator := context.allocator) -> ([]string, Error) {
|
||||
read_line :: proc(r: ^Reader) -> ([]byte, io.Error) {
|
||||
line, err := bufio.reader_read_slice(&r.r, '\n')
|
||||
if err == .Buffer_Full {
|
||||
clear(&r.raw_buffer)
|
||||
append(&r.raw_buffer, ..line)
|
||||
for err == .Buffer_Full {
|
||||
line, err = bufio.reader_read_slice(&r.r, '\n')
|
||||
if !r.multiline_fields {
|
||||
line, err := bufio.reader_read_slice(&r.r, '\n')
|
||||
if err == .Buffer_Full {
|
||||
clear(&r.raw_buffer)
|
||||
append(&r.raw_buffer, ..line)
|
||||
for err == .Buffer_Full {
|
||||
line, err = bufio.reader_read_slice(&r.r, '\n')
|
||||
append(&r.raw_buffer, ..line)
|
||||
}
|
||||
line = r.raw_buffer[:]
|
||||
}
|
||||
line = r.raw_buffer[:]
|
||||
}
|
||||
if len(line) > 0 && err == .EOF {
|
||||
err = nil
|
||||
if line[len(line)-1] == '\r' {
|
||||
line = line[:len(line)-1]
|
||||
if len(line) > 0 && err == .EOF {
|
||||
err = nil
|
||||
if line[len(line)-1] == '\r' {
|
||||
line = line[:len(line)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
r.line_count += 1
|
||||
r.line_count += 1
|
||||
|
||||
// normalize \r\n to \n
|
||||
n := len(line)
|
||||
for n >= 2 && string(line[n-2:]) == "\r\n" {
|
||||
line[n-2] = '\n'
|
||||
line = line[:n-1]
|
||||
}
|
||||
// normalize \r\n to \n
|
||||
n := len(line)
|
||||
for n >= 2 && string(line[n-2:]) == "\r\n" {
|
||||
line[n-2] = '\n'
|
||||
line = line[:n-1]
|
||||
}
|
||||
return line, err
|
||||
|
||||
return line, err
|
||||
} else {
|
||||
// Reading a "line" that can possibly contain multiline fields.
|
||||
// Unfortunately, this means we need to read a character at a time.
|
||||
|
||||
err: io.Error
|
||||
cur: rune
|
||||
is_quoted: bool
|
||||
|
||||
field_length := 0
|
||||
|
||||
clear(&r.raw_buffer)
|
||||
|
||||
read_loop: for err == .None {
|
||||
cur, _, err = bufio.reader_read_rune(&r.r)
|
||||
|
||||
if err != .None { break read_loop }
|
||||
|
||||
switch cur {
|
||||
case '"':
|
||||
is_quoted = field_length == 0
|
||||
field_length += 1
|
||||
|
||||
case '\n', '\r':
|
||||
if !is_quoted { break read_loop }
|
||||
|
||||
case r.comma:
|
||||
field_length = 0
|
||||
|
||||
case:
|
||||
field_length += 1
|
||||
}
|
||||
|
||||
rune_buf, rune_len := utf8.encode_rune(cur)
|
||||
append(&r.raw_buffer, ..rune_buf[:rune_len])
|
||||
}
|
||||
|
||||
return r.raw_buffer[:], err
|
||||
}
|
||||
unreachable()
|
||||
}
|
||||
|
||||
length_newline :: proc(b: []byte) -> int {
|
||||
|
||||
23
core/encoding/endian/doc.odin
Normal file
23
core/encoding/endian/doc.odin
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
Package endian implements sa simple translation between bytes and numbers with
|
||||
specific endian encodings.
|
||||
|
||||
buf: [100]u8
|
||||
put_u16(buf[:], .Little, 16) or_return
|
||||
|
||||
You may ask yourself, why isn't `byte_order` platform Endianness by default, so we can write:
|
||||
put_u16(buf[:], 16) or_return
|
||||
|
||||
The answer is that very few file formats are written in native/platform endianness. Most of them specify the endianness of
|
||||
each of their fields, or use a header field which specifies it for the entire file.
|
||||
|
||||
e.g. a file which specifies it at the top for all fields could do this:
|
||||
file_order := .Little if buf[0] == 0 else .Big
|
||||
field := get_u16(buf[1:], file_order) or_return
|
||||
|
||||
If on the other hand a field is *always* Big-Endian, you're wise to explicitly state it for the benefit of the reader,
|
||||
be that your future self or someone else.
|
||||
|
||||
field := get_u16(buf[:], .Big) or_return
|
||||
*/
|
||||
package encoding_endian
|
||||
153
core/encoding/endian/endian.odin
Normal file
153
core/encoding/endian/endian.odin
Normal file
@@ -0,0 +1,153 @@
|
||||
package encoding_endian
|
||||
|
||||
Byte_Order :: enum u8 {
|
||||
Little,
|
||||
Big,
|
||||
}
|
||||
|
||||
PLATFORM_BYTE_ORDER :: Byte_Order.Little when ODIN_ENDIAN == .Little else Byte_Order.Big
|
||||
|
||||
get_u16 :: proc(b: []byte, order: Byte_Order) -> (v: u16, ok: bool) {
|
||||
if len(b) < 2 {
|
||||
return 0, false
|
||||
}
|
||||
#no_bounds_check if order == .Little {
|
||||
v = u16(b[0]) | u16(b[1])<<8
|
||||
} else {
|
||||
v = u16(b[1]) | u16(b[0])<<8
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
get_u32 :: proc(b: []byte, order: Byte_Order) -> (v: u32, ok: bool) {
|
||||
if len(b) < 4 {
|
||||
return 0, false
|
||||
}
|
||||
#no_bounds_check if order == .Little {
|
||||
v = u32(b[0]) | u32(b[1])<<8 | u32(b[2])<<16 | u32(b[3])<<24
|
||||
} else {
|
||||
v = u32(b[3]) | u32(b[2])<<8 | u32(b[1])<<16 | u32(b[0])<<24
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
|
||||
get_u64 :: proc(b: []byte, order: Byte_Order) -> (v: u64, ok: bool) {
|
||||
if len(b) < 8 {
|
||||
return 0, false
|
||||
}
|
||||
#no_bounds_check if order == .Little {
|
||||
v = u64(b[0]) | u64(b[1])<<8 | u64(b[2])<<16 | u64(b[3])<<24 |
|
||||
u64(b[4])<<32 | u64(b[5])<<40 | u64(b[6])<<48 | u64(b[7])<<56
|
||||
} else {
|
||||
v = u64(b[7]) | u64(b[6])<<8 | u64(b[5])<<16 | u64(b[4])<<24 |
|
||||
u64(b[3])<<32 | u64(b[2])<<40 | u64(b[1])<<48 | u64(b[0])<<56
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
|
||||
get_i16 :: proc(b: []byte, order: Byte_Order) -> (i16, bool) {
|
||||
v, ok := get_u16(b, order)
|
||||
return i16(v), ok
|
||||
}
|
||||
get_i32 :: proc(b: []byte, order: Byte_Order) -> (i32, bool) {
|
||||
v, ok := get_u32(b, order)
|
||||
return i32(v), ok
|
||||
}
|
||||
get_i64 :: proc(b: []byte, order: Byte_Order) -> (i64, bool) {
|
||||
v, ok := get_u64(b, order)
|
||||
return i64(v), ok
|
||||
}
|
||||
|
||||
get_f16 :: proc(b: []byte, order: Byte_Order) -> (f16, bool) {
|
||||
v, ok := get_u16(b, order)
|
||||
return transmute(f16)v, ok
|
||||
}
|
||||
get_f32 :: proc(b: []byte, order: Byte_Order) -> (f32, bool) {
|
||||
v, ok := get_u32(b, order)
|
||||
return transmute(f32)v, ok
|
||||
}
|
||||
get_f64 :: proc(b: []byte, order: Byte_Order) -> (f64, bool) {
|
||||
v, ok := get_u64(b, order)
|
||||
return transmute(f64)v, ok
|
||||
}
|
||||
|
||||
|
||||
put_u16 :: proc(b: []byte, order: Byte_Order, v: u16) -> bool {
|
||||
if len(b) < 2 {
|
||||
return false
|
||||
}
|
||||
#no_bounds_check if order == .Little {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
} else {
|
||||
b[0] = byte(v >> 8)
|
||||
b[1] = byte(v)
|
||||
}
|
||||
return true
|
||||
}
|
||||
put_u32 :: proc(b: []byte, order: Byte_Order, v: u32) -> bool {
|
||||
if len(b) < 4 {
|
||||
return false
|
||||
}
|
||||
#no_bounds_check if order == .Little {
|
||||
b[0] = byte(v)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
} else {
|
||||
b[0] = byte(v >> 24)
|
||||
b[1] = byte(v >> 16)
|
||||
b[2] = byte(v >> 8)
|
||||
b[3] = byte(v)
|
||||
}
|
||||
return true
|
||||
}
|
||||
put_u64 :: proc(b: []byte, order: Byte_Order, v: u64) -> bool {
|
||||
if len(b) < 8 {
|
||||
return false
|
||||
}
|
||||
#no_bounds_check if order == .Little {
|
||||
b[0] = byte(v >> 0)
|
||||
b[1] = byte(v >> 8)
|
||||
b[2] = byte(v >> 16)
|
||||
b[3] = byte(v >> 24)
|
||||
b[4] = byte(v >> 32)
|
||||
b[5] = byte(v >> 40)
|
||||
b[6] = byte(v >> 48)
|
||||
b[7] = byte(v >> 56)
|
||||
} else {
|
||||
b[0] = byte(v >> 56)
|
||||
b[1] = byte(v >> 48)
|
||||
b[2] = byte(v >> 40)
|
||||
b[3] = byte(v >> 32)
|
||||
b[4] = byte(v >> 24)
|
||||
b[5] = byte(v >> 16)
|
||||
b[6] = byte(v >> 8)
|
||||
b[7] = byte(v)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
put_i16 :: proc(b: []byte, order: Byte_Order, v: i16) -> bool {
|
||||
return put_u16(b, order, u16(v))
|
||||
}
|
||||
|
||||
put_i32 :: proc(b: []byte, order: Byte_Order, v: i32) -> bool {
|
||||
return put_u32(b, order, u32(v))
|
||||
}
|
||||
|
||||
put_i64 :: proc(b: []byte, order: Byte_Order, v: i64) -> bool {
|
||||
return put_u64(b, order, u64(v))
|
||||
}
|
||||
|
||||
|
||||
put_f16 :: proc(b: []byte, order: Byte_Order, v: f16) -> bool {
|
||||
return put_u16(b, order, transmute(u16)v)
|
||||
}
|
||||
|
||||
put_f32 :: proc(b: []byte, order: Byte_Order, v: f32) -> bool {
|
||||
return put_u32(b, order, transmute(u32)v)
|
||||
}
|
||||
|
||||
put_f64 :: proc(b: []byte, order: Byte_Order, v: f64) -> bool {
|
||||
return put_u64(b, order, transmute(u64)v)
|
||||
}
|
||||
@@ -231,16 +231,16 @@ xml_decode_entity :: proc(entity: string) -> (decoded: rune, ok: bool) {
|
||||
for len(entity) > 0 {
|
||||
r := entity[0]
|
||||
switch r {
|
||||
case '0'..'9':
|
||||
case '0'..='9':
|
||||
val *= base
|
||||
val += int(r - '0')
|
||||
|
||||
case 'a'..'f':
|
||||
case 'a'..='f':
|
||||
if base == 10 { return -1, false }
|
||||
val *= base
|
||||
val += int(r - 'a' + 10)
|
||||
|
||||
case 'A'..'F':
|
||||
case 'A'..='F':
|
||||
if base == 10 { return -1, false }
|
||||
val *= base
|
||||
val += int(r - 'A' + 10)
|
||||
|
||||
@@ -209,7 +209,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
|
||||
variant := u.variants[0]
|
||||
v.id = variant.id
|
||||
ti = reflect.type_info_base(variant)
|
||||
if !(u.maybe && reflect.is_pointer(variant)) {
|
||||
if !reflect.is_pointer_internally(variant) {
|
||||
tag := any{rawptr(uintptr(v.data) + u.tag_offset), u.tag_type.id}
|
||||
assign_int(tag, 1)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ package varint
|
||||
// In theory we should use the bigint package. In practice, varints bigger than this indicate a corrupted file.
|
||||
// Instead we'll set limits on the values we'll encode/decode
|
||||
// 18 * 7 bits = 126, which means that a possible 19th byte may at most be `0b0000_0011`.
|
||||
LEB128_MAX_BYTES :: 19
|
||||
LEB128_MAX_BYTES :: 19
|
||||
|
||||
Error :: enum {
|
||||
None = 0,
|
||||
@@ -132,14 +132,12 @@ encode_uleb128 :: proc(buf: []u8, val: u128) -> (size: int, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
SIGN_MASK :: (i128(1) << 121) // sign extend mask
|
||||
|
||||
// Encode `val` into `buf` as a signed LEB128 encoded series of bytes.
|
||||
// `buf` must be appropriately sized.
|
||||
encode_ileb128 :: proc(buf: []u8, val: i128) -> (size: int, err: Error) {
|
||||
val := val
|
||||
more := true
|
||||
SIGN_MASK :: i128(1) << 121 // sign extend mask
|
||||
|
||||
val, more := val, true
|
||||
|
||||
for more {
|
||||
size += 1
|
||||
|
||||
@@ -23,7 +23,7 @@ print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error
|
||||
|
||||
written += wprintf(writer, "[XML Prolog]\n")
|
||||
|
||||
for attr in doc.prolog {
|
||||
for attr in doc.prologue {
|
||||
written += wprintf(writer, "\t%v: %v\n", attr.key, attr.val)
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ example :: proc() {
|
||||
times[round] = time.tick_diff(start, end)
|
||||
}
|
||||
|
||||
fastest := time.Duration(max(i64))
|
||||
fastest := max(time.Duration)
|
||||
slowest := time.Duration(0)
|
||||
total := time.Duration(0)
|
||||
|
||||
|
||||
@@ -198,7 +198,7 @@ is_valid_identifier_rune :: proc(r: rune) -> bool {
|
||||
switch r {
|
||||
case '_', '-', ':': return true
|
||||
case 'A'..='Z', 'a'..='z': return true
|
||||
case '0'..'9': return true
|
||||
case '0'..='9': return true
|
||||
case -1: return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,10 +36,8 @@ import "core:strings"
|
||||
|
||||
likely :: intrinsics.expect
|
||||
|
||||
DEFAULT_Options :: Options{
|
||||
flags = {
|
||||
.Ignore_Unsupported,
|
||||
},
|
||||
DEFAULT_OPTIONS :: Options{
|
||||
flags = {.Ignore_Unsupported},
|
||||
expected_doctype = "",
|
||||
}
|
||||
|
||||
@@ -51,7 +49,7 @@ Option_Flag :: enum {
|
||||
Input_May_Be_Modified,
|
||||
|
||||
/*
|
||||
Document MUST start with `<?xml` prolog.
|
||||
Document MUST start with `<?xml` prologue.
|
||||
*/
|
||||
Must_Have_Prolog,
|
||||
|
||||
@@ -94,7 +92,7 @@ Document :: struct {
|
||||
elements: [dynamic]Element,
|
||||
element_count: Element_ID,
|
||||
|
||||
prolog: Attributes,
|
||||
prologue: Attributes,
|
||||
encoding: Encoding,
|
||||
|
||||
doctype: struct {
|
||||
@@ -138,12 +136,12 @@ Element :: struct {
|
||||
children: [dynamic]Element_ID,
|
||||
}
|
||||
|
||||
Attr :: struct {
|
||||
Attribute :: struct {
|
||||
key: string,
|
||||
val: string,
|
||||
}
|
||||
|
||||
Attributes :: [dynamic]Attr
|
||||
Attributes :: [dynamic]Attribute
|
||||
|
||||
Options :: struct {
|
||||
flags: Option_Flags,
|
||||
@@ -221,7 +219,7 @@ Error :: enum {
|
||||
/*
|
||||
Implementation starts here.
|
||||
*/
|
||||
parse_from_slice :: proc(data: []u8, options := DEFAULT_Options, path := "", error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||
parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||
data := data
|
||||
context.allocator = allocator
|
||||
|
||||
@@ -411,10 +409,10 @@ parse_from_slice :: proc(data: []u8, options := DEFAULT_Options, path := "", err
|
||||
#partial switch next.kind {
|
||||
case .Ident:
|
||||
if len(next.text) == 3 && strings.to_lower(next.text, context.temp_allocator) == "xml" {
|
||||
parse_prolog(doc) or_return
|
||||
} else if len(doc.prolog) > 0 {
|
||||
parse_prologue(doc) or_return
|
||||
} else if len(doc.prologue) > 0 {
|
||||
/*
|
||||
We've already seen a prolog.
|
||||
We've already seen a prologue.
|
||||
*/
|
||||
return doc, .Too_Many_Prologs
|
||||
} else {
|
||||
@@ -481,7 +479,7 @@ parse_from_slice :: proc(data: []u8, options := DEFAULT_Options, path := "", err
|
||||
}
|
||||
}
|
||||
|
||||
if .Must_Have_Prolog in opts.flags && len(doc.prolog) == 0 {
|
||||
if .Must_Have_Prolog in opts.flags && len(doc.prologue) == 0 {
|
||||
return doc, .No_Prolog
|
||||
}
|
||||
|
||||
@@ -493,16 +491,16 @@ parse_from_slice :: proc(data: []u8, options := DEFAULT_Options, path := "", err
|
||||
return doc, .None
|
||||
}
|
||||
|
||||
parse_from_string :: proc(data: string, options := DEFAULT_Options, path := "", error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||
parse_string :: proc(data: string, options := DEFAULT_OPTIONS, path := "", error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||
_data := transmute([]u8)data
|
||||
|
||||
return parse_from_slice(_data, options, path, error_handler, allocator)
|
||||
return parse_bytes(_data, options, path, error_handler, allocator)
|
||||
}
|
||||
|
||||
parse :: proc { parse_from_string, parse_from_slice }
|
||||
parse :: proc { parse_string, parse_bytes }
|
||||
|
||||
// Load an XML file
|
||||
load_from_file :: proc(filename: string, options := DEFAULT_Options, error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||
load_from_file :: proc(filename: string, options := DEFAULT_OPTIONS, error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||
context.allocator = allocator
|
||||
options := options
|
||||
|
||||
@@ -511,7 +509,7 @@ load_from_file :: proc(filename: string, options := DEFAULT_Options, error_handl
|
||||
|
||||
options.flags += { .Input_May_Be_Modified }
|
||||
|
||||
return parse_from_slice(data, options, filename, error_handler, allocator)
|
||||
return parse_bytes(data, options, filename, error_handler, allocator)
|
||||
}
|
||||
|
||||
destroy :: proc(doc: ^Document) {
|
||||
@@ -523,7 +521,7 @@ destroy :: proc(doc: ^Document) {
|
||||
}
|
||||
delete(doc.elements)
|
||||
|
||||
delete(doc.prolog)
|
||||
delete(doc.prologue)
|
||||
delete(doc.comments)
|
||||
delete(doc.input)
|
||||
|
||||
@@ -556,7 +554,7 @@ expect :: proc(t: ^Tokenizer, kind: Token_Kind) -> (tok: Token, err: Error) {
|
||||
return tok, .Unexpected_Token
|
||||
}
|
||||
|
||||
parse_attribute :: proc(doc: ^Document) -> (attr: Attr, offset: int, err: Error) {
|
||||
parse_attribute :: proc(doc: ^Document) -> (attr: Attribute, offset: int, err: Error) {
|
||||
assert(doc != nil)
|
||||
context.allocator = doc.allocator
|
||||
t := doc.tokenizer
|
||||
@@ -574,7 +572,7 @@ parse_attribute :: proc(doc: ^Document) -> (attr: Attr, offset: int, err: Error)
|
||||
return
|
||||
}
|
||||
|
||||
check_duplicate_attributes :: proc(t: ^Tokenizer, attribs: Attributes, attr: Attr, offset: int) -> (err: Error) {
|
||||
check_duplicate_attributes :: proc(t: ^Tokenizer, attribs: Attributes, attr: Attribute, offset: int) -> (err: Error) {
|
||||
for a in attribs {
|
||||
if attr.key == a.key {
|
||||
error(t, offset, "Duplicate attribute: %v\n", attr.key)
|
||||
@@ -598,21 +596,21 @@ parse_attributes :: proc(doc: ^Document, attribs: ^Attributes) -> (err: Error) {
|
||||
return .None
|
||||
}
|
||||
|
||||
parse_prolog :: proc(doc: ^Document) -> (err: Error) {
|
||||
parse_prologue :: proc(doc: ^Document) -> (err: Error) {
|
||||
assert(doc != nil)
|
||||
context.allocator = doc.allocator
|
||||
t := doc.tokenizer
|
||||
|
||||
offset := t.offset
|
||||
parse_attributes(doc, &doc.prolog) or_return
|
||||
parse_attributes(doc, &doc.prologue) or_return
|
||||
|
||||
for attr in doc.prolog {
|
||||
for attr in doc.prologue {
|
||||
switch attr.key {
|
||||
case "version":
|
||||
switch attr.val {
|
||||
case "1.0", "1.1":
|
||||
case:
|
||||
error(t, offset, "[parse_prolog] Warning: Unhandled XML version: %v\n", attr.val)
|
||||
error(t, offset, "[parse_prologue] Warning: Unhandled XML version: %v\n", attr.val)
|
||||
}
|
||||
|
||||
case "encoding":
|
||||
@@ -627,7 +625,7 @@ parse_prolog :: proc(doc: ^Document) -> (err: Error) {
|
||||
/*
|
||||
Unrecognized encoding, assume UTF-8.
|
||||
*/
|
||||
error(t, offset, "[parse_prolog] Warning: Unrecognized encoding: %v\n", attr.val)
|
||||
error(t, offset, "[parse_prologue] Warning: Unrecognized encoding: %v\n", attr.val)
|
||||
}
|
||||
|
||||
case:
|
||||
|
||||
@@ -119,17 +119,17 @@ tprintf :: proc(fmt: string, args: ..any) -> string {
|
||||
|
||||
// bprint procedures return a string using a buffer from an array
|
||||
bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string {
|
||||
sb := strings.builder_from_slice(buf[0:len(buf)])
|
||||
sb := strings.builder_from_bytes(buf[0:len(buf)])
|
||||
return sbprint(buf=&sb, args=args, sep=sep)
|
||||
}
|
||||
// bprintln procedures return a string using a buffer from an array
|
||||
bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
|
||||
sb := strings.builder_from_slice(buf[0:len(buf)])
|
||||
sb := strings.builder_from_bytes(buf[0:len(buf)])
|
||||
return sbprintln(buf=&sb, args=args, sep=sep)
|
||||
}
|
||||
// bprintf procedures return a string using a buffer from an array
|
||||
bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
|
||||
sb := strings.builder_from_slice(buf[0:len(buf)])
|
||||
sb := strings.builder_from_bytes(buf[0:len(buf)])
|
||||
return sbprintf(&sb, fmt, ..args)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
length := len(data)
|
||||
|
||||
for length != 0 && uintptr(buffer) & 7 != 0 {
|
||||
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8)
|
||||
buffer = intrinsics.ptr_offset(buffer, 1)
|
||||
crc = crc32_table[0][byte(crc) ~ buffer[0]] ~ (crc >> 8)
|
||||
buffer = buffer[1:]
|
||||
length -= 1
|
||||
}
|
||||
|
||||
@@ -28,14 +28,14 @@ crc32 :: proc(data: []byte, seed := u32(0)) -> u32 #no_bounds_check {
|
||||
crc32_table[1][buf[6]] ~
|
||||
crc32_table[0][buf[7]]
|
||||
|
||||
buffer = intrinsics.ptr_offset(buffer, 8)
|
||||
buffer = buffer[8:]
|
||||
length -= 8
|
||||
}
|
||||
|
||||
|
||||
for length != 0 {
|
||||
crc = crc32_table[0][byte(crc) ~ buffer^] ~ (crc >> 8)
|
||||
buffer = intrinsics.ptr_offset(buffer, 1)
|
||||
crc = crc32_table[0][byte(crc) ~ buffer[0]] ~ (crc >> 8)
|
||||
buffer = buffer[1:]
|
||||
length -= 1
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ adler32 :: proc(data: []byte, seed := u32(1)) -> u32 #no_bounds_check {
|
||||
for len(buf) != 0 && uintptr(buffer) & 7 != 0 {
|
||||
a = (a + u64(buf[0]))
|
||||
b = (b + a)
|
||||
buffer = intrinsics.ptr_offset(buffer, 1)
|
||||
buffer = buffer[1:]
|
||||
buf = buf[1:]
|
||||
}
|
||||
|
||||
@@ -130,9 +130,9 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
h1: u32 = seed
|
||||
nblocks := len(data)/4
|
||||
p := raw_data(data)
|
||||
p1 := mem.ptr_offset(p, 4*nblocks)
|
||||
p1 := p[4*nblocks:]
|
||||
|
||||
for ; p < p1; p = mem.ptr_offset(p, 4) {
|
||||
for ; p < p1; p = p[4:] {
|
||||
k1 := (cast(^u32)p)^
|
||||
|
||||
k1 *= c1_32
|
||||
|
||||
@@ -54,9 +54,10 @@ Image :: struct {
|
||||
*/
|
||||
background: Maybe(RGB_Pixel_16),
|
||||
metadata: Image_Metadata,
|
||||
which: Which_File_Type,
|
||||
}
|
||||
|
||||
Image_Metadata :: union {
|
||||
Image_Metadata :: union #shared_nil {
|
||||
^Netpbm_Info,
|
||||
^PNG_Info,
|
||||
^QOI_Info,
|
||||
@@ -172,6 +173,7 @@ General_Image_Error :: enum {
|
||||
Unable_To_Write_File,
|
||||
|
||||
// Invalid
|
||||
Unsupported_Format,
|
||||
Invalid_Signature,
|
||||
Invalid_Input_Image,
|
||||
Image_Dimensions_Too_Large,
|
||||
|
||||
61
core/image/general_loader.odin
Normal file
61
core/image/general_loader.odin
Normal file
@@ -0,0 +1,61 @@
|
||||
package image
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:bytes"
|
||||
|
||||
Loader_Proc :: #type proc(data: []byte, options: Options, allocator: mem.Allocator) -> (img: ^Image, err: Error)
|
||||
Destroy_Proc :: #type proc(img: ^Image)
|
||||
|
||||
@(private)
|
||||
_internal_loaders: [Which_File_Type]Loader_Proc
|
||||
_internal_destroyers: [Which_File_Type]Destroy_Proc
|
||||
|
||||
register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) {
|
||||
assert(loader != nil)
|
||||
assert(destroyer != nil)
|
||||
assert(_internal_loaders[kind] == nil)
|
||||
_internal_loaders[kind] = loader
|
||||
|
||||
assert(_internal_destroyers[kind] == nil)
|
||||
_internal_destroyers[kind] = destroyer
|
||||
}
|
||||
|
||||
load :: proc{
|
||||
load_from_bytes,
|
||||
load_from_file,
|
||||
}
|
||||
|
||||
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
loader := _internal_loaders[which(data)]
|
||||
if loader == nil {
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
return loader(data, options, allocator)
|
||||
}
|
||||
|
||||
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
data, ok := os.read_entire_file(filename, allocator)
|
||||
defer delete(data, allocator)
|
||||
if ok {
|
||||
return load_from_bytes(data, options, allocator)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
|
||||
destroy :: proc(img: ^Image, allocator := context.allocator) {
|
||||
if img == nil {
|
||||
return
|
||||
}
|
||||
context.allocator = allocator
|
||||
destroyer := _internal_destroyers[img.which]
|
||||
if destroyer != nil {
|
||||
destroyer(img)
|
||||
} else {
|
||||
assert(img.metadata == nil)
|
||||
bytes.buffer_destroy(&img.pixels)
|
||||
free(img)
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ BINARY :: Formats{.P4, .P5, .P6} + PAM + PFM
|
||||
|
||||
load :: proc {
|
||||
load_from_file,
|
||||
load_from_buffer,
|
||||
load_from_bytes,
|
||||
}
|
||||
|
||||
load_from_file :: proc(filename: string, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
@@ -40,13 +40,14 @@ load_from_file :: proc(filename: string, allocator := context.allocator) -> (img
|
||||
return
|
||||
}
|
||||
|
||||
return load_from_buffer(data)
|
||||
return load_from_bytes(data)
|
||||
}
|
||||
|
||||
load_from_buffer :: proc(data: []byte, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
load_from_bytes :: proc(data: []byte, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
img = new(Image)
|
||||
img.which = .NetPBM
|
||||
|
||||
header: Header; defer header_destroy(&header)
|
||||
header_size: int
|
||||
@@ -748,4 +749,15 @@ autoselect_pbm_format_from_image :: proc(img: ^Image, prefer_binary := true, for
|
||||
|
||||
// We couldn't find a suitable format
|
||||
return {}, false
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
_register :: proc() {
|
||||
loader :: proc(data: []byte, options: image.Options, allocator: mem.Allocator) -> (img: ^Image, err: Error) {
|
||||
return load_from_bytes(data, allocator)
|
||||
}
|
||||
destroyer :: proc(img: ^Image) {
|
||||
_ = destroy(img)
|
||||
}
|
||||
image.register(.NetPBM, loader, destroyer)
|
||||
}
|
||||
@@ -18,7 +18,6 @@ import "core:compress/zlib"
|
||||
import "core:image"
|
||||
|
||||
import "core:os"
|
||||
import "core:strings"
|
||||
import "core:hash"
|
||||
import "core:bytes"
|
||||
import "core:io"
|
||||
@@ -318,13 +317,12 @@ read_header :: proc(ctx: ^$C) -> (image.PNG_IHDR, Error) {
|
||||
}
|
||||
|
||||
chunk_type_to_name :: proc(type: ^image.PNG_Chunk_Type) -> string {
|
||||
t := transmute(^u8)type
|
||||
return strings.string_from_ptr(t, 4)
|
||||
return string(([^]u8)(type)[:4])
|
||||
}
|
||||
|
||||
load_from_slice :: proc(slice: []u8, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
ctx := &compress.Context_Memory_Input{
|
||||
input_data = slice,
|
||||
input_data = data,
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -344,10 +342,9 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont
|
||||
defer delete(data)
|
||||
|
||||
if ok {
|
||||
return load_from_slice(data, options)
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
img = new(Image)
|
||||
return img, .Unable_To_Read_File
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,6 +372,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
if img == nil {
|
||||
img = new(Image)
|
||||
}
|
||||
img.which = .PNG
|
||||
|
||||
info := new(image.PNG_Info)
|
||||
img.metadata = info
|
||||
@@ -1639,4 +1637,10 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
|
||||
return nil
|
||||
}
|
||||
|
||||
load :: proc{load_from_file, load_from_slice, load_from_context}
|
||||
load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
|
||||
|
||||
@(init, private)
|
||||
_register :: proc() {
|
||||
image.register(.PNG, load_from_bytes, destroy)
|
||||
}
|
||||
@@ -180,9 +180,9 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato
|
||||
|
||||
save :: proc{save_to_memory, save_to_file}
|
||||
|
||||
load_from_slice :: proc(slice: []u8, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
ctx := &compress.Context_Memory_Input{
|
||||
input_data = slice,
|
||||
input_data = data,
|
||||
}
|
||||
|
||||
img, err = load_from_context(ctx, options, allocator)
|
||||
@@ -196,10 +196,9 @@ load_from_file :: proc(filename: string, options := Options{}, allocator := cont
|
||||
defer delete(data)
|
||||
|
||||
if ok {
|
||||
return load_from_slice(data, options)
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
img = new(Image)
|
||||
return img, .Unable_To_Read_File
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,6 +224,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
if img == nil {
|
||||
img = new(Image)
|
||||
}
|
||||
img.which = .QOI
|
||||
|
||||
if .return_metadata in options {
|
||||
info := new(image.QOI_Info)
|
||||
@@ -359,7 +359,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
return
|
||||
}
|
||||
|
||||
load :: proc{load_from_file, load_from_slice, load_from_context}
|
||||
load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
|
||||
/*
|
||||
Cleanup of image-specific data.
|
||||
@@ -403,4 +403,9 @@ qoi_hash :: #force_inline proc(pixel: RGBA_Pixel) -> (index: u8) {
|
||||
i4 := u16(pixel.a) * 11
|
||||
|
||||
return u8((i1 + i2 + i3 + i4) & 63)
|
||||
}
|
||||
|
||||
@(init, private)
|
||||
_register :: proc() {
|
||||
image.register(.QOI, load_from_bytes, destroy)
|
||||
}
|
||||
@@ -12,7 +12,6 @@ package tga
|
||||
|
||||
import "core:mem"
|
||||
import "core:image"
|
||||
import "core:compress"
|
||||
import "core:bytes"
|
||||
import "core:os"
|
||||
|
||||
|
||||
179
core/image/which.odin
Normal file
179
core/image/which.odin
Normal file
@@ -0,0 +1,179 @@
|
||||
package image
|
||||
|
||||
import "core:os"
|
||||
|
||||
Which_File_Type :: enum {
|
||||
Unknown,
|
||||
|
||||
BMP,
|
||||
DjVu, // AT&T DjVu file format
|
||||
EXR,
|
||||
FLIF,
|
||||
GIF,
|
||||
HDR, // Radiance RGBE HDR
|
||||
ICNS, // Apple Icon Image
|
||||
JPEG,
|
||||
JPEG_2000,
|
||||
JPEG_XL,
|
||||
NetPBM, // NetPBM family
|
||||
PIC, // Softimage PIC
|
||||
PNG, // Portable Network Graphics
|
||||
PSD, // Photoshop PSD
|
||||
QOI, // Quite Okay Image
|
||||
SGI_RGB, // Silicon Graphics Image RGB file format
|
||||
Sun_Rast, // Sun Raster Graphic
|
||||
TGA, // Targa Truevision
|
||||
TIFF, // Tagged Image File Format
|
||||
WebP,
|
||||
XBM, // X BitMap
|
||||
}
|
||||
|
||||
which :: proc{
|
||||
which_bytes,
|
||||
which_file,
|
||||
}
|
||||
|
||||
which_bytes :: proc(data: []byte) -> Which_File_Type {
|
||||
test_tga :: proc(s: string) -> bool {
|
||||
get8 :: #force_inline proc(s: ^string) -> u8 {
|
||||
v := s[0]
|
||||
s^ = s[1:]
|
||||
return v
|
||||
}
|
||||
get16le :: #force_inline proc(s: ^string) -> u16 {
|
||||
v := u16(s[0]) | u16(s[1])<<16
|
||||
s^ = s[2:]
|
||||
return v
|
||||
}
|
||||
s := s
|
||||
s = s[1:] // skip offset
|
||||
|
||||
color_type := get8(&s)
|
||||
if color_type > 1 {
|
||||
return false
|
||||
}
|
||||
image_type := get8(&s) // image type
|
||||
if color_type == 1 { // Colormap (Paletted) Image
|
||||
if image_type != 1 && image_type != 9 { // color type requires 1 or 9
|
||||
return false
|
||||
}
|
||||
s = s[4:] // skip index of first colormap
|
||||
bpcme := get8(&s) // check bits per colormap entry
|
||||
if bpcme != 8 && bpcme != 15 && bpcme != 16 && bpcme != 24 && bpcme != 32 {
|
||||
return false
|
||||
}
|
||||
s = s[4:] // skip image origin (x, y)
|
||||
} else { // Normal image without colormap
|
||||
if image_type != 2 && image_type != 3 && image_type != 10 && image_type != 11 {
|
||||
return false
|
||||
}
|
||||
s = s[9:] // skip colormap specification
|
||||
}
|
||||
if get16le(&s) < 1 || get16le(&s) < 1 { // test width and height
|
||||
return false
|
||||
}
|
||||
bpp := get8(&s) // bits per pixel
|
||||
if color_type == 1 && bpp != 8 && bpp != 16 {
|
||||
return false
|
||||
}
|
||||
if bpp != 8 && bpp != 15 && bpp != 16 && bpp != 24 && bpp != 32 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
header: [128]byte
|
||||
copy(header[:], data)
|
||||
s := string(header[:])
|
||||
|
||||
switch {
|
||||
case s[:2] == "BM":
|
||||
return .BMP
|
||||
case s[:8] == "AT&TFORM":
|
||||
switch s[12:16] {
|
||||
case "DJVU", "DJVM":
|
||||
return .DjVu
|
||||
}
|
||||
case s[:4] == "\x76\x2f\x31\x01":
|
||||
return .EXR
|
||||
case s[:6] == "GIF87a", s[:6] == "GIF89a":
|
||||
return .GIF
|
||||
case s[6:10] == "JFIF", s[6:10] == "Exif":
|
||||
return .JPEG
|
||||
case s[:3] == "\xff\xd8\xff":
|
||||
switch s[4] {
|
||||
case 0xdb, 0xee, 0xe1, 0xe0:
|
||||
return .JPEG
|
||||
}
|
||||
switch {
|
||||
case s[:12] == "\xff\xd8\xff\xe0\x00\x10\x4a\x46\x49\x46\x00\x01":
|
||||
return .JPEG
|
||||
}
|
||||
case s[:4] == "\xff\x4f\xff\x51", s[:12] == "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a":
|
||||
return .JPEG_2000
|
||||
case s[:12] == "\x00\x00\x00\x0c\x4a\x58\x4c\x20\x0d\x0a\x87\x0a":
|
||||
return .JPEG_XL
|
||||
case s[0] == 'P':
|
||||
switch s[2] {
|
||||
case '\t', '\n', '\r':
|
||||
switch s[1] {
|
||||
case '1', '4': // PBM
|
||||
return .NetPBM
|
||||
case '2', '5': // PGM
|
||||
return .NetPBM
|
||||
case '3', '6': // PPM
|
||||
return .NetPBM
|
||||
case '7': // PAM
|
||||
return .NetPBM
|
||||
case 'F', 'f': // PFM
|
||||
return .NetPBM
|
||||
}
|
||||
}
|
||||
case s[:8] == "\x89PNG\r\n\x1a\n":
|
||||
return .PNG
|
||||
case s[:4] == "qoif":
|
||||
return .QOI
|
||||
case s[:2] == "\x01\xda":
|
||||
return .SGI_RGB
|
||||
case s[:4] == "\x59\xA6\x6A\x95":
|
||||
return .Sun_Rast
|
||||
case s[:4] == "MM\x2a\x00", s[:4] == "II\x00\x2A":
|
||||
return .TIFF
|
||||
case s[:4] == "RIFF" && s[8:12] == "WEBP":
|
||||
return .WebP
|
||||
case s[:8] == "#define ":
|
||||
return .XBM
|
||||
|
||||
case s[:11] == "#?RADIANCE\n", s[:7] == "#?RGBE\n":
|
||||
return .HDR
|
||||
case s[:4] == "\x38\x42\x50\x53":
|
||||
return .PSD
|
||||
case s[:4] != "\x53\x80\xF6\x34" && s[88:92] == "PICT":
|
||||
return .PIC
|
||||
case s[:4] == "\x69\x63\x6e\x73":
|
||||
return .ICNS
|
||||
case s[:4] == "\x46\x4c\x49\x46":
|
||||
return .FLIF
|
||||
case:
|
||||
// More complex formats
|
||||
if test_tga(s) {
|
||||
return .TGA
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return .Unknown
|
||||
}
|
||||
|
||||
|
||||
which_file :: proc(path: string) -> Which_File_Type {
|
||||
f, err := os.open(path)
|
||||
if err != 0 {
|
||||
return .Unknown
|
||||
}
|
||||
header: [128]byte
|
||||
os.read(f, header[:])
|
||||
file_type := which_bytes(header[:])
|
||||
os.close(f)
|
||||
return file_type
|
||||
}
|
||||
@@ -6,12 +6,14 @@ package intrinsics
|
||||
is_package_imported :: proc(package_name: string) -> bool ---
|
||||
|
||||
// Types
|
||||
simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
|
||||
soa_struct :: proc($N: int, $T: typeid) -> type/#soa[N]T
|
||||
|
||||
// Volatile
|
||||
volatile_load :: proc(dst: ^$T) -> T ---
|
||||
volatile_store :: proc(dst: ^$T, val: T) -> T ---
|
||||
volatile_store :: proc(dst: ^$T, val: T) ---
|
||||
|
||||
non_temporal_load :: proc(dst: ^$T) -> T ---
|
||||
non_temporal_store :: proc(dst: ^$T, val: T) ---
|
||||
|
||||
// Trapping
|
||||
debug_trap :: proc() ---
|
||||
@@ -23,18 +25,20 @@ alloca :: proc(size, align: int) -> [^]u8 ---
|
||||
cpu_relax :: proc() ---
|
||||
read_cycle_counter :: proc() -> i64 ---
|
||||
|
||||
count_ones :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_trailing_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) ---
|
||||
count_ones :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_trailing_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
||||
|
||||
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
|
||||
|
||||
sqrt :: proc(x: $T) -> T where type_is_float(T) ---
|
||||
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
|
||||
|
||||
fused_mul_add :: proc(a, b, c: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
|
||||
|
||||
mem_copy :: proc(dst, src: rawptr, len: int) ---
|
||||
mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) ---
|
||||
@@ -87,20 +91,20 @@ atomic_load :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_explicit :: proc(dst: ^$T, order: Atomic_Memory_Order) -> T ---
|
||||
|
||||
// fetch then operator
|
||||
atomic_add :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_add_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_sub :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_sub_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_and :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_and_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_nand :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_nand_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_or :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_or_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_xor :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_xor_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_exchange :: proc(dst; ^$T, val: T) -> T ---
|
||||
atomic_exchange_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_add :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_add_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_sub :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_sub_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_and :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_and_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_nand :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_nand_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_or :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_or_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_xor :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_xor_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
atomic_exchange :: proc(dst: ^$T, val: T) -> T ---
|
||||
atomic_exchange_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) -> T ---
|
||||
|
||||
atomic_compare_exchange_strong :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
|
||||
atomic_compare_exchange_strong_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok ---
|
||||
@@ -186,10 +190,97 @@ type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawpt
|
||||
|
||||
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||
|
||||
// SIMD related
|
||||
simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) ---
|
||||
|
||||
// Keeps Odin's Behaviour
|
||||
// (x << y) if y <= mask else 0
|
||||
simd_shl :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||
simd_shr :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||
|
||||
// Similar to C's Behaviour
|
||||
// x << (y & mask)
|
||||
simd_shl_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||
simd_shr_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T ---
|
||||
|
||||
simd_add_sat :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_sub_sat :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
simd_and :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_or :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_xor :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_and_not :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
simd_neg :: proc(a: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
simd_abs :: proc(a: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
simd_min :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_max :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_clamp :: proc(v, min, max: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
// Return an unsigned integer of the same size as the input type
|
||||
// NOT A BOOLEAN
|
||||
// element-wise:
|
||||
// false => 0x00...00
|
||||
// true => 0xff...ff
|
||||
simd_lanes_eq :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
||||
simd_lanes_ne :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
||||
simd_lanes_lt :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
||||
simd_lanes_le :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
||||
simd_lanes_gt :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
||||
simd_lanes_ge :: proc(a, b: #simd[N]T) -> #simd[N]Integer ---
|
||||
|
||||
simd_extract :: proc(a: #simd[N]T, idx: uint) -> T ---
|
||||
simd_replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T ---
|
||||
|
||||
simd_reduce_add_ordered :: proc(a: #simd[N]T) -> T ---
|
||||
simd_reduce_mul_ordered :: proc(a: #simd[N]T) -> T ---
|
||||
simd_reduce_min :: proc(a: #simd[N]T) -> T ---
|
||||
simd_reduce_max :: proc(a: #simd[N]T) -> T ---
|
||||
simd_reduce_and :: proc(a: #simd[N]T) -> T ---
|
||||
simd_reduce_or :: proc(a: #simd[N]T) -> T ---
|
||||
simd_reduce_xor :: proc(a: #simd[N]T) -> T ---
|
||||
|
||||
simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T ---
|
||||
simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
// Lane-wise operations
|
||||
simd_ceil :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
|
||||
simd_floor :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
|
||||
simd_trunc :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
|
||||
// rounding to the nearest integral value; if two values are equally near, rounds to the even one
|
||||
simd_nearest :: proc(a: #simd[N]any_float) -> #simd[N]any_float ---
|
||||
|
||||
simd_to_bits :: proc(v: #simd[N]T) -> #simd[N]Integer where size_of(T) == size_of(Integer), type_is_unsigned(Integer) ---
|
||||
|
||||
// equivalent a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0)
|
||||
simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T ---
|
||||
|
||||
simd_rotate_left :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
||||
simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
|
||||
|
||||
|
||||
// WASM targets only
|
||||
wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
|
||||
wasm_memory_size :: proc(index: uintptr) -> int ---
|
||||
|
||||
// `timeout_ns` is maximum number of nanoseconds the calling thread will be blocked for
|
||||
// A negative value will be blocked forever
|
||||
// Return value:
|
||||
// 0 - indicates that the thread blocked and then was woken up
|
||||
// 1 - the loaded value from `ptr` did not match `expected`, the thread did not block
|
||||
// 2 - the thread blocked, but the timeout
|
||||
wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -> u32 ---
|
||||
wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
|
||||
|
||||
// x86 Targets (i386, amd64)
|
||||
x86_cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) ---
|
||||
x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
|
||||
|
||||
|
||||
// Darwin targets only
|
||||
objc_object :: struct{}
|
||||
|
||||
@@ -67,7 +67,7 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
|
||||
h = data.file_handle
|
||||
}
|
||||
backing: [1024]byte //NOTE(Hoej): 1024 might be too much for a header backing, unless somebody has really long paths.
|
||||
buf := strings.builder_from_slice(backing[:])
|
||||
buf := strings.builder_from_bytes(backing[:])
|
||||
|
||||
do_level_header(options, level, &buf)
|
||||
|
||||
|
||||
312
core/math/rand/distributions.odin
Normal file
312
core/math/rand/distributions.odin
Normal file
@@ -0,0 +1,312 @@
|
||||
package rand
|
||||
|
||||
import "core:math"
|
||||
|
||||
float64_uniform :: float64_range
|
||||
float32_uniform :: float32_range
|
||||
|
||||
// Triangular Distribution
|
||||
// See: http://wikipedia.org/wiki/Triangular_distribution
|
||||
float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64 {
|
||||
if hi-lo == 0 {
|
||||
return lo
|
||||
}
|
||||
lo, hi := lo, hi
|
||||
u := float64(r)
|
||||
c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
|
||||
if u > c {
|
||||
u = 1-u
|
||||
c = 1-c
|
||||
lo, hi = hi, lo
|
||||
}
|
||||
return lo + (hi - lo) * math.sqrt(u * c)
|
||||
|
||||
}
|
||||
// Triangular Distribution
|
||||
// See: http://wikipedia.org/wiki/Triangular_distribution
|
||||
float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32 {
|
||||
if hi-lo == 0 {
|
||||
return lo
|
||||
}
|
||||
lo, hi := lo, hi
|
||||
u := float32(r)
|
||||
c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
|
||||
if u > c {
|
||||
u = 1-u
|
||||
c = 1-c
|
||||
lo, hi = hi, lo
|
||||
}
|
||||
return lo + (hi - lo) * math.sqrt(u * c)
|
||||
}
|
||||
|
||||
|
||||
// Normal/Gaussian Distribution
|
||||
float64_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
|
||||
return norm_float64(r) * stddev + mean
|
||||
}
|
||||
// Normal/Gaussian Distribution
|
||||
float32_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_normal(f64(mean), f64(stddev), r))
|
||||
}
|
||||
|
||||
|
||||
// Log Normal Distribution
|
||||
float64_log_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
|
||||
return math.exp(float64_normal(mean, stddev, r))
|
||||
}
|
||||
// Log Normal Distribution
|
||||
float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_log_normal(f64(mean), f64(stddev), r))
|
||||
}
|
||||
|
||||
|
||||
// Exponential Distribution
|
||||
// `lambda` is 1.0/(desired mean). It should be non-zero.
|
||||
// Return values range from
|
||||
// 0 to positive infinity if lambda > 0
|
||||
// negative infinity to 0 if lambda <= 0
|
||||
float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 {
|
||||
return - math.ln(1 - float64(r)) / lambda
|
||||
}
|
||||
// Exponential Distribution
|
||||
// `lambda` is 1.0/(desired mean). It should be non-zero.
|
||||
// Return values range from
|
||||
// 0 to positive infinity if lambda > 0
|
||||
// negative infinity to 0 if lambda <= 0
|
||||
float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_exponential(f64(lambda), r))
|
||||
}
|
||||
|
||||
|
||||
// Gamma Distribution (NOT THE GAMMA FUNCTION)
|
||||
//
|
||||
// Required: alpha > 0 and beta > 0
|
||||
//
|
||||
// math.pow(x, alpha-1) * math.exp(-x / beta)
|
||||
// pdf(x) = --------------------------------------------
|
||||
// math.gamma(alpha) * math.pow(beta, alpha)
|
||||
//
|
||||
// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
|
||||
float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
if alpha <= 0 || beta <= 0 {
|
||||
panic(#procedure + ": alpha and beta must be > 0.0")
|
||||
}
|
||||
|
||||
LOG4 :: 1.3862943611198906188344642429163531361510002687205105082413600189
|
||||
SG_MAGIC_CONST :: 2.5040773967762740733732583523868748412194809812852436493487
|
||||
|
||||
switch {
|
||||
case alpha > 1:
|
||||
// R.C.H. Cheng, "The generation of Gamma variables with non-integral shape parameters", Applied Statistics, (1977), 26, No. 1, p71-74
|
||||
|
||||
ainv := math.sqrt(2 * alpha - 1)
|
||||
bbb := alpha - LOG4
|
||||
ccc := alpha + ainv
|
||||
for {
|
||||
u1 := float64(r)
|
||||
if !(1e-7 < u1 && u1 < 0.9999999) {
|
||||
continue
|
||||
}
|
||||
u2 := 1 - float64(r)
|
||||
v := math.ln(u1 / (1 - u1)) / ainv
|
||||
x := alpha * math.exp(v)
|
||||
z := u1 * u1 * u2
|
||||
t := bbb + ccc*v - x
|
||||
if t + SG_MAGIC_CONST - 4.5 * z >= 0 || t >= math.ln(z) {
|
||||
return x * beta
|
||||
}
|
||||
}
|
||||
case alpha == 1:
|
||||
// float64_exponential(1/beta)
|
||||
return -math.ln(1 - float64(r)) * beta
|
||||
case:
|
||||
// ALGORITHM GS of Statistical Computing - Kennedy & Gentle
|
||||
x: f64
|
||||
for {
|
||||
u := float64(r)
|
||||
b := (math.e + alpha) / math.e
|
||||
p := b * u
|
||||
if p <= 1 {
|
||||
x = math.pow(p, 1/alpha)
|
||||
} else {
|
||||
x = -math.ln((b - p) / alpha)
|
||||
}
|
||||
u1 := float64(r)
|
||||
if p > 1 {
|
||||
if u1 <= math.pow(x, alpha-1) {
|
||||
break
|
||||
}
|
||||
} else if u1 <= math.exp(-x) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return x * beta
|
||||
}
|
||||
}
|
||||
// Gamma Distribution (NOT THE GAMMA FUNCTION)
|
||||
//
|
||||
// Required: alpha > 0 and beta > 0
|
||||
//
|
||||
// math.pow(x, alpha-1) * math.exp(-x / beta)
|
||||
// pdf(x) = --------------------------------------------
|
||||
// math.gamma(alpha) * math.pow(beta, alpha)
|
||||
//
|
||||
// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
|
||||
float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_gamma(f64(alpha), f64(beta), r))
|
||||
}
|
||||
|
||||
|
||||
// Beta Distribution
|
||||
//
|
||||
// Required: alpha > 0 and beta > 0
|
||||
//
|
||||
// Return values range between 0 and 1
|
||||
float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
if alpha <= 0 || beta <= 0 {
|
||||
panic(#procedure + ": alpha and beta must be > 0.0")
|
||||
}
|
||||
// Knuth Vol 2 Ed 3 pg 134 "the beta distribution"
|
||||
y := float64_gamma(alpha, 1.0, r)
|
||||
if y != 0 {
|
||||
return y / (y + float64_gamma(beta, 1.0, r))
|
||||
}
|
||||
return 0
|
||||
}
|
||||
// Beta Distribution
|
||||
//
|
||||
// Required: alpha > 0 and beta > 0
|
||||
//
|
||||
// Return values range between 0 and 1
|
||||
float32_beta :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_beta(f64(alpha), f64(beta), r))
|
||||
}
|
||||
|
||||
|
||||
// Pareto distribution, `alpha` is the shape parameter.
|
||||
// https://wikipedia.org/wiki/Pareto_distribution
|
||||
float64_pareto :: proc(alpha: f64, r: ^Rand = nil) -> f64 {
|
||||
return math.pow(1 - float64(r), -1.0 / alpha)
|
||||
}
|
||||
// Pareto distribution, `alpha` is the shape parameter.
|
||||
// https://wikipedia.org/wiki/Pareto_distribution
|
||||
float32_pareto :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_pareto(f64(alpha), r))
|
||||
}
|
||||
|
||||
|
||||
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
|
||||
float64_weibull :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
|
||||
u := 1 - float64(r)
|
||||
return alpha * math.pow(-math.ln(u), 1.0/beta)
|
||||
}
|
||||
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
|
||||
float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_weibull(f64(alpha), f64(beta), r))
|
||||
}
|
||||
|
||||
|
||||
// Circular Data (von Mises) Distribution
|
||||
// `mean_angle` is the in mean angle between 0 and 2pi radians
|
||||
// `kappa` is the concentration parameter which must be >= 0
|
||||
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
|
||||
float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 {
|
||||
// Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993.
|
||||
|
||||
mu := mean_angle
|
||||
if kappa <= 1e-6 {
|
||||
return math.TAU * float64(r)
|
||||
}
|
||||
|
||||
s := 0.5 / kappa
|
||||
t := s + math.sqrt(1 + s*s)
|
||||
z: f64
|
||||
for {
|
||||
u1 := float64(r)
|
||||
z = math.cos(math.TAU * 0.5 * u1)
|
||||
|
||||
d := z / (t + z)
|
||||
u2 := float64(r)
|
||||
if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
q := 1.0 / t
|
||||
f := (q + z) / (1 + q*z)
|
||||
u3 := float64(r)
|
||||
if u3 > 0.5 {
|
||||
return math.mod(mu + math.acos(f), math.TAU)
|
||||
} else {
|
||||
return math.mod(mu - math.acos(f), math.TAU)
|
||||
}
|
||||
}
|
||||
// Circular Data (von Mises) Distribution
|
||||
// `mean_angle` is the in mean angle between 0 and 2pi radians
|
||||
// `kappa` is the concentration parameter which must be >= 0
|
||||
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
|
||||
float32_von_mises :: proc(mean_angle, kappa: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_von_mises(f64(mean_angle), f64(kappa), r))
|
||||
}
|
||||
|
||||
|
||||
// Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
float64_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
|
||||
assert(gamma > 0)
|
||||
|
||||
// Calculated from the inverse CDF
|
||||
|
||||
return math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0
|
||||
}
|
||||
// Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
float32_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma), r))
|
||||
}
|
||||
|
||||
|
||||
// Log Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
float64_log_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
|
||||
assert(gamma > 0)
|
||||
return math.exp(math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0)
|
||||
}
|
||||
// Log Cauchy-Lorentz Distribution
|
||||
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
|
||||
float32_log_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma), r))
|
||||
}
|
||||
|
||||
|
||||
// Laplace Distribution
|
||||
// `b` is the scale where `b` > 0
|
||||
float64_laplace :: proc(mean, b: f64, r: ^Rand = nil) -> f64 {
|
||||
assert(b > 0)
|
||||
p := float64(r)-0.5
|
||||
return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean
|
||||
}
|
||||
// Laplace Distribution
|
||||
// `b` is the scale where `b` > 0
|
||||
float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_laplace(f64(mean), f64(b), r))
|
||||
}
|
||||
|
||||
|
||||
// Gompertz Distribution
|
||||
// `eta` is the shape, `b` is the scale
|
||||
// Both `eta` and `b` must be > 0
|
||||
float64_gompertz :: proc(eta, b: f64, r: ^Rand = nil) -> f64 {
|
||||
if eta <= 0 || b <= 0 {
|
||||
panic(#procedure + ": eta and b must be > 0.0")
|
||||
}
|
||||
|
||||
p := float64(r)
|
||||
return math.ln(1 - math.ln(1 - p)/eta)/b
|
||||
}
|
||||
// Gompertz Distribution
|
||||
// `eta` is the shape, `b` is the scale
|
||||
// Both `eta` and `b` must be > 0
|
||||
float32_gompertz :: proc(eta, b: f32, r: ^Rand = nil) -> f32 {
|
||||
return f32(float64_gompertz(f64(eta), f64(b), r))
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import "core:intrinsics"
|
||||
Rand :: struct {
|
||||
state: u64,
|
||||
inc: u64,
|
||||
is_system: bool,
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +30,16 @@ init :: proc(r: ^Rand, seed: u64) {
|
||||
_random(r)
|
||||
}
|
||||
|
||||
init_as_system :: proc(r: ^Rand) {
|
||||
if !#defined(_system_random) {
|
||||
panic(#procedure + " is not supported on this platform yet")
|
||||
}
|
||||
r.state = 0
|
||||
r.inc = 0
|
||||
r.is_system = true
|
||||
}
|
||||
|
||||
@(private)
|
||||
_random :: proc(r: ^Rand) -> u32 {
|
||||
r := r
|
||||
if r == nil {
|
||||
@@ -36,6 +47,12 @@ _random :: proc(r: ^Rand) -> u32 {
|
||||
// enforce the global random state if necessary with `nil`
|
||||
r = &global_rand
|
||||
}
|
||||
when #defined(_system_random) {
|
||||
if r.is_system {
|
||||
return _system_random()
|
||||
}
|
||||
}
|
||||
|
||||
old_state := r.state
|
||||
r.state = old_state * 6364136223846793005 + (r.inc|1)
|
||||
xor_shifted := u32(((old_state>>18) ~ old_state) >> 27)
|
||||
@@ -119,13 +136,14 @@ int_max :: proc(n: int, r: ^Rand = nil) -> int {
|
||||
}
|
||||
}
|
||||
|
||||
// Uniform random distribution [0, 1)
|
||||
float64 :: proc(r: ^Rand = nil) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53) }
|
||||
// Uniform random distribution [0, 1)
|
||||
float32 :: proc(r: ^Rand = nil) -> f32 { return f32(float64(r)) }
|
||||
|
||||
float64_range :: proc(lo, hi: f64, r: ^Rand = nil) -> f64 { return (hi-lo)*float64(r) + lo }
|
||||
float32_range :: proc(lo, hi: f32, r: ^Rand = nil) -> f32 { return (hi-lo)*float32(r) + lo }
|
||||
|
||||
|
||||
read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
|
||||
pos := i8(0)
|
||||
val := i64(0)
|
||||
|
||||
21
core/math/rand/system_darwin.odin
Normal file
21
core/math/rand/system_darwin.odin
Normal file
@@ -0,0 +1,21 @@
|
||||
package rand
|
||||
|
||||
import "core:sys/darwin"
|
||||
|
||||
_system_random :: proc() -> u32 {
|
||||
for {
|
||||
value: u32
|
||||
ret := darwin.syscall_getentropy(([^]u8)(&value), 4)
|
||||
if ret < 0 {
|
||||
switch ret {
|
||||
case -4: // EINTR
|
||||
continue
|
||||
case -78: // ENOSYS
|
||||
panic("getentropy not available in kernel")
|
||||
case:
|
||||
panic("getentropy failed")
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
27
core/math/rand/system_linux.odin
Normal file
27
core/math/rand/system_linux.odin
Normal file
@@ -0,0 +1,27 @@
|
||||
package rand
|
||||
|
||||
import "core:sys/unix"
|
||||
|
||||
_system_random :: proc() -> u32 {
|
||||
for {
|
||||
value: u32
|
||||
ret := unix.sys_getrandom(([^]u8)(&value), 4, 0)
|
||||
if ret < 0 {
|
||||
switch ret {
|
||||
case -4: // EINTR
|
||||
// Call interupted by a signal handler, just retry the request.
|
||||
continue
|
||||
case -38: // ENOSYS
|
||||
// The kernel is apparently prehistoric (< 3.17 circa 2014)
|
||||
// and does not support getrandom.
|
||||
panic("getrandom not available in kernel")
|
||||
case:
|
||||
// All other failures are things that should NEVER happen
|
||||
// unless the kernel interface changes (ie: the Linux
|
||||
// developers break userland).
|
||||
panic("getrandom failed")
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
12
core/math/rand/system_windows.odin
Normal file
12
core/math/rand/system_windows.odin
Normal file
@@ -0,0 +1,12 @@
|
||||
package rand
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
_system_random :: proc() -> u32 {
|
||||
value: u32
|
||||
status := win32.BCryptGenRandom(nil, ([^]u8)(&value), 4, win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)
|
||||
if status < 0 {
|
||||
panic("BCryptGenRandom failed")
|
||||
}
|
||||
return value
|
||||
}
|
||||
@@ -55,6 +55,11 @@ Allocator :: struct {
|
||||
|
||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr)
|
||||
|
||||
DEFAULT_PAGE_SIZE ::
|
||||
64 * 1024 when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64 else
|
||||
16 * 1024 when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 else
|
||||
4 * 1024
|
||||
|
||||
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if size == 0 {
|
||||
return nil
|
||||
|
||||
@@ -52,15 +52,16 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
total_size := size + alignment
|
||||
#no_bounds_check end := &arena.data[arena.offset]
|
||||
|
||||
ptr := align_forward(end, uintptr(alignment))
|
||||
|
||||
total_size := size + ptr_sub((^byte)(ptr), (^byte)(end))
|
||||
|
||||
if arena.offset + total_size > len(arena.data) {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
#no_bounds_check end := &arena.data[arena.offset]
|
||||
|
||||
ptr := align_forward(end, uintptr(alignment))
|
||||
arena.offset += total_size
|
||||
arena.peak_used = max(arena.peak_used, arena.offset)
|
||||
zero(ptr, size)
|
||||
@@ -662,6 +663,7 @@ dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) {
|
||||
dynamic_pool_free_all(pool)
|
||||
delete(unused_blocks)
|
||||
delete(used_blocks)
|
||||
delete(out_band_allocations)
|
||||
|
||||
zero(pool, size_of(pool^))
|
||||
}
|
||||
@@ -857,7 +859,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
result: []byte
|
||||
err: Allocator_Error
|
||||
if mode == .Free && old_memory not_in data.allocation_map {
|
||||
if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
|
||||
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
|
||||
memory = old_memory,
|
||||
location = loc,
|
||||
|
||||
@@ -25,11 +25,13 @@ zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
intrinsics.atomic_thread_fence(.Seq_Cst) // Prevent reordering
|
||||
return data
|
||||
}
|
||||
zero_item :: proc "contextless" (item: $P/^$T) {
|
||||
zero_item :: proc "contextless" (item: $P/^$T) -> P {
|
||||
intrinsics.mem_zero(item, size_of(T))
|
||||
return item
|
||||
}
|
||||
zero_slice :: proc "contextless" (data: $T/[]$E) {
|
||||
zero_slice :: proc "contextless" (data: $T/[]$E) -> T {
|
||||
zero(raw_data(data), size_of(E)*len(data))
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
@@ -150,7 +152,7 @@ slice_ptr :: proc "contextless" (ptr: ^$T, len: int) -> []T {
|
||||
return ([^]T)(ptr)[:len]
|
||||
}
|
||||
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, #any_int len: int) -> []byte {
|
||||
return ([^]u8)(data)[:max(len, 0)]
|
||||
}
|
||||
|
||||
|
||||
@@ -20,20 +20,12 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
|
||||
return transmute(any)Raw_Any{data, id}
|
||||
}
|
||||
|
||||
raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E {
|
||||
return (^E)(a)
|
||||
}
|
||||
raw_string_data :: proc "contextless" (s: $T/string) -> ^byte {
|
||||
return (transmute(Raw_String)s).data
|
||||
}
|
||||
raw_slice_data :: proc "contextless" (a: $T/[]$E) -> ^E {
|
||||
return cast(^E)(transmute(Raw_Slice)a).data
|
||||
}
|
||||
raw_dynamic_array_data :: proc "contextless" (a: $T/[dynamic]$E) -> ^E {
|
||||
return cast(^E)(transmute(Raw_Dynamic_Array)a).data
|
||||
}
|
||||
|
||||
raw_data :: proc{raw_array_data, raw_string_data, raw_slice_data, raw_dynamic_array_data}
|
||||
raw_array_data :: runtime.raw_array_data
|
||||
raw_simd_data :: runtime.raw_simd_data
|
||||
raw_string_data :: runtime.raw_string_data
|
||||
raw_slice_data :: runtime.raw_slice_data
|
||||
raw_dynamic_array_data :: runtime.raw_dynamic_array_data
|
||||
raw_data :: runtime.raw_data
|
||||
|
||||
|
||||
Poly_Raw_Map_Entry :: struct($Key, $Value: typeid) {
|
||||
|
||||
@@ -37,9 +37,9 @@ MADV_WIPEONFORK :: 18
|
||||
MADV_KEEPONFORK :: 19
|
||||
MADV_HWPOISON :: 100
|
||||
|
||||
mmap :: proc "contextless" (addr: rawptr, length: uint, prot: c.int, flags: c.int, fd: c.int, offset: uintptr) -> rawptr {
|
||||
mmap :: proc "contextless" (addr: rawptr, length: uint, prot: c.int, flags: c.int, fd: c.int, offset: uintptr) -> int {
|
||||
res := intrinsics.syscall(unix.SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset)
|
||||
return rawptr(res)
|
||||
return int(res)
|
||||
}
|
||||
|
||||
munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int {
|
||||
@@ -59,12 +59,11 @@ madvise :: proc "contextless" (addr: rawptr, length: uint, advice: c.int) -> c.i
|
||||
|
||||
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
MAP_FAILED := rawptr(~uintptr(0))
|
||||
result := mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
|
||||
if result == MAP_FAILED {
|
||||
if result < 0 && result > -4096 {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
return ([^]byte)(result)[:size], nil
|
||||
return ([^]byte)(uintptr(result))[:size], nil
|
||||
}
|
||||
|
||||
_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
||||
|
||||
@@ -13,7 +13,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
|
||||
return
|
||||
}
|
||||
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:])})
|
||||
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:]) or_else ""})
|
||||
fi.fullpath = path
|
||||
fi.name = basename(path)
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
|
||||
@@ -22,7 +22,7 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
||||
}
|
||||
|
||||
if n <= u32(len(b)) {
|
||||
value = win32.utf16_to_utf8(b[:n], allocator)
|
||||
value, _ = win32.utf16_to_utf8(b[:n], allocator)
|
||||
found = true
|
||||
return
|
||||
}
|
||||
@@ -76,7 +76,7 @@ environ :: proc(allocator := context.allocator) -> []string {
|
||||
if i <= from {
|
||||
break
|
||||
}
|
||||
append(&r, win32.utf16_to_utf8(envs[from:i], allocator))
|
||||
append(&r, win32.utf16_to_utf8(envs[from:i], allocator) or_else "")
|
||||
from = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
|
||||
win32.ReleaseSRWLockExclusive(&cwd_lock)
|
||||
|
||||
return win32.utf16_to_utf8(dir_buf_wstr, allocator)
|
||||
return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
@@ -389,7 +389,8 @@ change_directory :: proc(path: string) -> Errno {
|
||||
return Errno(win32.SetCurrentDirectoryW(wpath))
|
||||
}
|
||||
|
||||
make_directory :: proc(path: string, mode: u32) -> Errno {
|
||||
make_directory :: proc(path: string, mode: u32 = 0) -> Errno {
|
||||
// Mode is unused on Windows, but is needed on *nix
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
return Errno(win32.CreateDirectoryW(wpath, nil))
|
||||
}
|
||||
|
||||
28
core/os/os2/env_linux.odin
Normal file
28
core/os/os2/env_linux.odin
Normal file
@@ -0,0 +1,28 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
_get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
//TODO
|
||||
return
|
||||
}
|
||||
|
||||
_set_env :: proc(key, value: string) -> bool {
|
||||
//TODO
|
||||
return false
|
||||
}
|
||||
|
||||
_unset_env :: proc(key: string) -> bool {
|
||||
//TODO
|
||||
return false
|
||||
}
|
||||
|
||||
_clear_env :: proc() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
_environ :: proc(allocator := context.allocator) -> []string {
|
||||
//TODO
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
package os2
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:runtime"
|
||||
|
||||
_lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
@@ -17,7 +18,7 @@ _lookup_env :: proc(key: string, allocator := context.allocator) -> (value: stri
|
||||
}
|
||||
return "", true
|
||||
}
|
||||
b := make([]u16, n+1, context.temp_allocator)
|
||||
b := make([]u16, n+1, _temp_allocator())
|
||||
|
||||
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
|
||||
if n == 0 {
|
||||
@@ -25,9 +26,10 @@ _lookup_env :: proc(key: string, allocator := context.allocator) -> (value: stri
|
||||
if err == win32.ERROR_ENVVAR_NOT_FOUND {
|
||||
return "", false
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
value = win32.utf16_to_utf8(b[:n], allocator)
|
||||
value = win32.utf16_to_utf8(b[:n], allocator) or_else ""
|
||||
found = true
|
||||
return
|
||||
}
|
||||
@@ -45,7 +47,7 @@ _unset_env :: proc(key: string) -> bool {
|
||||
}
|
||||
|
||||
_clear_env :: proc() {
|
||||
envs := environ(context.temp_allocator)
|
||||
envs := environ(_temp_allocator())
|
||||
for env in envs {
|
||||
for j in 1..<len(env) {
|
||||
if env[j] == '=' {
|
||||
@@ -56,7 +58,7 @@ _clear_env :: proc() {
|
||||
}
|
||||
}
|
||||
|
||||
_environ :: proc(allocator := context.allocator) -> []string {
|
||||
_environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
envs := win32.GetEnvironmentStringsW()
|
||||
if envs == nil {
|
||||
return nil
|
||||
@@ -71,7 +73,7 @@ _environ :: proc(allocator := context.allocator) -> []string {
|
||||
break
|
||||
}
|
||||
w := ([^]u16)(p)[from:i]
|
||||
append(&r, win32.utf16_to_utf8(w, allocator))
|
||||
append(&r, win32.utf16_to_utf8(w, allocator) or_else "")
|
||||
from = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:runtime"
|
||||
|
||||
General_Error :: enum u32 {
|
||||
Invalid_Argument,
|
||||
None,
|
||||
|
||||
Permission_Denied,
|
||||
Exist,
|
||||
@@ -13,15 +14,18 @@ General_Error :: enum u32 {
|
||||
Timeout,
|
||||
|
||||
Invalid_File,
|
||||
Invalid_Dir,
|
||||
Invalid_Path,
|
||||
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
Platform_Error :: struct {
|
||||
err: i32,
|
||||
}
|
||||
Platform_Error :: enum i32 {None=0}
|
||||
|
||||
Error :: union {
|
||||
Error :: union #shared_nil {
|
||||
General_Error,
|
||||
io.Error,
|
||||
runtime.Allocator_Error,
|
||||
Platform_Error,
|
||||
}
|
||||
#assert(size_of(Error) == size_of(u64))
|
||||
@@ -30,36 +34,56 @@ Error :: union {
|
||||
|
||||
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
|
||||
v := ferr.(Platform_Error) or_else {}
|
||||
return v.err, v.err != 0
|
||||
return i32(v), i32(v) != 0
|
||||
}
|
||||
|
||||
|
||||
error_string :: proc(ferr: Error) -> string {
|
||||
switch ferr {
|
||||
case nil: return ""
|
||||
case .Invalid_Argument: return "invalid argument"
|
||||
case .Permission_Denied: return "permission denied"
|
||||
case .Exist: return "file already exists"
|
||||
case .Not_Exist: return "file does not exist"
|
||||
case .Closed: return "file already closed"
|
||||
case .Timeout: return "i/o timeout"
|
||||
case .EOF: return "eof"
|
||||
case .Unexpected_EOF: return "unexpected eof"
|
||||
case .Short_Write: return "short write"
|
||||
case .Invalid_Write: return "invalid write result"
|
||||
case .Short_Buffer: return "short buffer"
|
||||
case .No_Progress: return "multiple read calls return no data or error"
|
||||
case .Invalid_Whence: return "invalid whence"
|
||||
case .Invalid_Offset: return "invalid offset"
|
||||
case .Invalid_Unread: return "invalid unread"
|
||||
case .Negative_Read: return "negative read"
|
||||
case .Negative_Write: return "negative write"
|
||||
case .Negative_Count: return "negative count"
|
||||
case .Buffer_Full: return "buffer full"
|
||||
if ferr == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if errno, ok := is_platform_error(ferr); ok {
|
||||
return _error_string(errno)
|
||||
switch e in ferr {
|
||||
case General_Error:
|
||||
switch e {
|
||||
case .None: return ""
|
||||
case .Permission_Denied: return "permission denied"
|
||||
case .Exist: return "file already exists"
|
||||
case .Not_Exist: return "file does not exist"
|
||||
case .Closed: return "file already closed"
|
||||
case .Timeout: return "i/o timeout"
|
||||
case .Invalid_File: return "invalid file"
|
||||
case .Invalid_Dir: return "invalid directory"
|
||||
case .Invalid_Path: return "invalid path"
|
||||
case .Unsupported: return "unsupported"
|
||||
}
|
||||
case io.Error:
|
||||
switch e {
|
||||
case .None: return ""
|
||||
case .EOF: return "eof"
|
||||
case .Unexpected_EOF: return "unexpected eof"
|
||||
case .Short_Write: return "short write"
|
||||
case .Invalid_Write: return "invalid write result"
|
||||
case .Short_Buffer: return "short buffer"
|
||||
case .No_Progress: return "multiple read calls return no data or error"
|
||||
case .Invalid_Whence: return "invalid whence"
|
||||
case .Invalid_Offset: return "invalid offset"
|
||||
case .Invalid_Unread: return "invalid unread"
|
||||
case .Negative_Read: return "negative read"
|
||||
case .Negative_Write: return "negative write"
|
||||
case .Negative_Count: return "negative count"
|
||||
case .Buffer_Full: return "buffer full"
|
||||
case .Unknown, .Empty: //
|
||||
}
|
||||
case runtime.Allocator_Error:
|
||||
switch e {
|
||||
case .None: return ""
|
||||
case .Out_Of_Memory: return "out of memory"
|
||||
case .Invalid_Pointer: return "invalid allocator pointer"
|
||||
case .Invalid_Argument: return "invalid allocator argument"
|
||||
case .Mode_Not_Implemented: return "allocator mode not implemented"
|
||||
}
|
||||
case Platform_Error:
|
||||
return _error_string(i32(e))
|
||||
}
|
||||
|
||||
return "unknown error"
|
||||
|
||||
145
core/os/os2/errors_linux.odin
Normal file
145
core/os/os2/errors_linux.odin
Normal file
@@ -0,0 +1,145 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:sys/unix"
|
||||
|
||||
EPERM :: 1
|
||||
ENOENT :: 2
|
||||
ESRCH :: 3
|
||||
EINTR :: 4
|
||||
EIO :: 5
|
||||
ENXIO :: 6
|
||||
EBADF :: 9
|
||||
EAGAIN :: 11
|
||||
ENOMEM :: 12
|
||||
EACCES :: 13
|
||||
EFAULT :: 14
|
||||
EEXIST :: 17
|
||||
ENODEV :: 19
|
||||
ENOTDIR :: 20
|
||||
EISDIR :: 21
|
||||
EINVAL :: 22
|
||||
ENFILE :: 23
|
||||
EMFILE :: 24
|
||||
ETXTBSY :: 26
|
||||
EFBIG :: 27
|
||||
ENOSPC :: 28
|
||||
ESPIPE :: 29
|
||||
EROFS :: 30
|
||||
EPIPE :: 32
|
||||
ERANGE :: 34 /* Result too large */
|
||||
EDEADLK :: 35 /* Resource deadlock would occur */
|
||||
ENAMETOOLONG :: 36 /* File name too long */
|
||||
ENOLCK :: 37 /* No record locks available */
|
||||
ENOSYS :: 38 /* Invalid system call number */
|
||||
ENOTEMPTY :: 39 /* Directory not empty */
|
||||
ELOOP :: 40 /* Too many symbolic links encountered */
|
||||
EWOULDBLOCK :: EAGAIN /* Operation would block */
|
||||
ENOMSG :: 42 /* No message of desired type */
|
||||
EIDRM :: 43 /* Identifier removed */
|
||||
ECHRNG :: 44 /* Channel number out of range */
|
||||
EL2NSYNC :: 45 /* Level 2 not synchronized */
|
||||
EL3HLT :: 46 /* Level 3 halted */
|
||||
EL3RST :: 47 /* Level 3 reset */
|
||||
ELNRNG :: 48 /* Link number out of range */
|
||||
EUNATCH :: 49 /* Protocol driver not attached */
|
||||
ENOCSI :: 50 /* No CSI structure available */
|
||||
EL2HLT :: 51 /* Level 2 halted */
|
||||
EBADE :: 52 /* Invalid exchange */
|
||||
EBADR :: 53 /* Invalid request descriptor */
|
||||
EXFULL :: 54 /* Exchange full */
|
||||
ENOANO :: 55 /* No anode */
|
||||
EBADRQC :: 56 /* Invalid request code */
|
||||
EBADSLT :: 57 /* Invalid slot */
|
||||
EDEADLOCK :: EDEADLK
|
||||
EBFONT :: 59 /* Bad font file format */
|
||||
ENOSTR :: 60 /* Device not a stream */
|
||||
ENODATA :: 61 /* No data available */
|
||||
ETIME :: 62 /* Timer expired */
|
||||
ENOSR :: 63 /* Out of streams resources */
|
||||
ENONET :: 64 /* Machine is not on the network */
|
||||
ENOPKG :: 65 /* Package not installed */
|
||||
EREMOTE :: 66 /* Object is remote */
|
||||
ENOLINK :: 67 /* Link has been severed */
|
||||
EADV :: 68 /* Advertise error */
|
||||
ESRMNT :: 69 /* Srmount error */
|
||||
ECOMM :: 70 /* Communication error on send */
|
||||
EPROTO :: 71 /* Protocol error */
|
||||
EMULTIHOP :: 72 /* Multihop attempted */
|
||||
EDOTDOT :: 73 /* RFS specific error */
|
||||
EBADMSG :: 74 /* Not a data message */
|
||||
EOVERFLOW :: 75 /* Value too large for defined data type */
|
||||
ENOTUNIQ :: 76 /* Name not unique on network */
|
||||
EBADFD :: 77 /* File descriptor in bad state */
|
||||
EREMCHG :: 78 /* Remote address changed */
|
||||
ELIBACC :: 79 /* Can not access a needed shared library */
|
||||
ELIBBAD :: 80 /* Accessing a corrupted shared library */
|
||||
ELIBSCN :: 81 /* .lib section in a.out corrupted */
|
||||
ELIBMAX :: 82 /* Attempting to link in too many shared libraries */
|
||||
ELIBEXEC :: 83 /* Cannot exec a shared library directly */
|
||||
EILSEQ :: 84 /* Illegal byte sequence */
|
||||
ERESTART :: 85 /* Interrupted system call should be restarted */
|
||||
ESTRPIPE :: 86 /* Streams pipe error */
|
||||
EUSERS :: 87 /* Too many users */
|
||||
ENOTSOCK :: 88 /* Socket operation on non-socket */
|
||||
EDESTADDRREQ :: 89 /* Destination address required */
|
||||
EMSGSIZE :: 90 /* Message too long */
|
||||
EPROTOTYPE :: 91 /* Protocol wrong type for socket */
|
||||
ENOPROTOOPT :: 92 /* Protocol not available */
|
||||
EPROTONOSUPPORT:: 93 /* Protocol not supported */
|
||||
ESOCKTNOSUPPORT:: 94 /* Socket type not supported */
|
||||
EOPNOTSUPP :: 95 /* Operation not supported on transport endpoint */
|
||||
EPFNOSUPPORT :: 96 /* Protocol family not supported */
|
||||
EAFNOSUPPORT :: 97 /* Address family not supported by protocol */
|
||||
EADDRINUSE :: 98 /* Address already in use */
|
||||
EADDRNOTAVAIL :: 99 /* Cannot assign requested address */
|
||||
ENETDOWN :: 100 /* Network is down */
|
||||
ENETUNREACH :: 101 /* Network is unreachable */
|
||||
ENETRESET :: 102 /* Network dropped connection because of reset */
|
||||
ECONNABORTED :: 103 /* Software caused connection abort */
|
||||
ECONNRESET :: 104 /* Connection reset by peer */
|
||||
ENOBUFS :: 105 /* No buffer space available */
|
||||
EISCONN :: 106 /* Transport endpoint is already connected */
|
||||
ENOTCONN :: 107 /* Transport endpoint is not connected */
|
||||
ESHUTDOWN :: 108 /* Cannot send after transport endpoint shutdown */
|
||||
ETOOMANYREFS :: 109 /* Too many references: cannot splice */
|
||||
ETIMEDOUT :: 110 /* Connection timed out */
|
||||
ECONNREFUSED :: 111 /* Connection refused */
|
||||
EHOSTDOWN :: 112 /* Host is down */
|
||||
EHOSTUNREACH :: 113 /* No route to host */
|
||||
EALREADY :: 114 /* Operation already in progress */
|
||||
EINPROGRESS :: 115 /* Operation now in progress */
|
||||
ESTALE :: 116 /* Stale file handle */
|
||||
EUCLEAN :: 117 /* Structure needs cleaning */
|
||||
ENOTNAM :: 118 /* Not a XENIX named type file */
|
||||
ENAVAIL :: 119 /* No XENIX semaphores available */
|
||||
EISNAM :: 120 /* Is a named type file */
|
||||
EREMOTEIO :: 121 /* Remote I/O error */
|
||||
EDQUOT :: 122 /* Quota exceeded */
|
||||
ENOMEDIUM :: 123 /* No medium found */
|
||||
EMEDIUMTYPE :: 124 /* Wrong medium type */
|
||||
ECANCELED :: 125 /* Operation Canceled */
|
||||
ENOKEY :: 126 /* Required key not available */
|
||||
EKEYEXPIRED :: 127 /* Key has expired */
|
||||
EKEYREVOKED :: 128 /* Key has been revoked */
|
||||
EKEYREJECTED :: 129 /* Key was rejected by service */
|
||||
EOWNERDEAD :: 130 /* Owner died */
|
||||
ENOTRECOVERABLE:: 131 /* State not recoverable */
|
||||
ERFKILL :: 132 /* Operation not possible due to RF-kill */
|
||||
EHWPOISON :: 133 /* Memory page has hardware error */
|
||||
|
||||
_get_platform_error :: proc(res: int) -> Error {
|
||||
errno := unix.get_errno(res)
|
||||
return Platform_Error(i32(errno))
|
||||
}
|
||||
|
||||
_ok_or_error :: proc(res: int) -> Error {
|
||||
return res >= 0 ? nil : _get_platform_error(res)
|
||||
}
|
||||
|
||||
_error_string :: proc(errno: i32) -> string {
|
||||
if errno == 0 {
|
||||
return ""
|
||||
}
|
||||
return "Error"
|
||||
}
|
||||
@@ -12,3 +12,49 @@ _error_string :: proc(errno: i32) -> string {
|
||||
// FormatMessageW
|
||||
return ""
|
||||
}
|
||||
|
||||
_get_platform_error :: proc() -> Error {
|
||||
err := win32.GetLastError()
|
||||
if err == 0 {
|
||||
return nil
|
||||
}
|
||||
switch err {
|
||||
case win32.ERROR_ACCESS_DENIED, win32.ERROR_SHARING_VIOLATION:
|
||||
return .Permission_Denied
|
||||
|
||||
case win32.ERROR_FILE_EXISTS, win32.ERROR_ALREADY_EXISTS:
|
||||
return .Exist
|
||||
|
||||
case win32.ERROR_FILE_NOT_FOUND, win32.ERROR_PATH_NOT_FOUND:
|
||||
return .Not_Exist
|
||||
|
||||
case win32.ERROR_NO_DATA:
|
||||
return .Closed
|
||||
|
||||
case win32.ERROR_TIMEOUT, win32.WAIT_TIMEOUT:
|
||||
return .Timeout
|
||||
|
||||
case win32.ERROR_NOT_SUPPORTED:
|
||||
return .Unsupported
|
||||
|
||||
case
|
||||
win32.ERROR_BAD_ARGUMENTS,
|
||||
win32.ERROR_INVALID_PARAMETER,
|
||||
win32.ERROR_NOT_ENOUGH_MEMORY,
|
||||
win32.ERROR_INVALID_HANDLE,
|
||||
win32.ERROR_NO_MORE_FILES,
|
||||
win32.ERROR_LOCK_VIOLATION,
|
||||
win32.ERROR_HANDLE_EOF,
|
||||
win32.ERROR_BROKEN_PIPE,
|
||||
win32.ERROR_CALL_NOT_IMPLEMENTED,
|
||||
win32.ERROR_INSUFFICIENT_BUFFER,
|
||||
win32.ERROR_INVALID_NAME,
|
||||
win32.ERROR_LOCK_FAILED,
|
||||
win32.ERROR_ENVVAR_NOT_FOUND,
|
||||
win32.ERROR_OPERATION_ABORTED,
|
||||
win32.ERROR_IO_PENDING,
|
||||
win32.ERROR_NO_UNICODE_TRANSLATION:
|
||||
// fallthrough
|
||||
}
|
||||
return Platform_Error(err)
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
import "core:runtime"
|
||||
|
||||
File :: struct {
|
||||
impl: _File,
|
||||
@@ -20,6 +21,7 @@ File_Mode_Device :: File_Mode(1<<18)
|
||||
File_Mode_Char_Device :: File_Mode(1<<19)
|
||||
File_Mode_Sym_Link :: File_Mode(1<<20)
|
||||
|
||||
File_Mode_Perm :: File_Mode(0o777) // Unix permision bits
|
||||
|
||||
File_Flags :: distinct bit_set[File_Flag; uint]
|
||||
File_Flag :: enum {
|
||||
@@ -31,17 +33,21 @@ File_Flag :: enum {
|
||||
Sync,
|
||||
Trunc,
|
||||
Sparse,
|
||||
Close_On_Exec,
|
||||
|
||||
Unbuffered_IO,
|
||||
}
|
||||
|
||||
O_RDONLY :: File_Flags{.Read}
|
||||
O_WRONLY :: File_Flags{.Write}
|
||||
O_RDWR :: File_Flags{.Read, .Write}
|
||||
O_APPEND :: File_Flags{.Append}
|
||||
O_CREATE :: File_Flags{.Create}
|
||||
O_EXCL :: File_Flags{.Excl}
|
||||
O_SYNC :: File_Flags{.Sync}
|
||||
O_TRUNC :: File_Flags{.Trunc}
|
||||
O_SPARSE :: File_Flags{.Sparse}
|
||||
O_RDONLY :: File_Flags{.Read}
|
||||
O_WRONLY :: File_Flags{.Write}
|
||||
O_RDWR :: File_Flags{.Read, .Write}
|
||||
O_APPEND :: File_Flags{.Append}
|
||||
O_CREATE :: File_Flags{.Create}
|
||||
O_EXCL :: File_Flags{.Excl}
|
||||
O_SYNC :: File_Flags{.Sync}
|
||||
O_TRUNC :: File_Flags{.Trunc}
|
||||
O_SPARSE :: File_Flags{.Sparse}
|
||||
O_CLOEXEC :: File_Flags{.Close_On_Exec}
|
||||
|
||||
|
||||
|
||||
@@ -137,23 +143,36 @@ symlink :: proc(old_name, new_name: string) -> Error {
|
||||
return _symlink(old_name, new_name)
|
||||
}
|
||||
|
||||
read_link :: proc(name: string) -> (string, Error) {
|
||||
return _read_link(name)
|
||||
read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) {
|
||||
return _read_link(name,allocator)
|
||||
}
|
||||
|
||||
|
||||
chdir :: proc(f: ^File) -> Error {
|
||||
return _chdir(f)
|
||||
chdir :: proc(name: string) -> Error {
|
||||
return _chdir(name)
|
||||
}
|
||||
|
||||
chmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||
return _chmod(f, mode)
|
||||
chmod :: proc(name: string, mode: File_Mode) -> Error {
|
||||
return _chmod(name, mode)
|
||||
}
|
||||
|
||||
chown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
return _chown(f, uid, gid)
|
||||
chown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return _chown(name, uid, gid)
|
||||
}
|
||||
|
||||
fchdir :: proc(f: ^File) -> Error {
|
||||
return _fchdir(f)
|
||||
}
|
||||
|
||||
fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||
return _fchmod(f, mode)
|
||||
}
|
||||
|
||||
fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
return _fchown(f, uid, gid)
|
||||
}
|
||||
|
||||
|
||||
|
||||
lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return _lchown(name, uid, gid)
|
||||
@@ -163,6 +182,9 @@ lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
return _chtimes(name, atime, mtime)
|
||||
}
|
||||
fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||
return _fchtimes(f, atime, mtime)
|
||||
}
|
||||
|
||||
exists :: proc(path: string) -> bool {
|
||||
return _exists(path)
|
||||
@@ -176,3 +198,20 @@ is_dir :: proc(path: string) -> bool {
|
||||
return _is_dir(path)
|
||||
}
|
||||
|
||||
|
||||
copy_file :: proc(dst_path, src_path: string) -> Error {
|
||||
src := open(src_path) or_return
|
||||
defer close(src)
|
||||
|
||||
info := fstat(src, _file_allocator()) or_return
|
||||
defer file_info_delete(info, _file_allocator())
|
||||
if info.is_dir {
|
||||
return .Invalid_File
|
||||
}
|
||||
|
||||
dst := open(dst_path, {.Read, .Write, .Create, .Trunc}, info.mode & File_Mode_Perm) or_return
|
||||
defer close(dst)
|
||||
|
||||
_, err := io.copy(to_writer(dst), to_reader(src))
|
||||
return err
|
||||
}
|
||||
422
core/os/os2/file_linux.odin
Normal file
422
core/os/os2/file_linux.odin
Normal file
@@ -0,0 +1,422 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
import "core:strings"
|
||||
import "core:runtime"
|
||||
import "core:sys/unix"
|
||||
|
||||
INVALID_HANDLE :: -1
|
||||
|
||||
_O_RDONLY :: 0o0
|
||||
_O_WRONLY :: 0o1
|
||||
_O_RDWR :: 0o2
|
||||
_O_CREAT :: 0o100
|
||||
_O_EXCL :: 0o200
|
||||
_O_TRUNC :: 0o1000
|
||||
_O_APPEND :: 0o2000
|
||||
_O_NONBLOCK :: 0o4000
|
||||
_O_LARGEFILE :: 0o100000
|
||||
_O_DIRECTORY :: 0o200000
|
||||
_O_NOFOLLOW :: 0o400000
|
||||
_O_SYNC :: 0o4010000
|
||||
_O_CLOEXEC :: 0o2000000
|
||||
_O_PATH :: 0o10000000
|
||||
|
||||
_AT_FDCWD :: -100
|
||||
|
||||
_CSTRING_NAME_HEAP_THRESHOLD :: 512
|
||||
|
||||
_File :: struct {
|
||||
name: string,
|
||||
fd: int,
|
||||
allocator: runtime.Allocator,
|
||||
}
|
||||
|
||||
_file_allocator :: proc() -> runtime.Allocator {
|
||||
return heap_allocator()
|
||||
}
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
|
||||
flags_i: int
|
||||
switch flags & O_RDONLY|O_WRONLY|O_RDWR {
|
||||
case O_RDONLY: flags_i = _O_RDONLY
|
||||
case O_WRONLY: flags_i = _O_WRONLY
|
||||
case O_RDWR: flags_i = _O_RDWR
|
||||
}
|
||||
|
||||
flags_i |= (_O_APPEND * int(.Append in flags))
|
||||
flags_i |= (_O_CREAT * int(.Create in flags))
|
||||
flags_i |= (_O_EXCL * int(.Excl in flags))
|
||||
flags_i |= (_O_SYNC * int(.Sync in flags))
|
||||
flags_i |= (_O_TRUNC * int(.Trunc in flags))
|
||||
flags_i |= (_O_CLOEXEC * int(.Close_On_Exec in flags))
|
||||
|
||||
fd := unix.sys_open(name_cstr, flags_i, int(perm))
|
||||
if fd < 0 {
|
||||
return nil, _get_platform_error(fd)
|
||||
}
|
||||
|
||||
return _new_file(uintptr(fd), name), nil
|
||||
}
|
||||
|
||||
_new_file :: proc(fd: uintptr, _: string) -> ^File {
|
||||
file := new(File, _file_allocator())
|
||||
file.impl.fd = int(fd)
|
||||
file.impl.allocator = _file_allocator()
|
||||
file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator)
|
||||
return file
|
||||
}
|
||||
|
||||
_destroy :: proc(f: ^File) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
delete(f.impl.name, f.impl.allocator)
|
||||
free(f, f.impl.allocator)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
_close :: proc(f: ^File) -> Error {
|
||||
res := unix.sys_close(f.impl.fd)
|
||||
return _ok_or_error(res)
|
||||
}
|
||||
|
||||
_fd :: proc(f: ^File) -> uintptr {
|
||||
if f == nil {
|
||||
return ~uintptr(0)
|
||||
}
|
||||
return uintptr(f.impl.fd)
|
||||
}
|
||||
|
||||
_name :: proc(f: ^File) -> string {
|
||||
return f.impl.name if f != nil else ""
|
||||
}
|
||||
|
||||
_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
res := unix.sys_lseek(f.impl.fd, offset, int(whence))
|
||||
if res < 0 {
|
||||
return -1, _get_platform_error(int(res))
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
n = unix.sys_read(f.impl.fd, &p[0], len(p))
|
||||
if n < 0 {
|
||||
return -1, _get_platform_error(n)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := p, offset
|
||||
for len(b) > 0 {
|
||||
m := unix.sys_pread(f.impl.fd, &b[0], len(b), offset)
|
||||
if m < 0 {
|
||||
return -1, _get_platform_error(m)
|
||||
}
|
||||
n += m
|
||||
b = b[m:]
|
||||
offset += i64(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
|
||||
//TODO
|
||||
return
|
||||
}
|
||||
|
||||
_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
n = unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
|
||||
if n < 0 {
|
||||
return -1, _get_platform_error(n)
|
||||
}
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := p, offset
|
||||
for len(b) > 0 {
|
||||
m := unix.sys_pwrite(f.impl.fd, &b[0], len(b), offset)
|
||||
if m < 0 {
|
||||
return -1, _get_platform_error(m)
|
||||
}
|
||||
n += m
|
||||
b = b[m:]
|
||||
offset += i64(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
|
||||
//TODO
|
||||
return
|
||||
}
|
||||
|
||||
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
s: _Stat = ---
|
||||
res := unix.sys_fstat(f.impl.fd, &s)
|
||||
if res < 0 {
|
||||
return -1, _get_platform_error(res)
|
||||
}
|
||||
return s.size, nil
|
||||
}
|
||||
|
||||
_sync :: proc(f: ^File) -> Error {
|
||||
return _ok_or_error(unix.sys_fsync(f.impl.fd))
|
||||
}
|
||||
|
||||
_flush :: proc(f: ^File) -> Error {
|
||||
return _ok_or_error(unix.sys_fsync(f.impl.fd))
|
||||
}
|
||||
|
||||
_truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
return _ok_or_error(unix.sys_ftruncate(f.impl.fd, size))
|
||||
}
|
||||
|
||||
_remove :: proc(name: string) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
|
||||
fd := unix.sys_open(name_cstr, int(File_Flags.Read))
|
||||
if fd < 0 {
|
||||
return _get_platform_error(fd)
|
||||
}
|
||||
defer unix.sys_close(fd)
|
||||
|
||||
if _is_dir_fd(fd) {
|
||||
return _ok_or_error(unix.sys_rmdir(name_cstr))
|
||||
}
|
||||
return _ok_or_error(unix.sys_unlink(name_cstr))
|
||||
}
|
||||
|
||||
_rename :: proc(old_name, new_name: string) -> Error {
|
||||
old_name_cstr, old_allocated := _name_to_cstring(old_name)
|
||||
new_name_cstr, new_allocated := _name_to_cstring(new_name)
|
||||
defer if old_allocated {
|
||||
delete(old_name_cstr)
|
||||
}
|
||||
defer if new_allocated {
|
||||
delete(new_name_cstr)
|
||||
}
|
||||
|
||||
return _ok_or_error(unix.sys_rename(old_name_cstr, new_name_cstr))
|
||||
}
|
||||
|
||||
_link :: proc(old_name, new_name: string) -> Error {
|
||||
old_name_cstr, old_allocated := _name_to_cstring(old_name)
|
||||
new_name_cstr, new_allocated := _name_to_cstring(new_name)
|
||||
defer if old_allocated {
|
||||
delete(old_name_cstr)
|
||||
}
|
||||
defer if new_allocated {
|
||||
delete(new_name_cstr)
|
||||
}
|
||||
|
||||
return _ok_or_error(unix.sys_link(old_name_cstr, new_name_cstr))
|
||||
}
|
||||
|
||||
_symlink :: proc(old_name, new_name: string) -> Error {
|
||||
old_name_cstr, old_allocated := _name_to_cstring(old_name)
|
||||
new_name_cstr, new_allocated := _name_to_cstring(new_name)
|
||||
defer if old_allocated {
|
||||
delete(old_name_cstr)
|
||||
}
|
||||
defer if new_allocated {
|
||||
delete(new_name_cstr)
|
||||
}
|
||||
|
||||
return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr))
|
||||
}
|
||||
|
||||
_read_link_cstr :: proc(name_cstr: cstring, allocator := context.allocator) -> (string, Error) {
|
||||
bufsz : uint = 256
|
||||
buf := make([]byte, bufsz, allocator)
|
||||
for {
|
||||
rc := unix.sys_readlink(name_cstr, &(buf[0]), bufsz)
|
||||
if rc < 0 {
|
||||
delete(buf)
|
||||
return "", _get_platform_error(rc)
|
||||
} else if rc == int(bufsz) {
|
||||
bufsz *= 2
|
||||
delete(buf)
|
||||
buf = make([]byte, bufsz, allocator)
|
||||
} else {
|
||||
return strings.string_from_ptr(&buf[0], rc), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_read_link :: proc(name: string, allocator := context.allocator) -> (string, Error) {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _read_link_cstr(name_cstr, allocator)
|
||||
}
|
||||
|
||||
_unlink :: proc(name: string) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_unlink(name_cstr))
|
||||
}
|
||||
|
||||
_chdir :: proc(name: string) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_chdir(name_cstr))
|
||||
}
|
||||
|
||||
_fchdir :: proc(f: ^File) -> Error {
|
||||
return _ok_or_error(unix.sys_fchdir(f.impl.fd))
|
||||
}
|
||||
|
||||
_chmod :: proc(name: string, mode: File_Mode) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_chmod(name_cstr, int(mode)))
|
||||
}
|
||||
|
||||
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||
return _ok_or_error(unix.sys_fchmod(f.impl.fd, int(mode)))
|
||||
}
|
||||
|
||||
// NOTE: will throw error without super user priviledges
|
||||
_chown :: proc(name: string, uid, gid: int) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_chown(name_cstr, uid, gid))
|
||||
}
|
||||
|
||||
// NOTE: will throw error without super user priviledges
|
||||
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid))
|
||||
}
|
||||
|
||||
// NOTE: will throw error without super user priviledges
|
||||
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
return _ok_or_error(unix.sys_fchown(f.impl.fd, uid, gid))
|
||||
}
|
||||
|
||||
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
times := [2]Unix_File_Time {
|
||||
{ atime._nsec, 0 },
|
||||
{ mtime._nsec, 0 },
|
||||
}
|
||||
return _ok_or_error(unix.sys_utimensat(_AT_FDCWD, name_cstr, ×, 0))
|
||||
}
|
||||
|
||||
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||
times := [2]Unix_File_Time {
|
||||
{ atime._nsec, 0 },
|
||||
{ mtime._nsec, 0 },
|
||||
}
|
||||
return _ok_or_error(unix.sys_utimensat(f.impl.fd, nil, ×, 0))
|
||||
}
|
||||
|
||||
_exists :: proc(name: string) -> bool {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return unix.sys_access(name_cstr, F_OK) == 0
|
||||
}
|
||||
|
||||
_is_file :: proc(name: string) -> bool {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
s: _Stat
|
||||
res := unix.sys_stat(name_cstr, &s)
|
||||
if res < 0 {
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
_is_file_fd :: proc(fd: int) -> bool {
|
||||
s: _Stat
|
||||
res := unix.sys_fstat(fd, &s)
|
||||
if res < 0 { // error
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
_is_dir :: proc(name: string) -> bool {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
s: _Stat
|
||||
res := unix.sys_stat(name_cstr, &s)
|
||||
if res < 0 {
|
||||
return false
|
||||
}
|
||||
return S_ISDIR(s.mode)
|
||||
}
|
||||
|
||||
_is_dir_fd :: proc(fd: int) -> bool {
|
||||
s: _Stat
|
||||
res := unix.sys_fstat(fd, &s)
|
||||
if res < 0 { // error
|
||||
return false
|
||||
}
|
||||
return S_ISDIR(s.mode)
|
||||
}
|
||||
|
||||
// Ideally we want to use the temp_allocator. PATH_MAX on Linux is commonly
|
||||
// defined as 512, however, it is well known that paths can exceed that limit.
|
||||
// So, in theory you could have a path larger than the entire temp_allocator's
|
||||
// buffer. Therefor, any large paths will use context.allocator.
|
||||
_name_to_cstring :: proc(name: string) -> (cname: cstring, allocated: bool) {
|
||||
if len(name) > _CSTRING_NAME_HEAP_THRESHOLD {
|
||||
cname = strings.clone_to_cstring(name)
|
||||
allocated = true
|
||||
return
|
||||
}
|
||||
cname = strings.clone_to_cstring(name, context.temp_allocator)
|
||||
return
|
||||
}
|
||||
@@ -2,12 +2,20 @@ package os2
|
||||
|
||||
import "core:io"
|
||||
|
||||
file_to_stream :: proc(f: ^File) -> (s: io.Stream) {
|
||||
to_stream :: proc(f: ^File) -> (s: io.Stream) {
|
||||
s.stream_data = f
|
||||
s.stream_vtable = _file_stream_vtable
|
||||
return
|
||||
}
|
||||
|
||||
to_writer :: proc(f: ^File) -> (s: io.Writer) {
|
||||
return {to_stream(f)}
|
||||
}
|
||||
to_reader :: proc(f: ^File) -> (s: io.Reader) {
|
||||
return {to_stream(f)}
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
error_to_io_error :: proc(ferr: Error) -> io.Error {
|
||||
if ferr == nil {
|
||||
|
||||
@@ -74,7 +74,7 @@ read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
|
||||
|
||||
|
||||
|
||||
read_entire_file :: proc(name: string, allocator := context.allocator) -> ([]byte, Error) {
|
||||
read_entire_file :: proc(name: string, allocator := context.allocator) -> (data: []byte, err: Error) {
|
||||
f, ferr := open(name)
|
||||
if ferr != nil {
|
||||
return nil, ferr
|
||||
@@ -91,15 +91,17 @@ read_entire_file :: proc(name: string, allocator := context.allocator) -> ([]byt
|
||||
|
||||
// TODO(bill): Is this correct logic?
|
||||
total: int
|
||||
data := make([]byte, size, allocator)
|
||||
data = make([]byte, size, allocator) or_return
|
||||
for {
|
||||
n, err := read(f, data[total:])
|
||||
n: int
|
||||
n, err = read(f, data[total:])
|
||||
total += n
|
||||
if err != nil {
|
||||
if err == .EOF {
|
||||
err = nil
|
||||
}
|
||||
return data[:total], err
|
||||
data = data[:total]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,17 +2,30 @@
|
||||
package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
import "core:mem"
|
||||
import "core:sync"
|
||||
import "core:runtime"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
import "core:unicode/utf16"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
INVALID_HANDLE :: ~uintptr(0)
|
||||
|
||||
S_IWRITE :: 0o200
|
||||
_ERROR_BAD_NETPATH :: 53
|
||||
MAX_RW :: 1<<30
|
||||
|
||||
_file_allocator :: proc() -> runtime.Allocator {
|
||||
return heap_allocator()
|
||||
}
|
||||
|
||||
_temp_allocator :: proc() -> runtime.Allocator {
|
||||
// TODO(bill): make this not depend on the context allocator
|
||||
return context.temp_allocator
|
||||
}
|
||||
|
||||
|
||||
_File_Kind :: enum u8 {
|
||||
File,
|
||||
Console,
|
||||
@@ -24,35 +37,109 @@ _File :: struct {
|
||||
name: string,
|
||||
wname: win32.wstring,
|
||||
kind: _File_Kind,
|
||||
|
||||
allocator: runtime.Allocator,
|
||||
|
||||
rw_mutex: sync.RW_Mutex, // read write calls
|
||||
p_mutex: sync.Mutex, // pread pwrite calls
|
||||
}
|
||||
|
||||
_get_platform_error :: proc() -> Error {
|
||||
err := i32(win32.GetLastError())
|
||||
if err != 0 {
|
||||
return Platform_Error{err}
|
||||
_handle :: proc(f: ^File) -> win32.HANDLE {
|
||||
return win32.HANDLE(_fd(f))
|
||||
}
|
||||
|
||||
_open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (handle: uintptr, err: Error) {
|
||||
if len(name) == 0 {
|
||||
err = .Not_Exist
|
||||
return
|
||||
}
|
||||
return nil
|
||||
|
||||
path := _fix_long_path(name)
|
||||
access: u32
|
||||
switch flags & {.Read, .Write} {
|
||||
case {.Read}: access = win32.FILE_GENERIC_READ
|
||||
case {.Write}: access = win32.FILE_GENERIC_WRITE
|
||||
case {.Read, .Write}: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE
|
||||
}
|
||||
|
||||
if .Create in flags {
|
||||
access |= win32.FILE_GENERIC_WRITE
|
||||
}
|
||||
if .Append in flags {
|
||||
access &~= win32.FILE_GENERIC_WRITE
|
||||
access |= win32.FILE_APPEND_DATA
|
||||
}
|
||||
share_mode := u32(win32.FILE_SHARE_READ | win32.FILE_SHARE_WRITE)
|
||||
sa: ^win32.SECURITY_ATTRIBUTES
|
||||
if .Close_On_Exec not_in flags {
|
||||
sa = &win32.SECURITY_ATTRIBUTES{}
|
||||
sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
|
||||
sa.bInheritHandle = true
|
||||
}
|
||||
|
||||
create_mode: u32 = win32.OPEN_EXISTING
|
||||
switch {
|
||||
case flags & {.Create, .Excl} == {.Create, .Excl}:
|
||||
create_mode = win32.CREATE_NEW
|
||||
case flags & {.Create, .Trunc} == {.Create, .Trunc}:
|
||||
create_mode = win32.CREATE_ALWAYS
|
||||
case flags & {.Create} == {.Create}:
|
||||
create_mode = win32.OPEN_ALWAYS
|
||||
case flags & {.Trunc} == {.Trunc}:
|
||||
create_mode = win32.TRUNCATE_EXISTING
|
||||
}
|
||||
|
||||
attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL
|
||||
if perm & S_IWRITE == 0 {
|
||||
attrs = win32.FILE_ATTRIBUTE_READONLY
|
||||
if create_mode == win32.CREATE_ALWAYS {
|
||||
// NOTE(bill): Open has just asked to create a file in read-only mode.
|
||||
// If the file already exists, to make it akin to a *nix open call,
|
||||
// the call preserves the existing permissions.
|
||||
h := win32.CreateFileW(path, access, share_mode, sa, win32.TRUNCATE_EXISTING, win32.FILE_ATTRIBUTE_NORMAL, nil)
|
||||
if h == win32.INVALID_HANDLE {
|
||||
switch e := win32.GetLastError(); e {
|
||||
case win32.ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, win32.ERROR_PATH_NOT_FOUND:
|
||||
// file does not exist, create the file
|
||||
case 0:
|
||||
return uintptr(h), nil
|
||||
case:
|
||||
return 0, Platform_Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
h := win32.CreateFileW(path, access, share_mode, sa, create_mode, attrs, nil)
|
||||
if h == win32.INVALID_HANDLE {
|
||||
return 0, _get_platform_error()
|
||||
}
|
||||
return uintptr(h), nil
|
||||
}
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) {
|
||||
return nil, nil
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) {
|
||||
flags := flags if flags != nil else {.Read}
|
||||
handle := _open_internal(name, flags + {.Close_On_Exec}, perm) or_return
|
||||
return _new_file(handle, name), nil
|
||||
}
|
||||
|
||||
_new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
if handle == INVALID_HANDLE {
|
||||
return nil
|
||||
}
|
||||
context.allocator = _file_allocator()
|
||||
f := new(File)
|
||||
f.impl.fd = rawptr(fd)
|
||||
f.impl.name = strings.clone(name, context.allocator)
|
||||
f.impl.wname = win32.utf8_to_wstring(name, context.allocator)
|
||||
f := new(File, _file_allocator())
|
||||
|
||||
f.impl.allocator = _file_allocator()
|
||||
f.impl.fd = rawptr(fd)
|
||||
f.impl.name = strings.clone(name, f.impl.allocator)
|
||||
f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
|
||||
|
||||
handle := _handle(f)
|
||||
kind := _File_Kind.File
|
||||
if m: u32; win32.GetConsoleMode(win32.HANDLE(fd), &m) {
|
||||
if m: u32; win32.GetConsoleMode(handle, &m) {
|
||||
kind = .Console
|
||||
}
|
||||
if win32.GetFileType(win32.HANDLE(fd)) == win32.FILE_TYPE_PIPE {
|
||||
if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
|
||||
kind = .Pipe
|
||||
}
|
||||
f.impl.kind = kind
|
||||
@@ -72,10 +159,10 @@ _destroy :: proc(f: ^File) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
context.allocator = _file_allocator()
|
||||
free(f.impl.wname)
|
||||
delete(f.impl.name)
|
||||
free(f)
|
||||
a := f.impl.allocator
|
||||
free(f.impl.wname, a)
|
||||
delete(f.impl.name, a)
|
||||
free(f, a)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -95,9 +182,16 @@ _name :: proc(f: ^File) -> string {
|
||||
}
|
||||
|
||||
_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
if f == nil {
|
||||
return
|
||||
handle := _handle(f)
|
||||
if handle == win32.INVALID_HANDLE {
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
if f.impl.kind == .Pipe {
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
|
||||
sync.guard(&f.impl.rw_mutex)
|
||||
|
||||
w: u32
|
||||
switch whence {
|
||||
case .Start: w = win32.FILE_BEGIN
|
||||
@@ -106,12 +200,8 @@ _seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error
|
||||
}
|
||||
hi := i32(offset>>32)
|
||||
lo := i32(offset)
|
||||
ft := win32.GetFileType(win32.HANDLE(fd))
|
||||
if ft == win32.FILE_TYPE_PIPE {
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
|
||||
dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w)
|
||||
dw_ptr := win32.SetFilePointer(handle, lo, &hi, w)
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
return 0, _get_platform_error()
|
||||
}
|
||||
@@ -119,35 +209,203 @@ _seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error
|
||||
}
|
||||
|
||||
_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
return
|
||||
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// TODO(bill): should this be moved to `_File` instead?
|
||||
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_platform_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+i < 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
|
||||
}
|
||||
|
||||
handle := _handle(f)
|
||||
|
||||
single_read_length: win32.DWORD
|
||||
total_read: int
|
||||
length := len(p)
|
||||
|
||||
sync.shared_guard(&f.impl.rw_mutex) // multiple readers
|
||||
|
||||
if sync.guard(&f.impl.p_mutex) {
|
||||
to_read := min(win32.DWORD(length), MAX_RW)
|
||||
ok: win32.BOOL
|
||||
if f.impl.kind == .Console {
|
||||
n, err := read_console(handle, p[total_read:][:to_read])
|
||||
total_read += n
|
||||
if err != nil {
|
||||
return int(total_read), err
|
||||
}
|
||||
} else {
|
||||
ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil)
|
||||
}
|
||||
|
||||
if single_read_length > 0 && ok {
|
||||
total_read += int(single_read_length)
|
||||
} else {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
}
|
||||
|
||||
return int(total_read), nil
|
||||
}
|
||||
|
||||
_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
pread :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
buf := data
|
||||
if len(buf) > MAX_RW {
|
||||
buf = buf[:MAX_RW]
|
||||
|
||||
}
|
||||
curr_offset := seek(f, offset, .Current) or_return
|
||||
defer seek(f, curr_offset, .Start)
|
||||
|
||||
o := win32.OVERLAPPED{
|
||||
OffsetHigh = u32(offset>>32),
|
||||
Offset = u32(offset),
|
||||
}
|
||||
|
||||
// TODO(bill): Determine the correct behaviour for consoles
|
||||
|
||||
h := _handle(f)
|
||||
done: win32.DWORD
|
||||
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
err = _get_platform_error()
|
||||
done = 0
|
||||
}
|
||||
n = int(done)
|
||||
return
|
||||
}
|
||||
|
||||
sync.guard(&f.impl.p_mutex)
|
||||
|
||||
p, offset := p, offset
|
||||
for len(p) > 0 {
|
||||
m := pread(f, p, offset) or_return
|
||||
n += m
|
||||
p = p[m:]
|
||||
offset += i64(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
|
||||
// TODO(bill)
|
||||
return
|
||||
}
|
||||
|
||||
_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
return
|
||||
if len(p) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
single_write_length: win32.DWORD
|
||||
total_write: i64
|
||||
length := i64(len(p))
|
||||
|
||||
handle := _handle(f)
|
||||
|
||||
sync.guard(&f.impl.rw_mutex)
|
||||
for total_write < length {
|
||||
remaining := length - total_write
|
||||
to_write := win32.DWORD(min(i32(remaining), MAX_RW))
|
||||
|
||||
e := win32.WriteFile(handle, &p[total_write], to_write, &single_write_length, nil)
|
||||
if single_write_length <= 0 || !e {
|
||||
n = int(total_write)
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
total_write += i64(single_write_length)
|
||||
}
|
||||
return int(total_write), nil
|
||||
}
|
||||
|
||||
_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
pwrite :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
buf := data
|
||||
if len(buf) > MAX_RW {
|
||||
buf = buf[:MAX_RW]
|
||||
|
||||
}
|
||||
curr_offset := seek(f, offset, .Current) or_return
|
||||
defer seek(f, curr_offset, .Start)
|
||||
|
||||
o := win32.OVERLAPPED{
|
||||
OffsetHigh = u32(offset>>32),
|
||||
Offset = u32(offset),
|
||||
}
|
||||
|
||||
h := _handle(f)
|
||||
done: win32.DWORD
|
||||
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
err = _get_platform_error()
|
||||
done = 0
|
||||
}
|
||||
n = int(done)
|
||||
return
|
||||
}
|
||||
|
||||
sync.guard(&f.impl.p_mutex)
|
||||
p, offset := p, offset
|
||||
for len(p) > 0 {
|
||||
m := pwrite(f, p, offset) or_return
|
||||
n += m
|
||||
p = p[m:]
|
||||
offset += i64(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
|
||||
// TODO(bill)
|
||||
return
|
||||
}
|
||||
|
||||
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
if f == nil {
|
||||
return
|
||||
}
|
||||
length: win32.LARGE_INTEGER
|
||||
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
|
||||
handle := _handle(f)
|
||||
if !win32.GetFileSizeEx(handle, &length) {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
n = i64(length)
|
||||
@@ -156,10 +414,14 @@ _file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
|
||||
|
||||
_sync :: proc(f: ^File) -> Error {
|
||||
return nil
|
||||
return _flush(f)
|
||||
}
|
||||
|
||||
_flush :: proc(f: ^File) -> Error {
|
||||
handle := _handle(f)
|
||||
if !win32.FlushFileBuffers(handle) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -170,7 +432,8 @@ _truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
curr_off := seek(f, 0, .Current) or_return
|
||||
defer seek(f, curr_off, .Start)
|
||||
seek(f, size, .Start) or_return
|
||||
if !win32.SetEndOfFile(win32.HANDLE(fd)) {
|
||||
handle := _handle(f)
|
||||
if !win32.SetEndOfFile(handle) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
@@ -234,43 +497,213 @@ _link :: proc(old_name, new_name: string) -> Error {
|
||||
}
|
||||
|
||||
_symlink :: proc(old_name, new_name: string) -> Error {
|
||||
return nil
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_read_link :: proc(name: string) -> (string, Error) {
|
||||
_open_sym_link :: proc(p: [^]u16) -> (handle: win32.HANDLE, err: Error) {
|
||||
attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS)
|
||||
attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
|
||||
handle = win32.CreateFileW(p, 0, 0, nil, win32.OPEN_EXISTING, attrs, nil)
|
||||
if handle == win32.INVALID_HANDLE {
|
||||
return nil, _get_platform_error()
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
_normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: string, err: Error) {
|
||||
has_prefix :: proc(p: []u16, str: string) -> bool {
|
||||
if len(p) < len(str) {
|
||||
return false
|
||||
}
|
||||
// assume ascii
|
||||
for i in 0..<len(str) {
|
||||
if p[i] != u16(str[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
has_unc_prefix :: proc(p: []u16) -> bool {
|
||||
return has_prefix(p, `\??\`)
|
||||
}
|
||||
|
||||
if !has_unc_prefix(p) {
|
||||
return win32.utf16_to_utf8(p, allocator)
|
||||
}
|
||||
|
||||
ws := p[4:]
|
||||
switch {
|
||||
case len(ws) >= 2 && ws[1] == ':':
|
||||
return win32.utf16_to_utf8(ws, allocator)
|
||||
case has_prefix(ws, `UNC\`):
|
||||
ws[3] = '\\' // override data in buffer
|
||||
return win32.utf16_to_utf8(ws[3:], allocator)
|
||||
}
|
||||
|
||||
|
||||
handle := _open_sym_link(raw_data(p)) or_return
|
||||
defer win32.CloseHandle(handle)
|
||||
|
||||
n := win32.GetFinalPathNameByHandleW(handle, nil, 0, win32.VOLUME_NAME_DOS)
|
||||
if n == 0 {
|
||||
return "", _get_platform_error()
|
||||
}
|
||||
buf := make([]u16, n+1, _temp_allocator())
|
||||
n = win32.GetFinalPathNameByHandleW(handle, raw_data(buf), u32(len(buf)), win32.VOLUME_NAME_DOS)
|
||||
if n == 0 {
|
||||
return "", _get_platform_error()
|
||||
}
|
||||
|
||||
ws = buf[:n]
|
||||
if has_unc_prefix(ws) {
|
||||
ws = ws[4:]
|
||||
if len(ws) > 3 && has_prefix(ws, `UNC`) {
|
||||
ws[2] = '\\'
|
||||
return win32.utf16_to_utf8(ws[2:], allocator)
|
||||
}
|
||||
return win32.utf16_to_utf8(ws, allocator)
|
||||
}
|
||||
return "", .Invalid_Path
|
||||
}
|
||||
|
||||
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
|
||||
MAXIMUM_REPARSE_DATA_BUFFER_SIZE :: 16 * 1024
|
||||
|
||||
@thread_local
|
||||
rdb_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
|
||||
|
||||
p := _fix_long_path(name)
|
||||
handle := _open_sym_link(p) or_return
|
||||
defer win32.CloseHandle(handle)
|
||||
|
||||
bytes_returned: u32
|
||||
if !win32.DeviceIoControl(handle, win32.FSCTL_GET_REPARSE_POINT, nil, 0, &rdb_buf[0], len(rdb_buf)-1, &bytes_returned, nil) {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
mem.zero_slice(rdb_buf[:min(bytes_returned+1, len(rdb_buf))])
|
||||
|
||||
|
||||
rdb := (^win32.REPARSE_DATA_BUFFER)(&rdb_buf[0])
|
||||
switch rdb.ReparseTag {
|
||||
case win32.IO_REPARSE_TAG_SYMLINK:
|
||||
rb := (^win32.SYMBOLIC_LINK_REPARSE_BUFFER)(&rdb.rest)
|
||||
pb := win32.wstring(&rb.PathBuffer)
|
||||
pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
|
||||
p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
|
||||
if rb.Flags & win32.SYMLINK_FLAG_RELATIVE != 0 {
|
||||
return win32.utf16_to_utf8(p, allocator)
|
||||
}
|
||||
return _normalize_link_path(p, allocator)
|
||||
|
||||
case win32.IO_REPARSE_TAG_MOUNT_POINT:
|
||||
rb := (^win32.MOUNT_POINT_REPARSE_BUFFER)(&rdb.rest)
|
||||
pb := win32.wstring(&rb.PathBuffer)
|
||||
pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
|
||||
p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
|
||||
return _normalize_link_path(p, allocator)
|
||||
}
|
||||
// Path wasn't a symlink/junction but another reparse point kind
|
||||
return "", nil
|
||||
}
|
||||
|
||||
|
||||
_chdir :: proc(f: ^File) -> Error {
|
||||
_fchdir :: proc(f: ^File) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
if win32.SetCurrentDirectoryW(f.impl.wname) {
|
||||
if !win32.SetCurrentDirectoryW(f.impl.wname) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
return _get_platform_error()
|
||||
}
|
||||
d: win32.BY_HANDLE_FILE_INFORMATION
|
||||
if !win32.GetFileInformationByHandle(_handle(f), &d) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
attrs := d.dwFileAttributes
|
||||
if mode & S_IWRITE != 0 {
|
||||
attrs &~= win32.FILE_ATTRIBUTE_READONLY
|
||||
} else {
|
||||
attrs |= win32.FILE_ATTRIBUTE_READONLY
|
||||
}
|
||||
|
||||
_chmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||
info: win32.FILE_BASIC_INFO
|
||||
info.FileAttributes = attrs
|
||||
if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_chown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_chdir :: proc(name: string) -> Error {
|
||||
p := _fix_long_path(name)
|
||||
if !win32.SetCurrentDirectoryW(p) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_chmod :: proc(name: string, mode: File_Mode) -> Error {
|
||||
f := open(name, {.Write}) or_return
|
||||
defer close(f)
|
||||
return _fchmod(f, mode)
|
||||
}
|
||||
|
||||
_chown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return nil
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
|
||||
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
f := open(name, {.Write}) or_return
|
||||
defer close(f)
|
||||
return _fchtimes(f, atime, mtime)
|
||||
}
|
||||
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
d: win32.BY_HANDLE_FILE_INFORMATION
|
||||
if !win32.GetFileInformationByHandle(_handle(f), &d) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
|
||||
to_windows_time :: #force_inline proc(t: time.Time) -> win32.LARGE_INTEGER {
|
||||
// a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC)
|
||||
return win32.LARGE_INTEGER(time.time_to_unix_nano(t) * 100 + 116444736000000000)
|
||||
}
|
||||
|
||||
atime, mtime := atime, mtime
|
||||
if time.time_to_unix_nano(atime) < time.time_to_unix_nano(mtime) {
|
||||
atime = mtime
|
||||
}
|
||||
|
||||
info: win32.FILE_BASIC_INFO
|
||||
info.LastAccessTime = to_windows_time(atime)
|
||||
info.LastWriteTime = to_windows_time(mtime)
|
||||
if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
_exists :: proc(path: string) -> bool {
|
||||
wpath := _fix_long_path(path)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
|
||||
722
core/os/os2/heap_linux.odin
Normal file
722
core/os/os2/heap_linux.odin
Normal file
@@ -0,0 +1,722 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:sys/unix"
|
||||
import "core:sync"
|
||||
import "core:mem"
|
||||
|
||||
// NOTEs
|
||||
//
|
||||
// All allocations below DIRECT_MMAP_THRESHOLD exist inside of memory "Regions." A region
|
||||
// consists of a Region_Header and the memory that will be divided into allocations to
|
||||
// send to the user. The memory is an array of "Allocation_Headers" which are 8 bytes.
|
||||
// Allocation_Headers are used to navigate the memory in the region. The "next" member of
|
||||
// the Allocation_Header points to the next header, and the space between the headers
|
||||
// can be used to send to the user. This space between is referred to as "blocks" in the
|
||||
// code. The indexes in the header refer to these blocks instead of bytes. This allows us
|
||||
// to index all the memory in the region with a u16.
|
||||
//
|
||||
// When an allocation request is made, it will use the first free block that can contain
|
||||
// the entire block. If there is an excess number of blocks (as specified by the constant
|
||||
// BLOCK_SEGMENT_THRESHOLD), this extra space will be segmented and left in the free_list.
|
||||
//
|
||||
// To keep the implementation simple, there can never exist 2 free blocks adjacent to each
|
||||
// other. Any freeing will result in attempting to merge the blocks before and after the
|
||||
// newly free'd blocks.
|
||||
//
|
||||
// Any request for size above the DIRECT_MMAP_THRESHOLD will result in the allocation
|
||||
// getting its own individual mmap. Individual mmaps will still get an Allocation_Header
|
||||
// that contains the size with the last bit set to 1 to indicate it is indeed a direct
|
||||
// mmap allocation.
|
||||
|
||||
// Why not brk?
|
||||
// glibc's malloc utilizes a mix of the brk and mmap system calls. This implementation
|
||||
// does *not* utilize the brk system call to avoid possible conflicts with foreign C
|
||||
// code. Just because we aren't directly using libc, there is nothing stopping the user
|
||||
// from doing it.
|
||||
|
||||
// What's with all the #no_bounds_check?
|
||||
// When memory is returned from mmap, it technically doesn't get written ... well ... anywhere
|
||||
// until that region is written to by *you*. So, when a new region is created, we call mmap
|
||||
// to get a pointer to some memory, and we claim that memory is a ^Region. Therefor, the
|
||||
// region itself is never formally initialized by the compiler as this would result in writing
|
||||
// zeros to memory that we can already assume are 0. This would also have the effect of
|
||||
// actually commiting this data to memory whether it gets used or not.
|
||||
|
||||
|
||||
//
|
||||
// Some variables to play with
|
||||
//
|
||||
|
||||
// Minimum blocks used for any one allocation
|
||||
MINIMUM_BLOCK_COUNT :: 2
|
||||
|
||||
// Number of extra blocks beyond the requested amount where we would segment.
|
||||
// E.g. (blocks) |H0123456| 7 available
|
||||
// |H01H0123| Ask for 2, now 4 available
|
||||
BLOCK_SEGMENT_THRESHOLD :: 4
|
||||
|
||||
// Anything above this threshold will get its own memory map. Since regions
|
||||
// are indexed by 16 bit integers, this value should not surpass max(u16) * 6
|
||||
DIRECT_MMAP_THRESHOLD_USER :: int(max(u16))
|
||||
|
||||
// The point at which we convert direct mmap to region. This should be a decent
|
||||
// amount less than DIRECT_MMAP_THRESHOLD to avoid jumping in and out of regions.
|
||||
MMAP_TO_REGION_SHRINK_THRESHOLD :: DIRECT_MMAP_THRESHOLD - PAGE_SIZE * 4
|
||||
|
||||
// free_list is dynamic and is initialized in the begining of the region memory
|
||||
// when the region is initialized. Once resized, it can be moved anywhere.
|
||||
FREE_LIST_DEFAULT_CAP :: 32
|
||||
|
||||
|
||||
//
|
||||
// Other constants that should not be touched
|
||||
//
|
||||
|
||||
// This universally seems to be 4096 outside of uncommon archs.
|
||||
PAGE_SIZE :: 4096
|
||||
|
||||
// just rounding up to nearest PAGE_SIZE
|
||||
DIRECT_MMAP_THRESHOLD :: (DIRECT_MMAP_THRESHOLD_USER-1) + PAGE_SIZE - (DIRECT_MMAP_THRESHOLD_USER-1) % PAGE_SIZE
|
||||
|
||||
// Regions must be big enough to hold DIRECT_MMAP_THRESHOLD - 1 as well
|
||||
// as end right on a page boundary as to not waste space.
|
||||
SIZE_OF_REGION :: DIRECT_MMAP_THRESHOLD + 4 * int(PAGE_SIZE)
|
||||
|
||||
// size of user memory blocks
|
||||
BLOCK_SIZE :: size_of(Allocation_Header)
|
||||
|
||||
// number of allocation sections (call them blocks) of the region used for allocations
|
||||
BLOCKS_PER_REGION :: u16((SIZE_OF_REGION - size_of(Region_Header)) / BLOCK_SIZE)
|
||||
|
||||
// minimum amount of space that can used by any individual allocation (includes header)
|
||||
MINIMUM_ALLOCATION :: (MINIMUM_BLOCK_COUNT * BLOCK_SIZE) + BLOCK_SIZE
|
||||
|
||||
// This is used as a boolean value for Region_Header.local_addr.
|
||||
CURRENTLY_ACTIVE :: (^^Region)(~uintptr(0))
|
||||
|
||||
FREE_LIST_ENTRIES_PER_BLOCK :: BLOCK_SIZE / size_of(u16)
|
||||
|
||||
MMAP_FLAGS :: unix.MAP_ANONYMOUS | unix.MAP_PRIVATE
|
||||
MMAP_PROT :: unix.PROT_READ | unix.PROT_WRITE
|
||||
|
||||
|
||||
@thread_local _local_region: ^Region
|
||||
global_regions: ^Region
|
||||
|
||||
|
||||
// There is no way of correctly setting the last bit of free_idx or
|
||||
// the last bit of requested, so we can safely use it as a flag to
|
||||
// determine if we are interacting with a direct mmap.
|
||||
REQUESTED_MASK :: 0x7FFFFFFFFFFFFFFF
|
||||
IS_DIRECT_MMAP :: 0x8000000000000000
|
||||
|
||||
// Special free_idx value that does not index the free_list.
|
||||
NOT_FREE :: 0x7FFF
|
||||
Allocation_Header :: struct #raw_union {
|
||||
using _: struct {
|
||||
// Block indicies
|
||||
idx: u16,
|
||||
prev: u16,
|
||||
next: u16,
|
||||
free_idx: u16,
|
||||
},
|
||||
requested: u64,
|
||||
}
|
||||
|
||||
Region_Header :: struct #align 16 {
|
||||
next_region: ^Region, // points to next region in global_heap (linked list)
|
||||
local_addr: ^^Region, // tracks region ownership via address of _local_region
|
||||
reset_addr: ^^Region, // tracks old local addr for reset
|
||||
free_list: []u16,
|
||||
free_list_len: u16,
|
||||
free_blocks: u16, // number of free blocks in region (includes headers)
|
||||
last_used: u16, // farthest back block that has been used (need zeroing?)
|
||||
_reserved: u16,
|
||||
}
|
||||
|
||||
Region :: struct {
|
||||
hdr: Region_Header,
|
||||
memory: [BLOCKS_PER_REGION]Allocation_Header,
|
||||
}
|
||||
|
||||
_heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, mem.Allocator_Error) {
|
||||
//
|
||||
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
|
||||
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
|
||||
// padding. We also store the original pointer returned by heap_alloc right before
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr))
|
||||
space := size + a - 1
|
||||
|
||||
allocated_mem: rawptr
|
||||
if old_ptr != nil {
|
||||
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr))
|
||||
}
|
||||
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
|
||||
|
||||
ptr := uintptr(aligned_mem)
|
||||
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
|
||||
diff := int(aligned_ptr - ptr)
|
||||
if (size + diff) > space {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
aligned_mem = rawptr(aligned_ptr)
|
||||
mem.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem
|
||||
|
||||
return mem.byte_slice(aligned_mem, size), nil
|
||||
}
|
||||
|
||||
aligned_free :: proc(p: rawptr) {
|
||||
if p != nil {
|
||||
heap_free(mem.ptr_offset((^rawptr)(p), -1)^)
|
||||
}
|
||||
}
|
||||
|
||||
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> (new_memory: []byte, err: mem.Allocator_Error) {
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return aligned_alloc(new_size, new_alignment, p)
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return aligned_alloc(size, alignment)
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory)
|
||||
|
||||
case .Free_All:
|
||||
return nil, .Mode_Not_Implemented
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return aligned_alloc(size, alignment)
|
||||
}
|
||||
return aligned_resize(old_memory, old_size, size, alignment)
|
||||
|
||||
case .Query_Features:
|
||||
set := (^mem.Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
case .Query_Info:
|
||||
return nil, .Mode_Not_Implemented
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
if size >= DIRECT_MMAP_THRESHOLD {
|
||||
return _direct_mmap_alloc(size)
|
||||
}
|
||||
|
||||
// atomically check if the local region has been stolen
|
||||
if _local_region != nil {
|
||||
res := sync.atomic_compare_exchange_strong_explicit(
|
||||
&_local_region.hdr.local_addr,
|
||||
&_local_region,
|
||||
CURRENTLY_ACTIVE,
|
||||
.Acquire,
|
||||
.Relaxed,
|
||||
)
|
||||
if res != &_local_region {
|
||||
// At this point, the region has been stolen and res contains the unexpected value
|
||||
expected := res
|
||||
if res != CURRENTLY_ACTIVE {
|
||||
expected = res
|
||||
res = sync.atomic_compare_exchange_strong_explicit(
|
||||
&_local_region.hdr.local_addr,
|
||||
expected,
|
||||
CURRENTLY_ACTIVE,
|
||||
.Acquire,
|
||||
.Relaxed,
|
||||
)
|
||||
}
|
||||
if res != expected {
|
||||
_local_region = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size := size
|
||||
size = _round_up_to_nearest(size, BLOCK_SIZE)
|
||||
blocks_needed := u16(max(MINIMUM_BLOCK_COUNT, size / BLOCK_SIZE))
|
||||
|
||||
// retrieve a region if new thread or stolen
|
||||
if _local_region == nil {
|
||||
_local_region, _ = _region_retrieve_with_space(blocks_needed)
|
||||
if _local_region == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
defer sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
|
||||
|
||||
// At this point we have a usable region. Let's find the user some memory
|
||||
idx: u16
|
||||
local_region_idx := _region_get_local_idx()
|
||||
back_idx := -1
|
||||
infinite: for {
|
||||
for i := 0; i < int(_local_region.hdr.free_list_len); i += 1 {
|
||||
idx = _local_region.hdr.free_list[i]
|
||||
#no_bounds_check if _get_block_count(_local_region.memory[idx]) >= blocks_needed {
|
||||
break infinite
|
||||
}
|
||||
}
|
||||
sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
|
||||
_local_region, back_idx = _region_retrieve_with_space(blocks_needed, local_region_idx, back_idx)
|
||||
}
|
||||
user_ptr, used := _region_get_block(_local_region, idx, blocks_needed)
|
||||
_local_region.hdr.free_blocks -= (used + 1)
|
||||
|
||||
// If this memory was ever used before, it now needs to be zero'd.
|
||||
if idx < _local_region.hdr.last_used {
|
||||
mem.zero(user_ptr, int(used) * BLOCK_SIZE)
|
||||
} else {
|
||||
_local_region.hdr.last_used = idx + used
|
||||
}
|
||||
|
||||
return user_ptr
|
||||
}
|
||||
|
||||
heap_resize :: proc(old_memory: rawptr, new_size: int) -> rawptr #no_bounds_check {
|
||||
alloc := _get_allocation_header(old_memory)
|
||||
if alloc.requested & IS_DIRECT_MMAP > 0 {
|
||||
return _direct_mmap_resize(alloc, new_size)
|
||||
}
|
||||
|
||||
if new_size > DIRECT_MMAP_THRESHOLD {
|
||||
return _direct_mmap_from_region(alloc, new_size)
|
||||
}
|
||||
|
||||
return _region_resize(alloc, new_size)
|
||||
}
|
||||
|
||||
heap_free :: proc(memory: rawptr) {
|
||||
alloc := _get_allocation_header(memory)
|
||||
if alloc.requested & IS_DIRECT_MMAP == IS_DIRECT_MMAP {
|
||||
_direct_mmap_free(alloc)
|
||||
return
|
||||
}
|
||||
|
||||
assert(alloc.free_idx == NOT_FREE)
|
||||
|
||||
_region_find_and_assign_local(alloc)
|
||||
_region_local_free(alloc)
|
||||
sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
|
||||
}
|
||||
|
||||
//
|
||||
// Regions
|
||||
//
|
||||
_new_region :: proc() -> ^Region #no_bounds_check {
|
||||
res := unix.sys_mmap(nil, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
||||
if res < 0 {
|
||||
return nil
|
||||
}
|
||||
new_region := (^Region)(uintptr(res))
|
||||
|
||||
new_region.hdr.local_addr = CURRENTLY_ACTIVE
|
||||
new_region.hdr.reset_addr = &_local_region
|
||||
|
||||
free_list_blocks := _round_up_to_nearest(FREE_LIST_DEFAULT_CAP, FREE_LIST_ENTRIES_PER_BLOCK)
|
||||
_region_assign_free_list(new_region, &new_region.memory[1], u16(free_list_blocks) * FREE_LIST_ENTRIES_PER_BLOCK)
|
||||
|
||||
// + 2 to account for free_list's allocation header
|
||||
first_user_block := len(new_region.hdr.free_list) / FREE_LIST_ENTRIES_PER_BLOCK + 2
|
||||
|
||||
// first allocation header (this is a free list)
|
||||
new_region.memory[0].next = u16(first_user_block)
|
||||
new_region.memory[0].free_idx = NOT_FREE
|
||||
new_region.memory[first_user_block].idx = u16(first_user_block)
|
||||
new_region.memory[first_user_block].next = BLOCKS_PER_REGION - 1
|
||||
|
||||
// add the first user block to the free list
|
||||
new_region.hdr.free_list[0] = u16(first_user_block)
|
||||
new_region.hdr.free_list_len = 1
|
||||
new_region.hdr.free_blocks = _get_block_count(new_region.memory[first_user_block]) + 1
|
||||
|
||||
for r := sync.atomic_compare_exchange_strong(&global_regions, nil, new_region);
|
||||
r != nil;
|
||||
r = sync.atomic_compare_exchange_strong(&r.hdr.next_region, nil, new_region) {}
|
||||
|
||||
return new_region
|
||||
}
|
||||
|
||||
_region_resize :: proc(alloc: ^Allocation_Header, new_size: int, alloc_is_free_list: bool = false) -> rawptr #no_bounds_check {
|
||||
assert(alloc.free_idx == NOT_FREE)
|
||||
|
||||
old_memory := mem.ptr_offset(alloc, 1)
|
||||
|
||||
old_block_count := _get_block_count(alloc^)
|
||||
new_block_count := u16(
|
||||
max(MINIMUM_BLOCK_COUNT, _round_up_to_nearest(new_size, BLOCK_SIZE) / BLOCK_SIZE),
|
||||
)
|
||||
if new_block_count < old_block_count {
|
||||
if new_block_count - old_block_count >= MINIMUM_BLOCK_COUNT {
|
||||
_region_find_and_assign_local(alloc)
|
||||
_region_segment(_local_region, alloc, new_block_count, alloc.free_idx)
|
||||
new_block_count = _get_block_count(alloc^)
|
||||
sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
|
||||
}
|
||||
// need to zero anything within the new block that that lies beyond new_size
|
||||
extra_bytes := int(new_block_count * BLOCK_SIZE) - new_size
|
||||
extra_bytes_ptr := mem.ptr_offset((^u8)(alloc), new_size + BLOCK_SIZE)
|
||||
mem.zero(extra_bytes_ptr, extra_bytes)
|
||||
return old_memory
|
||||
}
|
||||
|
||||
if !alloc_is_free_list {
|
||||
_region_find_and_assign_local(alloc)
|
||||
}
|
||||
defer if !alloc_is_free_list {
|
||||
sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
|
||||
}
|
||||
|
||||
// First, let's see if we can grow in place.
|
||||
if alloc.next != BLOCKS_PER_REGION - 1 && _local_region.memory[alloc.next].free_idx != NOT_FREE {
|
||||
next_alloc := _local_region.memory[alloc.next]
|
||||
total_available := old_block_count + _get_block_count(next_alloc) + 1
|
||||
if total_available >= new_block_count {
|
||||
alloc.next = next_alloc.next
|
||||
_local_region.memory[alloc.next].prev = alloc.idx
|
||||
if total_available - new_block_count > BLOCK_SEGMENT_THRESHOLD {
|
||||
_region_segment(_local_region, alloc, new_block_count, next_alloc.free_idx)
|
||||
} else {
|
||||
_region_free_list_remove(_local_region, next_alloc.free_idx)
|
||||
}
|
||||
mem.zero(&_local_region.memory[next_alloc.idx], int(alloc.next - next_alloc.idx) * BLOCK_SIZE)
|
||||
_local_region.hdr.last_used = max(alloc.next, _local_region.hdr.last_used)
|
||||
_local_region.hdr.free_blocks -= (_get_block_count(alloc^) - old_block_count)
|
||||
if alloc_is_free_list {
|
||||
_region_assign_free_list(_local_region, old_memory, _get_block_count(alloc^))
|
||||
}
|
||||
return old_memory
|
||||
}
|
||||
}
|
||||
|
||||
// If we made it this far, we need to resize, copy, zero and free.
|
||||
region_iter := _local_region
|
||||
local_region_idx := _region_get_local_idx()
|
||||
back_idx := -1
|
||||
idx: u16
|
||||
infinite: for {
|
||||
for i := 0; i < len(region_iter.hdr.free_list); i += 1 {
|
||||
idx = region_iter.hdr.free_list[i]
|
||||
if _get_block_count(region_iter.memory[idx]) >= new_block_count {
|
||||
break infinite
|
||||
}
|
||||
}
|
||||
if region_iter != _local_region {
|
||||
sync.atomic_store_explicit(
|
||||
®ion_iter.hdr.local_addr,
|
||||
region_iter.hdr.reset_addr,
|
||||
.Release,
|
||||
)
|
||||
}
|
||||
region_iter, back_idx = _region_retrieve_with_space(new_block_count, local_region_idx, back_idx)
|
||||
}
|
||||
if region_iter != _local_region {
|
||||
sync.atomic_store_explicit(
|
||||
®ion_iter.hdr.local_addr,
|
||||
region_iter.hdr.reset_addr,
|
||||
.Release,
|
||||
)
|
||||
}
|
||||
|
||||
// copy from old memory
|
||||
new_memory, used_blocks := _region_get_block(region_iter, idx, new_block_count)
|
||||
mem.copy(new_memory, old_memory, int(old_block_count * BLOCK_SIZE))
|
||||
|
||||
// zero any new memory
|
||||
addon_section := mem.ptr_offset((^Allocation_Header)(new_memory), old_block_count)
|
||||
new_blocks := used_blocks - old_block_count
|
||||
mem.zero(addon_section, int(new_blocks) * BLOCK_SIZE)
|
||||
|
||||
region_iter.hdr.free_blocks -= (used_blocks + 1)
|
||||
|
||||
// Set free_list before freeing.
|
||||
if alloc_is_free_list {
|
||||
_region_assign_free_list(_local_region, new_memory, used_blocks)
|
||||
}
|
||||
|
||||
// free old memory
|
||||
_region_local_free(alloc)
|
||||
return new_memory
|
||||
}
|
||||
|
||||
_region_local_free :: proc(alloc: ^Allocation_Header) #no_bounds_check {
|
||||
alloc := alloc
|
||||
add_to_free_list := true
|
||||
|
||||
_local_region.hdr.free_blocks += _get_block_count(alloc^) + 1
|
||||
|
||||
// try to merge with prev
|
||||
if alloc.idx > 0 && _local_region.memory[alloc.prev].free_idx != NOT_FREE {
|
||||
_local_region.memory[alloc.prev].next = alloc.next
|
||||
_local_region.memory[alloc.next].prev = alloc.prev
|
||||
alloc = &_local_region.memory[alloc.prev]
|
||||
add_to_free_list = false
|
||||
}
|
||||
|
||||
// try to merge with next
|
||||
if alloc.next < BLOCKS_PER_REGION - 1 && _local_region.memory[alloc.next].free_idx != NOT_FREE {
|
||||
old_next := alloc.next
|
||||
alloc.next = _local_region.memory[old_next].next
|
||||
_local_region.memory[alloc.next].prev = alloc.idx
|
||||
|
||||
if add_to_free_list {
|
||||
_local_region.hdr.free_list[_local_region.memory[old_next].free_idx] = alloc.idx
|
||||
alloc.free_idx = _local_region.memory[old_next].free_idx
|
||||
} else {
|
||||
// NOTE: We have aleady merged with prev, and now merged with next.
|
||||
// Now, we are actually going to remove from the free_list.
|
||||
_region_free_list_remove(_local_region, _local_region.memory[old_next].free_idx)
|
||||
}
|
||||
add_to_free_list = false
|
||||
}
|
||||
|
||||
// This is the only place where anything is appended to the free list.
|
||||
if add_to_free_list {
|
||||
fl := _local_region.hdr.free_list
|
||||
alloc.free_idx = _local_region.hdr.free_list_len
|
||||
fl[alloc.free_idx] = alloc.idx
|
||||
_local_region.hdr.free_list_len += 1
|
||||
if int(_local_region.hdr.free_list_len) == len(fl) {
|
||||
free_alloc := _get_allocation_header(mem.raw_data(_local_region.hdr.free_list))
|
||||
_region_resize(free_alloc, len(fl) * 2 * size_of(fl[0]), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_region_assign_free_list :: proc(region: ^Region, memory: rawptr, blocks: u16) {
|
||||
raw_free_list := transmute(mem.Raw_Slice)region.hdr.free_list
|
||||
raw_free_list.len = int(blocks) * FREE_LIST_ENTRIES_PER_BLOCK
|
||||
raw_free_list.data = memory
|
||||
region.hdr.free_list = transmute([]u16)(raw_free_list)
|
||||
}
|
||||
|
||||
_region_retrieve_with_space :: proc(blocks: u16, local_idx: int = -1, back_idx: int = -1) -> (^Region, int) {
|
||||
r: ^Region
|
||||
idx: int
|
||||
for r = global_regions; r != nil; r = r.hdr.next_region {
|
||||
if idx == local_idx || idx < back_idx || r.hdr.free_blocks < blocks {
|
||||
idx += 1
|
||||
continue
|
||||
}
|
||||
idx += 1
|
||||
local_addr: ^^Region = sync.atomic_load(&r.hdr.local_addr)
|
||||
if local_addr != CURRENTLY_ACTIVE {
|
||||
res := sync.atomic_compare_exchange_strong_explicit(
|
||||
&r.hdr.local_addr,
|
||||
local_addr,
|
||||
CURRENTLY_ACTIVE,
|
||||
.Acquire,
|
||||
.Relaxed,
|
||||
)
|
||||
if res == local_addr {
|
||||
r.hdr.reset_addr = local_addr
|
||||
return r, idx
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _new_region(), idx
|
||||
}
|
||||
|
||||
_region_retrieve_from_addr :: proc(addr: rawptr) -> ^Region {
|
||||
r: ^Region
|
||||
for r = global_regions; r != nil; r = r.hdr.next_region {
|
||||
if _region_contains_mem(r, addr) {
|
||||
return r
|
||||
}
|
||||
}
|
||||
unreachable()
|
||||
}
|
||||
|
||||
_region_get_block :: proc(region: ^Region, idx, blocks_needed: u16) -> (rawptr, u16) #no_bounds_check {
|
||||
alloc := ®ion.memory[idx]
|
||||
|
||||
assert(alloc.free_idx != NOT_FREE)
|
||||
assert(alloc.next > 0)
|
||||
|
||||
block_count := _get_block_count(alloc^)
|
||||
if block_count - blocks_needed > BLOCK_SEGMENT_THRESHOLD {
|
||||
_region_segment(region, alloc, blocks_needed, alloc.free_idx)
|
||||
} else {
|
||||
_region_free_list_remove(region, alloc.free_idx)
|
||||
}
|
||||
|
||||
alloc.free_idx = NOT_FREE
|
||||
return mem.ptr_offset(alloc, 1), _get_block_count(alloc^)
|
||||
}
|
||||
|
||||
_region_segment :: proc(region: ^Region, alloc: ^Allocation_Header, blocks, new_free_idx: u16) #no_bounds_check {
|
||||
old_next := alloc.next
|
||||
alloc.next = alloc.idx + blocks + 1
|
||||
region.memory[old_next].prev = alloc.next
|
||||
|
||||
// Initialize alloc.next allocation header here.
|
||||
region.memory[alloc.next].prev = alloc.idx
|
||||
region.memory[alloc.next].next = old_next
|
||||
region.memory[alloc.next].idx = alloc.next
|
||||
region.memory[alloc.next].free_idx = new_free_idx
|
||||
|
||||
// Replace our original spot in the free_list with new segment.
|
||||
region.hdr.free_list[new_free_idx] = alloc.next
|
||||
}
|
||||
|
||||
_region_get_local_idx :: proc() -> int {
|
||||
idx: int
|
||||
for r := global_regions; r != nil; r = r.hdr.next_region {
|
||||
if r == _local_region {
|
||||
return idx
|
||||
}
|
||||
idx += 1
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
_region_find_and_assign_local :: proc(alloc: ^Allocation_Header) {
|
||||
// Find the region that contains this memory
|
||||
if !_region_contains_mem(_local_region, alloc) {
|
||||
_local_region = _region_retrieve_from_addr(alloc)
|
||||
}
|
||||
|
||||
// At this point, _local_region is set correctly. Spin until acquired
|
||||
res: ^^Region
|
||||
for res != &_local_region {
|
||||
res = sync.atomic_compare_exchange_strong_explicit(
|
||||
&_local_region.hdr.local_addr,
|
||||
&_local_region,
|
||||
CURRENTLY_ACTIVE,
|
||||
.Acquire,
|
||||
.Relaxed,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
_region_contains_mem :: proc(r: ^Region, memory: rawptr) -> bool #no_bounds_check {
|
||||
if r == nil {
|
||||
return false
|
||||
}
|
||||
mem_int := uintptr(memory)
|
||||
return mem_int >= uintptr(&r.memory[0]) && mem_int <= uintptr(&r.memory[BLOCKS_PER_REGION - 1])
|
||||
}
|
||||
|
||||
_region_free_list_remove :: proc(region: ^Region, free_idx: u16) #no_bounds_check {
|
||||
// pop, swap and update allocation hdr
|
||||
if n := region.hdr.free_list_len - 1; free_idx != n {
|
||||
region.hdr.free_list[free_idx] = region.hdr.free_list[n]
|
||||
alloc_idx := region.hdr.free_list[free_idx]
|
||||
region.memory[alloc_idx].free_idx = free_idx
|
||||
}
|
||||
region.hdr.free_list_len -= 1
|
||||
}
|
||||
|
||||
//
|
||||
// Direct mmap
|
||||
//
|
||||
_direct_mmap_alloc :: proc(size: int) -> rawptr {
|
||||
mmap_size := _round_up_to_nearest(size + BLOCK_SIZE, PAGE_SIZE)
|
||||
new_allocation := unix.sys_mmap(nil, uint(mmap_size), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
||||
if new_allocation < 0 && new_allocation > -4096 {
|
||||
return nil
|
||||
}
|
||||
|
||||
alloc := (^Allocation_Header)(uintptr(new_allocation))
|
||||
alloc.requested = u64(size) // NOTE: requested = requested size
|
||||
alloc.requested += IS_DIRECT_MMAP
|
||||
return rawptr(mem.ptr_offset(alloc, 1))
|
||||
}
|
||||
|
||||
_direct_mmap_resize :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr {
|
||||
old_requested := int(alloc.requested & REQUESTED_MASK)
|
||||
old_mmap_size := _round_up_to_nearest(old_requested + BLOCK_SIZE, PAGE_SIZE)
|
||||
new_mmap_size := _round_up_to_nearest(new_size + BLOCK_SIZE, PAGE_SIZE)
|
||||
if int(new_mmap_size) < MMAP_TO_REGION_SHRINK_THRESHOLD {
|
||||
return _direct_mmap_to_region(alloc, new_size)
|
||||
} else if old_requested == new_size {
|
||||
return mem.ptr_offset(alloc, 1)
|
||||
}
|
||||
|
||||
new_allocation := unix.sys_mremap(
|
||||
alloc,
|
||||
uint(old_mmap_size),
|
||||
uint(new_mmap_size),
|
||||
unix.MREMAP_MAYMOVE,
|
||||
)
|
||||
if new_allocation < 0 && new_allocation > -4096 {
|
||||
return nil
|
||||
}
|
||||
|
||||
new_header := (^Allocation_Header)(uintptr(new_allocation))
|
||||
new_header.requested = u64(new_size)
|
||||
new_header.requested += IS_DIRECT_MMAP
|
||||
|
||||
if new_mmap_size > old_mmap_size {
|
||||
// new section may not be pointer aligned, so cast to ^u8
|
||||
new_section := mem.ptr_offset((^u8)(new_header), old_requested + BLOCK_SIZE)
|
||||
mem.zero(new_section, new_mmap_size - old_mmap_size)
|
||||
}
|
||||
return mem.ptr_offset(new_header, 1)
|
||||
|
||||
}
|
||||
|
||||
_direct_mmap_from_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr {
|
||||
new_memory := _direct_mmap_alloc(new_size)
|
||||
if new_memory != nil {
|
||||
old_memory := mem.ptr_offset(alloc, 1)
|
||||
mem.copy(new_memory, old_memory, int(_get_block_count(alloc^)) * BLOCK_SIZE)
|
||||
}
|
||||
_region_find_and_assign_local(alloc)
|
||||
_region_local_free(alloc)
|
||||
sync.atomic_store_explicit(&_local_region.hdr.local_addr, &_local_region, .Release)
|
||||
return new_memory
|
||||
}
|
||||
|
||||
_direct_mmap_to_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr {
|
||||
new_memory := heap_alloc(new_size)
|
||||
if new_memory != nil {
|
||||
mem.copy(new_memory, mem.ptr_offset(alloc, -1), new_size)
|
||||
_direct_mmap_free(alloc)
|
||||
}
|
||||
return new_memory
|
||||
}
|
||||
|
||||
_direct_mmap_free :: proc(alloc: ^Allocation_Header) {
|
||||
requested := int(alloc.requested & REQUESTED_MASK)
|
||||
mmap_size := _round_up_to_nearest(requested + BLOCK_SIZE, PAGE_SIZE)
|
||||
unix.sys_munmap(alloc, uint(mmap_size))
|
||||
}
|
||||
|
||||
//
|
||||
// Util
|
||||
//
|
||||
|
||||
_get_block_count :: #force_inline proc(alloc: Allocation_Header) -> u16 {
|
||||
return alloc.next - alloc.idx - 1
|
||||
}
|
||||
|
||||
_get_allocation_header :: #force_inline proc(raw_mem: rawptr) -> ^Allocation_Header {
|
||||
return mem.ptr_offset((^Allocation_Header)(raw_mem), -1)
|
||||
}
|
||||
|
||||
_round_up_to_nearest :: #force_inline proc(size, round: int) -> int {
|
||||
return (size-1) + round - (size-1) % round
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
Path_Separator :: _Path_Separator // OS-Specific
|
||||
Path_List_Separator :: _Path_List_Separator // OS-Specific
|
||||
|
||||
@@ -21,7 +23,7 @@ remove_all :: proc(path: string) -> Error {
|
||||
|
||||
|
||||
|
||||
getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) {
|
||||
getwd :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
return _getwd(allocator)
|
||||
}
|
||||
setwd :: proc(dir: string) -> (err: Error) {
|
||||
|
||||
247
core/os/os2/path_linux.odin
Normal file
247
core/os/os2/path_linux.odin
Normal file
@@ -0,0 +1,247 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:strings"
|
||||
import "core:strconv"
|
||||
import "core:runtime"
|
||||
import "core:sys/unix"
|
||||
|
||||
_Path_Separator :: '/'
|
||||
_Path_List_Separator :: ':'
|
||||
|
||||
_S_IFMT :: 0o170000 // Type of file mask
|
||||
_S_IFIFO :: 0o010000 // Named pipe (fifo)
|
||||
_S_IFCHR :: 0o020000 // Character special
|
||||
_S_IFDIR :: 0o040000 // Directory
|
||||
_S_IFBLK :: 0o060000 // Block special
|
||||
_S_IFREG :: 0o100000 // Regular
|
||||
_S_IFLNK :: 0o120000 // Symbolic link
|
||||
_S_IFSOCK :: 0o140000 // Socket
|
||||
|
||||
_OPENDIR_FLAGS :: _O_RDONLY|_O_NONBLOCK|_O_DIRECTORY|_O_LARGEFILE|_O_CLOEXEC
|
||||
|
||||
_is_path_separator :: proc(c: byte) -> bool {
|
||||
return c == '/'
|
||||
}
|
||||
|
||||
_mkdir :: proc(path: string, perm: File_Mode) -> Error {
|
||||
// NOTE: These modes would require sys_mknod, however, that would require
|
||||
// additional arguments to this function.
|
||||
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
|
||||
return .Invalid_Argument
|
||||
}
|
||||
|
||||
path_cstr, allocated := _name_to_cstring(path)
|
||||
defer if allocated {
|
||||
delete(path_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_mkdir(path_cstr, int(perm & 0o777)))
|
||||
}
|
||||
|
||||
_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
|
||||
_mkdirat :: proc(dfd: int, path: []u8, perm: int, has_created: ^bool) -> Error {
|
||||
if len(path) == 0 {
|
||||
return _ok_or_error(unix.sys_close(dfd))
|
||||
}
|
||||
i: int
|
||||
for /**/; i < len(path) - 1 && path[i] != '/'; i += 1 {}
|
||||
path[i] = 0
|
||||
new_dfd := unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
|
||||
switch new_dfd {
|
||||
case -ENOENT:
|
||||
if res := unix.sys_mkdirat(dfd, cstring(&path[0]), perm); res < 0 {
|
||||
return _get_platform_error(res)
|
||||
}
|
||||
has_created^ = true
|
||||
if new_dfd = unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); new_dfd < 0 {
|
||||
return _get_platform_error(new_dfd)
|
||||
}
|
||||
fallthrough
|
||||
case 0:
|
||||
if res := unix.sys_close(dfd); res < 0 {
|
||||
return _get_platform_error(res)
|
||||
}
|
||||
// skip consecutive '/'
|
||||
for i += 1; i < len(path) && path[i] == '/'; i += 1 {}
|
||||
return _mkdirat(new_dfd, path[i:], perm, has_created)
|
||||
case:
|
||||
return _get_platform_error(new_dfd)
|
||||
}
|
||||
unreachable()
|
||||
}
|
||||
|
||||
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
|
||||
return .Invalid_Argument
|
||||
}
|
||||
|
||||
// need something we can edit, and use to generate cstrings
|
||||
allocated: bool
|
||||
path_bytes: []u8
|
||||
if len(path) > _CSTRING_NAME_HEAP_THRESHOLD {
|
||||
allocated = true
|
||||
path_bytes = make([]u8, len(path) + 1)
|
||||
} else {
|
||||
path_bytes = make([]u8, len(path) + 1, context.temp_allocator)
|
||||
}
|
||||
defer if allocated {
|
||||
delete(path_bytes)
|
||||
}
|
||||
|
||||
// NULL terminate the byte slice to make it a valid cstring
|
||||
copy(path_bytes, path)
|
||||
path_bytes[len(path)] = 0
|
||||
|
||||
dfd: int
|
||||
if path_bytes[0] == '/' {
|
||||
dfd = unix.sys_open("/", _OPENDIR_FLAGS)
|
||||
path_bytes = path_bytes[1:]
|
||||
} else {
|
||||
dfd = unix.sys_open(".", _OPENDIR_FLAGS)
|
||||
}
|
||||
if dfd < 0 {
|
||||
return _get_platform_error(dfd)
|
||||
}
|
||||
|
||||
has_created: bool
|
||||
_mkdirat(dfd, path_bytes, int(perm & 0o777), &has_created) or_return
|
||||
if has_created {
|
||||
return nil
|
||||
}
|
||||
return .Exist
|
||||
//return has_created ? nil : .Exist
|
||||
}
|
||||
|
||||
dirent64 :: struct {
|
||||
d_ino: u64,
|
||||
d_off: u64,
|
||||
d_reclen: u16,
|
||||
d_type: u8,
|
||||
d_name: [1]u8,
|
||||
}
|
||||
|
||||
_remove_all :: proc(path: string) -> Error {
|
||||
DT_DIR :: 4
|
||||
|
||||
_remove_all_dir :: proc(dfd: int) -> Error {
|
||||
n := 64
|
||||
buf := make([]u8, n)
|
||||
defer delete(buf)
|
||||
|
||||
loop: for {
|
||||
getdents_res := unix.sys_getdents64(dfd, &buf[0], n)
|
||||
switch getdents_res {
|
||||
case -EINVAL:
|
||||
delete(buf)
|
||||
n *= 2
|
||||
buf = make([]u8, n)
|
||||
continue loop
|
||||
case -4096..<0:
|
||||
return _get_platform_error(getdents_res)
|
||||
case 0:
|
||||
break loop
|
||||
}
|
||||
|
||||
d: ^dirent64
|
||||
|
||||
for i := 0; i < getdents_res; i += int(d.d_reclen) {
|
||||
d = (^dirent64)(rawptr(&buf[i]))
|
||||
d_name_cstr := cstring(&d.d_name[0])
|
||||
|
||||
buf_len := uintptr(d.d_reclen) - offset_of(d.d_name)
|
||||
|
||||
/* check for current directory (.) */
|
||||
#no_bounds_check if buf_len > 1 && d.d_name[0] == '.' && d.d_name[1] == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
/* check for parent directory (..) */
|
||||
#no_bounds_check if buf_len > 2 && d.d_name[0] == '.' && d.d_name[1] == '.' && d.d_name[2] == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
unlink_res: int
|
||||
|
||||
switch d.d_type {
|
||||
case DT_DIR:
|
||||
new_dfd := unix.sys_openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
|
||||
if new_dfd < 0 {
|
||||
return _get_platform_error(new_dfd)
|
||||
}
|
||||
defer unix.sys_close(new_dfd)
|
||||
_remove_all_dir(new_dfd) or_return
|
||||
unlink_res = unix.sys_unlinkat(dfd, d_name_cstr, int(unix.AT_REMOVEDIR))
|
||||
case:
|
||||
unlink_res = unix.sys_unlinkat(dfd, d_name_cstr)
|
||||
}
|
||||
|
||||
if unlink_res < 0 {
|
||||
return _get_platform_error(unlink_res)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
path_cstr, allocated := _name_to_cstring(path)
|
||||
defer if allocated {
|
||||
delete(path_cstr)
|
||||
}
|
||||
|
||||
fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS)
|
||||
switch fd {
|
||||
case -ENOTDIR:
|
||||
return _ok_or_error(unix.sys_unlink(path_cstr))
|
||||
case -4096..<0:
|
||||
return _get_platform_error(fd)
|
||||
}
|
||||
|
||||
defer unix.sys_close(fd)
|
||||
_remove_all_dir(fd) or_return
|
||||
return _ok_or_error(unix.sys_rmdir(path_cstr))
|
||||
}
|
||||
|
||||
_getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
||||
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
|
||||
// an authoritative value for it across all systems.
|
||||
// The largest value I could find was 4096, so might as well use the page size.
|
||||
// NOTE(jason): Avoiding libc, so just use 4096 directly
|
||||
PATH_MAX :: 4096
|
||||
buf := make([dynamic]u8, PATH_MAX, allocator)
|
||||
for {
|
||||
#no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
|
||||
|
||||
if res >= 0 {
|
||||
return strings.string_from_nul_terminated_ptr(&buf[0], len(buf)), nil
|
||||
}
|
||||
if res != -ERANGE {
|
||||
return "", _get_platform_error(res)
|
||||
}
|
||||
resize(&buf, len(buf)+PATH_MAX)
|
||||
}
|
||||
unreachable()
|
||||
}
|
||||
|
||||
_setwd :: proc(dir: string) -> Error {
|
||||
dir_cstr, allocated := _name_to_cstring(dir)
|
||||
defer if allocated {
|
||||
delete(dir_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_chdir(dir_cstr))
|
||||
}
|
||||
|
||||
_get_full_path :: proc(fd: int, allocator := context.allocator) -> string {
|
||||
PROC_FD_PATH :: "/proc/self/fd/"
|
||||
|
||||
buf: [32]u8
|
||||
copy(buf[:], PROC_FD_PATH)
|
||||
|
||||
strconv.itoa(buf[len(PROC_FD_PATH):], fd)
|
||||
|
||||
fullpath: string
|
||||
err: Error
|
||||
if fullpath, err = _read_link_cstr(cstring(&buf[0]), allocator); err != nil || fullpath[0] != '/' {
|
||||
return ""
|
||||
}
|
||||
return fullpath
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
package os2
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:runtime"
|
||||
import "core:strings"
|
||||
|
||||
_Path_Separator :: '\\'
|
||||
_Path_List_Separator :: ';'
|
||||
@@ -11,11 +13,58 @@ _is_path_separator :: proc(c: byte) -> bool {
|
||||
}
|
||||
|
||||
_mkdir :: proc(name: string, perm: File_Mode) -> Error {
|
||||
if !win32.CreateDirectoryW(_fix_long_path(name), nil) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
|
||||
// TODO(bill): _mkdir_all for windows
|
||||
fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) {
|
||||
if len(p) == len(`\\?\c:`) {
|
||||
if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' {
|
||||
s = strings.concatenate_safe({p, `\`}, _file_allocator()) or_return
|
||||
allocated = true
|
||||
return
|
||||
}
|
||||
}
|
||||
return p, false, nil
|
||||
}
|
||||
|
||||
dir, err := stat(path, _temp_allocator())
|
||||
if err == nil {
|
||||
if dir.is_dir {
|
||||
return nil
|
||||
}
|
||||
return .Exist
|
||||
}
|
||||
|
||||
i := len(path)
|
||||
for i > 0 && is_path_separator(path[i-1]) {
|
||||
i -= 1
|
||||
}
|
||||
|
||||
j := i
|
||||
for j > 0 && !is_path_separator(path[j-1]) {
|
||||
j -= 1
|
||||
}
|
||||
|
||||
if j > 1 {
|
||||
new_path, allocated := fix_root_directory(path[:j-1]) or_return
|
||||
defer if allocated {
|
||||
delete(new_path, _file_allocator())
|
||||
}
|
||||
mkdir_all(new_path, perm) or_return
|
||||
}
|
||||
|
||||
err = mkdir(path, perm)
|
||||
if err != nil {
|
||||
dir1, err1 := lstat(path, _temp_allocator())
|
||||
if err1 == nil && dir1.is_dir {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -24,11 +73,13 @@ _remove_all :: proc(path: string) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
_getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) {
|
||||
_getwd :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
// TODO(bill)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
_setwd :: proc(dir: string) -> (err: Error) {
|
||||
// TODO(bill)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -75,7 +126,7 @@ _fix_long_path_internal :: proc(path: string) -> string {
|
||||
}
|
||||
|
||||
PREFIX :: `\\?`
|
||||
path_buf := make([]byte, len(PREFIX)+len(path)+1, context.temp_allocator)
|
||||
path_buf := make([]byte, len(PREFIX)+len(path)+1, _temp_allocator())
|
||||
copy(path_buf, PREFIX)
|
||||
n := len(path)
|
||||
r, w := 0, len(PREFIX)
|
||||
|
||||
7
core/os/os2/pipe_linux.odin
Normal file
7
core/os/os2/pipe_linux.odin
Normal file
@@ -0,0 +1,7 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
_pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import win32 "core:sys/windows"
|
||||
_pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
p: [2]win32.HANDLE
|
||||
if !win32.CreatePipe(&p[0], &p[1], nil, 0) {
|
||||
return nil, nil, Platform_Error{i32(win32.GetLastError())}
|
||||
return nil, nil, _get_platform_error()
|
||||
}
|
||||
return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package os2
|
||||
|
||||
import "core:time"
|
||||
import "core:runtime"
|
||||
|
||||
File_Info :: struct {
|
||||
fullpath: string,
|
||||
@@ -13,26 +14,26 @@ File_Info :: struct {
|
||||
access_time: time.Time,
|
||||
}
|
||||
|
||||
file_info_slice_delete :: proc(infos: []File_Info, allocator := context.allocator) {
|
||||
file_info_slice_delete :: proc(infos: []File_Info, allocator: runtime.Allocator) {
|
||||
for i := len(infos)-1; i >= 0; i -= 1 {
|
||||
file_info_delete(infos[i], allocator)
|
||||
}
|
||||
delete(infos, allocator)
|
||||
}
|
||||
|
||||
file_info_delete :: proc(fi: File_Info, allocator := context.allocator) {
|
||||
file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) {
|
||||
delete(fi.fullpath, allocator)
|
||||
}
|
||||
|
||||
fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) {
|
||||
fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
return _fstat(f, allocator)
|
||||
}
|
||||
|
||||
stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
|
||||
stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
return _stat(name, allocator)
|
||||
}
|
||||
|
||||
lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
|
||||
lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
return _lstat(name, allocator)
|
||||
}
|
||||
|
||||
|
||||
152
core/os/os2/stat_linux.odin
Normal file
152
core/os/os2/stat_linux.odin
Normal file
@@ -0,0 +1,152 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:time"
|
||||
import "core:runtime"
|
||||
import "core:sys/unix"
|
||||
import "core:path/filepath"
|
||||
|
||||
// File type
|
||||
S_IFMT :: 0o170000 // Type of file mask
|
||||
S_IFIFO :: 0o010000 // Named pipe (fifo)
|
||||
S_IFCHR :: 0o020000 // Character special
|
||||
S_IFDIR :: 0o040000 // Directory
|
||||
S_IFBLK :: 0o060000 // Block special
|
||||
S_IFREG :: 0o100000 // Regular
|
||||
S_IFLNK :: 0o120000 // Symbolic link
|
||||
S_IFSOCK :: 0o140000 // Socket
|
||||
|
||||
// File mode
|
||||
// Read, write, execute/search by owner
|
||||
S_IRWXU :: 0o0700 // RWX mask for owner
|
||||
S_IRUSR :: 0o0400 // R for owner
|
||||
S_IWUSR :: 0o0200 // W for owner
|
||||
S_IXUSR :: 0o0100 // X for owner
|
||||
|
||||
// Read, write, execute/search by group
|
||||
S_IRWXG :: 0o0070 // RWX mask for group
|
||||
S_IRGRP :: 0o0040 // R for group
|
||||
S_IWGRP :: 0o0020 // W for group
|
||||
S_IXGRP :: 0o0010 // X for group
|
||||
|
||||
// Read, write, execute/search by others
|
||||
S_IRWXO :: 0o0007 // RWX mask for other
|
||||
S_IROTH :: 0o0004 // R for other
|
||||
S_IWOTH :: 0o0002 // W for other
|
||||
S_IXOTH :: 0o0001 // X for other
|
||||
|
||||
S_ISUID :: 0o4000 // Set user id on execution
|
||||
S_ISGID :: 0o2000 // Set group id on execution
|
||||
S_ISVTX :: 0o1000 // Directory restrcted delete
|
||||
|
||||
|
||||
S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
|
||||
S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
|
||||
S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
|
||||
S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
|
||||
S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
|
||||
S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
|
||||
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
|
||||
|
||||
F_OK :: 0 // Test for file existance
|
||||
X_OK :: 1 // Test for execute permission
|
||||
W_OK :: 2 // Test for write permission
|
||||
R_OK :: 4 // Test for read permission
|
||||
|
||||
@private
|
||||
Unix_File_Time :: struct {
|
||||
seconds: i64,
|
||||
nanoseconds: i64,
|
||||
}
|
||||
|
||||
@private
|
||||
_Stat :: struct {
|
||||
device_id: u64, // ID of device containing file
|
||||
serial: u64, // File serial number
|
||||
nlink: u64, // Number of hard links
|
||||
mode: u32, // Mode of the file
|
||||
uid: u32, // User ID of the file's owner
|
||||
gid: u32, // Group ID of the file's group
|
||||
_padding: i32, // 32 bits of padding
|
||||
rdev: u64, // Device ID, if device
|
||||
size: i64, // Size of the file, in bytes
|
||||
block_size: i64, // Optimal bllocksize for I/O
|
||||
blocks: i64, // Number of 512-byte blocks allocated
|
||||
|
||||
last_access: Unix_File_Time, // Time of last access
|
||||
modified: Unix_File_Time, // Time of last modification
|
||||
status_change: Unix_File_Time, // Time of last status change
|
||||
|
||||
_reserve1,
|
||||
_reserve2,
|
||||
_reserve3: i64,
|
||||
}
|
||||
|
||||
|
||||
_fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) {
|
||||
return _fstat_internal(f.impl.fd, allocator)
|
||||
}
|
||||
|
||||
_fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
s: _Stat
|
||||
result := unix.sys_fstat(fd, &s)
|
||||
if result < 0 {
|
||||
return {}, _get_platform_error(result)
|
||||
}
|
||||
|
||||
// TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time
|
||||
fi := File_Info {
|
||||
fullpath = _get_full_path(fd, allocator),
|
||||
name = "",
|
||||
size = s.size,
|
||||
mode = 0,
|
||||
is_dir = S_ISDIR(s.mode),
|
||||
modification_time = time.Time {s.modified.seconds},
|
||||
access_time = time.Time {s.last_access.seconds},
|
||||
creation_time = time.Time{0}, // regular stat does not provide this
|
||||
}
|
||||
|
||||
fi.name = filepath.base(fi.fullpath)
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
// NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath
|
||||
_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
|
||||
fd := unix.sys_open(name_cstr, _O_RDONLY)
|
||||
if fd < 0 {
|
||||
return {}, _get_platform_error(fd)
|
||||
}
|
||||
defer unix.sys_close(fd)
|
||||
return _fstat_internal(fd, allocator)
|
||||
}
|
||||
|
||||
_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
fd := unix.sys_open(name_cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW)
|
||||
if fd < 0 {
|
||||
return {}, _get_platform_error(fd)
|
||||
}
|
||||
defer unix.sys_close(fd)
|
||||
return _fstat_internal(fd, allocator)
|
||||
}
|
||||
|
||||
_same_file :: proc(fi1, fi2: File_Info) -> bool {
|
||||
return fi1.fullpath == fi2.fullpath
|
||||
}
|
||||
|
||||
_stat_internal :: proc(name: string) -> (s: _Stat, res: int) {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
res = unix.sys_stat(name_cstr, &s)
|
||||
return
|
||||
}
|
||||
@@ -1,22 +1,22 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
import "core:time"
|
||||
import "core:strings"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
_fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) {
|
||||
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
if f == nil || f.impl.fd == nil {
|
||||
return {}, .Invalid_Argument
|
||||
return {}, nil
|
||||
}
|
||||
context.allocator = allocator
|
||||
|
||||
path, err := _cleanpath_from_handle(f)
|
||||
path, err := _cleanpath_from_handle(f, allocator)
|
||||
if err != nil {
|
||||
return {}, err
|
||||
}
|
||||
|
||||
h := win32.HANDLE(f.impl.fd)
|
||||
h := _handle(f)
|
||||
switch win32.GetFileType(h) {
|
||||
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
|
||||
fi: File_Info
|
||||
@@ -26,13 +26,13 @@ _fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) {
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
return _file_info_from_get_file_information_by_handle(path, h)
|
||||
return _file_info_from_get_file_information_by_handle(path, h, allocator)
|
||||
}
|
||||
_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
|
||||
return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS)
|
||||
_stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS, allocator)
|
||||
}
|
||||
_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
|
||||
return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT)
|
||||
_lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT, allocator)
|
||||
}
|
||||
_same_file :: proc(fi1, fi2: File_Info) -> bool {
|
||||
return fi1.fullpath == fi2.fullpath
|
||||
@@ -40,50 +40,38 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool {
|
||||
|
||||
|
||||
|
||||
_stat_errno :: proc(errno: win32.DWORD) -> Error {
|
||||
return Platform_Error{i32(errno)}
|
||||
}
|
||||
|
||||
|
||||
full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) {
|
||||
name := name
|
||||
if name == "" {
|
||||
name = "."
|
||||
}
|
||||
p := win32.utf8_to_utf16(name, context.temp_allocator)
|
||||
buf := make([dynamic]u16, 100)
|
||||
for {
|
||||
n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
|
||||
if n == 0 {
|
||||
delete(buf)
|
||||
return "", _stat_errno(win32.GetLastError())
|
||||
}
|
||||
if n <= u32(len(buf)) {
|
||||
return win32.utf16_to_utf8(buf[:n]), nil
|
||||
}
|
||||
resize(&buf, len(buf)*2)
|
||||
}
|
||||
p := win32.utf8_to_utf16(name, _temp_allocator())
|
||||
|
||||
return
|
||||
n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil)
|
||||
if n == 0 {
|
||||
return "", _get_platform_error()
|
||||
}
|
||||
buf := make([]u16, n+1, _temp_allocator())
|
||||
n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
|
||||
if n == 0 {
|
||||
return "", _get_platform_error()
|
||||
}
|
||||
return win32.utf16_to_utf8(buf[:n], allocator)
|
||||
}
|
||||
|
||||
|
||||
internal_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Error) {
|
||||
internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) {
|
||||
if len(name) == 0 {
|
||||
return {}, .Not_Exist
|
||||
}
|
||||
|
||||
context.allocator = allocator
|
||||
|
||||
|
||||
wname := _fix_long_path(name)
|
||||
fa: win32.WIN32_FILE_ATTRIBUTE_DATA
|
||||
ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa)
|
||||
if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||
// Not a symlink
|
||||
return _file_info_from_win32_file_attribute_data(&fa, name)
|
||||
return _file_info_from_win32_file_attribute_data(&fa, name, allocator)
|
||||
}
|
||||
|
||||
err := 0 if ok else win32.GetLastError()
|
||||
@@ -97,7 +85,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co
|
||||
}
|
||||
win32.FindClose(sh)
|
||||
|
||||
return _file_info_from_win32_find_data(&fd, name)
|
||||
return _file_info_from_win32_find_data(&fd, name, allocator)
|
||||
}
|
||||
|
||||
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
|
||||
@@ -106,7 +94,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co
|
||||
return
|
||||
}
|
||||
defer win32.CloseHandle(h)
|
||||
return _file_info_from_get_file_information_by_handle(name, h)
|
||||
return _file_info_from_get_file_information_by_handle(name, h, allocator)
|
||||
}
|
||||
|
||||
|
||||
@@ -131,56 +119,40 @@ _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
|
||||
}
|
||||
|
||||
|
||||
_cleanpath_from_handle :: proc(f: ^File) -> (string, Error) {
|
||||
_cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) {
|
||||
if f == nil || f.impl.fd == nil {
|
||||
return "", .Invalid_Argument
|
||||
return "", nil
|
||||
}
|
||||
h := win32.HANDLE(f.impl.fd)
|
||||
h := _handle(f)
|
||||
|
||||
MAX_PATH := win32.DWORD(260) + 1
|
||||
buf: []u16
|
||||
for {
|
||||
buf = make([]u16, MAX_PATH, context.temp_allocator)
|
||||
err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0)
|
||||
switch err {
|
||||
case win32.ERROR_PATH_NOT_FOUND, win32.ERROR_INVALID_PARAMETER:
|
||||
return "", _stat_errno(err)
|
||||
case win32.ERROR_NOT_ENOUGH_MEMORY:
|
||||
MAX_PATH = MAX_PATH*2 + 1
|
||||
continue
|
||||
}
|
||||
break
|
||||
n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0)
|
||||
if n == 0 {
|
||||
return "", _get_platform_error()
|
||||
}
|
||||
return _cleanpath_from_buf(buf), nil
|
||||
buf := make([]u16, max(n, 260)+1, _temp_allocator())
|
||||
n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
|
||||
return _cleanpath_from_buf(buf[:n], allocator)
|
||||
}
|
||||
|
||||
_cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
|
||||
if f == nil || f.impl.fd == nil {
|
||||
return nil, .Invalid_Argument
|
||||
return nil, nil
|
||||
}
|
||||
h := win32.HANDLE(f.impl.fd)
|
||||
h := _handle(f)
|
||||
|
||||
MAX_PATH := win32.DWORD(260) + 1
|
||||
buf: []u16
|
||||
for {
|
||||
buf = make([]u16, MAX_PATH, context.temp_allocator)
|
||||
err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0)
|
||||
switch err {
|
||||
case win32.ERROR_PATH_NOT_FOUND, win32.ERROR_INVALID_PARAMETER:
|
||||
return nil, _stat_errno(err)
|
||||
case win32.ERROR_NOT_ENOUGH_MEMORY:
|
||||
MAX_PATH = MAX_PATH*2 + 1
|
||||
continue
|
||||
}
|
||||
break
|
||||
n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0)
|
||||
if n == 0 {
|
||||
return nil, _get_platform_error()
|
||||
}
|
||||
return _cleanpath_strip_prefix(buf), nil
|
||||
buf := make([]u16, max(n, 260)+1, _temp_allocator())
|
||||
n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
|
||||
return _cleanpath_strip_prefix(buf[:n]), nil
|
||||
}
|
||||
|
||||
_cleanpath_from_buf :: proc(buf: []u16) -> string {
|
||||
_cleanpath_from_buf :: proc(buf: []u16, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
|
||||
buf := buf
|
||||
buf = _cleanpath_strip_prefix(buf)
|
||||
return win32.utf16_to_utf8(buf, context.allocator)
|
||||
return win32.utf16_to_utf8(buf, allocator)
|
||||
}
|
||||
|
||||
|
||||
@@ -222,15 +194,15 @@ file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
|
||||
|
||||
|
||||
|
||||
_file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) {
|
||||
if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
_file_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) {
|
||||
if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
mode |= 0o444
|
||||
} else {
|
||||
mode |= 0o666
|
||||
}
|
||||
|
||||
is_sym := false
|
||||
if FileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
|
||||
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
|
||||
@@ -239,7 +211,7 @@ _file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HA
|
||||
if is_sym {
|
||||
mode |= File_Mode_Sym_Link
|
||||
} else {
|
||||
if FileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
mode |= 0o111 | File_Mode_Dir
|
||||
}
|
||||
|
||||
@@ -252,7 +224,7 @@ _file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HA
|
||||
}
|
||||
|
||||
|
||||
_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Error) {
|
||||
_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) {
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
|
||||
fi.mode |= _file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||
@@ -262,14 +234,14 @@ _file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE
|
||||
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))
|
||||
|
||||
fi.fullpath, e = full_path_from_name(name)
|
||||
fi.fullpath, e = full_path_from_name(name, allocator)
|
||||
fi.name = basename(fi.fullpath)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Error) {
|
||||
_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) {
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
|
||||
fi.mode |= _file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||
@@ -279,17 +251,17 @@ _file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string
|
||||
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))
|
||||
|
||||
fi.fullpath, e = full_path_from_name(name)
|
||||
fi.fullpath, e = full_path_from_name(name, allocator)
|
||||
fi.name = basename(fi.fullpath)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Error) {
|
||||
_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
d: win32.BY_HANDLE_FILE_INFORMATION
|
||||
if !win32.GetFileInformationByHandle(h, &d) {
|
||||
return {}, _stat_errno(win32.GetLastError())
|
||||
return {}, _get_platform_error()
|
||||
|
||||
}
|
||||
|
||||
@@ -297,7 +269,7 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA
|
||||
if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) {
|
||||
err := win32.GetLastError()
|
||||
if err != win32.ERROR_INVALID_PARAMETER {
|
||||
return {}, _stat_errno(err)
|
||||
return {}, Platform_Error(err)
|
||||
}
|
||||
// Indicate this is a symlink on FAT file systems
|
||||
ti.ReparseTag = 0
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
create_temp :: proc(dir, pattern: string) -> (^File, Error) {
|
||||
return _create_temp(dir, pattern)
|
||||
}
|
||||
|
||||
mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) {
|
||||
return _mkdir_temp(dir, pattern)
|
||||
mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) {
|
||||
return _mkdir_temp(dir, pattern, allocator)
|
||||
}
|
||||
|
||||
temp_dir :: proc(allocator := context.allocator) -> string {
|
||||
temp_dir :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
||||
return _temp_dir(allocator)
|
||||
}
|
||||
|
||||
20
core/os/os2/temp_file_linux.odin
Normal file
20
core/os/os2/temp_file_linux.odin
Normal file
@@ -0,0 +1,20 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
|
||||
_create_temp :: proc(dir, pattern: string) -> (^File, Error) {
|
||||
//TODO
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
_mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) {
|
||||
//TODO
|
||||
return "", nil
|
||||
}
|
||||
|
||||
_temp_dir :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
||||
//TODO
|
||||
return "", nil
|
||||
}
|
||||
@@ -1,29 +1,29 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
_create_temp :: proc(dir, pattern: string) -> (^File, Error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
_mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) {
|
||||
_mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
_temp_dir :: proc(allocator := context.allocator) -> string {
|
||||
b := make([dynamic]u16, u32(win32.MAX_PATH), context.temp_allocator)
|
||||
for {
|
||||
n := win32.GetTempPathW(u32(len(b)), raw_data(b))
|
||||
if n > u32(len(b)) {
|
||||
resize(&b, int(n))
|
||||
continue
|
||||
}
|
||||
if n == 3 && b[1] == ':' && b[2] == '\\' {
|
||||
|
||||
} else if n > 0 && b[n-1] == '\\' {
|
||||
n -= 1
|
||||
}
|
||||
return win32.utf16_to_utf8(b[:n], allocator)
|
||||
_temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
|
||||
n := win32.GetTempPathW(0, nil)
|
||||
if n == 0 {
|
||||
return "", nil
|
||||
}
|
||||
b := make([]u16, max(win32.MAX_PATH, n), _temp_allocator())
|
||||
n = win32.GetTempPathW(u32(len(b)), raw_data(b))
|
||||
|
||||
if n == 3 && b[1] == ':' && b[2] == '\\' {
|
||||
|
||||
} else if n > 0 && b[n-1] == '\\' {
|
||||
n -= 1
|
||||
}
|
||||
return win32.utf16_to_utf8(b[:n], allocator)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
package os2
|
||||
|
||||
import "core:strings"
|
||||
import "core:runtime"
|
||||
|
||||
user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) {
|
||||
user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
dir = get_env("LocalAppData")
|
||||
if dir != "" {
|
||||
dir = strings.clone(dir, allocator)
|
||||
dir = strings.clone_safe(dir, allocator) or_return
|
||||
}
|
||||
case .Darwin:
|
||||
dir = get_env("HOME")
|
||||
if dir != "" {
|
||||
dir = strings.concatenate({dir, "/Library/Caches"}, allocator)
|
||||
dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return
|
||||
}
|
||||
case: // All other UNIX systems
|
||||
dir = get_env("XDG_CACHE_HOME")
|
||||
@@ -21,24 +22,26 @@ user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defin
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
dir = strings.concatenate({dir, "/.cache"}, allocator)
|
||||
dir = strings.concatenate_safe({dir, "/.cache"}, allocator) or_return
|
||||
}
|
||||
}
|
||||
is_defined = dir != ""
|
||||
if dir == "" {
|
||||
err = .Invalid_Path
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) {
|
||||
user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
dir = get_env("AppData")
|
||||
if dir != "" {
|
||||
dir = strings.clone(dir, allocator)
|
||||
dir = strings.clone_safe(dir, allocator) or_return
|
||||
}
|
||||
case .Darwin:
|
||||
dir = get_env("HOME")
|
||||
if dir != "" {
|
||||
dir = strings.concatenate({dir, "/Library/Application Support"}, allocator)
|
||||
dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return
|
||||
}
|
||||
case: // All other UNIX systems
|
||||
dir = get_env("XDG_CACHE_HOME")
|
||||
@@ -47,22 +50,24 @@ user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defi
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
dir = strings.concatenate({dir, "/.config"}, allocator)
|
||||
dir = strings.concatenate_safe({dir, "/.config"}, allocator) or_return
|
||||
}
|
||||
}
|
||||
is_defined = dir != ""
|
||||
if dir == "" {
|
||||
err = .Invalid_Path
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
user_home_dir :: proc() -> (dir: string, is_defined: bool) {
|
||||
user_home_dir :: proc() -> (dir: string, err: Error) {
|
||||
env := "HOME"
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
env = "USERPROFILE"
|
||||
}
|
||||
if v := get_env(env); v != "" {
|
||||
return v, true
|
||||
return v, nil
|
||||
}
|
||||
return "", false
|
||||
return "", .Invalid_Path
|
||||
}
|
||||
|
||||
|
||||
@@ -276,7 +276,7 @@ foreign libc {
|
||||
@(link_name="__error") __error :: proc() -> ^int ---
|
||||
|
||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: i32, mode: u16) -> Handle ---
|
||||
@(link_name="close") _unix_close :: proc(handle: Handle) ---
|
||||
@(link_name="close") _unix_close :: proc(handle: Handle) -> c.int ---
|
||||
@(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---
|
||||
@(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---
|
||||
@(link_name="lseek") _unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int ---
|
||||
@@ -295,13 +295,13 @@ foreign libc {
|
||||
|
||||
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
|
||||
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
|
||||
|
||||
@(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, buf: ^byte) -> c.int ---
|
||||
|
||||
@(link_name="__fcntl") _unix__fcntl :: proc(fd: Handle, cmd: c.int, buf: ^byte) -> c.int ---
|
||||
|
||||
@(link_name="rename") _unix_rename :: proc(old: cstring, new: cstring) -> c.int ---
|
||||
@(link_name="remove") _unix_remove :: proc(path: cstring) -> c.int ---
|
||||
|
||||
@(link_name="fchmod") _unix_fchmod :: proc(fildes: Handle, mode: u16) -> c.int ---
|
||||
@(link_name="fchmod") _unix_fchmod :: proc(fd: Handle, mode: u16) -> c.int ---
|
||||
|
||||
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---
|
||||
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---
|
||||
@@ -361,12 +361,12 @@ when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
|
||||
return handle, 0
|
||||
}
|
||||
|
||||
fchmod :: proc(fildes: Handle, mode: u16) -> Errno {
|
||||
return cast(Errno)_unix_fchmod(fildes, mode)
|
||||
fchmod :: proc(fd: Handle, mode: u16) -> Errno {
|
||||
return cast(Errno)_unix_fchmod(fd, mode)
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
_unix_close(fd)
|
||||
close :: proc(fd: Handle) -> bool {
|
||||
return _unix_close(fd) == 0
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
@@ -473,16 +473,21 @@ is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
|
||||
is_file :: proc {is_file_path, is_file_handle}
|
||||
is_dir :: proc {is_dir_path, is_dir_handle}
|
||||
|
||||
exists :: proc(path: string) -> bool {
|
||||
cpath := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_access(cpath, O_RDONLY)
|
||||
return res == 0
|
||||
}
|
||||
|
||||
rename :: proc(old: string, new: string) -> bool {
|
||||
old_cstr := strings.clone_to_cstring(old, context.temp_allocator)
|
||||
new_cstr := strings.clone_to_cstring(new, context.temp_allocator)
|
||||
return _unix_rename(old_cstr, new_cstr) != -1
|
||||
return _unix_rename(old_cstr, new_cstr) != -1
|
||||
}
|
||||
|
||||
remove :: proc(path: string) -> bool {
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
return _unix_remove(path_cstr) != -1
|
||||
return _unix_remove(path_cstr) != -1
|
||||
}
|
||||
|
||||
@private
|
||||
@@ -546,7 +551,7 @@ _rewinddir :: proc(dirp: Dir) {
|
||||
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
|
||||
result: ^Dirent
|
||||
rc := _unix_readdir_r(dirp, &entry, &result)
|
||||
|
||||
|
||||
if rc != 0 {
|
||||
err = Errno(get_last_error())
|
||||
return
|
||||
@@ -586,7 +591,7 @@ _readlink :: proc(path: string) -> (string, Errno) {
|
||||
|
||||
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
|
||||
buf : [256]byte
|
||||
res := _unix_fcntl(fd, F_GETPATH, &buf[0])
|
||||
res := _unix__fcntl(fd, F_GETPATH, &buf[0])
|
||||
if res != 0 {
|
||||
return "", Errno(get_last_error())
|
||||
}
|
||||
@@ -633,13 +638,18 @@ heap_free :: proc(ptr: rawptr) {
|
||||
_unix_free(ptr)
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.clone_to_cstring(name, context.temp_allocator)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
path_str := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
cstr := _unix_getenv(path_str)
|
||||
if cstr == nil {
|
||||
return "", false
|
||||
}
|
||||
return string(cstr), true
|
||||
return strings.clone(string(cstr), allocator), true
|
||||
}
|
||||
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
get_current_directory :: proc() -> string {
|
||||
|
||||
@@ -618,13 +618,18 @@ heap_free :: proc(ptr: rawptr) {
|
||||
_unix_free(ptr)
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.clone_to_cstring(name, context.temp_allocator)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
path_str := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
cstr := _unix_getenv(path_str)
|
||||
if cstr == nil {
|
||||
return "", false
|
||||
}
|
||||
return string(cstr), true
|
||||
return strings.clone(string(cstr), allocator), true
|
||||
}
|
||||
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
get_current_directory :: proc() -> string {
|
||||
|
||||
@@ -415,6 +415,7 @@ foreign libc {
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
|
||||
|
||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
|
||||
@(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int ---
|
||||
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
|
||||
|
||||
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
|
||||
@@ -579,6 +580,11 @@ is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
|
||||
is_file :: proc {is_file_path, is_file_handle}
|
||||
is_dir :: proc {is_dir_path, is_dir_handle}
|
||||
|
||||
exists :: proc(path: string) -> bool {
|
||||
cpath := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_access(cpath, O_RDONLY)
|
||||
return res == 0
|
||||
}
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
|
||||
@@ -764,13 +770,37 @@ heap_free :: proc(ptr: rawptr) {
|
||||
_unix_free(ptr)
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.clone_to_cstring(name, context.temp_allocator)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
path_str := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
// NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
|
||||
cstr := _unix_getenv(path_str)
|
||||
if cstr == nil {
|
||||
return "", false
|
||||
}
|
||||
return string(cstr), true
|
||||
return strings.clone(string(cstr), allocator), true
|
||||
}
|
||||
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
set_env :: proc(key, value: string) -> Errno {
|
||||
s := strings.concatenate({key, "=", value, "\x00"}, context.temp_allocator)
|
||||
res := _unix_putenv(strings.unsafe_string_to_cstring(s))
|
||||
if res < 0 {
|
||||
return Errno(get_last_error())
|
||||
}
|
||||
return ERROR_NONE
|
||||
}
|
||||
|
||||
unset_env :: proc(key: string) -> Errno {
|
||||
s := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
res := _unix_putenv(s)
|
||||
if res < 0 {
|
||||
return Errno(get_last_error())
|
||||
}
|
||||
return ERROR_NONE
|
||||
}
|
||||
|
||||
get_current_directory :: proc() -> string {
|
||||
|
||||
@@ -620,13 +620,18 @@ heap_free :: proc(ptr: rawptr) {
|
||||
_unix_free(ptr)
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.clone_to_cstring(name, context.temp_allocator)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
path_str := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
cstr := _unix_getenv(path_str)
|
||||
if cstr == nil {
|
||||
return "", false
|
||||
}
|
||||
return string(cstr), true
|
||||
return strings.clone(string(cstr), allocator), true
|
||||
}
|
||||
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
get_current_directory :: proc() -> string {
|
||||
|
||||
@@ -119,7 +119,6 @@ lstat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, e
|
||||
}
|
||||
|
||||
stat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Errno) {
|
||||
|
||||
context.allocator = allocator
|
||||
|
||||
s: OS_Stat
|
||||
|
||||
@@ -20,7 +20,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
|
||||
return "", Errno(win32.GetLastError())
|
||||
}
|
||||
if n <= u32(len(buf)) {
|
||||
return win32.utf16_to_utf8(buf[:n], allocator), ERROR_NONE
|
||||
return win32.utf16_to_utf8(buf[:n], allocator) or_else "", ERROR_NONE
|
||||
}
|
||||
resize(&buf, len(buf)*2)
|
||||
}
|
||||
@@ -136,7 +136,7 @@ cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) {
|
||||
if err != 0 {
|
||||
return "", err
|
||||
}
|
||||
return win32.utf16_to_utf8(buf, context.allocator), err
|
||||
return win32.utf16_to_utf8(buf, context.allocator) or_else "", err
|
||||
}
|
||||
@(private)
|
||||
cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
|
||||
@@ -157,7 +157,7 @@ cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
|
||||
cleanpath_from_buf :: proc(buf: []u16) -> string {
|
||||
buf := buf
|
||||
buf = cleanpath_strip_prefix(buf)
|
||||
return win32.utf16_to_utf8(buf, context.allocator)
|
||||
return win32.utf16_to_utf8(buf, context.allocator) or_else ""
|
||||
}
|
||||
|
||||
@(private)
|
||||
|
||||
@@ -305,7 +305,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := cont
|
||||
n := fi.name
|
||||
matched := match(pattern, n) or_return
|
||||
if matched {
|
||||
append(&m, join(dir, n))
|
||||
append(&m, join({dir, n}))
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
@@ -38,7 +38,7 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
|
||||
return path_str, true
|
||||
}
|
||||
|
||||
join :: proc(elems: ..string, allocator := context.allocator) -> string {
|
||||
join :: proc(elems: []string, allocator := context.allocator) -> string {
|
||||
for e, i in elems {
|
||||
if e != "" {
|
||||
p := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator)
|
||||
|
||||
@@ -68,7 +68,7 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) {
|
||||
return "", os.Errno(win32.GetLastError())
|
||||
}
|
||||
if n <= u32(len(buf)) {
|
||||
return win32.utf16_to_utf8(buf[:n], ta), os.ERROR_NONE
|
||||
return win32.utf16_to_utf8(buf[:n], ta) or_else "", os.ERROR_NONE
|
||||
}
|
||||
resize(&buf, len(buf)*2)
|
||||
}
|
||||
@@ -88,7 +88,7 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
|
||||
}
|
||||
|
||||
|
||||
join :: proc(elems: ..string, allocator := context.allocator) -> string {
|
||||
join :: proc(elems: []string, allocator := context.allocator) -> string {
|
||||
for e, i in elems {
|
||||
if e != "" {
|
||||
return join_non_empty(elems[i:], allocator)
|
||||
|
||||
@@ -654,7 +654,7 @@ union_variant_type_info :: proc(a: any) -> ^Type_Info {
|
||||
}
|
||||
|
||||
type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool {
|
||||
return info.maybe && len(info.variants) == 1 && is_pointer(info.variants[0])
|
||||
return len(info.variants) == 1 && is_pointer(info.variants[0])
|
||||
}
|
||||
|
||||
union_variant_typeid :: proc(a: any) -> typeid {
|
||||
|
||||
@@ -256,6 +256,17 @@ is_multi_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer)
|
||||
return ok
|
||||
}
|
||||
is_pointer_internally :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
#partial switch v in info.variant {
|
||||
case Type_Info_Pointer, Type_Info_Multi_Pointer,
|
||||
Type_Info_Procedure:
|
||||
return true
|
||||
case Type_Info_String:
|
||||
return v.is_cstring
|
||||
}
|
||||
return false
|
||||
}
|
||||
is_procedure :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Procedure)
|
||||
@@ -531,9 +542,8 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
|
||||
|
||||
case Type_Info_Union:
|
||||
io.write_string(w, "union ", &n) or_return
|
||||
if info.maybe {
|
||||
io.write_string(w, "#maybe ", &n) or_return
|
||||
}
|
||||
if info.no_nil { io.write_string(w, "#no_nil ", &n) or_return }
|
||||
if info.shared_nil { io.write_string(w, "#shared_nil ", &n) or_return }
|
||||
if info.custom_align {
|
||||
io.write_string(w, "#align ", &n) or_return
|
||||
io.write_i64(w, i64(ti.align), 10, &n) or_return
|
||||
|
||||
@@ -135,7 +135,6 @@ Type_Info_Union :: struct {
|
||||
|
||||
custom_align: bool,
|
||||
no_nil: bool,
|
||||
maybe: bool,
|
||||
shared_nil: bool,
|
||||
}
|
||||
Type_Info_Enum :: struct {
|
||||
|
||||
@@ -3,7 +3,7 @@ package runtime
|
||||
import "core:intrinsics"
|
||||
|
||||
@builtin
|
||||
Maybe :: union($T: typeid) #maybe {T}
|
||||
Maybe :: union($T: typeid) {T}
|
||||
|
||||
|
||||
@builtin
|
||||
@@ -600,26 +600,30 @@ card :: proc(s: $S/bit_set[$E; $U]) -> int {
|
||||
|
||||
|
||||
@builtin
|
||||
raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> ^E {
|
||||
return (^E)(a)
|
||||
raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> [^]E {
|
||||
return ([^]E)(a)
|
||||
}
|
||||
@builtin
|
||||
raw_slice_data :: proc "contextless" (s: $S/[]$E) -> ^E {
|
||||
raw_simd_data :: proc "contextless" (a: $P/^($T/#simd[$N]$E)) -> [^]E {
|
||||
return ([^]E)(a)
|
||||
}
|
||||
@builtin
|
||||
raw_slice_data :: proc "contextless" (s: $S/[]$E) -> [^]E {
|
||||
ptr := (transmute(Raw_Slice)s).data
|
||||
return (^E)(ptr)
|
||||
return ([^]E)(ptr)
|
||||
}
|
||||
@builtin
|
||||
raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> ^E {
|
||||
raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> [^]E {
|
||||
ptr := (transmute(Raw_Dynamic_Array)s).data
|
||||
return (^E)(ptr)
|
||||
return ([^]E)(ptr)
|
||||
}
|
||||
@builtin
|
||||
raw_string_data :: proc "contextless" (s: $S/string) -> ^u8 {
|
||||
raw_string_data :: proc "contextless" (s: $S/string) -> [^]u8 {
|
||||
return (transmute(Raw_String)s).data
|
||||
}
|
||||
|
||||
@builtin
|
||||
raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data}
|
||||
raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data, raw_simd_data}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
||||
foreign import lib "system:NtDll.lib"
|
||||
|
||||
@(private="file")
|
||||
@(default_calling_convention="std")
|
||||
@(default_calling_convention="stdcall")
|
||||
foreign lib {
|
||||
RtlMoveMemory :: proc(dst, src: rawptr, length: int) ---
|
||||
RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---
|
||||
|
||||
187
core/simd/simd.odin
Normal file
187
core/simd/simd.odin
Normal file
@@ -0,0 +1,187 @@
|
||||
package simd
|
||||
|
||||
import "core:builtin"
|
||||
import "core:intrinsics"
|
||||
|
||||
// 128-bit vector aliases
|
||||
u8x16 :: #simd[16]u8
|
||||
i8x16 :: #simd[16]i8
|
||||
u16x8 :: #simd[8]u16
|
||||
i16x8 :: #simd[8]i16
|
||||
u32x4 :: #simd[4]u32
|
||||
i32x4 :: #simd[4]i32
|
||||
u64x2 :: #simd[2]u64
|
||||
i64x2 :: #simd[2]i64
|
||||
f32x4 :: #simd[4]f32
|
||||
f64x2 :: #simd[2]f64
|
||||
|
||||
boolx16 :: #simd[16]bool
|
||||
b8x16 :: #simd[16]b8
|
||||
b16x8 :: #simd[8]b16
|
||||
b32x4 :: #simd[4]b32
|
||||
b64x2 :: #simd[2]b64
|
||||
|
||||
// 256-bit vector aliases
|
||||
u8x32 :: #simd[32]u8
|
||||
i8x32 :: #simd[32]i8
|
||||
u16x16 :: #simd[16]u16
|
||||
i16x16 :: #simd[16]i16
|
||||
u32x8 :: #simd[8]u32
|
||||
i32x8 :: #simd[8]i32
|
||||
u64x4 :: #simd[4]u64
|
||||
i64x4 :: #simd[4]i64
|
||||
f32x8 :: #simd[8]f32
|
||||
f64x4 :: #simd[4]f64
|
||||
|
||||
boolx32 :: #simd[32]bool
|
||||
b8x32 :: #simd[32]b8
|
||||
b16x16 :: #simd[16]b16
|
||||
b32x8 :: #simd[8]b32
|
||||
b64x4 :: #simd[4]b64
|
||||
|
||||
// 512-bit vector aliases
|
||||
u8x64 :: #simd[64]u8
|
||||
i8x64 :: #simd[64]i8
|
||||
u16x32 :: #simd[32]u16
|
||||
i16x32 :: #simd[32]i16
|
||||
u32x16 :: #simd[16]u32
|
||||
i32x16 :: #simd[16]i32
|
||||
u64x8 :: #simd[8]u64
|
||||
i64x8 :: #simd[8]i64
|
||||
f32x16 :: #simd[16]f32
|
||||
f64x8 :: #simd[8]f64
|
||||
|
||||
boolx64 :: #simd[64]bool
|
||||
b8x64 :: #simd[64]b8
|
||||
b16x32 :: #simd[32]b16
|
||||
b32x16 :: #simd[16]b32
|
||||
b64x8 :: #simd[8]b64
|
||||
|
||||
|
||||
add :: intrinsics.simd_add
|
||||
sub :: intrinsics.simd_sub
|
||||
mul :: intrinsics.simd_mul
|
||||
div :: intrinsics.simd_div // floats only
|
||||
|
||||
// Keeps Odin's Behaviour
|
||||
// (x << y) if y <= mask else 0
|
||||
shl :: intrinsics.simd_shl
|
||||
shr :: intrinsics.simd_shr
|
||||
|
||||
// Similar to C's Behaviour
|
||||
// x << (y & mask)
|
||||
shl_masked :: intrinsics.simd_shl_masked
|
||||
shr_masked :: intrinsics.simd_shr_masked
|
||||
|
||||
// Saturation Arithmetic
|
||||
add_sat :: intrinsics.simd_add_sat
|
||||
sub_sat :: intrinsics.simd_sub_sat
|
||||
|
||||
and :: intrinsics.simd_and
|
||||
or :: intrinsics.simd_or
|
||||
xor :: intrinsics.simd_xor
|
||||
and_not :: intrinsics.simd_and_not
|
||||
|
||||
neg :: intrinsics.simd_neg
|
||||
|
||||
abs :: intrinsics.simd_abs
|
||||
|
||||
min :: intrinsics.simd_min
|
||||
max :: intrinsics.simd_max
|
||||
clamp :: intrinsics.simd_clamp
|
||||
|
||||
// Return an unsigned integer of the same size as the input type
|
||||
// NOT A BOOLEAN
|
||||
// element-wise:
|
||||
// false => 0x00...00
|
||||
// true => 0xff...ff
|
||||
lanes_eq :: intrinsics.simd_lanes_eq
|
||||
lanes_ne :: intrinsics.simd_lanes_ne
|
||||
lanes_lt :: intrinsics.simd_lanes_lt
|
||||
lanes_le :: intrinsics.simd_lanes_le
|
||||
lanes_gt :: intrinsics.simd_lanes_gt
|
||||
lanes_ge :: intrinsics.simd_lanes_ge
|
||||
|
||||
// extract :: proc(a: #simd[N]T, idx: uint) -> T
|
||||
extract :: intrinsics.simd_extract
|
||||
// replace :: proc(a: #simd[N]T, idx: uint, elem: T) -> #simd[N]T
|
||||
replace :: intrinsics.simd_replace
|
||||
|
||||
reduce_add_ordered :: intrinsics.simd_reduce_add_ordered
|
||||
reduce_mul_ordered :: intrinsics.simd_reduce_mul_ordered
|
||||
reduce_min :: intrinsics.simd_reduce_min
|
||||
reduce_max :: intrinsics.simd_reduce_max
|
||||
reduce_and :: intrinsics.simd_reduce_and
|
||||
reduce_or :: intrinsics.simd_reduce_or
|
||||
reduce_xor :: intrinsics.simd_reduce_xor
|
||||
|
||||
// swizzle :: proc(a: #simd[N]T, indices: ..int) -> #simd[len(indices)]T
|
||||
swizzle :: builtin.swizzle
|
||||
|
||||
// shuffle :: proc(a, b: #simd[N]T, indices: #simd[max 2*N]u32) -> #simd[len(indices)]T
|
||||
shuffle :: intrinsics.simd_shuffle
|
||||
|
||||
// select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T
|
||||
select :: intrinsics.simd_select
|
||||
|
||||
|
||||
sqrt :: intrinsics.sqrt
|
||||
ceil :: intrinsics.simd_ceil
|
||||
floor :: intrinsics.simd_floor
|
||||
trunc :: intrinsics.simd_trunc
|
||||
nearest :: intrinsics.simd_nearest
|
||||
|
||||
to_bits :: intrinsics.simd_to_bits
|
||||
|
||||
lanes_reverse :: intrinsics.simd_lanes_reverse
|
||||
|
||||
lanes_rotate_left :: intrinsics.simd_lanes_rotate_left
|
||||
lanes_rotate_right :: intrinsics.simd_lanes_rotate_right
|
||||
|
||||
count_ones :: intrinsics.count_ones
|
||||
count_zeros :: intrinsics.count_zeros
|
||||
count_trailing_zeros :: intrinsics.count_trailing_zeros
|
||||
count_leading_zeros :: intrinsics.count_leading_zeros
|
||||
reverse_bits :: intrinsics.reverse_bits
|
||||
|
||||
fused_mul_add :: intrinsics.fused_mul_add
|
||||
fma :: intrinsics.fused_mul_add
|
||||
|
||||
to_array_ptr :: #force_inline proc "contextless" (v: ^#simd[$LANES]$E) -> ^[LANES]E {
|
||||
return (^[LANES]E)(v)
|
||||
}
|
||||
to_array :: #force_inline proc "contextless" (v: #simd[$LANES]$E) -> [LANES]E {
|
||||
return transmute([LANES]E)(v)
|
||||
}
|
||||
from_array :: #force_inline proc "contextless" (v: $A/[$LANES]$E) -> #simd[LANES]E {
|
||||
return transmute(#simd[LANES]E)v
|
||||
}
|
||||
|
||||
from_slice :: proc($T: typeid/#simd[$LANES]$E, slice: []E) -> T {
|
||||
assert(len(slice) >= LANES, "slice length must be a least the number of lanes")
|
||||
array: [LANES]E
|
||||
#no_bounds_check for i in 0..<LANES {
|
||||
array[i] = slice[i]
|
||||
}
|
||||
return transmute(T)array
|
||||
}
|
||||
|
||||
bit_not :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_integer(E) {
|
||||
return xor(v, T(~E(0)))
|
||||
}
|
||||
|
||||
copysign :: #force_inline proc "contextless" (v, sign: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) {
|
||||
neg_zero := to_bits(T(-0.0))
|
||||
sign_bit := to_bits(sign) & neg_zero
|
||||
magnitude := to_bits(v) &~ neg_zero
|
||||
return transmute(T)(sign_bit|magnitude)
|
||||
}
|
||||
|
||||
signum :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) {
|
||||
is_nan := lanes_ne(v, v)
|
||||
return select(is_nan, v, copysign(T(1), v))
|
||||
}
|
||||
|
||||
recip :: #force_inline proc "contextless" (v: $T/#simd[$LANES]$E) -> T where intrinsics.type_is_float(E) {
|
||||
return T(1) / v
|
||||
}
|
||||
24
core/simd/x86/abm.odin
Normal file
24
core/simd/x86/abm.odin
Normal file
@@ -0,0 +1,24 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
@(require_results, enable_target_feature="lzcnt")
|
||||
_lzcnt_u32 :: #force_inline proc "c" (x: u32) -> u32 {
|
||||
return intrinsics.count_leading_zeros(x)
|
||||
}
|
||||
@(require_results, enable_target_feature="popcnt")
|
||||
_popcnt32 :: #force_inline proc "c" (x: u32) -> i32 {
|
||||
return i32(intrinsics.count_ones(x))
|
||||
}
|
||||
|
||||
when ODIN_ARCH == .amd64 {
|
||||
@(require_results, enable_target_feature="lzcnt")
|
||||
_lzcnt_u64 :: #force_inline proc "c" (x: u64) -> u64 {
|
||||
return intrinsics.count_leading_zeros(x)
|
||||
}
|
||||
@(require_results, enable_target_feature="popcnt")
|
||||
_popcnt64 :: #force_inline proc "c" (x: u64) -> i32 {
|
||||
return i32(intrinsics.count_ones(x))
|
||||
}
|
||||
}
|
||||
56
core/simd/x86/adx.odin
Normal file
56
core/simd/x86/adx.odin
Normal file
@@ -0,0 +1,56 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
@(require_results)
|
||||
_addcarry_u32 :: #force_inline proc "c" (c_in: u8, a: u32, b: u32, out: ^u32) -> u8 {
|
||||
x, y := llvm_addcarry_u32(c_in, a, b)
|
||||
out^ = y
|
||||
return x
|
||||
}
|
||||
@(require_results)
|
||||
_addcarryx_u32 :: #force_inline proc "c" (c_in: u8, a: u32, b: u32, out: ^u32) -> u8 {
|
||||
return llvm_addcarryx_u32(c_in, a, b, out)
|
||||
}
|
||||
@(require_results)
|
||||
_subborrow_u32 :: #force_inline proc "c" (c_in: u8, a: u32, b: u32, out: ^u32) -> u8 {
|
||||
x, y := llvm_subborrow_u32(c_in, a, b)
|
||||
out^ = y
|
||||
return x
|
||||
}
|
||||
|
||||
when ODIN_ARCH == .amd64 {
|
||||
@(require_results)
|
||||
_addcarry_u64 :: #force_inline proc "c" (c_in: u8, a: u64, b: u64, out: ^u64) -> u8 {
|
||||
x, y := llvm_addcarry_u64(c_in, a, b)
|
||||
out^ = y
|
||||
return x
|
||||
}
|
||||
@(require_results)
|
||||
_addcarryx_u64 :: #force_inline proc "c" (c_in: u8, a: u64, b: u64, out: ^u64) -> u8 {
|
||||
return llvm_addcarryx_u64(c_in, a, b, out)
|
||||
}
|
||||
@(require_results)
|
||||
_subborrow_u64 :: #force_inline proc "c" (c_in: u8, a: u64, b: u64, out: ^u64) -> u8 {
|
||||
x, y := llvm_subborrow_u64(c_in, a, b)
|
||||
out^ = y
|
||||
return x
|
||||
}
|
||||
}
|
||||
|
||||
@(private, default_calling_convention="c")
|
||||
foreign _ {
|
||||
@(link_name="llvm.x86.addcarry.32")
|
||||
llvm_addcarry_u32 :: proc(a: u8, b: u32, c: u32) -> (u8, u32) ---
|
||||
@(link_name="llvm.x86.addcarryx.u32")
|
||||
llvm_addcarryx_u32 :: proc(a: u8, b: u32, c: u32, d: rawptr) -> u8 ---
|
||||
@(link_name="llvm.x86.subborrow.32")
|
||||
llvm_subborrow_u32 :: proc(a: u8, b: u32, c: u32) -> (u8, u32) ---
|
||||
|
||||
// amd64 only
|
||||
@(link_name="llvm.x86.addcarry.64")
|
||||
llvm_addcarry_u64 :: proc(a: u8, b: u64, c: u64) -> (u8, u64) ---
|
||||
@(link_name="llvm.x86.addcarryx.u64")
|
||||
llvm_addcarryx_u64 :: proc(a: u8, b: u64, c: u64, d: rawptr) -> u8 ---
|
||||
@(link_name="llvm.x86.subborrow.64")
|
||||
llvm_subborrow_u64 :: proc(a: u8, b: u64, c: u64) -> (u8, u64) ---
|
||||
}
|
||||
8
core/simd/x86/cmpxchg16b.odin
Normal file
8
core/simd/x86/cmpxchg16b.odin
Normal file
@@ -0,0 +1,8 @@
|
||||
//+build amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
cmpxchg16b :: #force_inline proc "c" (dst: ^u128, old, new: u128, $success, $failure: intrinsics.Atomic_Memory_Order) -> (val: u128) {
|
||||
return intrinsics.atomic_compare_exchange_strong_explicit(dst, old, new, success, failure)
|
||||
}
|
||||
94
core/simd/x86/cpu.odin
Normal file
94
core/simd/x86/cpu.odin
Normal file
@@ -0,0 +1,94 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
// cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) ---
|
||||
cpuid :: intrinsics.x86_cpuid
|
||||
|
||||
// xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
|
||||
xgetbv :: intrinsics.x86_xgetbv
|
||||
|
||||
|
||||
CPU_Feature :: enum u64 {
|
||||
aes, // AES hardware implementation (AES NI)
|
||||
adx, // Multi-precision add-carry instruction extensions
|
||||
avx, // Advanced vector extension
|
||||
avx2, // Advanced vector extension 2
|
||||
bmi1, // Bit manipulation instruction set 1
|
||||
bmi2, // Bit manipulation instruction set 2
|
||||
erms, // Enhanced REP for MOVSB and STOSB
|
||||
fma, // Fused-multiply-add instructions
|
||||
os_xsave, // OS supports XSAVE/XRESTOR for saving/restoring XMM registers.
|
||||
pclmulqdq, // PCLMULQDQ instruction - most often used for AES-GCM
|
||||
popcnt, // Hamming weight instruction POPCNT.
|
||||
rdrand, // RDRAND instruction (on-chip random number generator)
|
||||
rdseed, // RDSEED instruction (on-chip random number generator)
|
||||
sse2, // Streaming SIMD extension 2 (always available on amd64)
|
||||
sse3, // Streaming SIMD extension 3
|
||||
ssse3, // Supplemental streaming SIMD extension 3
|
||||
sse41, // Streaming SIMD extension 4 and 4.1
|
||||
sse42, // Streaming SIMD extension 4 and 4.2
|
||||
}
|
||||
|
||||
CPU_Features :: distinct bit_set[CPU_Feature; u64]
|
||||
|
||||
cpu_features: Maybe(CPU_Features)
|
||||
|
||||
@(init, private)
|
||||
init_cpu_features :: proc "c" () {
|
||||
is_set :: #force_inline proc "c" (hwc: u32, value: u32) -> bool {
|
||||
return hwc&value != 0
|
||||
}
|
||||
try_set :: #force_inline proc "c" (set: ^CPU_Features, feature: CPU_Feature, hwc: u32, value: u32) {
|
||||
if is_set(hwc, value) {
|
||||
set^ += {feature}
|
||||
}
|
||||
}
|
||||
|
||||
max_id, _, _, _ := cpuid(0, 0)
|
||||
if max_id < 1 {
|
||||
return
|
||||
}
|
||||
|
||||
set: CPU_Features
|
||||
|
||||
_, _, ecx1, edx1 := cpuid(1, 0)
|
||||
|
||||
try_set(&set, .sse2, 26, edx1)
|
||||
try_set(&set, .sse3, 0, ecx1)
|
||||
try_set(&set, .pclmulqdq, 1, ecx1)
|
||||
try_set(&set, .ssse3, 9, ecx1)
|
||||
try_set(&set, .fma, 12, ecx1)
|
||||
try_set(&set, .sse41, 19, ecx1)
|
||||
try_set(&set, .sse42, 20, ecx1)
|
||||
try_set(&set, .popcnt, 23, ecx1)
|
||||
try_set(&set, .aes, 25, ecx1)
|
||||
try_set(&set, .os_xsave, 27, ecx1)
|
||||
try_set(&set, .rdrand, 30, ecx1)
|
||||
|
||||
os_supports_avx := false
|
||||
if .os_xsave in set {
|
||||
eax, _ := xgetbv(0)
|
||||
os_supports_avx = is_set(1, eax) && is_set(2, eax)
|
||||
}
|
||||
if os_supports_avx {
|
||||
try_set(&set, .avx, 28, ecx1)
|
||||
}
|
||||
|
||||
if max_id < 7 {
|
||||
return
|
||||
}
|
||||
|
||||
_, ebx7, _, _ := cpuid(7, 0)
|
||||
try_set(&set, .bmi1, 3, ebx7)
|
||||
if os_supports_avx {
|
||||
try_set(&set, .avx2, 5, ebx7)
|
||||
}
|
||||
try_set(&set, .bmi2, 8, ebx7)
|
||||
try_set(&set, .erms, 9, ebx7)
|
||||
try_set(&set, .rdseed, 18, ebx7)
|
||||
try_set(&set, .adx, 19, ebx7)
|
||||
|
||||
cpu_features = set
|
||||
}
|
||||
36
core/simd/x86/fxsr.odin
Normal file
36
core/simd/x86/fxsr.odin
Normal file
@@ -0,0 +1,36 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
@(enable_target_feature="fxsr")
|
||||
_fxsave :: #force_inline proc "c" (mem_addr: rawptr) {
|
||||
fxsave(mem_addr)
|
||||
}
|
||||
@(enable_target_feature="fxsr")
|
||||
_fxrstor :: #force_inline proc "c" (mem_addr: rawptr) {
|
||||
fxrstor(mem_addr)
|
||||
}
|
||||
|
||||
when ODIN_ARCH == .amd64 {
|
||||
@(enable_target_feature="fxsr")
|
||||
_fxsave64 :: #force_inline proc "c" (mem_addr: rawptr) {
|
||||
fxsave64(mem_addr)
|
||||
}
|
||||
@(enable_target_feature="fxsr")
|
||||
_fxrstor64 :: #force_inline proc "c" (mem_addr: rawptr) {
|
||||
fxrstor64(mem_addr)
|
||||
}
|
||||
}
|
||||
|
||||
@(private, default_calling_convention="c")
|
||||
foreign _ {
|
||||
@(link_name="llvm.x86.fxsave")
|
||||
fxsave :: proc(p: rawptr) ---
|
||||
@(link_name="llvm.x86.fxrstor")
|
||||
fxrstor :: proc(p: rawptr) ---
|
||||
|
||||
// amd64 only
|
||||
@(link_name="llvm.x86.fxsave64")
|
||||
fxsave64 :: proc(p: rawptr) ---
|
||||
@(link_name="llvm.x86.fxrstor64")
|
||||
fxrstor64 :: proc(p: rawptr) ---
|
||||
}
|
||||
13
core/simd/x86/pclmulqdq.odin
Normal file
13
core/simd/x86/pclmulqdq.odin
Normal file
@@ -0,0 +1,13 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
@(require_results, enable_target_feature="pclmulqdq")
|
||||
_mm_clmulepi64_si128 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u8) -> __m128i {
|
||||
return pclmulqdq(a, b, u8(IMM8))
|
||||
}
|
||||
|
||||
@(private, default_calling_convention="c")
|
||||
foreign _ {
|
||||
@(link_name="llvm.x86.pclmulqdq")
|
||||
pclmulqdq :: proc(a, round_key: __m128i, #const imm8: u8) -> __m128i ---
|
||||
}
|
||||
20
core/simd/x86/rdtsc.odin
Normal file
20
core/simd/x86/rdtsc.odin
Normal file
@@ -0,0 +1,20 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
@(require_results)
|
||||
_rdtsc :: #force_inline proc "c" () -> u64 {
|
||||
return rdtsc()
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
__rdtscp :: #force_inline proc "c" (aux: ^u32) -> u64 {
|
||||
return rdtscp(aux)
|
||||
}
|
||||
|
||||
@(private, default_calling_convention="c")
|
||||
foreign _ {
|
||||
@(link_name="llvm.x86.rdtsc")
|
||||
rdtsc :: proc() -> u64 ---
|
||||
@(link_name="llvm.x86.rdtscp")
|
||||
rdtscp :: proc(aux: rawptr) -> u64 ---
|
||||
}
|
||||
49
core/simd/x86/sha.odin
Normal file
49
core/simd/x86/sha.odin
Normal file
@@ -0,0 +1,49 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
@(require_results, enable_target_feature="sha")
|
||||
_mm_sha1msg1_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)sha1msg1(transmute(i32x4)a, transmute(i32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sha")
|
||||
_mm_sha1msg2_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)sha1msg2(transmute(i32x4)a, transmute(i32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sha")
|
||||
_mm_sha1nexte_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)sha1nexte(transmute(i32x4)a, transmute(i32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sha")
|
||||
_mm_sha1rnds4_epu32 :: #force_inline proc "c" (a, b: __m128i, $FUNC: u32) -> __m128i where 0 <= FUNC, FUNC <= 3 {
|
||||
return transmute(__m128i)sha1rnds4(transmute(i32x4)a, transmute(i32x4)b, u8(FUNC & 0xff))
|
||||
}
|
||||
@(require_results, enable_target_feature="sha")
|
||||
_mm_sha256msg1_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)sha256msg1(transmute(i32x4)a, transmute(i32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sha")
|
||||
_mm_sha256msg2_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)sha256msg2(transmute(i32x4)a, transmute(i32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sha")
|
||||
_mm_sha256rnds2_epu32 :: #force_inline proc "c" (a, b, k: __m128i) -> __m128i {
|
||||
return transmute(__m128i)sha256rnds2(transmute(i32x4)a, transmute(i32x4)b, transmute(i32x4)k)
|
||||
}
|
||||
|
||||
@(private, default_calling_convention="c")
|
||||
foreign _ {
|
||||
@(link_name="llvm.x86.sha1msg1")
|
||||
sha1msg1 :: proc(a, b: i32x4) -> i32x4 ---
|
||||
@(link_name="llvm.x86.sha1msg2")
|
||||
sha1msg2 :: proc(a, b: i32x4) -> i32x4 ---
|
||||
@(link_name="llvm.x86.sha1nexte")
|
||||
sha1nexte :: proc(a, b: i32x4) -> i32x4 ---
|
||||
@(link_name="llvm.x86.sha1rnds4")
|
||||
sha1rnds4 :: proc(a, b: i32x4, #const c: u8) -> i32x4 ---
|
||||
@(link_name="llvm.x86.sha256msg1")
|
||||
sha256msg1 :: proc(a, b: i32x4) -> i32x4 ---
|
||||
@(link_name="llvm.x86.sha256msg2")
|
||||
sha256msg2 :: proc(a, b: i32x4) -> i32x4 ---
|
||||
@(link_name="llvm.x86.sha256rnds2")
|
||||
sha256rnds2 :: proc(a, b, k: i32x4) -> i32x4 ---
|
||||
}
|
||||
618
core/simd/x86/sse.odin
Normal file
618
core/simd/x86/sse.odin
Normal file
@@ -0,0 +1,618 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:simd"
|
||||
|
||||
// _MM_SHUFFLE(z, y, x, w) -> (z<<6 | y<<4 | x<<2 | w)
|
||||
_MM_SHUFFLE :: intrinsics.simd_x86__MM_SHUFFLE
|
||||
|
||||
_MM_HINT_T0 :: 3
|
||||
_MM_HINT_T1 :: 2
|
||||
_MM_HINT_T2 :: 1
|
||||
_MM_HINT_NTA :: 0
|
||||
_MM_HINT_ET0 :: 7
|
||||
_MM_HINT_ET1 :: 6
|
||||
|
||||
|
||||
_MM_EXCEPT_INVALID :: 0x0001
|
||||
_MM_EXCEPT_DENORM :: 0x0002
|
||||
_MM_EXCEPT_DIV_ZERO :: 0x0004
|
||||
_MM_EXCEPT_OVERFLOW :: 0x0008
|
||||
_MM_EXCEPT_UNDERFLOW :: 0x0010
|
||||
_MM_EXCEPT_INEXACT :: 0x0020
|
||||
_MM_EXCEPT_MASK :: 0x003f
|
||||
|
||||
_MM_MASK_INVALID :: 0x0080
|
||||
_MM_MASK_DENORM :: 0x0100
|
||||
_MM_MASK_DIV_ZERO :: 0x0200
|
||||
_MM_MASK_OVERFLOW :: 0x0400
|
||||
_MM_MASK_UNDERFLOW :: 0x0800
|
||||
_MM_MASK_INEXACT :: 0x1000
|
||||
_MM_MASK_MASK :: 0x1f80
|
||||
|
||||
_MM_ROUND_NEAREST :: 0x0000
|
||||
_MM_ROUND_DOWN :: 0x2000
|
||||
_MM_ROUND_UP :: 0x4000
|
||||
_MM_ROUND_TOWARD_ZERO :: 0x6000
|
||||
|
||||
_MM_ROUND_MASK :: 0x6000
|
||||
|
||||
_MM_FLUSH_ZERO_MASK :: 0x8000
|
||||
_MM_FLUSH_ZERO_ON :: 0x8000
|
||||
_MM_FLUSH_ZERO_OFF :: 0x0000
|
||||
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_add_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return addss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_add_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return simd.add(a, b)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_sub_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return subss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_sub_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return simd.sub(a, b)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_mul_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return mulss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_mul_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return simd.mul(a, b)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_div_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return divss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_div_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return simd.div(a, b)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_sqrt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return sqrtss(a)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_sqrt_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
|
||||
return sqrtps(a)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_rcp_ss :: #force_inline proc "c" (a: __m128) -> __m128 {
|
||||
return rcpss(a)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_rcp_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
|
||||
return rcpps(a)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_rsqrt_ss :: #force_inline proc "c" (a: __m128) -> __m128 {
|
||||
return rsqrtss(a)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_rsqrt_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
|
||||
return rsqrtps(a)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_min_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return minss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_min_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return minps(a, b)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_max_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return maxss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_max_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return maxps(a, b)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_and_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return transmute(__m128)simd.and(transmute(__m128i)a, transmute(__m128i)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_andnot_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return transmute(__m128)simd.and_not(transmute(__m128i)a, transmute(__m128i)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_or_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return transmute(__m128)simd.or(transmute(__m128i)a, transmute(__m128i)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_xor_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return transmute(__m128)simd.xor(transmute(__m128i)a, transmute(__m128i)b)
|
||||
}
|
||||
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpeq_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpss(a, b, 0)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmplt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpss(a, b, 1)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmple_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpss(a, b, 2)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpgt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return simd.shuffle(a, cmpss(b, a, 1), 4, 1, 2, 3)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpge_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return simd.shuffle(a, cmpss(b, a, 2), 4, 1, 2, 3)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpneq_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpss(a, b, 4)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpnlt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpss(a, b, 5)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpnle_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpss(a, b, 6)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpngt_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return simd.shuffle(a, cmpss(b, a, 5), 4, 1, 2, 3)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpnge_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return simd.shuffle(a, cmpss(b, a, 6), 4, 1, 2, 3)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpord_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpss(a, b, 7)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpunord_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpss(a, b, 3)
|
||||
}
|
||||
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpeq_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpps(a, b, 0)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmplt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpps(a, b, 1)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmple_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpps(a, b, 2)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpgt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpps(b, a, 1)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpge_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpps(b, a, 2)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpneq_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpps(a, b, 4)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpnlt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpps(a, b, 5)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpnle_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpps(a, b, 6)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpngt_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpps(b, a, 5)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpnge_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpps(b, a, 6)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpord_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpps(b, a, 7)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cmpunord_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return cmpps(b, a, 3)
|
||||
}
|
||||
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_comieq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
|
||||
return comieq_ss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_comilt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
|
||||
return comilt_ss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_comile_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
|
||||
return comile_ss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_comigt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
|
||||
return comigt_ss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_comige_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
|
||||
return comige_ss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_comineq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
|
||||
return comineq_ss(a, b)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_ucomieq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
|
||||
return ucomieq_ss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_ucomilt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
|
||||
return ucomilt_ss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_ucomile_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
|
||||
return ucomile_ss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_ucomigt_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
|
||||
return ucomigt_ss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_ucomige_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
|
||||
return ucomige_ss(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_ucomineq_ss :: #force_inline proc "c" (a, b: __m128) -> b32 {
|
||||
return ucomineq_ss(a, b)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cvtss_si32 :: #force_inline proc "c" (a: __m128) -> i32 {
|
||||
return cvtss2si(a)
|
||||
}
|
||||
_mm_cvt_ss2si :: _mm_cvtss_si32
|
||||
_mm_cvttss_si32 :: _mm_cvtss_si32
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cvtss_f32 :: #force_inline proc "c" (a: __m128) -> f32 {
|
||||
return simd.extract(a, 0)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cvtsi32_ss :: #force_inline proc "c" (a: __m128, b: i32) -> __m128 {
|
||||
return cvtsi2ss(a, b)
|
||||
}
|
||||
_mm_cvt_si2ss :: _mm_cvtsi32_ss
|
||||
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_set_ss :: #force_inline proc "c" (a: f32) -> __m128 {
|
||||
return __m128{a, 0, 0, 0}
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_set1_ps :: #force_inline proc "c" (a: f32) -> __m128 {
|
||||
return __m128(a)
|
||||
}
|
||||
_mm_set_ps1 :: _mm_set1_ps
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_set_ps :: #force_inline proc "c" (a, b, c, d: f32) -> __m128 {
|
||||
return __m128{d, c, b, a}
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_setr_ps :: #force_inline proc "c" (a, b, c, d: f32) -> __m128 {
|
||||
return __m128{a, b, c, d}
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_setzero_ps :: #force_inline proc "c" () -> __m128 {
|
||||
return __m128{0, 0, 0, 0}
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_shuffle_ps :: #force_inline proc "c" (a, b: __m128, $MASK: u32) -> __m128 {
|
||||
return simd.shuffle(
|
||||
a, b,
|
||||
u32(MASK) & 0b11,
|
||||
(u32(MASK)>>2) & 0b11,
|
||||
((u32(MASK)>>4) & 0b11)+4,
|
||||
((u32(MASK)>>6) & 0b11)+4)
|
||||
}
|
||||
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_unpackhi_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return simd.shuffle(a, b, 2, 6, 3, 7)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_unpacklo_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return simd.shuffle(a, b, 0, 4, 1, 5)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_movehl_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return simd.shuffle(a, b, 6, 7, 2, 3)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_movelh_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return simd.shuffle(a, b, 0, 1, 4, 5)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_movemask_ps :: #force_inline proc "c" (a: __m128) -> u32 {
|
||||
return movmskps(a)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_load_ss :: #force_inline proc "c" (p: ^f32) -> __m128 {
|
||||
return __m128{p^, 0, 0, 0}
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_load1_ps :: #force_inline proc "c" (p: ^f32) -> __m128 {
|
||||
a := p^
|
||||
return __m128(a)
|
||||
}
|
||||
_mm_load_ps1 :: _mm_load1_ps
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_load_ps :: #force_inline proc "c" (p: [^]f32) -> __m128 {
|
||||
return (^__m128)(p)^
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_loadu_ps :: #force_inline proc "c" (p: [^]f32) -> __m128 {
|
||||
dst := _mm_undefined_ps()
|
||||
intrinsics.mem_copy_non_overlapping(&dst, p, size_of(__m128))
|
||||
return dst
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_loadr_ps :: #force_inline proc "c" (p: [^]f32) -> __m128 {
|
||||
return simd.lanes_reverse(_mm_load_ps(p))
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_loadu_si64 :: #force_inline proc "c" (mem_addr: rawptr) -> __m128i {
|
||||
a := intrinsics.unaligned_load((^i64)(mem_addr))
|
||||
return __m128i{a, 0}
|
||||
}
|
||||
|
||||
@(enable_target_feature="sse")
|
||||
_mm_store_ss :: #force_inline proc "c" (p: ^f32, a: __m128) {
|
||||
p^ = simd.extract(a, 0)
|
||||
}
|
||||
|
||||
@(enable_target_feature="sse")
|
||||
_mm_store1_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) {
|
||||
b := simd.swizzle(a, 0, 0, 0, 0)
|
||||
(^__m128)(p)^ = b
|
||||
}
|
||||
_mm_store_ps1 :: _mm_store1_ps
|
||||
|
||||
|
||||
@(enable_target_feature="sse")
|
||||
_mm_store_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) {
|
||||
(^__m128)(p)^ = a
|
||||
}
|
||||
@(enable_target_feature="sse")
|
||||
_mm_storeu_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) {
|
||||
b := a
|
||||
intrinsics.mem_copy_non_overlapping(p, &b, size_of(__m128))
|
||||
}
|
||||
@(enable_target_feature="sse")
|
||||
_mm_storer_ps :: #force_inline proc "c" (p: [^]f32, a: __m128) {
|
||||
(^__m128)(p)^ = simd.lanes_reverse(a)
|
||||
}
|
||||
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_move_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return simd.shuffle(a, b, 4, 1, 2, 3)
|
||||
}
|
||||
|
||||
@(enable_target_feature="sse")
|
||||
_mm_sfence :: #force_inline proc "c" () {
|
||||
sfence()
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_getcsr :: #force_inline proc "c" () -> (result: u32) {
|
||||
stmxcsr(&result)
|
||||
return result
|
||||
}
|
||||
|
||||
@(enable_target_feature="sse")
|
||||
_mm_setcsr :: #force_inline proc "c" (val: u32) {
|
||||
val := val
|
||||
ldmxcsr(&val)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_MM_GET_EXCEPTION_MASK :: #force_inline proc "c" () -> u32 {
|
||||
return _mm_getcsr() & _MM_MASK_MASK
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_MM_GET_EXCEPTION_STATE :: #force_inline proc "c" () -> u32 {
|
||||
return _mm_getcsr() & _MM_EXCEPT_MASK
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_MM_GET_FLUSH_ZERO_MODE :: #force_inline proc "c" () -> u32 {
|
||||
return _mm_getcsr() & _MM_FLUSH_ZERO_MASK
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_MM_GET_ROUNDING_MODE :: #force_inline proc "c" () -> u32 {
|
||||
return _mm_getcsr() & _MM_ROUND_MASK
|
||||
}
|
||||
|
||||
@(enable_target_feature="sse")
|
||||
_MM_SET_EXCEPTION_MASK :: #force_inline proc "c" (x: u32) {
|
||||
_mm_setcsr((_mm_getcsr() &~ _MM_MASK_MASK) | x)
|
||||
}
|
||||
@(enable_target_feature="sse")
|
||||
_MM_SET_EXCEPTION_STATE :: #force_inline proc "c" (x: u32) {
|
||||
_mm_setcsr((_mm_getcsr() &~ _MM_EXCEPT_MASK) | x)
|
||||
}
|
||||
@(enable_target_feature="sse")
|
||||
_MM_SET_FLUSH_ZERO_MODE :: #force_inline proc "c" (x: u32) {
|
||||
_mm_setcsr((_mm_getcsr() &~ _MM_FLUSH_ZERO_MASK) | x)
|
||||
}
|
||||
@(enable_target_feature="sse")
|
||||
_MM_SET_ROUNDING_MODE :: #force_inline proc "c" (x: u32) {
|
||||
_mm_setcsr((_mm_getcsr() &~ _MM_ROUND_MASK) | x)
|
||||
}
|
||||
|
||||
@(enable_target_feature="sse")
|
||||
_mm_prefetch :: #force_inline proc "c" (p: rawptr, $STRATEGY: u32) {
|
||||
prefetch(p, (STRATEGY>>2)&1, STRATEGY&3, 1)
|
||||
}
|
||||
|
||||
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_undefined_ps :: #force_inline proc "c" () -> __m128 {
|
||||
return _mm_set1_ps(0)
|
||||
}
|
||||
|
||||
@(enable_target_feature="sse")
|
||||
_MM_TRANSPOSE4_PS :: #force_inline proc "c" (row0, row1, row2, row3: ^__m128) {
|
||||
tmp0 := _mm_unpacklo_ps(row0^, row1^)
|
||||
tmp1 := _mm_unpacklo_ps(row2^, row3^)
|
||||
tmp2 := _mm_unpackhi_ps(row0^, row1^)
|
||||
tmp3 := _mm_unpackhi_ps(row2^, row3^)
|
||||
|
||||
row0^ = _mm_movelh_ps(tmp0, tmp2)
|
||||
row1^ = _mm_movelh_ps(tmp2, tmp0)
|
||||
row2^ = _mm_movelh_ps(tmp1, tmp3)
|
||||
row3^ = _mm_movelh_ps(tmp3, tmp1)
|
||||
}
|
||||
|
||||
@(enable_target_feature="sse")
|
||||
_mm_stream_ps :: #force_inline proc "c" (addr: [^]f32, a: __m128) {
|
||||
intrinsics.non_temporal_store((^__m128)(addr), a)
|
||||
}
|
||||
|
||||
when ODIN_ARCH == .amd64 {
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cvtss_si64 :: #force_inline proc "c"(a: __m128) -> i64 {
|
||||
return cvtss2si64(a)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cvttss_si64 :: #force_inline proc "c"(a: __m128) -> i64 {
|
||||
return cvttss2si64(a)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse")
|
||||
_mm_cvtsi64_ss :: #force_inline proc "c"(a: __m128, b: i64) -> __m128 {
|
||||
return cvtsi642ss(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@(private, default_calling_convention="c")
|
||||
foreign _ {
|
||||
@(link_name="llvm.x86.sse.add.ss")
|
||||
addss :: proc(a, b: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.sub.ss")
|
||||
subss :: proc(a, b: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.mul.ss")
|
||||
mulss :: proc(a, b: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.div.ss")
|
||||
divss :: proc(a, b: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.sqrt.ss")
|
||||
sqrtss :: proc(a: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.sqrt.ps")
|
||||
sqrtps :: proc(a: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.rcp.ss")
|
||||
rcpss :: proc(a: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.rcp.ps")
|
||||
rcpps :: proc(a: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.rsqrt.ss")
|
||||
rsqrtss :: proc(a: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.rsqrt.ps")
|
||||
rsqrtps :: proc(a: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.min.ss")
|
||||
minss :: proc(a, b: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.min.ps")
|
||||
minps :: proc(a, b: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.max.ss")
|
||||
maxss :: proc(a, b: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.max.ps")
|
||||
maxps :: proc(a, b: __m128) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.movmsk.ps")
|
||||
movmskps :: proc(a: __m128) -> u32 ---
|
||||
@(link_name="llvm.x86.sse.cmp.ps")
|
||||
cmpps :: proc(a, b: __m128, #const imm8: u8) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.comieq.ss")
|
||||
comieq_ss :: proc(a, b: __m128) -> b32 ---
|
||||
@(link_name="llvm.x86.sse.comilt.ss")
|
||||
comilt_ss :: proc(a, b: __m128) -> b32 ---
|
||||
@(link_name="llvm.x86.sse.comile.ss")
|
||||
comile_ss :: proc(a, b: __m128) -> b32 ---
|
||||
@(link_name="llvm.x86.sse.comigt.ss")
|
||||
comigt_ss :: proc(a, b: __m128) -> b32 ---
|
||||
@(link_name="llvm.x86.sse.comige.ss")
|
||||
comige_ss :: proc(a, b: __m128) -> b32 ---
|
||||
@(link_name="llvm.x86.sse.comineq.ss")
|
||||
comineq_ss :: proc(a, b: __m128) -> b32 ---
|
||||
@(link_name="llvm.x86.sse.ucomieq.ss")
|
||||
ucomieq_ss :: proc(a, b: __m128) -> b32 ---
|
||||
@(link_name="llvm.x86.sse.ucomilt.ss")
|
||||
ucomilt_ss :: proc(a, b: __m128) -> b32 ---
|
||||
@(link_name="llvm.x86.sse.ucomile.ss")
|
||||
ucomile_ss :: proc(a, b: __m128) -> b32 ---
|
||||
@(link_name="llvm.x86.sse.ucomigt.ss")
|
||||
ucomigt_ss :: proc(a, b: __m128) -> b32 ---
|
||||
@(link_name="llvm.x86.sse.ucomige.ss")
|
||||
ucomige_ss :: proc(a, b: __m128) -> b32 ---
|
||||
@(link_name="llvm.x86.sse.ucomineq.ss")
|
||||
ucomineq_ss :: proc(a, b: __m128) -> b32 ---
|
||||
@(link_name="llvm.x86.sse.cvtss2si")
|
||||
cvtss2si :: proc(a: __m128) -> i32 ---
|
||||
@(link_name="llvm.x86.sse.cvttss2si")
|
||||
cvttss2si :: proc(a: __m128) -> i32 ---
|
||||
@(link_name="llvm.x86.sse.cvtsi2ss")
|
||||
cvtsi2ss :: proc(a: __m128, b: i32) -> __m128 ---
|
||||
@(link_name="llvm.x86.sse.sfence")
|
||||
sfence :: proc() ---
|
||||
@(link_name="llvm.x86.sse.stmxcsr")
|
||||
stmxcsr :: proc(p: rawptr) ---
|
||||
@(link_name="llvm.x86.sse.ldmxcsr")
|
||||
ldmxcsr :: proc(p: rawptr) ---
|
||||
@(link_name="llvm.prefetch")
|
||||
prefetch :: proc(p: rawptr, #const rw, loc, ty: u32) ---
|
||||
@(link_name="llvm.x86.sse.cmp.ss")
|
||||
cmpss :: proc(a, b: __m128, #const imm8: u8) -> __m128 ---
|
||||
|
||||
|
||||
// amd64 only
|
||||
@(link_name="llvm.x86.sse.cvtss2si64")
|
||||
cvtss2si64 :: proc(a: __m128) -> i64 ---
|
||||
@(link_name="llvm.x86.sse.cvttss2si64")
|
||||
cvttss2si64 :: proc(a: __m128) -> i64 ---
|
||||
@(link_name="llvm.x86.sse.cvtsi642ss")
|
||||
cvtsi642ss :: proc(a: __m128, b: i64) -> __m128 ---
|
||||
}
|
||||
1191
core/simd/x86/sse2.odin
Normal file
1191
core/simd/x86/sse2.odin
Normal file
File diff suppressed because it is too large
Load Diff
68
core/simd/x86/sse3.odin
Normal file
68
core/simd/x86/sse3.odin
Normal file
@@ -0,0 +1,68 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:simd"
|
||||
|
||||
@(require_results, enable_target_feature="sse3")
|
||||
_mm_addsub_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return addsubps(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse3")
|
||||
_mm_addsub_pd :: #force_inline proc "c" (a: __m128d, b: __m128d) -> __m128d {
|
||||
return addsubpd(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse3")
|
||||
_mm_hadd_pd :: #force_inline proc "c" (a: __m128d, b: __m128d) -> __m128d {
|
||||
return haddpd(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse3")
|
||||
_mm_hadd_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return haddps(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse3")
|
||||
_mm_hsub_pd :: #force_inline proc "c" (a: __m128d, b: __m128d) -> __m128d {
|
||||
return hsubpd(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse3")
|
||||
_mm_hsub_ps :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return hsubps(a, b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse3")
|
||||
_mm_lddqu_si128 :: #force_inline proc "c" (mem_addr: ^__m128i) -> __m128i {
|
||||
return transmute(__m128i)lddqu(mem_addr)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse3")
|
||||
_mm_movedup_pd :: #force_inline proc "c" (a: __m128d) -> __m128d {
|
||||
return simd.shuffle(a, a, 0, 0)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse3")
|
||||
_mm_loaddup_pd :: #force_inline proc "c" (mem_addr: [^]f64) -> __m128d {
|
||||
return _mm_load1_pd(mem_addr)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse3")
|
||||
_mm_movehdup_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
|
||||
return simd.shuffle(a, a, 1, 1, 3, 3)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse3")
|
||||
_mm_moveldup_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
|
||||
return simd.shuffle(a, a, 0, 0, 2, 2)
|
||||
}
|
||||
|
||||
@(private, default_calling_convention="c")
|
||||
foreign _ {
|
||||
@(link_name = "llvm.x86.sse3.addsub.ps")
|
||||
addsubps :: proc(a, b: __m128) -> __m128 ---
|
||||
@(link_name = "llvm.x86.sse3.addsub.pd")
|
||||
addsubpd :: proc(a: __m128d, b: __m128d) -> __m128d ---
|
||||
@(link_name = "llvm.x86.sse3.hadd.pd")
|
||||
haddpd :: proc(a: __m128d, b: __m128d) -> __m128d ---
|
||||
@(link_name = "llvm.x86.sse3.hadd.ps")
|
||||
haddps :: proc(a, b: __m128) -> __m128 ---
|
||||
@(link_name = "llvm.x86.sse3.hsub.pd")
|
||||
hsubpd :: proc(a: __m128d, b: __m128d) -> __m128d ---
|
||||
@(link_name = "llvm.x86.sse3.hsub.ps")
|
||||
hsubps :: proc(a, b: __m128) -> __m128 ---
|
||||
@(link_name = "llvm.x86.sse3.ldu.dq")
|
||||
lddqu :: proc(mem_addr: rawptr) -> i8x16 ---
|
||||
}
|
||||
352
core/simd/x86/sse41.odin
Normal file
352
core/simd/x86/sse41.odin
Normal file
@@ -0,0 +1,352 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:simd"
|
||||
|
||||
// SSE4 rounding constants
|
||||
_MM_FROUND_TO_NEAREST_INT :: 0x00
|
||||
_MM_FROUND_TO_NEG_INF :: 0x01
|
||||
_MM_FROUND_TO_POS_INF :: 0x02
|
||||
_MM_FROUND_TO_ZERO :: 0x03
|
||||
_MM_FROUND_CUR_DIRECTION :: 0x04
|
||||
_MM_FROUND_RAISE_EXC :: 0x00
|
||||
_MM_FROUND_NO_EXC :: 0x08
|
||||
_MM_FROUND_NINT :: 0x00
|
||||
_MM_FROUND_FLOOR :: _MM_FROUND_RAISE_EXC | _MM_FROUND_TO_NEG_INF
|
||||
_MM_FROUND_CEIL :: _MM_FROUND_RAISE_EXC | _MM_FROUND_TO_POS_INF
|
||||
_MM_FROUND_TRUNC :: _MM_FROUND_RAISE_EXC | _MM_FROUND_TO_ZERO
|
||||
_MM_FROUND_RINT :: _MM_FROUND_RAISE_EXC | _MM_FROUND_CUR_DIRECTION
|
||||
_MM_FROUND_NEARBYINT :: _MM_FROUND_NO_EXC | _MM_FROUND_CUR_DIRECTION
|
||||
|
||||
|
||||
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_blendv_epi8 :: #force_inline proc "c" (a, b, mask: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pblendvb(transmute(i8x16)a, transmute(i8x16)b, transmute(i8x16)mask)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_blend_epi16 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u8) -> __m128i {
|
||||
return transmute(__m128i)pblendw(transmute(i16x8)a, transmute(i16x8)b, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_blendv_pd :: #force_inline proc "c" (a, b, mask: __m128d) -> __m128d {
|
||||
return blendvpd(a, b, mask)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_blendv_ps :: #force_inline proc "c" (a, b, mask: __m128) -> __m128 {
|
||||
return blendvps(a, b, mask)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_blend_pd :: #force_inline proc "c" (a, b: __m128d, $IMM2: u8) -> __m128d {
|
||||
return blendpd(a, b, IMM2)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_blend_ps :: #force_inline proc "c" (a, b: __m128, $IMM4: u8) -> __m128 {
|
||||
return blendps(a, b, IMM4)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_extract_ps :: #force_inline proc "c" (a: __m128, $IMM8: u32) -> i32 {
|
||||
return transmute(i32)simd.extract(a, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_extract_epi8 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> i32 {
|
||||
return i32(simd.extract(transmute(u8x16)a, IMM8))
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_extract_epi32 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> i32 {
|
||||
return simd.extract(transmute(i32x4)a, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_insert_ps :: #force_inline proc "c" (a, b: __m128, $IMM8: u8) -> __m128 {
|
||||
return insertps(a, b, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_insert_epi8 :: #force_inline proc "c" (a: __m128i, i: i32, $IMM8: u32) -> __m128i {
|
||||
return transmute(__m128i)simd.replace(transmute(i8x16)a, IMM8, i8(i))
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_insert_epi32 :: #force_inline proc "c" (a: __m128i, i: i32, $IMM8: u32) -> __m128i {
|
||||
return transmute(__m128i)simd.replace(transmute(i32x4)a, IMM8, i)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_max_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pmaxsb(transmute(i8x16)a, transmute(i8x16)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_max_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pmaxuw(transmute(u16x8)a, transmute(u16x8)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_max_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pmaxsd(transmute(i32x4)a, transmute(i32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_max_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pmaxud(transmute(u32x4)a, transmute(u32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_min_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pminsb(transmute(i8x16)a, transmute(i8x16)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_min_epu16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pminuw(transmute(u16x8)a, transmute(u16x8)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_min_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pminsd(transmute(i32x4)a, transmute(i32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_min_epu32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pminud(transmute(u32x4)a, transmute(u32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_packus_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)packusdw(transmute(i32x4)a, transmute(i32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_cmpeq_epi64 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)simd.lanes_eq(transmute(i64x2)a, transmute(i64x2)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_cvtepi8_epi16 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
x := transmute(i8x16)a
|
||||
y := simd.shuffle(x, x, 0, 1, 2, 3, 4, 5, 6, 7)
|
||||
return transmute(__m128i)i16x8(y)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_cvtepi8_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
x := transmute(i8x16)a
|
||||
y := simd.shuffle(x, x, 0, 1, 2, 3)
|
||||
return transmute(__m128i)i32x4(y)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_cvtepi8_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
x := transmute(i8x16)a
|
||||
y := simd.shuffle(x, x, 0, 1)
|
||||
return transmute(__m128i)i64x2(y)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_cvtepi16_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
x := transmute(i16x8)a
|
||||
y := simd.shuffle(x, x, 0, 1, 2, 3)
|
||||
return transmute(__m128i)i32x4(y)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_cvtepi16_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
x := transmute(i16x8)a
|
||||
y := simd.shuffle(x, x, 0, 1)
|
||||
return transmute(__m128i)i64x2(y)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_cvtepi32_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
x := transmute(i32x4)a
|
||||
y := simd.shuffle(x, x, 0, 1)
|
||||
return transmute(__m128i)i64x2(y)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_cvtepu8_epi16 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
x := transmute(u8x16)a
|
||||
y := simd.shuffle(x, x, 0, 1, 2, 3, 4, 5, 6, 7)
|
||||
return transmute(__m128i)i16x8(y)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_cvtepu8_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
x := transmute(u8x16)a
|
||||
y := simd.shuffle(x, x, 0, 1, 2, 3)
|
||||
return transmute(__m128i)i32x4(y)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_cvtepu8_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
x := transmute(u8x16)a
|
||||
y := simd.shuffle(x, x, 0, 1)
|
||||
return transmute(__m128i)i64x2(y)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_cvtepu16_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
x := transmute(u16x8)a
|
||||
y := simd.shuffle(x, x, 0, 1, 2, 3)
|
||||
return transmute(__m128i)i32x4(y)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_cvtepu16_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
x := transmute(u16x8)a
|
||||
y := simd.shuffle(x, x, 0, 1)
|
||||
return transmute(__m128i)i64x2(y)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_cvtepu32_epi64 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
x := transmute(u32x4)a
|
||||
y := simd.shuffle(x, x, 0, 1)
|
||||
return transmute(__m128i)i64x2(y)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_dp_pd :: #force_inline proc "c" (a, b: __m128d, $IMM8: u8) -> __m128d {
|
||||
return dppd(a, b, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_dp_ps :: #force_inline proc "c" (a, b: __m128, $IMM8: u8) -> __m128 {
|
||||
return dpps(a, b, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_floor_pd :: #force_inline proc "c" (a: __m128d) -> __m128d {
|
||||
return simd.floor(a)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_floor_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
|
||||
return simd.floor(a)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_floor_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
|
||||
return roundsd(a, b, _MM_FROUND_FLOOR)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_floor_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return roundss(a, b, _MM_FROUND_FLOOR)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_ceil_pd :: #force_inline proc "c" (a: __m128d) -> __m128d {
|
||||
return simd.ceil(a)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_ceil_ps :: #force_inline proc "c" (a: __m128) -> __m128 {
|
||||
return simd.ceil(a)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_ceil_sd :: #force_inline proc "c" (a, b: __m128d) -> __m128d {
|
||||
return roundsd(a, b, _MM_FROUND_CEIL)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_ceil_ss :: #force_inline proc "c" (a, b: __m128) -> __m128 {
|
||||
return roundss(a, b, _MM_FROUND_CEIL)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_round_pd :: #force_inline proc "c" (a: __m128d, $ROUNDING: i32) -> __m128d {
|
||||
return roundpd(a, ROUNDING)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_round_ps :: #force_inline proc "c" (a: __m128, $ROUNDING: i32) -> __m128 {
|
||||
return roundps(a, ROUNDING)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_round_sd :: #force_inline proc "c" (a, b: __m128d, $ROUNDING: i32) -> __m128d {
|
||||
return roundsd(a, b, ROUNDING)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_round_ss :: #force_inline proc "c" (a, b: __m128, $ROUNDING: i32) -> __m128 {
|
||||
return roundss(a, b, ROUNDING)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_minpos_epu16 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
return transmute(__m128i)phminposuw(transmute(u16x8)a)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_mul_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pmuldq(transmute(i32x4)a, transmute(i32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_mullo_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)simd.mul(transmute(i32x4)a, transmute(i32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_mpsadbw_epu8 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u8) -> __m128i {
|
||||
return transmute(__m128i)mpsadbw(transmute(u8x16)a, transmute(u8x16)b, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_testz_si128 :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
|
||||
return ptestz(transmute(i64x2)a, transmute(i64x2)mask)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_testc_si128 :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
|
||||
return ptestc(transmute(i64x2)a, transmute(i64x2)mask)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_testnzc_si128 :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
|
||||
return ptestnzc(transmute(i64x2)a, transmute(i64x2)mask)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_test_all_zeros :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
|
||||
return _mm_testz_si128(a, mask)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_test_all_ones :: #force_inline proc "c" (a: __m128i) -> i32 {
|
||||
return _mm_testc_si128(a, _mm_cmpeq_epi32(a, a))
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_test_mix_ones_zeros :: #force_inline proc "c" (a: __m128i, mask: __m128i) -> i32 {
|
||||
return _mm_testnzc_si128(a, mask)
|
||||
}
|
||||
|
||||
|
||||
when ODIN_ARCH == .amd64 {
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_extract_epi64 :: #force_inline proc "c" (a: __m128i, $IMM1: u32) -> i64 {
|
||||
return simd.extract(transmute(i64x2)a, IMM1)
|
||||
}
|
||||
|
||||
@(require_results, enable_target_feature="sse4.1")
|
||||
_mm_insert_epi64 :: #force_inline proc "c" (a: __m128i, i: i64, $IMM1: u32) -> __m128i {
|
||||
return transmute(__m128i)simd.replace(transmute(i64x2)a, IMM1, i)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@(private, default_calling_convention="c")
|
||||
foreign _ {
|
||||
@(link_name = "llvm.x86.sse41.pblendvb")
|
||||
pblendvb :: proc(a, b: i8x16, mask: i8x16) -> i8x16 ---
|
||||
@(link_name = "llvm.x86.sse41.blendvpd")
|
||||
blendvpd :: proc(a, b, mask: __m128d) -> __m128d ---
|
||||
@(link_name = "llvm.x86.sse41.blendvps")
|
||||
blendvps :: proc(a, b, mask: __m128) -> __m128 ---
|
||||
@(link_name = "llvm.x86.sse41.blendpd")
|
||||
blendpd :: proc(a, b: __m128d, #const imm2: u8) -> __m128d ---
|
||||
@(link_name = "llvm.x86.sse41.blendps")
|
||||
blendps :: proc(a, b: __m128, #const imm4: u8) -> __m128 ---
|
||||
@(link_name = "llvm.x86.sse41.pblendw")
|
||||
pblendw :: proc(a: i16x8, b: i16x8, #const imm8: u8) -> i16x8 ---
|
||||
@(link_name = "llvm.x86.sse41.insertps")
|
||||
insertps :: proc(a, b: __m128, #const imm8: u8) -> __m128 ---
|
||||
@(link_name = "llvm.x86.sse41.pmaxsb")
|
||||
pmaxsb :: proc(a, b: i8x16) -> i8x16 ---
|
||||
@(link_name = "llvm.x86.sse41.pmaxuw")
|
||||
pmaxuw :: proc(a, b: u16x8) -> u16x8 ---
|
||||
@(link_name = "llvm.x86.sse41.pmaxsd")
|
||||
pmaxsd :: proc(a, b: i32x4) -> i32x4 ---
|
||||
@(link_name = "llvm.x86.sse41.pmaxud")
|
||||
pmaxud :: proc(a, b: u32x4) -> u32x4 ---
|
||||
@(link_name = "llvm.x86.sse41.pminsb")
|
||||
pminsb :: proc(a, b: i8x16) -> i8x16 ---
|
||||
@(link_name = "llvm.x86.sse41.pminuw")
|
||||
pminuw :: proc(a, b: u16x8) -> u16x8 ---
|
||||
@(link_name = "llvm.x86.sse41.pminsd")
|
||||
pminsd :: proc(a, b: i32x4) -> i32x4 ---
|
||||
@(link_name = "llvm.x86.sse41.pminud")
|
||||
pminud :: proc(a, b: u32x4) -> u32x4 ---
|
||||
@(link_name = "llvm.x86.sse41.packusdw")
|
||||
packusdw :: proc(a, b: i32x4) -> u16x8 ---
|
||||
@(link_name = "llvm.x86.sse41.dppd")
|
||||
dppd :: proc(a, b: __m128d, #const imm8: u8) -> __m128d ---
|
||||
@(link_name = "llvm.x86.sse41.dpps")
|
||||
dpps :: proc(a, b: __m128, #const imm8: u8) -> __m128 ---
|
||||
@(link_name = "llvm.x86.sse41.round.pd")
|
||||
roundpd :: proc(a: __m128d, rounding: i32) -> __m128d ---
|
||||
@(link_name = "llvm.x86.sse41.round.ps")
|
||||
roundps :: proc(a: __m128, rounding: i32) -> __m128 ---
|
||||
@(link_name = "llvm.x86.sse41.round.sd")
|
||||
roundsd :: proc(a, b: __m128d, rounding: i32) -> __m128d ---
|
||||
@(link_name = "llvm.x86.sse41.round.ss")
|
||||
roundss :: proc(a, b: __m128, rounding: i32) -> __m128 ---
|
||||
@(link_name = "llvm.x86.sse41.phminposuw")
|
||||
phminposuw :: proc(a: u16x8) -> u16x8 ---
|
||||
@(link_name = "llvm.x86.sse41.pmuldq")
|
||||
pmuldq :: proc(a, b: i32x4) -> i64x2 ---
|
||||
@(link_name = "llvm.x86.sse41.mpsadbw")
|
||||
mpsadbw :: proc(a, b: u8x16, #const imm8: u8) -> u16x8 ---
|
||||
@(link_name = "llvm.x86.sse41.ptestz")
|
||||
ptestz :: proc(a, mask: i64x2) -> i32 ---
|
||||
@(link_name = "llvm.x86.sse41.ptestc")
|
||||
ptestc :: proc(a, mask: i64x2) -> i32 ---
|
||||
@(link_name = "llvm.x86.sse41.ptestnzc")
|
||||
ptestnzc :: proc(a, mask: i64x2) -> i32 ---
|
||||
}
|
||||
149
core/simd/x86/sse42.odin
Normal file
149
core/simd/x86/sse42.odin
Normal file
@@ -0,0 +1,149 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:simd"
|
||||
|
||||
_SIDD_UBYTE_OPS :: 0b0000_0000
|
||||
_SIDD_UWORD_OPS :: 0b0000_0001
|
||||
_SIDD_SBYTE_OPS :: 0b0000_0010
|
||||
_SIDD_SWORD_OPS :: 0b0000_0011
|
||||
|
||||
_SIDD_CMP_EQUAL_ANY :: 0b0000_0000
|
||||
_SIDD_CMP_RANGES :: 0b0000_0100
|
||||
_SIDD_CMP_EQUAL_EACH :: 0b0000_1000
|
||||
_SIDD_CMP_EQUAL_ORDERED :: 0b0000_1100
|
||||
|
||||
_SIDD_POSITIVE_POLARITY :: 0b0000_0000
|
||||
_SIDD_NEGATIVE_POLARITY :: 0b0001_0000
|
||||
_SIDD_MASKED_POSITIVE_POLARITY :: 0b0010_0000
|
||||
_SIDD_MASKED_NEGATIVE_POLARITY :: 0b0011_0000
|
||||
|
||||
_SIDD_LEAST_SIGNIFICANT :: 0b0000_0000
|
||||
_SIDD_MOST_SIGNIFICANT :: 0b0100_0000
|
||||
|
||||
_SIDD_BIT_MASK :: 0b0000_0000
|
||||
_SIDD_UNIT_MASK :: 0b0100_0000
|
||||
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpistrm :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> __m128i {
|
||||
return transmute(__m128i)pcmpistrm128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpistri :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 {
|
||||
return pcmpistri128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpistrz :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 {
|
||||
return pcmpistriz128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpistrc :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 {
|
||||
return pcmpistric128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpistrs :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 {
|
||||
return pcmpistris128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpistro :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 {
|
||||
return pcmpistrio128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpistra :: #force_inline proc "c" (a: __m128i, b: __m128i, $IMM8: i8) -> i32 {
|
||||
return pcmpistria128(transmute(i8x16)a, transmute(i8x16)b, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpestrm :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> __m128i {
|
||||
return transmute(__m128i)pcmpestrm128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpestri :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 {
|
||||
return pcmpestri128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpestrz :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 {
|
||||
return pcmpestriz128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpestrc :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 {
|
||||
return pcmpestric128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpestrs :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 {
|
||||
return pcmpestris128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpestro :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 {
|
||||
return pcmpestrio128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpestra :: #force_inline proc "c" (a: __m128i, la: i32, b: __m128i, lb: i32, $IMM8: i8) -> i32 {
|
||||
return pcmpestria128(transmute(i8x16)a, la, transmute(i8x16)b, lb, IMM8)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_crc32_u8 :: #force_inline proc "c" (crc: u32, v: u8) -> u32 {
|
||||
return crc32_32_8(crc, v)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_crc32_u16 :: #force_inline proc "c" (crc: u32, v: u16) -> u32 {
|
||||
return crc32_32_16(crc, v)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_crc32_u32 :: #force_inline proc "c" (crc: u32, v: u32) -> u32 {
|
||||
return crc32_32_32(crc, v)
|
||||
}
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_cmpgt_epi64 :: #force_inline proc "c" (a: __m128i, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)simd.lanes_gt(transmute(i64x2)a, transmute(i64x2)b)
|
||||
}
|
||||
|
||||
when ODIN_ARCH == .amd64 {
|
||||
@(require_results, enable_target_feature="sse4.2")
|
||||
_mm_crc32_u64 :: #force_inline proc "c" (crc: u64, v: u64) -> u64 {
|
||||
return crc32_64_64(crc, v)
|
||||
}
|
||||
}
|
||||
|
||||
@(private, default_calling_convention="c")
|
||||
foreign _ {
|
||||
// SSE 4.2 string and text comparison ops
|
||||
@(link_name="llvm.x86.sse42.pcmpestrm128")
|
||||
pcmpestrm128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> u8x16 ---
|
||||
@(link_name="llvm.x86.sse42.pcmpestri128")
|
||||
pcmpestri128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 ---
|
||||
@(link_name="llvm.x86.sse42.pcmpestriz128")
|
||||
pcmpestriz128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 ---
|
||||
@(link_name="llvm.x86.sse42.pcmpestric128")
|
||||
pcmpestric128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 ---
|
||||
@(link_name="llvm.x86.sse42.pcmpestris128")
|
||||
pcmpestris128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 ---
|
||||
@(link_name="llvm.x86.sse42.pcmpestrio128")
|
||||
pcmpestrio128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 ---
|
||||
@(link_name="llvm.x86.sse42.pcmpestria128")
|
||||
pcmpestria128 :: proc(a: i8x16, la: i32, b: i8x16, lb: i32, #const imm8: i8) -> i32 ---
|
||||
@(link_name="llvm.x86.sse42.pcmpistrm128")
|
||||
pcmpistrm128 :: proc(a, b: i8x16, #const imm8: i8) -> i8x16 ---
|
||||
@(link_name="llvm.x86.sse42.pcmpistri128")
|
||||
pcmpistri128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 ---
|
||||
@(link_name="llvm.x86.sse42.pcmpistriz128")
|
||||
pcmpistriz128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 ---
|
||||
@(link_name="llvm.x86.sse42.pcmpistric128")
|
||||
pcmpistric128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 ---
|
||||
@(link_name="llvm.x86.sse42.pcmpistris128")
|
||||
pcmpistris128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 ---
|
||||
@(link_name="llvm.x86.sse42.pcmpistrio128")
|
||||
pcmpistrio128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 ---
|
||||
@(link_name="llvm.x86.sse42.pcmpistria128")
|
||||
pcmpistria128 :: proc(a, b: i8x16, #const imm8: i8) -> i32 ---
|
||||
// SSE 4.2 CRC instructions
|
||||
@(link_name="llvm.x86.sse42.crc32.32.8")
|
||||
crc32_32_8 :: proc(crc: u32, v: u8) -> u32 ---
|
||||
@(link_name="llvm.x86.sse42.crc32.32.16")
|
||||
crc32_32_16 :: proc(crc: u32, v: u16) -> u32 ---
|
||||
@(link_name="llvm.x86.sse42.crc32.32.32")
|
||||
crc32_32_32 :: proc(crc: u32, v: u32) -> u32 ---
|
||||
|
||||
// AMD64 Only
|
||||
@(link_name="llvm.x86.sse42.crc32.64.64")
|
||||
crc32_64_64 :: proc(crc: u64, v: u64) -> u64 ---
|
||||
}
|
||||
140
core/simd/x86/ssse3.odin
Normal file
140
core/simd/x86/ssse3.odin
Normal file
@@ -0,0 +1,140 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:simd"
|
||||
_ :: simd
|
||||
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_abs_epi8 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pabsb128(transmute(i8x16)a)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_abs_epi16 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pabsw128(transmute(i16x8)a)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_abs_epi32 :: #force_inline proc "c" (a: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pabsd128(transmute(i32x4)a)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_shuffle_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pshufb128(transmute(u8x16)a, transmute(u8x16)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_alignr_epi8 :: #force_inline proc "c" (a, b: __m128i, $IMM8: u32) -> __m128i {
|
||||
shift :: IMM8
|
||||
|
||||
// If palignr is shifting the pair of vectors more than the size of two
|
||||
// lanes, emit zero.
|
||||
if shift > 32 {
|
||||
return _mm_set1_epi8(0)
|
||||
}
|
||||
a, b := a, b
|
||||
if shift > 16 {
|
||||
a, b = _mm_set1_epi8(0), a
|
||||
}
|
||||
|
||||
return transmute(__m128i)simd.shuffle(
|
||||
transmute(i8x16)b,
|
||||
transmute(i8x16)a,
|
||||
0 when shift > 32 else shift - 16 + 0 when shift > 16 else shift + 0,
|
||||
1 when shift > 32 else shift - 16 + 1 when shift > 16 else shift + 1,
|
||||
2 when shift > 32 else shift - 16 + 2 when shift > 16 else shift + 2,
|
||||
3 when shift > 32 else shift - 16 + 3 when shift > 16 else shift + 3,
|
||||
4 when shift > 32 else shift - 16 + 4 when shift > 16 else shift + 4,
|
||||
5 when shift > 32 else shift - 16 + 5 when shift > 16 else shift + 5,
|
||||
6 when shift > 32 else shift - 16 + 6 when shift > 16 else shift + 6,
|
||||
7 when shift > 32 else shift - 16 + 7 when shift > 16 else shift + 7,
|
||||
8 when shift > 32 else shift - 16 + 8 when shift > 16 else shift + 8,
|
||||
9 when shift > 32 else shift - 16 + 9 when shift > 16 else shift + 9,
|
||||
10 when shift > 32 else shift - 16 + 10 when shift > 16 else shift + 10,
|
||||
11 when shift > 32 else shift - 16 + 11 when shift > 16 else shift + 11,
|
||||
12 when shift > 32 else shift - 16 + 12 when shift > 16 else shift + 12,
|
||||
13 when shift > 32 else shift - 16 + 13 when shift > 16 else shift + 13,
|
||||
14 when shift > 32 else shift - 16 + 14 when shift > 16 else shift + 14,
|
||||
15 when shift > 32 else shift - 16 + 15 when shift > 16 else shift + 15,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_hadd_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)phaddw128(transmute(i16x8)a, transmute(i16x8)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_hadds_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)phaddsw128(transmute(i16x8)a, transmute(i16x8)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_hadd_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)phaddd128(transmute(i32x4)a, transmute(i32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_hsub_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)phsubw128(transmute(i16x8)a, transmute(i16x8)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_hsubs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)phsubsw128(transmute(i16x8)a, transmute(i16x8)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_hsub_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)phsubd128(transmute(i32x4)a, transmute(i32x4)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_maddubs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pmaddubsw128(transmute(u8x16)a, transmute(i8x16)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_mulhrs_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)pmulhrsw128(transmute(i16x8)a, transmute(i16x8)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_sign_epi8 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)psignb128(transmute(i8x16)a, transmute(i8x16)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_sign_epi16 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)psignw128(transmute(i16x8)a, transmute(i16x8)b)
|
||||
}
|
||||
@(require_results, enable_target_feature="ssse3")
|
||||
_mm_sign_epi32 :: #force_inline proc "c" (a, b: __m128i) -> __m128i {
|
||||
return transmute(__m128i)psignd128(transmute(i32x4)a, transmute(i32x4)b)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private, default_calling_convention="c")
|
||||
foreign _ {
|
||||
@(link_name = "llvm.x86.ssse3.pabs.b.128")
|
||||
pabsb128 :: proc(a: i8x16) -> u8x16 ---
|
||||
@(link_name = "llvm.x86.ssse3.pabs.w.128")
|
||||
pabsw128 :: proc(a: i16x8) -> u16x8 ---
|
||||
@(link_name = "llvm.x86.ssse3.pabs.d.128")
|
||||
pabsd128 :: proc(a: i32x4) -> u32x4 ---
|
||||
@(link_name = "llvm.x86.ssse3.pshuf.b.128")
|
||||
pshufb128 :: proc(a, b: u8x16) -> u8x16 ---
|
||||
@(link_name = "llvm.x86.ssse3.phadd.w.128")
|
||||
phaddw128 :: proc(a, b: i16x8) -> i16x8 ---
|
||||
@(link_name = "llvm.x86.ssse3.phadd.sw.128")
|
||||
phaddsw128 :: proc(a, b: i16x8) -> i16x8 ---
|
||||
@(link_name = "llvm.x86.ssse3.phadd.d.128")
|
||||
phaddd128 :: proc(a, b: i32x4) -> i32x4 ---
|
||||
@(link_name = "llvm.x86.ssse3.phsub.w.128")
|
||||
phsubw128 :: proc(a, b: i16x8) -> i16x8 ---
|
||||
@(link_name = "llvm.x86.ssse3.phsub.sw.128")
|
||||
phsubsw128 :: proc(a, b: i16x8) -> i16x8 ---
|
||||
@(link_name = "llvm.x86.ssse3.phsub.d.128")
|
||||
phsubd128 :: proc(a, b: i32x4) -> i32x4 ---
|
||||
@(link_name = "llvm.x86.ssse3.pmadd.ub.sw.128")
|
||||
pmaddubsw128 :: proc(a: u8x16, b: i8x16) -> i16x8 ---
|
||||
@(link_name = "llvm.x86.ssse3.pmul.hr.sw.128")
|
||||
pmulhrsw128 :: proc(a, b: i16x8) -> i16x8 ---
|
||||
@(link_name = "llvm.x86.ssse3.psign.b.128")
|
||||
psignb128 :: proc(a, b: i8x16) -> i8x16 ---
|
||||
@(link_name = "llvm.x86.ssse3.psign.w.128")
|
||||
psignw128 :: proc(a, b: i16x8) -> i16x8 ---
|
||||
@(link_name = "llvm.x86.ssse3.psign.d.128")
|
||||
psignd128 :: proc(a, b: i32x4) -> i32x4 ---
|
||||
}
|
||||
57
core/simd/x86/types.odin
Normal file
57
core/simd/x86/types.odin
Normal file
@@ -0,0 +1,57 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:simd"
|
||||
|
||||
bf16 :: u16
|
||||
|
||||
__m128i :: #simd[2]i64
|
||||
__m128 :: #simd[4]f32
|
||||
__m128d :: #simd[2]f64
|
||||
|
||||
__m256i :: #simd[4]i64
|
||||
__m256 :: #simd[8]f32
|
||||
__m256d :: #simd[4]f64
|
||||
|
||||
__m512i :: #simd[8]i64
|
||||
__m512 :: #simd[16]f32
|
||||
__m512d :: #simd[8]f64
|
||||
|
||||
__m128bh :: #simd[8]bf16
|
||||
__m256bh :: #simd[16]bf16
|
||||
__m512bh :: #simd[32]bf16
|
||||
|
||||
|
||||
/// The `__mmask64` type used in AVX-512 intrinsics, a 64-bit integer
|
||||
__mmask64 :: u64
|
||||
|
||||
/// The `__mmask32` type used in AVX-512 intrinsics, a 32-bit integer
|
||||
__mmask32 :: u32
|
||||
|
||||
/// The `__mmask16` type used in AVX-512 intrinsics, a 16-bit integer
|
||||
__mmask16 :: u16
|
||||
|
||||
/// The `__mmask8` type used in AVX-512 intrinsics, a 8-bit integer
|
||||
__mmask8 :: u8
|
||||
|
||||
/// The `_MM_CMPINT_ENUM` type used to specify comparison operations in AVX-512 intrinsics.
|
||||
_MM_CMPINT_ENUM :: i32
|
||||
|
||||
/// The `MM_MANTISSA_NORM_ENUM` type used to specify mantissa normalized operations in AVX-512 intrinsics.
|
||||
_MM_MANTISSA_NORM_ENUM :: i32
|
||||
|
||||
/// The `MM_MANTISSA_SIGN_ENUM` type used to specify mantissa signed operations in AVX-512 intrinsics.
|
||||
_MM_MANTISSA_SIGN_ENUM :: i32
|
||||
|
||||
_MM_PERM_ENUM :: i32
|
||||
|
||||
@(private) u8x16 :: simd.u8x16
|
||||
@(private) i8x16 :: simd.i8x16
|
||||
@(private) u16x8 :: simd.u16x8
|
||||
@(private) i16x8 :: simd.i16x8
|
||||
@(private) u32x4 :: simd.u32x4
|
||||
@(private) i32x4 :: simd.i32x4
|
||||
@(private) u64x2 :: simd.u64x2
|
||||
@(private) i64x2 :: simd.i64x2
|
||||
@(private) f32x4 :: simd.f32x4
|
||||
@(private) f64x2 :: simd.f64x2
|
||||
@@ -2,11 +2,9 @@ package slice
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:runtime"
|
||||
import "core:mem"
|
||||
|
||||
_ :: intrinsics
|
||||
_ :: runtime
|
||||
_ :: mem
|
||||
|
||||
map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K) {
|
||||
keys = make(type_of(keys), len(m), allocator)
|
||||
@@ -52,7 +50,7 @@ map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries
|
||||
|
||||
map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check {
|
||||
m := m
|
||||
rm := (^mem.Raw_Map)(&m)
|
||||
rm := (^runtime.Raw_Map)(&m)
|
||||
|
||||
info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map)
|
||||
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package strings
|
||||
|
||||
import "core:mem"
|
||||
import "core:runtime"
|
||||
import "core:unicode/utf8"
|
||||
import "core:strconv"
|
||||
import "core:io"
|
||||
@@ -124,22 +124,23 @@ reset_builder :: proc(b: ^Builder) {
|
||||
used in `fmt.bprint*`
|
||||
|
||||
bytes: [8]byte // <-- gets filled
|
||||
builder := strings.builder_from_slice(bytes[:])
|
||||
builder := strings.builder_from_bytes(bytes[:])
|
||||
strings.write_byte(&builder, 'a') -> "a"
|
||||
strings.write_byte(&builder, 'b') -> "ab"
|
||||
*/
|
||||
builder_from_slice :: proc(backing: []byte) -> Builder {
|
||||
s := transmute(mem.Raw_Slice)backing
|
||||
d := mem.Raw_Dynamic_Array{
|
||||
builder_from_bytes :: proc(backing: []byte) -> Builder {
|
||||
s := transmute(runtime.Raw_Slice)backing
|
||||
d := runtime.Raw_Dynamic_Array{
|
||||
data = s.data,
|
||||
len = 0,
|
||||
cap = s.len,
|
||||
allocator = mem.nil_allocator(),
|
||||
allocator = runtime.nil_allocator(),
|
||||
}
|
||||
return Builder{
|
||||
buf = transmute([dynamic]byte)d,
|
||||
}
|
||||
}
|
||||
builder_from_slice :: builder_from_bytes
|
||||
|
||||
// cast the builder byte buffer to a string and return it
|
||||
to_string :: proc(b: Builder) -> string {
|
||||
@@ -275,7 +276,7 @@ pop_byte :: proc(b: ^Builder) -> (r: byte) {
|
||||
}
|
||||
|
||||
r = b.buf[len(b.buf)-1]
|
||||
d := cast(^mem.Raw_Dynamic_Array)&b.buf
|
||||
d := cast(^runtime.Raw_Dynamic_Array)&b.buf
|
||||
d.len = max(d.len-1, 0)
|
||||
return
|
||||
}
|
||||
@@ -288,7 +289,7 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
|
||||
}
|
||||
|
||||
r, width = utf8.decode_last_rune(b.buf[:])
|
||||
d := cast(^mem.Raw_Dynamic_Array)&b.buf
|
||||
d := cast(^runtime.Raw_Dynamic_Array)&b.buf
|
||||
d.len = max(d.len-width, 0)
|
||||
return
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user