Merge branch 'master' into netbsd-ci

This commit is contained in:
Andreas T Jonsson
2024-05-16 13:48:44 +02:00
42 changed files with 659 additions and 235 deletions
@@ -12,6 +12,7 @@ Memory_Block :: struct {
capacity: uint,
}
// NOTE: This is for internal use, prefer `Arena` from `core:mem/virtual` if necessary
Arena :: struct {
backing_allocator: Allocator,
curr_block: ^Memory_Block,
+2 -2
View File
@@ -137,8 +137,8 @@ IP4_Address :: distinct [4]u8
IP6_Address :: distinct [8]u16be
Address :: union {IP4_Address, IP6_Address}
IP4_Loopback := IP4_Address{127, 0, 0, 1}
IP6_Loopback := IP6_Address{0, 0, 0, 0, 0, 0, 0, 1}
IP4_Loopback :: IP4_Address{127, 0, 0, 1}
IP6_Loopback :: IP6_Address{0, 0, 0, 0, 0, 0, 0, 1}
IP4_Any := IP4_Address{}
IP6_Any := IP6_Address{}
+48
View File
@@ -0,0 +1,48 @@
//+private
package os2
import "base:runtime"
@(require_results)
file_allocator :: proc() -> runtime.Allocator {
return heap_allocator()
}
temp_allocator_proc :: runtime.arena_allocator_proc
@(private="file", thread_local)
global_default_temp_allocator_arena: runtime.Arena
@(require_results)
temp_allocator :: proc() -> runtime.Allocator {
return runtime.Allocator{
procedure = temp_allocator_proc,
data = &global_default_temp_allocator_arena,
}
}
@(require_results)
temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: runtime.Arena_Temp) {
temp = runtime.arena_temp_begin(&global_default_temp_allocator_arena, loc)
return
}
temp_allocator_temp_end :: proc(temp: runtime.Arena_Temp, loc := #caller_location) {
runtime.arena_temp_end(temp, loc)
}
@(fini, private)
temp_allocator_fini :: proc() {
runtime.arena_destroy(&global_default_temp_allocator_arena)
global_default_temp_allocator_arena = {}
}
@(deferred_out=temp_allocator_temp_end)
TEMP_ALLOCATOR_GUARD :: #force_inline proc(ignore := false, loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) {
if ignore {
return {}, loc
} else {
return temp_allocator_temp_begin(loc), loc
}
}
+3
View File
@@ -6,6 +6,7 @@ import "base:runtime"
// It returns the value, which will be empty if the variable is not present
// To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator
@(require_results)
get_env :: proc(key: string, allocator: runtime.Allocator) -> string {
value, _ := lookup_env(key, allocator)
return value
@@ -15,6 +16,7 @@ get_env :: proc(key: string, allocator: runtime.Allocator) -> string {
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
// Otherwise the returned value will be empty and the boolean will be false
// NOTE: the value will be allocated with the supplied allocator
@(require_results)
lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
return _lookup_env(key, allocator)
}
@@ -38,6 +40,7 @@ clear_env :: proc() {
// environ returns a copy of strings representing the environment, in the form "key=value"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
@(require_results)
environ :: proc(allocator: runtime.Allocator) -> []string {
return _environ(allocator)
}
+4 -4
View File
@@ -19,9 +19,9 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return "", true
}
_TEMP_ALLOCATOR_GUARD()
TEMP_ALLOCATOR_GUARD()
b := make([]u16, n+1, _temp_allocator())
b := make([]u16, n+1, temp_allocator())
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
if n == 0 {
@@ -50,8 +50,8 @@ _unset_env :: proc(key: string) -> bool {
}
_clear_env :: proc() {
_TEMP_ALLOCATOR_GUARD()
envs := environ(_temp_allocator())
TEMP_ALLOCATOR_GUARD()
envs := environ(temp_allocator())
for env in envs {
for j in 1..<len(env) {
if env[j] == '=' {
+3
View File
@@ -23,6 +23,8 @@ General_Error :: enum u32 {
Invalid_Dir,
Invalid_Path,
Pattern_Has_Separator,
Unsupported,
}
@@ -63,6 +65,7 @@ error_string :: proc(ferr: Error) -> string {
case .Invalid_Dir: return "invalid directory"
case .Invalid_Path: return "invalid path"
case .Unsupported: return "unsupported"
case .Pattern_Has_Separator: return "pattern has separator"
}
case io.Error:
switch e {
+13 -4
View File
@@ -5,8 +5,9 @@ import "core:time"
import "base:runtime"
File :: struct {
impl: _File,
impl: _File,
stream: io.Stream,
user_fstat: Fstat_Callback,
}
File_Mode :: distinct u32
@@ -51,22 +52,27 @@ stdout: ^File = nil // OS-Specific
stderr: ^File = nil // OS-Specific
@(require_results)
create :: proc(name: string) -> (^File, Error) {
return open(name, {.Read, .Write, .Create}, File_Mode(0o777))
}
@(require_results)
open :: proc(name: string, flags := File_Flags{.Read}, perm := File_Mode(0o777)) -> (^File, Error) {
return _open(name, flags, perm)
}
@(require_results)
new_file :: proc(handle: uintptr, name: string) -> ^File {
return _new_file(handle, name)
}
@(require_results)
fd :: proc(f: ^File) -> uintptr {
return _fd(f)
}
@(require_results)
name :: proc(f: ^File) -> string {
return _name(f)
}
@@ -200,15 +206,18 @@ fchange_times :: proc(f: ^File, atime, mtime: time.Time) -> Error {
return _fchtimes(f, atime, mtime)
}
@(require_results)
exists :: proc(path: string) -> bool {
return _exists(path)
}
@(require_results)
is_file :: proc(path: string) -> bool {
return _is_file(path)
}
is_dir :: is_directory
@(require_results)
is_directory :: proc(path: string) -> bool {
return _is_dir(path)
}
@@ -218,8 +227,8 @@ 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())
info := fstat(src, file_allocator()) or_return
defer file_info_delete(info, file_allocator())
if info.is_directory {
return .Invalid_File
}
@@ -229,4 +238,4 @@ copy_file :: proc(dst_path, src_path: string) -> Error {
_, err := io.copy(to_writer(dst), to_reader(src))
return err
}
}
+52 -50
View File
@@ -1,10 +1,9 @@
//+private
package os2
import "base:runtime"
import "core:io"
import "core:time"
import "core:strings"
import "base:runtime"
import "core:sys/unix"
INVALID_HANDLE :: -1
@@ -35,13 +34,9 @@ _File :: struct {
allocator: runtime.Allocator,
}
_file_allocator :: proc() -> runtime.Allocator {
return heap_allocator()
}
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
// Just default to using O_NOCTTY because needing to open a controlling
// terminal would be incredibly rare. This has no effect on files while
@@ -53,12 +48,12 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error
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))
if .Append in flags { flags_i |= _O_APPEND }
if .Create in flags { flags_i |= _O_CREAT }
if .Excl in flags { flags_i |= _O_EXCL }
if .Sync in flags { flags_i |= _O_SYNC }
if .Trunc in flags { flags_i |= _O_TRUNC }
if .Close_On_Exec in flags { flags_i |= _O_CLOEXEC }
fd := unix.sys_open(name_cstr, flags_i, uint(perm))
if fd < 0 {
@@ -69,9 +64,9 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error
}
_new_file :: proc(fd: uintptr, _: string) -> ^File {
file := new(File, _file_allocator())
file := new(File, file_allocator())
file.impl.fd = int(fd)
file.impl.allocator = _file_allocator()
file.impl.allocator = file_allocator()
file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator)
file.stream = {
data = file,
@@ -91,8 +86,12 @@ _destroy :: proc(f: ^File) -> Error {
_close :: proc(f: ^File) -> Error {
res := unix.sys_close(f.impl.fd)
return _ok_or_error(res)
if f != nil {
res := unix.sys_close(f.impl.fd)
_destroy(f)
return _ok_or_error(res)
}
return nil
}
_fd :: proc(f: ^File) -> uintptr {
@@ -194,7 +193,8 @@ _truncate :: proc(f: ^File, size: i64) -> Error {
}
_remove :: proc(name: string) -> Error {
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
fd := unix.sys_open(name_cstr, int(File_Flags.Read))
if fd < 0 {
@@ -209,22 +209,25 @@ _remove :: proc(name: string) -> Error {
}
_rename :: proc(old_name, new_name: string) -> Error {
old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator)
new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
old_name_cstr := temp_cstring(old_name) or_return
new_name_cstr := temp_cstring(new_name) or_return
return _ok_or_error(unix.sys_rename(old_name_cstr, new_name_cstr))
}
_link :: proc(old_name, new_name: string) -> Error {
old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator)
new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
old_name_cstr := temp_cstring(old_name) or_return
new_name_cstr := temp_cstring(new_name) or_return
return _ok_or_error(unix.sys_link(old_name_cstr, new_name_cstr))
}
_symlink :: proc(old_name, new_name: string) -> Error {
old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator)
new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
old_name_cstr := temp_cstring(old_name) or_return
new_name_cstr := temp_cstring(new_name) or_return
return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr))
}
@@ -233,7 +236,7 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (st
bufsz : uint = 256
buf := make([]byte, bufsz, allocator)
for {
rc := unix.sys_readlink(name_cstr, &(buf[0]), bufsz)
rc := unix.sys_readlink(name_cstr, &buf[0], bufsz)
if rc < 0 {
delete(buf)
return "", _get_platform_error(rc)
@@ -242,23 +245,26 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (st
delete(buf)
buf = make([]byte, bufsz, allocator)
} else {
return strings.string_from_ptr(&buf[0], rc), nil
return string(buf[:rc]), nil
}
}
}
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) {
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
return _read_link_cstr(name_cstr, allocator)
}
_unlink :: proc(name: string) -> Error {
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
return _ok_or_error(unix.sys_unlink(name_cstr))
}
_chdir :: proc(name: string) -> Error {
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
return _ok_or_error(unix.sys_chdir(name_cstr))
}
@@ -267,7 +273,8 @@ _fchdir :: proc(f: ^File) -> Error {
}
_chmod :: proc(name: string, mode: File_Mode) -> Error {
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
return _ok_or_error(unix.sys_chmod(name_cstr, uint(mode)))
}
@@ -277,13 +284,15 @@ _fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
// NOTE: will throw error without super user priviledges
_chown :: proc(name: string, uid, gid: int) -> Error {
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
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 := strings.clone_to_cstring(name, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid))
}
@@ -293,7 +302,8 @@ _fchown :: proc(f: ^File, uid, gid: int) -> Error {
}
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
times := [2]Unix_File_Time {
{ atime._nsec, 0 },
{ mtime._nsec, 0 },
@@ -310,12 +320,14 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
}
_exists :: proc(name: string) -> bool {
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
name_cstr, _ := temp_cstring(name)
return unix.sys_access(name_cstr, F_OK) == 0
}
_is_file :: proc(name: string) -> bool {
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
name_cstr, _ := temp_cstring(name)
s: _Stat
res := unix.sys_stat(name_cstr, &s)
if res < 0 {
@@ -334,7 +346,8 @@ _is_file_fd :: proc(fd: int) -> bool {
}
_is_dir :: proc(name: string) -> bool {
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
name_cstr, _ := temp_cstring(name)
s: _Stat
res := unix.sys_stat(name_cstr, &s)
if res < 0 {
@@ -352,21 +365,10 @@ _is_dir_fd :: proc(fd: int) -> bool {
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.
@(private="file")
_temp_name_to_cstring :: proc(name: string) -> (cname: cstring) {
return strings.clone_to_cstring(name, context.temp_allocator)
}
@(private="package")
_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
f := (^File)(stream_data)
ferr: Error
i: int
switch mode {
case .Read:
n, ferr = _read(f, p)
+5 -2
View File
@@ -76,6 +76,7 @@ read_entire_file :: proc{
read_entire_file_from_file,
}
@(require_results)
read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator) -> (data: []byte, err: Error) {
f, ferr := open(name)
if ferr != nil {
@@ -85,14 +86,15 @@ read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator) -
return read_entire_file_from_file(f, allocator)
}
@(require_results)
read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (data: []byte, err: Error) {
size: int
has_size := true
if size64, err := file_size(f); err == nil {
if size64, serr := file_size(f); serr == nil {
if i64(int(size64)) != size64 {
size = int(size64)
}
} else if err == .No_Size {
} else if serr == .No_Size {
has_size = false
} else {
return
@@ -135,6 +137,7 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d
}
}
@(require_results)
write_entire_file :: proc(name: string, data: []byte, perm: File_Mode, truncate := true) -> Error {
flags := O_WRONLY|O_CREATE
if truncate {
+23 -55
View File
@@ -6,7 +6,6 @@ import "base:runtime"
import "core:io"
import "core:mem"
import "core:sync"
import "core:strings"
import "core:time"
import "core:unicode/utf16"
import win32 "core:sys/windows"
@@ -17,49 +16,6 @@ S_IWRITE :: 0o200
_ERROR_BAD_NETPATH :: 53
MAX_RW :: 1<<30
_file_allocator :: proc() -> runtime.Allocator {
return heap_allocator()
}
_temp_allocator_proc :: runtime.arena_allocator_proc
@(private="file", thread_local)
_global_default_temp_allocator_arena: runtime.Arena
_temp_allocator :: proc() -> runtime.Allocator {
return runtime.Allocator{
procedure = _temp_allocator_proc,
data = &_global_default_temp_allocator_arena,
}
}
@(require_results)
_temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: runtime.Arena_Temp) {
temp = runtime.arena_temp_begin(&_global_default_temp_allocator_arena, loc)
return
}
_temp_allocator_temp_end :: proc(temp: runtime.Arena_Temp, loc := #caller_location) {
runtime.arena_temp_end(temp, loc)
}
@(fini, private)
_destroy_temp_allocator_fini :: proc() {
runtime.arena_destroy(&_global_default_temp_allocator_arena)
_global_default_temp_allocator_arena = {}
}
@(deferred_out=_temp_allocator_temp_end)
_TEMP_ALLOCATOR_GUARD :: #force_inline proc(ignore := false, loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) {
if ignore {
return {}, loc
} else {
return _temp_allocator_temp_begin(loc), loc
}
}
_File_Kind :: enum u8 {
File,
@@ -79,6 +35,20 @@ _File :: struct {
p_mutex: sync.Mutex, // pread pwrite calls
}
@(init)
init_std_files :: proc() {
stdin = new_file(uintptr(win32.GetStdHandle(win32.STD_INPUT_HANDLE)), "<stdin>")
stdout = new_file(uintptr(win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)), "<stdout>")
stderr = new_file(uintptr(win32.GetStdHandle(win32.STD_ERROR_HANDLE)), "<stderr>")
}
@(fini)
fini_std_files :: proc() {
close(stdin)
close(stdout)
close(stderr)
}
_handle :: proc(f: ^File) -> win32.HANDLE {
return win32.HANDLE(_fd(f))
}
@@ -162,11 +132,11 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
if handle == INVALID_HANDLE {
return nil
}
f := new(File, _file_allocator())
f := new(File, file_allocator())
f.impl.allocator = _file_allocator()
f.impl.allocator = file_allocator()
f.impl.fd = rawptr(handle)
f.impl.name = strings.clone(name, f.impl.allocator)
f.impl.name, _ = clone_string(name, f.impl.allocator)
f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
handle := _handle(f)
@@ -311,10 +281,10 @@ _read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
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])
n, cerr := read_console(handle, p[total_read:][:to_read])
total_read += n
if err != nil {
return i64(total_read), err
if cerr != nil {
return i64(total_read), cerr
}
} else {
ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil)
@@ -583,9 +553,9 @@ _normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: st
return "", _get_platform_error()
}
_TEMP_ALLOCATOR_GUARD()
TEMP_ALLOCATOR_GUARD()
buf := make([]u16, n+1, _temp_allocator())
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()
@@ -798,14 +768,12 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
ferr = _flush(f)
err = error_to_io_error(ferr)
return
case .Close:
case .Close, .Destroy:
ferr = _close(f)
err = error_to_io_error(ferr)
return
case .Query:
return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Query})
case .Destroy:
return 0, .Empty
}
return 0, .Empty
}
+2
View File
@@ -2,6 +2,7 @@ package os2
import "base:runtime"
@(require_results)
heap_allocator :: proc() -> runtime.Allocator {
return runtime.Allocator{
procedure = heap_allocator_proc,
@@ -10,6 +11,7 @@ heap_allocator :: proc() -> runtime.Allocator {
}
@(require_results)
heap_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, runtime.Allocator_Error) {
+128
View File
@@ -0,0 +1,128 @@
//+private
package os2
import "base:intrinsics"
import "base:runtime"
// Splits pattern by the last wildcard "*", if it exists, and returns the prefix and suffix
// parts which are split by the last "*"
@(require_results)
_prefix_and_suffix :: proc(pattern: string) -> (prefix, suffix: string, err: Error) {
for i in 0..<len(pattern) {
if is_path_separator(pattern[i]) {
err = .Pattern_Has_Separator
return
}
}
prefix = pattern
for i := len(pattern)-1; i >= 0; i -= 1 {
if pattern[i] == '*' {
prefix, suffix = pattern[:i], pattern[i+1:]
break
}
}
return
}
@(require_results)
clone_string :: proc(s: string, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) {
buf := make([]byte, len(s), allocator) or_return
copy(buf, s)
return string(buf), nil
}
@(require_results)
clone_to_cstring :: proc(s: string, allocator: runtime.Allocator) -> (res: cstring, err: runtime.Allocator_Error) {
res = "" // do not use a `nil` cstring
buf := make([]byte, len(s)+1, allocator) or_return
copy(buf, s)
buf[len(s)] = 0
return cstring(&buf[0]), nil
}
@(require_results)
temp_cstring :: proc(s: string) -> (cstring, runtime.Allocator_Error) {
return clone_to_cstring(s, temp_allocator())
}
@(require_results)
string_from_null_terminated_bytes :: proc(b: []byte) -> (res: string) {
s := string(b)
i := 0
for ; i < len(s); i += 1 {
if s[i] == 0 {
break
}
}
return s[:i]
}
@(require_results)
concatenate_strings_from_buffer :: proc(buf: []byte, strings: ..string) -> string {
n := 0
for s in strings {
(n < len(buf)) or_break
n += copy(buf[n:], s)
}
n = min(len(buf), n)
return string(buf[:n])
}
@(require_results)
concatenate :: proc(strings: []string, allocator: runtime.Allocator) -> (res: string, err: runtime.Allocator_Error) {
n := 0
for s in strings {
n += len(s)
}
buf := make([]byte, n) or_return
n = 0
for s in strings {
n += copy(buf[n:], s)
}
return string(buf), nil
}
@(private="file")
random_string_seed: [2]u64
@(init, private="file")
init_random_string_seed :: proc() {
seed := u64(intrinsics.read_cycle_counter())
s := &random_string_seed
s[0] = 0
s[1] = (seed << 1) | 1
_ = next_random(s)
s[1] += seed
_ = next_random(s)
}
@(require_results)
next_random :: proc(r: ^[2]u64) -> u64 {
old_state := r[0]
r[0] = old_state * 6364136223846793005 + (r[1]|1)
xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081
rot := (old_state >> 59)
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63))
}
@(require_results)
random_string :: proc(buf: []byte) -> string {
@static digits := "0123456789"
u := next_random(&random_string_seed)
b :: 10
i := len(buf)
for u >= b {
i -= 1
buf[i] = digits[u % b]
u /= b
}
i -= 1
buf[i] = digits[u % b]
return string(buf[i:])
}
+5 -2
View File
@@ -2,9 +2,11 @@ package os2
import "base:runtime"
Path_Separator :: _Path_Separator // OS-Specific
Path_List_Separator :: _Path_List_Separator // OS-Specific
Path_Separator :: _Path_Separator // OS-Specific
Path_Separator_String :: _Path_Separator_String // OS-Specific
Path_List_Separator :: _Path_List_Separator // OS-Specific
@(require_results)
is_path_separator :: proc(c: byte) -> bool {
return _is_path_separator(c)
}
@@ -25,6 +27,7 @@ remove_all :: proc(path: string) -> Error {
getwd :: get_working_directory
@(require_results)
get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _getwd(allocator)
}
+12 -8
View File
@@ -1,13 +1,13 @@
//+private
package os2
import "core:strings"
import "core:strconv"
import "base:runtime"
import "core:sys/unix"
_Path_Separator :: '/'
_Path_List_Separator :: ':'
_Path_Separator :: '/'
_Path_Separator_String :: "/"
_Path_List_Separator :: ':'
_S_IFMT :: 0o170000 // Type of file mask
_S_IFIFO :: 0o010000 // Named pipe (fifo)
@@ -31,7 +31,8 @@ _mkdir :: proc(path: string, perm: File_Mode) -> Error {
return .Invalid_Argument
}
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
path_cstr := temp_cstring(path) or_return
return _ok_or_error(unix.sys_mkdir(path_cstr, uint(perm & 0o777)))
}
@@ -71,6 +72,8 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
return .Invalid_Argument
}
TEMP_ALLOCATOR_GUARD()
// need something we can edit, and use to generate cstrings
allocated: bool
path_bytes: []u8
@@ -78,7 +81,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
allocated = true
path_bytes = make([]u8, len(path) + 1)
} else {
path_bytes = make([]u8, len(path) + 1, context.temp_allocator)
path_bytes = make([]u8, len(path) + 1, temp_allocator())
}
// NULL terminate the byte slice to make it a valid cstring
@@ -176,7 +179,8 @@ _remove_all :: proc(path: string) -> Error {
return nil
}
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
TEMP_ALLOCATOR_GUARD()
path_cstr := temp_cstring(path) or_return
fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS)
switch fd {
@@ -202,7 +206,7 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
#no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
if res >= 0 {
return strings.string_from_null_terminated_ptr(&buf[0], len(buf)), nil
return string_from_null_terminated_bytes(buf[:]), nil
}
if res != -ERANGE {
return "", _get_platform_error(res)
@@ -213,7 +217,7 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
}
_setwd :: proc(dir: string) -> Error {
dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator)
dir_cstr := temp_cstring(dir) or_return
return _ok_or_error(unix.sys_chdir(dir_cstr))
}
+10 -10
View File
@@ -3,10 +3,10 @@ package os2
import win32 "core:sys/windows"
import "base:runtime"
import "core:strings"
_Path_Separator :: '\\'
_Path_List_Separator :: ';'
_Path_Separator :: '\\'
_Path_Separator_String :: "\\"
_Path_List_Separator :: ';'
_is_path_separator :: proc(c: byte) -> bool {
return c == '\\' || c == '/'
@@ -23,7 +23,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
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({p, `\`}, _file_allocator()) or_return
s = concatenate({p, `\`}, file_allocator()) or_return
allocated = true
return
}
@@ -31,9 +31,9 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
return p, false, nil
}
_TEMP_ALLOCATOR_GUARD()
TEMP_ALLOCATOR_GUARD()
dir, err := stat(path, _temp_allocator())
dir, err := stat(path, temp_allocator())
if err == nil {
if dir.is_directory {
return nil
@@ -54,14 +54,14 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
if j > 1 {
new_path, allocated := fix_root_directory(path[:j-1]) or_return
defer if allocated {
delete(new_path, _file_allocator())
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())
dir1, err1 := lstat(path, temp_allocator())
if err1 == nil && dir1.is_directory {
return nil
}
@@ -127,10 +127,10 @@ _fix_long_path_internal :: proc(path: string) -> string {
return path
}
_TEMP_ALLOCATOR_GUARD()
TEMP_ALLOCATOR_GUARD()
PREFIX :: `\\?`
path_buf := make([]byte, len(PREFIX)+len(path)+1, _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)
+1
View File
@@ -1,5 +1,6 @@
package os2
@(require_results)
pipe :: proc() -> (r, w: ^File, err: Error) {
return _pipe()
}
+9
View File
@@ -3,6 +3,8 @@ package os2
import "core:time"
import "base:runtime"
Fstat_Callback :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error)
File_Info :: struct {
fullpath: string,
name: string,
@@ -25,20 +27,27 @@ file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) {
delete(fi.fullpath, allocator)
}
@(require_results)
fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
if f != nil && f.user_fstat != nil {
return f->user_fstat(allocator)
}
return _fstat(f, allocator)
}
@(require_results)
stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
return _stat(name, allocator)
}
lstat :: stat_do_not_follow_links
@(require_results)
stat_do_not_follow_links :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
return _lstat(name, allocator)
}
@(require_results)
same_file :: proc(fi1, fi2: File_Info) -> bool {
return _same_file(fi1, fi2)
}
+6 -11
View File
@@ -3,7 +3,6 @@ package os2
import "core:time"
import "base:runtime"
import "core:strings"
import "core:sys/unix"
import "core:path/filepath"
@@ -112,8 +111,9 @@ _fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Er
}
// NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath
_stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
_stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
fd := unix.sys_open(name_cstr, _O_RDONLY)
if fd < 0 {
@@ -123,8 +123,9 @@ _stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error)
return _fstat_internal(fd, allocator)
}
_lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
_lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
fd := unix.sys_open(name_cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW)
if fd < 0 {
@@ -137,9 +138,3 @@ _lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error)
_same_file :: proc(fi1, fi2: File_Info) -> bool {
return fi1.fullpath == fi2.fullpath
}
_stat_internal :: proc(name: string) -> (s: _Stat, res: int) {
name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
res = unix.sys_stat(name_cstr, &s)
return
}
+7 -7
View File
@@ -46,15 +46,15 @@ full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path
if name == "" {
name = "."
}
_TEMP_ALLOCATOR_GUARD()
TEMP_ALLOCATOR_GUARD()
p := win32.utf8_to_utf16(name, _temp_allocator())
p := win32.utf8_to_utf16(name, temp_allocator())
n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil)
if n == 0 {
return "", _get_platform_error()
}
buf := make([]u16, n+1, _temp_allocator())
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()
@@ -131,8 +131,8 @@ _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (strin
if n == 0 {
return "", _get_platform_error()
}
_TEMP_ALLOCATOR_GUARD()
buf := make([]u16, max(n, 260)+1, _temp_allocator())
TEMP_ALLOCATOR_GUARD()
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)
}
@@ -147,8 +147,8 @@ _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
if n == 0 {
return nil, _get_platform_error()
}
_TEMP_ALLOCATOR_GUARD()
buf := make([]u16, max(n, 260)+1, _temp_allocator())
TEMP_ALLOCATOR_GUARD()
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
}
+84 -4
View File
@@ -2,16 +2,96 @@ package os2
import "base:runtime"
create_temp_file :: proc(dir, pattern: string) -> (^File, Error) {
return _create_temp(dir, pattern)
@(private="file")
MAX_ATTEMPTS :: 1<<13 // Should be enough for everyone, right?
// Creates a new temperatory file in the directory `dir`.
//
// Opens the file for reading and writing, with 0o666 permissions, and returns the new `^File`.
// The filename is generated by taking a pattern, and adding a randomized string to the end.
// If the pattern includes an "*", the random string replaces the last "*".
// If `dir` is an empty tring, `temp_directory()` will be used.
//
// The caller must `close` the file once finished with.
@(require_results)
create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) {
TEMP_ALLOCATOR_GUARD()
dir := dir if dir != "" else temp_directory(temp_allocator()) or_return
prefix, suffix := _prefix_and_suffix(pattern) or_return
prefix = temp_join_path(dir, prefix) or_return
rand_buf: [32]byte
name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator())
attempts := 0
for {
name := concatenate_strings_from_buffer(name_buf[:], prefix, random_string(rand_buf[:]), suffix)
f, err = open(name, {.Read, .Write, .Create, .Excl}, File_Mode(0o666))
if err == .Exist {
close(f)
attempts += 1
if attempts < MAX_ATTEMPTS {
continue
}
return nil, err
}
return f, err
}
}
mkdir_temp :: make_directory_temp
make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) {
return _mkdir_temp(dir, pattern, allocator)
// Creates a new temporary directory in the directory `dir`, and returns the path of the new directory.
//
// The directory name is generated by taking a pattern, and adding a randomized string to the end.
// If the pattern includes an "*", the random string replaces the last "*".
// If `dir` is an empty tring, `temp_directory()` will be used.
@(require_results)
make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (temp_path: string, err: Error) {
TEMP_ALLOCATOR_GUARD()
dir := dir if dir != "" else temp_directory(temp_allocator()) or_return
prefix, suffix := _prefix_and_suffix(pattern) or_return
prefix = temp_join_path(dir, prefix) or_return
rand_buf: [32]byte
name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator())
attempts := 0
for {
name := concatenate_strings_from_buffer(name_buf[:], prefix, random_string(rand_buf[:]), suffix)
err = make_directory(name, 0o700)
if err == nil {
return clone_string(name, allocator)
}
if err == .Exist {
attempts += 1
if attempts < MAX_ATTEMPTS {
continue
}
return "", err
}
if err == .Not_Exist {
if _, serr := stat(dir, temp_allocator()); serr == .Not_Exist {
return "", serr
}
}
return "", err
}
}
temp_dir :: temp_directory
@(require_results)
temp_directory :: proc(allocator: runtime.Allocator) -> (string, Error) {
return _temp_dir(allocator)
}
@(private="file")
temp_join_path :: proc(dir, name: string) -> (string, runtime.Allocator_Error) {
if len(dir) > 0 && is_path_separator(dir[len(dir)-1]) {
return concatenate({dir, name}, temp_allocator(),)
}
return concatenate({dir, Path_Separator_String, name}, temp_allocator())
}
-10
View File
@@ -4,16 +4,6 @@ package os2
import "base: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
+2 -10
View File
@@ -4,22 +4,14 @@ package os2
import "base:runtime"
import win32 "core:sys/windows"
_create_temp :: proc(dir, pattern: string) -> (^File, Error) {
return nil, nil
}
_mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) {
return "", nil
}
_temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
n := win32.GetTempPathW(0, nil)
if n == 0 {
return "", nil
}
_TEMP_ALLOCATOR_GUARD()
TEMP_ALLOCATOR_GUARD()
b := make([]u16, max(win32.MAX_PATH, n), _temp_allocator())
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] == '\\' {
+9 -7
View File
@@ -1,19 +1,19 @@
package os2
import "core:strings"
import "base:runtime"
@(require_results)
user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Windows:
dir = get_env("LocalAppData", allocator)
if dir != "" {
dir = strings.clone(dir, allocator) or_return
dir = clone_string(dir, allocator) or_return
}
case .Darwin:
dir = get_env("HOME", allocator)
if dir != "" {
dir = strings.concatenate({dir, "/Library/Caches"}, allocator) or_return
dir = concatenate({dir, "/Library/Caches"}, allocator) or_return
}
case: // All other UNIX systems
dir = get_env("XDG_CACHE_HOME", allocator)
@@ -22,7 +22,7 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error
if dir == "" {
return
}
dir = strings.concatenate({dir, "/.cache"}, allocator) or_return
dir = concatenate({dir, "/.cache"}, allocator) or_return
}
}
if dir == "" {
@@ -31,17 +31,18 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error
return
}
@(require_results)
user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Windows:
dir = get_env("AppData", allocator)
if dir != "" {
dir = strings.clone(dir, allocator) or_return
dir = clone_string(dir, allocator) or_return
}
case .Darwin:
dir = get_env("HOME", allocator)
if dir != "" {
dir = strings.concatenate({dir, "/Library/Application Support"}, allocator) or_return
dir = concatenate({dir, "/Library/Application Support"}, allocator) or_return
}
case: // All other UNIX systems
dir = get_env("XDG_CACHE_HOME", allocator)
@@ -50,7 +51,7 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro
if dir == "" {
return
}
dir = strings.concatenate({dir, "/.config"}, allocator) or_return
dir = concatenate({dir, "/.config"}, allocator) or_return
}
}
if dir == "" {
@@ -59,6 +60,7 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro
return
}
@(require_results)
user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
env := "HOME"
#partial switch ODIN_OS {
+1 -1
View File
@@ -1,7 +1,7 @@
//+build i386, amd64
package simd_x86
import "core:intrinsics"
import "base:intrinsics"
@(require_results, enable_target_feature="lzcnt")
_lzcnt_u32 :: #force_inline proc "c" (x: u32) -> u32 {
+1 -1
View File
@@ -1,7 +1,7 @@
//+build amd64
package simd_x86
import "core:intrinsics"
import "base: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)
+1 -1
View File
@@ -1,7 +1,7 @@
//+build i386, amd64
package simd_x86
import "core:intrinsics"
import "base:intrinsics"
import "core:simd"
// _MM_SHUFFLE(z, y, x, w) -> (z<<6 | y<<4 | x<<2 | w)
+1 -1
View File
@@ -1,7 +1,7 @@
//+build i386, amd64
package simd_x86
import "core:intrinsics"
import "base:intrinsics"
import "core:simd"
@(enable_target_feature="sse2")
+1 -1
View File
@@ -1,7 +1,7 @@
//+build i386, amd64
package simd_x86
import "core:intrinsics"
import "base:intrinsics"
import "core:simd"
@(require_results, enable_target_feature="sse3")
+1 -1
View File
@@ -1,7 +1,7 @@
//+build i386, amd64
package simd_x86
import "core:intrinsics"
import "base:intrinsics"
import "core:simd"
_ :: simd
+9
View File
@@ -36,6 +36,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
sync.wait(&t.cond, &t.mutex)
}
if .Joined in t.flags {
return nil
}
when ODIN_OS != .Darwin {
// Enable thread's cancelability.
if can_set_thread_cancel_state {
@@ -143,6 +147,11 @@ _join :: proc(t: ^Thread) {
if res, ok := CAS(&t.flags, unjoined, joined); res == joined && !ok {
return
}
// Prevent non-started threads from blocking main thread with initial wait
// condition.
if .Started not_in unjoined {
_start(t)
}
unix.pthread_join(t.unix_thread, nil)
}
+11 -2
View File
@@ -24,6 +24,10 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
__windows_thread_entry_proc :: proc "system" (t_: rawptr) -> win32.DWORD {
t := (^Thread)(t_)
if .Joined in t.flags {
return 0
}
t.id = sync.current_thread_id()
{
@@ -93,11 +97,16 @@ _join :: proc(t: ^Thread) {
return
}
t.flags += {.Joined}
if .Started not_in t.flags {
t.flags += {.Started}
win32.ResumeThread(t.win32_thread)
}
win32.WaitForSingleObject(t.win32_thread, win32.INFINITE)
win32.CloseHandle(t.win32_thread)
t.win32_thread = win32.INVALID_HANDLE
t.flags += {.Joined}
}
_join_multiple :: proc(threads: ..^Thread) {
+1 -1
View File
@@ -8,7 +8,7 @@ import "core:thread"
import "core:time"
import "core:reflect"
import "base:runtime"
import "core:intrinsics"
import "base:intrinsics"
import "core:math/big"
/*
+4 -1
View File
@@ -732,10 +732,11 @@ enum VetFlags : u64 {
VetFlag_Semicolon = 1u<<4,
VetFlag_UnusedVariables = 1u<<5,
VetFlag_UnusedImports = 1u<<6,
VetFlag_Deprecated = 1u<<7,
VetFlag_Unused = VetFlag_UnusedVariables|VetFlag_UnusedImports,
VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt,
VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt|VetFlag_Deprecated,
VetFlag_Using = VetFlag_UsingStmt|VetFlag_UsingParam,
};
@@ -757,6 +758,8 @@ u64 get_vet_flag_from_name(String const &name) {
return VetFlag_Style;
} else if (name == "semicolon") {
return VetFlag_Semicolon;
} else if (name == "deprecated") {
return VetFlag_Deprecated;
}
return VetFlag_NONE;
}
+25
View File
@@ -1441,6 +1441,13 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
// return check_is_assignable_to(c, &o, poly); // && is_type_subtype_of_and_allow_polymorphic(o.type, poly);
}
return false;
case Type_BitField:
if (source->kind == Type_BitField) {
return is_polymorphic_type_assignable(c, poly->BitField.backing_type, source->BitField.backing_type, true, modify_type);
}
return false;
case Type_Tuple:
GB_PANIC("This should never happen");
return false;
@@ -1787,6 +1794,13 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
gb_string_free(str);
return false;
}
if (o->mode == Addressing_Type) {
gbString str = type_to_string(o->type);
error(o->expr, "Expected an expression for operator '%.*s', got type '%s'", LIT(op.string), str);
gb_string_free(str);
return false;
}
Type *type = base_type(core_array_type(o->type));
gbString str = nullptr;
switch (op.kind) {
@@ -10249,6 +10263,17 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
case Type_Struct:
if (is_type_soa_struct(t)) {
valid = true;
if (t->Struct.soa_kind == StructSoa_Fixed) {
max_count = t->Struct.soa_count;
if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) {
gbString str = expr_to_string(node);
error(node, "Cannot slice #soa array '%s', value is not addressable", str);
gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
}
o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
}
break;
+50 -4
View File
@@ -169,9 +169,16 @@ gb_internal bool check_has_break_list(Slice<Ast *> const &stmts, String const &l
return false;
}
gb_internal bool check_has_break_expr(Ast * expr, String const &label) {
if (expr && expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) {
return true;
}
return false;
}
gb_internal bool check_has_break_expr_list(Slice<Ast *> const &exprs, String const &label) {
for (Ast *expr : exprs) {
if (expr && expr->viral_state_flags & ViralStateFlag_ContainsOrBreak) {
if (check_has_break_expr(expr, label)) {
return true;
}
}
@@ -196,6 +203,13 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
return check_has_break_list(stmt->BlockStmt.stmts, label, implicit);
case Ast_IfStmt:
if (stmt->IfStmt.init && check_has_break(stmt->IfStmt.init, label, implicit)) {
return true;
}
if (stmt->IfStmt.cond && check_has_break_expr(stmt->IfStmt.cond, label)) {
return true;
}
if (check_has_break(stmt->IfStmt.body, label, implicit) ||
(stmt->IfStmt.else_stmt != nullptr && check_has_break(stmt->IfStmt.else_stmt, label, implicit))) {
return true;
@@ -206,6 +220,9 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
return check_has_break_list(stmt->CaseClause.stmts, label, implicit);
case Ast_SwitchStmt:
if (stmt->SwitchStmt.init && check_has_break_expr(stmt->SwitchStmt.init, label)) {
return true;
}
if (label != "" && check_has_break(stmt->SwitchStmt.body, label, false)) {
return true;
}
@@ -218,6 +235,16 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
break;
case Ast_ForStmt:
if (stmt->ForStmt.init && check_has_break(stmt->ForStmt.init, label, implicit)) {
return true;
}
if (stmt->ForStmt.cond && check_has_break_expr(stmt->ForStmt.cond, label)) {
return true;
}
if (stmt->ForStmt.post && check_has_break(stmt->ForStmt.post, label, implicit)) {
return true;
}
if (label != "" && check_has_break(stmt->ForStmt.body, label, false)) {
return true;
}
@@ -253,7 +280,16 @@ gb_internal bool check_has_break(Ast *stmt, String const &label, bool implicit)
return false;
}
String label_string(Ast *node) {
GB_ASSERT(node != nullptr);
if (node->kind == Ast_Ident) {
return node->Ident.token.string;
} else if (node->kind == Ast_Label) {
return label_string(node->Label.name);
}
GB_ASSERT("INVALID LABEL");
return {};
}
// NOTE(bill): The last expression has to be a 'return' statement
// TODO(bill): This is a mild hack and should be probably handled properly
@@ -264,7 +300,12 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) {
case_end;
case_ast_node(bs, BlockStmt, node);
return check_is_terminating_list(bs->stmts, label);
if (check_is_terminating_list(bs->stmts, label)) {
if (bs->label != nullptr) {
return check_is_terminating_list(bs->stmts, label_string(bs->label));
}
return true;
}
case_end;
case_ast_node(es, ExprStmt, node);
@@ -321,6 +362,9 @@ gb_internal bool check_is_terminating(Ast *node, String const &label) {
case_ast_node(fs, ForStmt, node);
if (fs->cond == nullptr && !check_has_break(fs->body, label, true)) {
if (fs->label) {
return !check_has_break(fs->body, label_string(fs->label), false);
}
return true;
}
case_end;
@@ -734,7 +778,7 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us,
for (auto const &entry : scope->elements) {
String name = entry.key;
Entity *decl = entry.value;
if (!is_entity_exported(decl)) continue;
if (!is_entity_exported(decl, true)) continue;
Entity *found = scope_insert_with_name(ctx->scope, name, decl);
if (found != nullptr) {
@@ -759,6 +803,8 @@ gb_internal bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us,
bool is_ptr = is_type_pointer(e->type);
Type *t = base_type(type_deref(e->type));
if (t->kind == Type_Struct) {
wait_signal_until_available(&t->Struct.fields_wait_signal);
Scope *found = t->Struct.scope;
GB_ASSERT(found != nullptr);
for (auto const &entry : found->elements) {
+8 -5
View File
@@ -19,10 +19,12 @@ gb_internal void populate_using_array_index(CheckerContext *ctx, Ast *node, AstF
}
} else {
Token tok = make_token_ident(name);
if (field->names.count > 0) {
tok.pos = ast_token(field->names[0]).pos;
} else {
tok.pos = ast_token(field->type).pos;
if (field) {
if (field->names.count > 0) {
tok.pos = ast_token(field->names[0]).pos;
} else {
tok.pos = ast_token(field->type).pos;
}
}
Entity *f = alloc_entity_array_elem(nullptr, tok, t->Array.elem, idx);
add_entity(ctx, ctx->scope, nullptr, f);
@@ -191,9 +193,10 @@ gb_internal void check_struct_fields(CheckerContext *ctx, Ast *node, Slice<Entit
if (is_using && p->names.count > 0) {
Type *first_type = fields_array[fields_array.count-1]->type;
bool soa_ptr = is_type_soa_pointer(first_type);
Type *t = base_type(type_deref(first_type));
if (!does_field_type_allow_using(t) &&
if ((soa_ptr || !does_field_type_allow_using(t)) &&
p->names.count >= 1 &&
p->names[0]->kind == Ast_Ident) {
Token name_token = p->names[0]->Ident.token;
+15
View File
@@ -672,7 +672,20 @@ gb_internal int error_value_cmp(void const *a, void const *b) {
return token_pos_cmp(x->pos, y->pos);
}
gb_internal bool errors_already_printed = false;
gb_internal void print_all_errors(void) {
if (errors_already_printed) {
if (global_error_collector.warning_count.load() == global_error_collector.error_values.count) {
for (ErrorValue &ev : global_error_collector.error_values) {
array_free(&ev.msg);
}
array_clear(&global_error_collector.error_values);
errors_already_printed = false;
}
return;
}
auto const &escape_char = [](gbString res, u8 c) -> gbString {
switch (c) {
case '\n': res = gb_string_append_length(res, "\\n", 2); break;
@@ -827,4 +840,6 @@ gb_internal void print_all_errors(void) {
}
gbFile *f = gb_file_get_standard(gbFileStandard_Error);
gb_file_write(f, res, gb_string_length(res));
errors_already_printed = true;
}
+50 -2
View File
@@ -4141,7 +4141,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
if (se->high == nullptr) {
lbValue offset = base;
LLVMValueRef indices[1] = {low.value};
offset.value = LLVMBuildGEP2(p->builder, lb_type(p->module, offset.type->MultiPointer.elem), offset.value, indices, 1, "");
offset.value = LLVMBuildGEP2(p->builder, lb_type(p->module, base_type(offset.type)->MultiPointer.elem), offset.value, indices, 1, "");
lb_addr_store(p, res, offset);
} else {
low = lb_emit_conv(p, low, t_int);
@@ -4150,7 +4150,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
lb_emit_multi_pointer_slice_bounds_check(p, se->open, low, high);
LLVMValueRef indices[1] = {low.value};
LLVMValueRef ptr = LLVMBuildGEP2(p->builder, lb_type(p->module, base.type->MultiPointer.elem), base.value, indices, 1, "");
LLVMValueRef ptr = LLVMBuildGEP2(p->builder, lb_type(p->module, base_type(base.type)->MultiPointer.elem), base.value, indices, 1, "");
LLVMValueRef len = LLVMBuildSub(p->builder, high.value, low.value, "");
LLVMValueRef gep0 = lb_emit_struct_ep(p, res.addr, 0).value;
@@ -5194,6 +5194,54 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
lbValue ptr = lb_address_from_load_or_generate_local(p, lb_build_expr(p, expr));
return lb_addr(ptr);
case_end;
case_ast_node(be, OrBranchExpr, expr);
lbBlock *block = nullptr;
if (be->label != nullptr) {
lbBranchBlocks bb = lb_lookup_branch_blocks(p, be->label);
switch (be->token.kind) {
case Token_or_break: block = bb.break_; break;
case Token_or_continue: block = bb.continue_; break;
}
} else {
for (lbTargetList *t = p->target_list; t != nullptr && block == nullptr; t = t->prev) {
if (t->is_block) {
continue;
}
switch (be->token.kind) {
case Token_or_break: block = t->break_; break;
case Token_or_continue: block = t->continue_; break;
}
}
}
GB_ASSERT(block != nullptr);
TypeAndValue tv = expr->tav;
lbValue lhs = {};
lbValue rhs = {};
lb_emit_try_lhs_rhs(p, be->expr, tv, &lhs, &rhs);
Type *type = default_type(tv.type);
if (lhs.value) {
lhs = lb_emit_conv(p, lhs, type);
} else if (type != nullptr && type != t_invalid) {
lhs = lb_const_nil(p->module, type);
}
lbBlock *then = lb_create_block(p, "or_branch.then");
lbBlock *else_ = lb_create_block(p, "or_branch.else");
lb_emit_if(p, lb_emit_try_has_value(p, rhs), then, else_);
lb_start_block(p, else_);
lb_emit_defer_stmts(p, lbDeferExit_Branch, block);
lb_emit_jump(p, block);
lb_start_block(p, then);
return lb_addr(lb_address_from_load_or_generate_local(p, lhs));
case_end;
}
TokenPos token_pos = ast_token(expr).pos;
+1 -1
View File
@@ -1180,7 +1180,7 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
Type *t = addr.bitfield.type;
if (do_mask) {
GB_ASSERT(addr.bitfield.bit_size < 8*type_size_of(ct));
GB_ASSERT(addr.bitfield.bit_size <= 8*type_size_of(ct));
lbValue mask = lb_const_int(p->module, t, (1ull<<cast(u64)addr.bitfield.bit_size)-1);
r = lb_emit_arith(p, Token_And, r, mask, t);
+21 -20
View File
@@ -2157,6 +2157,16 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
lb_open_scope(p, is->scope); // Scope #1
defer (lb_close_scope(p, lbDeferExit_Default, nullptr));
lbBlock *then = lb_create_block(p, "if.then");
lbBlock *done = lb_create_block(p, "if.done");
lbBlock *else_ = done;
if (is->else_stmt != nullptr) {
else_ = lb_create_block(p, "if.else");
}
if (is->label != nullptr) {
lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
tl->is_block = true;
}
if (is->init != nullptr) {
lbBlock *init = lb_create_block(p, "if.init");
lb_emit_jump(p, init);
@@ -2164,22 +2174,12 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
lb_build_stmt(p, is->init);
}
lbBlock *then = lb_create_block(p, "if.then");
lbBlock *done = lb_create_block(p, "if.done");
lbBlock *else_ = done;
if (is->else_stmt != nullptr) {
else_ = lb_create_block(p, "if.else");
}
lbValue cond = lb_build_cond(p, is->cond, then, else_);
// Note `cond.value` only set for non-and/or conditions and const negs so that the `LLVMIsConstant()`
// and `LLVMConstIntGetZExtValue()` calls below will be valid and `LLVMInstructionEraseFromParent()`
// will target the correct (& only) branch statement
if (is->label != nullptr) {
lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
tl->is_block = true;
}
if (cond.value && LLVMIsConstant(cond.value)) {
// NOTE(bill): Do a compile time short circuit for when the condition is constantly known.
@@ -2244,15 +2244,6 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
if (p->debug_info != nullptr) {
LLVMSetCurrentDebugLocation2(p->builder, lb_debug_location_from_ast(p, node));
}
if (fs->init != nullptr) {
#if 1
lbBlock *init = lb_create_block(p, "for.init");
lb_emit_jump(p, init);
lb_start_block(p, init);
#endif
lb_build_stmt(p, fs->init);
}
lbBlock *body = lb_create_block(p, "for.body");
lbBlock *done = lb_create_block(p, "for.done"); // NOTE(bill): Append later
lbBlock *loop = body;
@@ -2264,6 +2255,17 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
post = lb_create_block(p, "for.post");
}
lb_push_target_list(p, fs->label, done, post, nullptr);
if (fs->init != nullptr) {
#if 1
lbBlock *init = lb_create_block(p, "for.init");
lb_emit_jump(p, init);
lb_start_block(p, init);
#endif
lb_build_stmt(p, fs->init);
}
lb_emit_jump(p, loop);
lb_start_block(p, loop);
@@ -2276,7 +2278,6 @@ gb_internal void lb_build_for_stmt(lbProcedure *p, Ast *node) {
lb_start_block(p, body);
}
lb_push_target_list(p, fs->label, done, post, nullptr);
lb_build_stmt(p, fs->body);
+28 -3
View File
@@ -11,6 +11,9 @@ gb_internal bool ast_file_vet_style(AstFile *f) {
return (ast_file_vet_flags(f) & VetFlag_Style) != 0;
}
gb_internal bool ast_file_vet_deprecated(AstFile *f) {
return (ast_file_vet_flags(f) & VetFlag_Deprecated) != 0;
}
gb_internal bool file_allow_newline(AstFile *f) {
bool is_strict = build_context.strict_style || ast_file_vet_style(f);
@@ -1569,7 +1572,7 @@ gb_internal Token expect_operator(AstFile *f) {
LIT(p));
}
if (prev.kind == Token_Ellipsis) {
syntax_warning(prev, "'..' for ranges has now been deprecated, prefer '..='");
syntax_error(prev, "'..' for ranges are not allowed, did you mean '..<' or '..='?");
f->tokens[f->curr_token_index].flags |= TokenFlag_Replace;
}
@@ -5508,11 +5511,15 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
}
}
if (files_with_ext == 0 || files_to_reserve == 1) {
ERROR_BLOCK();
if (files_with_ext != 0) {
syntax_error(pos, "Directory contains no .odin files for the specified platform: %.*s", LIT(rel_path));
} else {
syntax_error(pos, "Empty directory that contains no .odin files: %.*s", LIT(rel_path));
}
if (build_context.command_kind == Command_test) {
error_line("\tSuggestion: Make an .odin file that imports packages to test and use the `-all-packages` flag.");
}
return nullptr;
}
@@ -5690,8 +5697,26 @@ gb_internal bool determine_path_from_string(BlockingMutex *file_mutex, Ast *node
if (collection_name.len > 0) {
// NOTE(bill): `base:runtime` == `core:runtime`
if (collection_name == "core" && string_starts_with(file_str, str_lit("runtime"))) {
collection_name = str_lit("base");
if (collection_name == "core") {
bool replace_with_base = false;
if (string_starts_with(file_str, str_lit("runtime"))) {
replace_with_base = true;
} else if (string_starts_with(file_str, str_lit("intrinsics"))) {
replace_with_base = true;
} if (string_starts_with(file_str, str_lit("builtin"))) {
replace_with_base = true;
}
if (replace_with_base) {
collection_name = str_lit("base");
}
if (replace_with_base) {
if (ast_file_vet_deprecated(node->file())) {
syntax_error(node, "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str));
} else {
syntax_warning(ast_token(node), "import \"core:%.*s\" has been deprecated in favour of \"base:%.*s\"", LIT(file_str), LIT(file_str));
}
}
}
if (collection_name == "system") {
+1 -4
View File
@@ -400,16 +400,13 @@ gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi)
continue;
}
if (S_ISDIR(dir_stat.st_mode)) {
continue;
}
i64 size = dir_stat.st_size;
FileInfo info = {};
info.name = copy_string(a, name);
info.fullpath = path_to_full_path(a, filepath);
info.size = size;
info.is_dir = S_ISDIR(dir_stat.st_mode);
array_add(fi, info);
}