Merge branch 'master' into windows-llvm-13.0.0

This commit is contained in:
gingerBill
2022-06-03 14:18:26 +01:00
221 changed files with 45292 additions and 12835 deletions

2
.gitignore vendored
View File

@@ -269,6 +269,8 @@ bin/
# - Linux/MacOS
odin
odin.dSYM
*.bin
demo.bin
# shared collection
shared/

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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))

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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 {

View 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

View 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)
}

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -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
}
}

View File

@@ -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:

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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,

View 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)
}
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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
View 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
}

View File

@@ -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{}

View File

@@ -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)

View 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))
}

View File

@@ -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)

View 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
}
}

View 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
}
}

View 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
}

View File

@@ -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

View File

@@ -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,

View File

@@ -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)]
}

View File

@@ -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) {

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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
}
}

View File

@@ -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))
}

View 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
}

View File

@@ -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
}
}

View File

@@ -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"

View 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"
}

View File

@@ -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)
}

View File

@@ -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
View 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, &times, 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, &times, 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
}

View File

@@ -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 {

View File

@@ -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
}
}
}

View File

@@ -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
View 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(
&region_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(
&region_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 := &region.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
}

View File

@@ -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
View 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
}

View File

@@ -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)

View File

@@ -0,0 +1,7 @@
//+private
package os2
_pipe :: proc() -> (r, w: ^File, err: Error) {
return nil, nil, nil
}

View File

@@ -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
}

View File

@@ -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
View 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
}

View File

@@ -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

View File

@@ -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)
}

View 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
}

View File

@@ -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)
}

View File

@@ -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
}

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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 {

View File

@@ -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

View File

@@ -135,7 +135,6 @@ Type_Info_Union :: struct {
custom_align: bool,
no_nil: bool,
maybe: bool,
shared_nil: bool,
}
Type_Info_Enum :: struct {

View File

@@ -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}

View File

@@ -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
View 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
View 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
View 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) ---
}

View 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
View 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
View 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) ---
}

View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

68
core/simd/x86/sse3.odin Normal file
View 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
View 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
View 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
View 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
View 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

View File

@@ -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)

View File

@@ -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