mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-16 02:42:22 -07:00
Merge branch 'master' into netbsd-ci
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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{}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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] == '=' {
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,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) {
|
||||
|
||||
@@ -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:])
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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,5 +1,6 @@
|
||||
package os2
|
||||
|
||||
@(require_results)
|
||||
pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
return _pipe()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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] == '\\' {
|
||||
|
||||
@@ -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,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,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,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,7 +1,7 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
import "core:simd"
|
||||
|
||||
@(enable_target_feature="sse2")
|
||||
|
||||
@@ -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,7 +1,7 @@
|
||||
//+build i386, amd64
|
||||
package simd_x86
|
||||
|
||||
import "core:intrinsics"
|
||||
import "base:intrinsics"
|
||||
import "core:simd"
|
||||
_ :: simd
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
/*
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user