Merge branch 'master' into allocator-mode-alloc-non-zeroed

This commit is contained in:
gingerBill
2022-11-03 12:47:11 +00:00
93 changed files with 3803 additions and 2576 deletions
+2 -5
View File
@@ -240,14 +240,11 @@ buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
b.last_read = .Invalid
if offset < 0 || offset >= len(b.buf) {
if uint(offset) >= len(b.buf) {
err = .Invalid_Offset
return
}
if 0 <= offset && offset < len(b.buf) {
n = copy(p, b.buf[offset:])
}
n = copy(p, b.buf[offset:])
if n > 0 {
b.last_read = .Read
}
+1 -1
View File
@@ -638,7 +638,7 @@ trim_left_proc :: proc(s: []byte, p: proc(rune) -> bool) -> []byte {
index_rune :: proc(s: []byte, r: rune) -> int {
switch {
case 0 <= r && r < utf8.RUNE_SELF:
case u32(r) < utf8.RUNE_SELF:
return index_byte(s, byte(r))
case r == utf8.RUNE_ERROR:
+15 -2
View File
@@ -14,11 +14,24 @@ when ODIN_OS == .Windows {
// EDOM,
// EILSEQ
// ERANGE
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
when ODIN_OS == .Linux {
@(private="file")
@(default_calling_convention="c")
foreign libc {
@(link_name="__libc_errno_location")
@(link_name="__errno_location")
_get_errno :: proc() -> ^int ---
}
EDOM :: 33
EILSEQ :: 84
ERANGE :: 34
}
when ODIN_OS == .FreeBSD {
@(private="file")
@(default_calling_convention="c")
foreign libc {
@(link_name="__error")
_get_errno :: proc() -> ^int ---
}
+4 -1
View File
@@ -1,7 +1,10 @@
package libc
when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
foreign import libc {
"system:libucrt.lib",
"system:legacy_stdio_definitions.lib",
}
} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
+7 -9
View File
@@ -380,20 +380,18 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
field := any{field_ptr, type.id}
unmarshal_value(p, field) or_return
if parse_comma(p) {
break struct_loop
}
continue struct_loop
} else {
// allows skipping unused struct fields
parse_value(p) or_return
if parse_comma(p) {
break struct_loop
}
continue struct_loop
}
// NOTE(bill, 2022-09-14): Previously this would not be allowed
// {"foo": 123, "bar": 456}
// T :: struct{foo: int}
// `T` is missing the `bar` field
// The line below is commented out to ignore fields in an object which
// do not have a corresponding target field
//
// return Unsupported_Type_Error{v.id, p.curr_token}
}
case reflect.Type_Info_Map:
+1 -1
View File
@@ -975,7 +975,7 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
}
}
else {
io.write_string(fi.writer, s[:fi.width], &fi.n)
io.write_string(fi.writer, s, &fi.n)
}
}
else
+3 -3
View File
@@ -16,7 +16,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
w := io.to_writer(os.stream_from_handle(fd))
return wprintln(w=w, args=args, sep=sep)
}
// fprintf formats according to the specififed format string and writes to fd
// fprintf formats according to the specified format string and writes to fd
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
w := io.to_writer(os.stream_from_handle(fd))
return wprintf(w, fmt, ..args)
@@ -34,12 +34,12 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
// println formats using the default print settings and writes to os.stdout
println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
// printf formats according to the specififed format string and writes to os.stdout
// printf formats according to the specified format string and writes to os.stdout
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
// eprint formats using the default print settings and writes to os.stderr
eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
// eprintln formats using the default print settings and writes to os.stderr
eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
// eprintf formats according to the specififed format string and writes to os.stderr
// eprintf formats according to the specified format string and writes to os.stderr
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
+4 -2
View File
@@ -72,8 +72,9 @@ djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bound
return
}
// If you have a choice, prefer fnv32a
@(optimization_mode="speed")
fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
fnv32_no_a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed
for b in data {
h = (h * 0x01000193) ~ u32(b)
@@ -81,8 +82,9 @@ fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
return h
}
// If you have a choice, prefer fnv64a
@(optimization_mode="speed")
fnv64 :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
h: u64 = seed
for b in data {
h = (h * 0x100000001b3) ~ u64(b)
+9
View File
@@ -182,3 +182,12 @@ shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
array[i], array[j] = array[j], array[i]
}
}
// Returns a random element from the given slice
choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) {
n := i64(len(array))
if n < 1 {
return E{}
}
return array[int63_max(n, r)]
}
+2 -6
View File
@@ -1,5 +1,6 @@
package mem
import "core:builtin"
import "core:runtime"
Raw_Any :: runtime.Raw_Any
@@ -21,12 +22,7 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
return transmute(any)Raw_Any{data, id}
}
raw_array_data :: runtime.raw_array_data
raw_simd_data :: runtime.raw_simd_data
raw_string_data :: runtime.raw_string_data
raw_slice_data :: runtime.raw_slice_data
raw_dynamic_array_data :: runtime.raw_dynamic_array_data
raw_data :: runtime.raw_data
raw_data :: builtin.raw_data
Poly_Raw_Map_Entry :: struct($Key, $Value: typeid) {
+331
View File
@@ -0,0 +1,331 @@
package mem_virtual
import "core:mem"
Arena_Kind :: enum uint {
Growing = 0, // Chained memory blocks (singly linked list).
Static = 1, // Fixed reservation sized.
Buffer = 2, // Uses a fixed sized buffer.
}
Arena :: struct {
kind: Arena_Kind,
curr_block: ^Memory_Block,
total_used: uint,
total_reserved: uint,
minimum_block_size: uint,
temp_count: uint,
}
// 1 MiB should be enough to start with
DEFAULT_ARENA_STATIC_COMMIT_SIZE :: 1<<20
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: DEFAULT_ARENA_STATIC_COMMIT_SIZE
// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
DEFAULT_ARENA_STATIC_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
@(require_results)
arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
arena.kind = .Growing
arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
arena.total_used = 0
arena.total_reserved = arena.curr_block.reserved
return
}
@(require_results)
arena_init_static :: proc(arena: ^Arena, reserved: uint, commit_size: uint = DEFAULT_ARENA_STATIC_COMMIT_SIZE) -> (err: Allocator_Error) {
arena.kind = .Static
arena.curr_block = memory_block_alloc(commit_size, reserved, {}) or_return
arena.total_used = 0
arena.total_reserved = arena.curr_block.reserved
return
}
@(require_results)
arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Error) {
if len(buffer) < size_of(Memory_Block) {
return .Out_Of_Memory
}
arena.kind = .Buffer
mem.zero_slice(buffer)
block_base := raw_data(buffer)
block := (^Memory_Block)(block_base)
block.base = block_base[size_of(Memory_Block):]
block.reserved = len(buffer) - size_of(Memory_Block)
block.committed = block.reserved
block.used = 0
arena.curr_block = block
arena.total_used = 0
arena.total_reserved = arena.curr_block.reserved
return
}
@(require_results)
arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
assert(alignment & (alignment-1) == 0, "non-power of two alignment", loc)
size := size
if size == 0 {
return nil, nil
}
switch arena.kind {
case .Growing:
if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.reserved {
size = mem.align_forward_uint(size, alignment)
if arena.minimum_block_size == 0 {
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
}
block_size := max(size, arena.minimum_block_size)
new_block := memory_block_alloc(size, block_size, {}) or_return
new_block.prev = arena.curr_block
arena.curr_block = new_block
arena.total_reserved += new_block.reserved
}
prev_used := arena.curr_block.used
data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
arena.total_used += arena.curr_block.used - prev_used
case .Static:
if arena.curr_block == nil {
if arena.minimum_block_size == 0 {
arena.minimum_block_size = DEFAULT_ARENA_STATIC_RESERVE_SIZE
}
arena_init_static(arena=arena, reserved=arena.minimum_block_size, commit_size=DEFAULT_ARENA_STATIC_COMMIT_SIZE) or_return
}
fallthrough
case .Buffer:
if arena.curr_block == nil {
return nil, .Out_Of_Memory
}
data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
arena.total_used = arena.curr_block.used
}
return
}
arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) -> bool {
if arena.curr_block != nil {
assert(arena.kind != .Growing, "expected a non .Growing arena", loc)
prev_pos := arena.curr_block.used
arena.curr_block.used = clamp(pos, 0, arena.curr_block.reserved)
if prev_pos < pos {
mem.zero_slice(arena.curr_block.base[arena.curr_block.used:][:pos-prev_pos])
}
arena.total_used = arena.curr_block.used
return true
} else if pos == 0 {
arena.total_used = 0
return true
}
return false
}
arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_location) {
if free_block := arena.curr_block; free_block != nil {
assert(arena.kind == .Growing, "expected a .Growing arena", loc)
arena.curr_block = free_block.prev
memory_block_dealloc(free_block)
}
}
arena_free_all :: proc(arena: ^Arena) {
switch arena.kind {
case .Growing:
for arena.curr_block != nil {
arena_growing_free_last_memory_block(arena)
}
arena.total_reserved = 0
case .Static, .Buffer:
arena_static_reset_to(arena, 0)
}
arena.total_used = 0
}
arena_destroy :: proc(arena: ^Arena) {
arena_free_all(arena)
if arena.kind != .Buffer {
memory_block_dealloc(arena.curr_block)
}
arena.curr_block = nil
arena.total_used = 0
arena.total_reserved = 0
arena.temp_count = 0
}
arena_growing_bootstrap_new :: proc{
arena_growing_bootstrap_new_by_offset,
arena_growing_bootstrap_new_by_name,
}
arena_static_bootstrap_new :: proc{
arena_static_bootstrap_new_by_offset,
arena_static_bootstrap_new_by_name,
}
@(require_results)
arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
bootstrap: Arena
bootstrap.kind = .Growing
bootstrap.minimum_block_size = minimum_block_size
data := arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
ptr = (^T)(raw_data(data))
(^Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
return
}
@(require_results)
arena_growing_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
return arena_growing_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
}
@(require_results)
arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
bootstrap: Arena
bootstrap.kind = .Static
bootstrap.minimum_block_size = reserved
data := arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
ptr = (^T)(raw_data(data))
(^Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
return
}
@(require_results)
arena_static_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
return arena_static_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
}
@(require_results)
arena_allocator :: proc(arena: ^Arena) -> mem.Allocator {
return mem.Allocator{arena_allocator_proc, arena}
}
arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
arena := (^Arena)(allocator_data)
size, alignment := uint(size), uint(alignment)
old_size := uint(old_size)
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
return arena_alloc(arena, size, alignment)
case .Free:
err = .Mode_Not_Implemented
case .Free_All:
arena_free_all(arena)
case .Resize:
old_data := ([^]byte)(old_memory)
switch {
case old_data == nil:
return arena_alloc(arena, size, alignment)
case size == old_size:
// return old memory
data = old_data[:size]
return
case size == 0:
err = .Mode_Not_Implemented
return
case (uintptr(old_data) & uintptr(alignment-1) == 0) && size < old_size:
// shrink data in-place
data = old_data[:size]
return
}
new_memory := arena_alloc(arena, size, alignment) or_return
if new_memory == nil {
return
}
copy(new_memory, old_data[:old_size])
return new_memory, nil
case .Query_Features:
set := (^mem.Allocator_Mode_Set)(old_memory)
if set != nil {
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
}
case .Query_Info:
err = .Mode_Not_Implemented
}
return
}
Arena_Temp :: struct {
arena: ^Arena,
block: ^Memory_Block,
used: uint,
}
@(require_results)
arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) {
assert(arena != nil, "nil arena", loc)
temp.arena = arena
temp.block = arena.curr_block
if arena.curr_block != nil {
temp.used = arena.curr_block.used
}
arena.temp_count += 1
return
}
arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
assert(temp.arena != nil, "nil arena", loc)
arena := temp.arena
memory_block_found := false
for block := arena.curr_block; block != nil; block = block.prev {
if block == temp.block {
memory_block_found = true
break
}
}
if !memory_block_found {
assert(arena.curr_block == temp.block, "memory block stored within Arena_Temp not owned by Arena", loc)
}
for arena.curr_block != temp.block {
arena_growing_free_last_memory_block(arena)
}
if block := arena.curr_block; block != nil {
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
block.used = temp.used
}
assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
arena.temp_count -= 1
}
arena_check_temp :: proc(arena: ^Arena, loc := #caller_location) {
assert(arena.temp_count == 0, "Arena_Temp not been ended", loc)
}
-41
View File
@@ -1,41 +0,0 @@
package mem_virtual
arena_init :: proc{
static_arena_init,
growing_arena_init,
}
arena_temp_begin :: proc{
static_arena_temp_begin,
growing_arena_temp_begin,
}
arena_temp_end :: proc{
static_arena_temp_end,
growing_arena_temp_end,
}
arena_check_temp :: proc{
static_arena_check_temp,
growing_arena_check_temp,
}
arena_allocator :: proc{
static_arena_allocator,
growing_arena_allocator,
}
arena_alloc :: proc{
static_arena_alloc,
growing_arena_alloc,
}
arena_free_all :: proc{
static_arena_free_all,
growing_arena_free_all,
}
arena_destroy :: proc{
static_arena_destroy,
growing_arena_destroy,
}
-171
View File
@@ -1,171 +0,0 @@
package mem_virtual
import "core:mem"
Growing_Arena :: struct {
curr_block: ^Memory_Block,
total_used: uint,
total_reserved: uint,
minimum_block_size: uint,
temp_count: int,
}
DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough
growing_arena_init :: proc(arena: ^Growing_Arena, reserved: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
arena.total_used = 0
arena.total_reserved = arena.curr_block.reserved
return
}
growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check {
alignment_offset := uint(0)
ptr := uintptr(arena.curr_block.base[arena.curr_block.used:])
mask := uintptr(alignment-1)
if ptr & mask != 0 {
alignment_offset = uint(alignment) - uint(ptr & mask)
}
return alignment_offset
}
assert(mem.is_power_of_two(uintptr(alignment)))
size := uint(0)
if arena.curr_block != nil {
size = uint(min_size) + align_forward_offset(arena, alignment)
}
if arena.curr_block == nil || arena.curr_block.used + size > arena.curr_block.reserved {
size = uint(mem.align_forward_int(min_size, alignment))
arena.minimum_block_size = max(DEFAULT_MINIMUM_BLOCK_SIZE, arena.minimum_block_size)
block_size := max(size, arena.minimum_block_size)
new_block := memory_block_alloc(size, block_size, {}) or_return
new_block.prev = arena.curr_block
arena.curr_block = new_block
arena.total_reserved += new_block.reserved
}
data, err = alloc_from_memory_block(arena.curr_block, int(size), alignment)
if err == nil {
arena.total_used += size
}
return
}
growing_arena_free_last_memory_block :: proc(arena: ^Growing_Arena) {
free_block := arena.curr_block
arena.curr_block = free_block.prev
memory_block_dealloc(free_block)
}
growing_arena_free_all :: proc(arena: ^Growing_Arena) {
for arena.curr_block != nil {
growing_arena_free_last_memory_block(arena)
}
arena.total_used = 0
arena.total_reserved = 0
}
growing_arena_destroy :: proc(arena: ^Growing_Arena) {
growing_arena_free_all(arena)
}
growing_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
bootstrap: Growing_Arena
bootstrap.minimum_block_size = minimum_block_size
data := growing_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
ptr = (^T)(raw_data(data))
(^Growing_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
return
}
growing_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
return growing_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
}
growing_arena_bootstrap_new :: proc{
growing_arena_bootstrap_new_by_offset,
growing_arena_bootstrap_new_by_name,
}
growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
return mem.Allocator{growing_arena_allocator_proc, arena}
}
growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
arena := (^Growing_Arena)(allocator_data)
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
return growing_arena_alloc(arena, size, alignment)
case .Free:
err = .Mode_Not_Implemented
return
case .Free_All:
growing_arena_free_all(arena)
return
case .Resize:
return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, growing_arena_allocator(arena), location)
case .Query_Features, .Query_Info:
err = .Mode_Not_Implemented
return
}
err = .Mode_Not_Implemented
return
}
Growing_Arena_Temp :: struct {
arena: ^Growing_Arena,
block: ^Memory_Block,
used: uint,
}
growing_arena_temp_begin :: proc(arena: ^Growing_Arena) -> (temp: Growing_Arena_Temp) {
temp.arena = arena
temp.block = arena.curr_block
if arena.curr_block != nil {
temp.used = arena.curr_block.used
}
arena.temp_count += 1
return
}
growing_arena_temp_end :: proc(temp: Growing_Arena_Temp, loc := #caller_location) {
assert(temp.arena != nil, "nil arena", loc)
arena := temp.arena
for arena.curr_block != temp.block {
growing_arena_free_last_memory_block(arena)
}
if block := arena.curr_block; block != nil {
assert(block.used >= temp.used, "out of order use of growing_arena_temp_end", loc)
amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
block.used = temp.used
}
assert(arena.temp_count > 0, "double-use of growing_arena_temp_end", loc)
arena.temp_count -= 1
}
growing_arena_check_temp :: proc(arena: ^Growing_Arena, loc := #caller_location) {
assert(arena.temp_count == 0, "Growing_Arena_Temp not been ended", loc)
}
-153
View File
@@ -1,153 +0,0 @@
package mem_virtual
import "core:mem"
Static_Arena :: struct {
block: ^Memory_Block,
total_used: uint,
total_reserved: uint,
minimum_block_size: uint,
temp_count: int,
}
STATIC_ARENA_DEFAULT_COMMIT_SIZE :: 1<<20 // 1 MiB should be enough to start with
// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
STATIC_ARENA_DEFAULT_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
static_arena_init :: proc(arena: ^Static_Arena, reserved: uint, commit_size: uint = STATIC_ARENA_DEFAULT_COMMIT_SIZE) -> (err: Allocator_Error) {
arena.block = memory_block_alloc(commit_size, reserved, {}) or_return
arena.total_used = 0
arena.total_reserved = arena.block.reserved
return
}
static_arena_destroy :: proc(arena: ^Static_Arena) {
memory_block_dealloc(arena.block)
arena^ = {}
}
static_arena_alloc :: proc(arena: ^Static_Arena, size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
align_forward :: #force_inline proc "contextless" (ptr: uint, align: uint) -> uint {
mask := align-1
return (ptr + mask) &~ mask
}
if arena.block == nil {
reserve_size := max(arena.minimum_block_size, STATIC_ARENA_DEFAULT_RESERVE_SIZE)
static_arena_init(arena, reserve_size, STATIC_ARENA_DEFAULT_COMMIT_SIZE) or_return
}
MINIMUM_ALIGN :: 2*align_of(uintptr)
defer arena.total_used = arena.block.used
return alloc_from_memory_block(arena.block, size, max(MINIMUM_ALIGN, alignment))
}
static_arena_reset_to :: proc(arena: ^Static_Arena, pos: uint) -> bool {
if arena.block != nil {
prev_pos := arena.block.used
arena.block.used = clamp(pos, 0, arena.block.reserved)
if prev_pos < pos {
mem.zero_slice(arena.block.base[arena.block.used:][:pos-prev_pos])
}
return true
} else if pos == 0 {
return true
}
return false
}
static_arena_free_all :: proc(arena: ^Static_Arena) {
static_arena_reset_to(arena, 0)
}
static_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
bootstrap: Static_Arena
bootstrap.minimum_block_size = reserved
data := static_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
ptr = (^T)(raw_data(data))
(^Static_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
return
}
static_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
return static_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
}
static_arena_bootstrap_new :: proc{
static_arena_bootstrap_new_by_offset,
static_arena_bootstrap_new_by_name,
}
static_arena_allocator :: proc(arena: ^Static_Arena) -> mem.Allocator {
return mem.Allocator{static_arena_allocator_proc, arena}
}
static_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
arena := (^Static_Arena)(allocator_data)
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
return static_arena_alloc(arena, size, alignment)
case .Free:
err = .Mode_Not_Implemented
return
case .Free_All:
static_arena_free_all(arena)
return
case .Resize:
return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, static_arena_allocator(arena), location)
case .Query_Features, .Query_Info:
err = .Mode_Not_Implemented
return
}
err = .Mode_Not_Implemented
return
}
Static_Arena_Temp :: struct {
arena: ^Static_Arena,
used: uint,
}
static_arena_temp_begin :: proc(arena: ^Static_Arena) -> (temp: Static_Arena_Temp) {
temp.arena = arena
temp.used = arena.block.used if arena.block != nil else 0
arena.temp_count += 1
return
}
static_arena_temp_end :: proc(temp: Static_Arena_Temp, loc := #caller_location) {
assert(temp.arena != nil, "nil arena", loc)
arena := temp.arena
used := arena.block.used if arena.block != nil else 0
assert(temp.used >= used, "invalid Static_Arena_Temp", loc)
static_arena_reset_to(arena, temp.used)
assert(arena.temp_count > 0, "double-use of static_arena_temp_end", loc)
arena.temp_count -= 1
}
static_arena_check_temp :: proc(arena: ^Static_Arena, loc := #caller_location) {
assert(arena.temp_count == 0, "Static_Arena_Temp not been ended", loc)
}
+18 -15
View File
@@ -1,6 +1,7 @@
package mem_virtual
import "core:mem"
import "core:intrinsics"
DEFAULT_PAGE_SIZE := uint(4096)
@@ -95,18 +96,11 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
pmblock.block.committed = committed
pmblock.block.reserved = reserved
sentinel := &global_platform_memory_block_sentinel
platform_mutex_lock()
pmblock.next = sentinel
pmblock.prev = sentinel.prev
pmblock.prev.next = pmblock
pmblock.next.prev = pmblock
platform_mutex_unlock()
return &pmblock.block, nil
}
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint) -> (data: []byte, err: Allocator_Error) {
calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
alignment_offset := uint(0)
ptr := uintptr(block.base[block.used:])
@@ -134,11 +128,18 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
return nil
}
if block == nil {
return nil, .Out_Of_Memory
}
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
size := uint(min_size) + alignment_offset
size, size_ok := safe_add(min_size, alignment_offset)
if !size_ok {
err = .Out_Of_Memory
return
}
if block.used + size > block.reserved {
if to_be_used, ok := safe_add(block.used, size); !ok || to_be_used > block.reserved {
err = .Out_Of_Memory
return
}
@@ -153,12 +154,14 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
if block := (^Platform_Memory_Block)(block_to_free); block != nil {
platform_mutex_lock()
block.prev.next = block.next
block.next.prev = block.prev
platform_mutex_unlock()
platform_memory_free(block)
}
}
@(private)
safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
z, did_overflow := intrinsics.overflow_add(x, y)
return z, !did_overflow
}
-26
View File
@@ -1,13 +1,10 @@
//+private
package mem_virtual
import "core:sync"
Platform_Memory_Block :: struct {
block: Memory_Block,
committed: uint,
reserved: uint,
prev, next: ^Platform_Memory_Block,
}
platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
@@ -33,28 +30,6 @@ platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) {
}
}
platform_mutex_lock :: proc() {
sync.mutex_lock(&global_memory_block_mutex)
}
platform_mutex_unlock :: proc() {
sync.mutex_unlock(&global_memory_block_mutex)
}
global_memory_block_mutex: sync.Mutex
global_platform_memory_block_sentinel: Platform_Memory_Block
global_platform_memory_block_sentinel_set: bool
@(init)
platform_memory_init :: proc() {
if !global_platform_memory_block_sentinel_set {
_platform_memory_init()
global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel
global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel
global_platform_memory_block_sentinel_set = true
}
}
platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
if to_commit < block.committed {
return nil
@@ -63,7 +38,6 @@ platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_
return .Out_Of_Memory
}
commit(block, to_commit) or_return
block.committed = to_commit
return nil
+1 -4
View File
@@ -2,7 +2,6 @@ package os
import win32 "core:sys/windows"
import "core:strings"
import "core:time"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
@@ -41,9 +40,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
// fi.mode |= file_type_mode(h);
}
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
windows_set_file_info_times(&fi, d)
fi.is_dir = fi.mode & File_Mode_Dir != 0
return
+4 -2
View File
@@ -162,7 +162,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
total_read: int
length := len(data)
to_read := min(win32.DWORD(length), MAX_RW)
// NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that.
to_read := min(i64(length), MAX_RW)
e: win32.BOOL
if is_console {
@@ -172,7 +173,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
return int(total_read), err
}
} else {
e = win32.ReadFile(handle, &data[total_read], to_read, &single_read_length, nil)
// NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB)
e = win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &single_read_length, nil)
}
if single_read_length <= 0 || !e {
err := Errno(win32.GetLastError())
+1 -1
View File
@@ -130,7 +130,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
f := new(File, _file_allocator())
f.impl.allocator = _file_allocator()
f.impl.fd = rawptr(fd)
f.impl.fd = rawptr(handle)
f.impl.name = strings.clone(name, f.impl.allocator)
f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
+32 -11
View File
@@ -369,27 +369,48 @@ close :: proc(fd: Handle) -> bool {
return _unix_close(fd) == 0
}
@(private)
MAX_RW :: 0x7fffffff // The limit on Darwin is max(i32), trying to read/write more than that fails.
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
assert(fd != -1)
if len(data) == 0 {
return 0, 0
bytes_total := len(data)
bytes_written_total := 0
for bytes_written_total < bytes_total {
bytes_to_write := min(bytes_total - bytes_written_total, MAX_RW)
slice := data[bytes_written_total:bytes_written_total + bytes_to_write]
bytes_written := _unix_write(fd, raw_data(slice), bytes_to_write)
if bytes_written == -1 {
return bytes_written_total, 1
}
bytes_written_total += bytes_written
}
bytes_written := _unix_write(fd, raw_data(data), len(data))
if bytes_written == -1 {
return 0, 1
}
return bytes_written, 0
return bytes_written_total, 0
}
read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
assert(fd != -1)
bytes_read := _unix_read(fd, raw_data(data), len(data))
if bytes_read == -1 {
return 0, 1
bytes_total := len(data)
bytes_read_total := 0
for bytes_read_total < bytes_total {
bytes_to_read := min(bytes_total - bytes_read_total, MAX_RW)
slice := data[bytes_read_total:bytes_read_total + bytes_to_read]
bytes_read := _unix_read(fd, raw_data(slice), bytes_to_read)
if bytes_read == -1 {
return bytes_read_total, 1
}
if bytes_read == 0 {
break
}
bytes_read_total += bytes_read
}
return bytes_read, 0
return bytes_read_total, 0
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
+32 -3
View File
@@ -24,7 +24,7 @@ O_CLOEXEC :: 0x80000
stdin: Handle = 0
stdout: Handle = 1
stderr: Handle = 2
current_dir: Handle = 3
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
iovs := wasi.ciovec_t(data)
@@ -47,7 +47,36 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
return int(n), Errno(err)
}
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
return 0, -1
oflags: wasi.oflags_t
if mode & O_CREATE == O_CREATE {
oflags += {.CREATE}
}
if mode & O_EXCL == O_EXCL {
oflags += {.EXCL}
}
if mode & O_TRUNC == O_TRUNC {
oflags += {.TRUNC}
}
rights: wasi.rights_t = {.FD_SEEK, .FD_FILESTAT_GET}
switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
case O_RDONLY: rights += {.FD_READ}
case O_WRONLY: rights += {.FD_WRITE}
case O_RDWR: rights += {.FD_READ, .FD_WRITE}
}
fdflags: wasi.fdflags_t
if mode & O_APPEND == O_APPEND {
fdflags += {.APPEND}
}
if mode & O_NONBLOCK == O_NONBLOCK {
fdflags += {.NONBLOCK}
}
if mode & O_SYNC == O_SYNC {
fdflags += {.SYNC}
}
fd, err := wasi.path_open(wasi.fd_t(current_dir),{.SYMLINK_FOLLOW},path,oflags,rights,{},fdflags)
return Handle(fd), Errno(err)
}
close :: proc(fd: Handle) -> Errno {
err := wasi.fd_close(wasi.fd_t(fd))
@@ -96,4 +125,4 @@ heap_free :: proc(ptr: rawptr) {
exit :: proc "contextless" (code: int) -> ! {
runtime._cleanup_runtime_contextless()
wasi.proc_exit(wasi.exitcode_t(code))
}
}
+10 -10
View File
@@ -228,6 +228,13 @@ file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HAN
return
}
@(private)
windows_set_file_info_times :: proc(fi: ^File_Info, d: ^$T) {
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
}
@(private)
file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
@@ -235,9 +242,7 @@ file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.is_dir = fi.mode & File_Mode_Dir != 0
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
windows_set_file_info_times(&fi, d)
fi.fullpath, e = full_path_from_name(name)
fi.name = basename(fi.fullpath)
@@ -252,9 +257,7 @@ file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string)
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
fi.is_dir = fi.mode & File_Mode_Dir != 0
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
windows_set_file_info_times(&fi, d)
fi.fullpath, e = full_path_from_name(name)
fi.name = basename(fi.fullpath)
@@ -290,10 +293,7 @@ file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HAN
fi.mode |= file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag)
fi.is_dir = fi.mode & File_Mode_Dir != 0
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
windows_set_file_info_times(&fi, &d)
return fi, ERROR_NONE
}
+1 -29
View File
@@ -672,7 +672,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
@builtin
map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
key, value := key, value
h := __get_map_header(T)
h := __get_map_header_table(T)
e := __dynamic_map_set(m, h, __get_map_key_hash(&key), &key, &value, loc)
return (^V)(uintptr(e) + h.value_offset)
@@ -731,34 +731,6 @@ card :: proc(s: $S/bit_set[$E; $U]) -> int {
@builtin
raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> [^]E {
return ([^]E)(a)
}
@builtin
raw_simd_data :: proc "contextless" (a: $P/^($T/#simd[$N]$E)) -> [^]E {
return ([^]E)(a)
}
@builtin
raw_slice_data :: proc "contextless" (s: $S/[]$E) -> [^]E {
ptr := (transmute(Raw_Slice)s).data
return ([^]E)(ptr)
}
@builtin
raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> [^]E {
ptr := (transmute(Raw_Dynamic_Array)s).data
return ([^]E)(ptr)
}
@builtin
raw_string_data :: proc "contextless" (s: $S/string) -> [^]u8 {
return (transmute(Raw_String)s).data
}
@builtin
raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data, raw_simd_data}
@builtin
@(disabled=ODIN_DISABLE_ASSERT)
assert :: proc(condition: bool, message := "", loc := #caller_location) {
+3 -3
View File
@@ -18,7 +18,7 @@ type_assertion_trap :: proc "contextless" () -> ! {
bounds_check_error :: proc "contextless" (file: string, line, column: i32, index, count: int) {
if 0 <= index && index < count {
if uint(index) < uint(count) {
return
}
@(cold)
@@ -99,8 +99,8 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) {
if 0 <= row_index && row_index < row_count &&
0 <= column_index && column_index < column_count {
if uint(row_index) < uint(row_count) &&
uint(column_index) < uint(column_count) {
return
}
@(cold)
+1 -1
View File
@@ -12,7 +12,7 @@ objc_SEL :: ^intrinsics.objc_selector
foreign Foundation {
objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
sel_registerName :: proc "c" (name: cstring) -> objc_SEL ---
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) ---
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class ---
objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
+2 -2
View File
@@ -321,14 +321,14 @@ last_ptr :: proc(array: $T/[]$E) -> ^E {
}
get :: proc(array: $T/[]$E, index: int) -> (value: E, ok: bool) {
if 0 <= index && index < len(array) {
if uint(index) < len(array) {
value = array[index]
ok = true
}
return
}
get_ptr :: proc(array: $T/[]$E, index: int) -> (value: ^E, ok: bool) {
if 0 <= index && index < len(array) {
if uint(index) < len(array) {
value = &array[index]
ok = true
}
+3 -2
View File
@@ -177,7 +177,6 @@ _quick_sort_general :: proc(data: $T/[]$E, a, b, max_depth: int, call: $P, $KIND
}
// merge sort
_stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check {
less :: #force_inline proc(a, b: E, call: P) -> bool {
when KIND == .Ordered {
@@ -190,7 +189,9 @@ _stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (O
#panic("unhandled Sort_Kind")
}
}
// insertion sort
// TODO(bill): use a different algorithm as insertion sort is O(n^2)
n := len(data)
for i in 1..<n {
for j := i; j > 0 && less(data[j], data[j-1], call); j -= 1 {
+41 -2
View File
@@ -567,7 +567,7 @@ parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) {
// ```
parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
s := str
defer if n != nil { n^ = len(str)-len(s) }
defer if n != nil { n^ = len(str) - len(s) }
if s == "" {
return
}
@@ -575,9 +575,11 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
i := 0
sign: f64 = 1
seen_sign := true
switch s[i] {
case '-': i += 1; sign = -1
case '+': i += 1
case: seen_sign = false
}
for ; i < len(s); i += 1 {
@@ -588,6 +590,38 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
v := _digit_value(r)
if v >= 10 {
if r == '.' || r == 'e' || r == 'E' { // Skip parsing NaN and Inf if it's probably a regular float
break
}
if len(s) >= 3 + i {
buf: [4]u8
copy(buf[:], s[i:][:3])
v2 := transmute(u32)buf
v2 &= 0xDFDFDFDF // Knock out lower-case bits
buf = transmute([4]u8)v2
when ODIN_ENDIAN == .Little {
if v2 == 0x464e49 { // "INF"
s = s[3+i:]
value = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
return value, len(s) == 0
} else if v2 == 0x4e414e { // "NAN"
s = s[3+i:]
return 0h7ff80000_00000001, len(s) == 0
}
} else {
if v2 == 0x494e4600 { // "\0FNI"
s = s[3+i:]
value = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
return value, len(s) == 0
} else if v2 == 0x4e414e00 { // "\0NAN"
s = s[3+i:]
return 0h7ff80000_00000001, len(s) == 0
}
}
}
break
}
value *= 10
@@ -645,8 +679,13 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
for exp > 0 { scale *= 10; exp -= 1 }
}
}
s = s[i:]
// If we only consumed a sign, return false
if i == 1 && seen_sign {
return 0, false
}
s = s[i:]
if frac {
value = sign * (value/scale)
} else {
+1 -1
View File
@@ -760,7 +760,7 @@ last_index_byte :: proc(s: string, c: byte) -> int {
*/
index_rune :: proc(s: string, r: rune) -> int {
switch {
case 0 <= r && r < utf8.RUNE_SELF:
case u32(r) < utf8.RUNE_SELF:
return index_byte(s, byte(r))
case r == utf8.RUNE_ERROR:
+1
View File
@@ -464,6 +464,7 @@ macos_release_map: map[string]Darwin_To_Release = {
"21F2092" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
"21G72" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}},
"21G83" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}},
"21G115" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 0}}},
}
@(private)
+30
View File
@@ -128,4 +128,34 @@ foreign advapi32 {
lpData: LPCVOID,
cbData: DWORD,
) -> LSTATUS ---
GetFileSecurityW :: proc(
lpFileName: LPCWSTR,
RequestedInformation: SECURITY_INFORMATION,
pSecurityDescriptor: PSECURITY_DESCRIPTOR,
nLength: DWORD,
lpnLengthNeeded: LPDWORD,
) -> BOOL ---
DuplicateToken :: proc(
ExistingTokenHandle: HANDLE,
ImpersonationLevel: SECURITY_IMPERSONATION_LEVEL,
DuplicateTokenHandle: PHANDLE,
) -> BOOL ---
MapGenericMask :: proc(
AccessMask: PDWORD,
GenericMapping: PGENERIC_MAPPING,
) ---
AccessCheck :: proc(
pSecurityDescriptor: PSECURITY_DESCRIPTOR,
ClientToken: HANDLE,
DesiredAccess: DWORD,
GenericMapping: PGENERIC_MAPPING,
PrivilegeSet: PPRIVILEGE_SET,
PrivilegeSetLength: LPDWORD,
GrantedAccess: LPDWORD,
AccessStatus: LPBOOL,
) -> BOOL ---
}
+9
View File
@@ -0,0 +1,9 @@
// +build windows
package sys_windows
foreign import "system:Comctl32.lib"
@(default_calling_convention="stdcall")
foreign Comctl32 {
LoadIconWithScaleDown :: proc(hinst: HINSTANCE, pszName: PCWSTR, cx: c_int, cy: c_int, phico: ^HICON) -> HRESULT ---
}
+1
View File
@@ -77,6 +77,7 @@ foreign gdi32 {
) -> HFONT ---
TextOutW :: proc(hdc: HDC, x, y: c_int, lpString: LPCWSTR, c: c_int) -> BOOL ---
GetTextExtentPoint32W :: proc(hdc: HDC, lpString: LPCWSTR, c: c_int, psizl: LPSIZE) -> BOOL ---
GetTextMetricsW :: proc(hdc: HDC, lptm: LPTEXTMETRICW) -> BOOL ---
}
RGB :: #force_inline proc "contextless" (r, g, b: u8) -> COLORREF {
+173
View File
@@ -248,6 +248,17 @@ foreign kernel32 {
GetModuleHandleW :: proc(lpModuleName: LPCWSTR) -> HMODULE ---
GetModuleHandleA :: proc(lpModuleName: LPCSTR) -> HMODULE ---
GetSystemTimeAsFileTime :: proc(lpSystemTimeAsFileTime: LPFILETIME) ---
GetSystemTimePreciseAsFileTime :: proc(lpSystemTimeAsFileTime: LPFILETIME) ---
FileTimeToSystemTime :: proc(lpFileTime: ^FILETIME, lpSystemTime: ^SYSTEMTIME) -> BOOL ---
SystemTimeToTzSpecificLocalTime :: proc(
lpTimeZoneInformation: ^TIME_ZONE_INFORMATION,
lpUniversalTime: ^SYSTEMTIME,
lpLocalTime: ^SYSTEMTIME,
) -> BOOL ---
SystemTimeToFileTime :: proc(
lpSystemTime: ^SYSTEMTIME,
lpFileTime: LPFILETIME,
) -> BOOL ---
CreateEventW :: proc(
lpEventAttributes: LPSECURITY_ATTRIBUTES,
bManualReset: BOOL,
@@ -346,6 +357,13 @@ foreign kernel32 {
GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL ---
FreeConsole :: proc() -> BOOL ---
GetConsoleWindow :: proc() -> HWND ---
GetDiskFreeSpaceExW :: proc(
lpDirectoryName: LPCWSTR,
lpFreeBytesAvailableToCaller: PULARGE_INTEGER,
lpTotalNumberOfBytes: PULARGE_INTEGER,
lpTotalNumberOfFreeBytes: PULARGE_INTEGER,
) -> BOOL ---
}
@@ -820,3 +838,158 @@ foreign kernel32 {
HandlerRoutine :: proc "stdcall" (dwCtrlType: DWORD) -> BOOL
PHANDLER_ROUTINE :: HandlerRoutine
DCB_Config :: struct {
fParity: bool,
fOutxCtsFlow: bool,
fOutxDsrFlow: bool,
fDtrControl: DTR_Control,
fDsrSensitivity: bool,
fTXContinueOnXoff: bool,
fOutX: bool,
fInX: bool,
fErrorChar: bool,
fNull: bool,
fRtsControl: RTS_Control,
fAbortOnError: bool,
BaudRate: DWORD,
ByteSize: BYTE,
Parity: Parity,
StopBits: Stop_Bits,
XonChar: byte,
XoffChar: byte,
ErrorChar: byte,
EvtChar: byte,
}
DTR_Control :: enum byte {
Disable = 0,
Enable = 1,
Handshake = 2,
}
RTS_Control :: enum byte {
Disable = 0,
Enable = 1,
Handshake = 2,
Toggle = 3,
}
Parity :: enum byte {
None = 0,
Odd = 1,
Even = 2,
Mark = 3,
Space = 4,
}
Stop_Bits :: enum byte {
One = 0,
One_And_A_Half = 1,
Two = 2,
}
// A helper procedure to set the values of a DCB structure.
init_dcb_with_config :: proc "contextless" (dcb: ^DCB, config: DCB_Config) {
out: u32
// NOTE(tetra, 2022-09-21): On both Clang 14 on Windows, and MSVC, the bits in the bitfield
// appear to be defined from LSB to MSB order.
// i.e: `fBinary` (the first bitfield in the C source) is the LSB in the `settings` u32.
out |= u32(1) << 0 // fBinary must always be true on Windows.
out |= u32(config.fParity) << 1
out |= u32(config.fOutxCtsFlow) << 2
out |= u32(config.fOutxDsrFlow) << 3
out |= u32(config.fDtrControl) << 4
out |= u32(config.fDsrSensitivity) << 6
out |= u32(config.fTXContinueOnXoff) << 7
out |= u32(config.fOutX) << 8
out |= u32(config.fInX) << 9
out |= u32(config.fErrorChar) << 10
out |= u32(config.fNull) << 11
out |= u32(config.fRtsControl) << 12
out |= u32(config.fAbortOnError) << 14
dcb.settings = out
dcb.BaudRate = config.BaudRate
dcb.ByteSize = config.ByteSize
dcb.Parity = config.Parity
dcb.StopBits = config.StopBits
dcb.XonChar = config.XonChar
dcb.XoffChar = config.XoffChar
dcb.ErrorChar = config.ErrorChar
dcb.EvtChar = config.EvtChar
dcb.DCBlength = size_of(DCB)
}
get_dcb_config :: proc "contextless" (dcb: DCB) -> (config: DCB_Config) {
config.fParity = bool((dcb.settings >> 1) & 0x01)
config.fOutxCtsFlow = bool((dcb.settings >> 2) & 0x01)
config.fOutxDsrFlow = bool((dcb.settings >> 3) & 0x01)
config.fDtrControl = DTR_Control((dcb.settings >> 4) & 0x02)
config.fDsrSensitivity = bool((dcb.settings >> 6) & 0x01)
config.fTXContinueOnXoff = bool((dcb.settings >> 7) & 0x01)
config.fOutX = bool((dcb.settings >> 8) & 0x01)
config.fInX = bool((dcb.settings >> 9) & 0x01)
config.fErrorChar = bool((dcb.settings >> 10) & 0x01)
config.fNull = bool((dcb.settings >> 11) & 0x01)
config.fRtsControl = RTS_Control((dcb.settings >> 12) & 0x02)
config.fAbortOnError = bool((dcb.settings >> 14) & 0x01)
config.BaudRate = dcb.BaudRate
config.ByteSize = dcb.ByteSize
config.Parity = dcb.Parity
config.StopBits = dcb.StopBits
config.XonChar = dcb.XonChar
config.XoffChar = dcb.XoffChar
config.ErrorChar = dcb.ErrorChar
config.EvtChar = dcb.EvtChar
return
}
// NOTE(tetra): See get_dcb_config() and init_dcb_with_config() for help with initializing this.
DCB :: struct {
DCBlength: DWORD, // NOTE(tetra): Must be set to size_of(DCB).
BaudRate: DWORD,
settings: u32, // NOTE(tetra): These are bitfields in the C struct.
wReserved: WORD,
XOnLim: WORD,
XOffLim: WORD,
ByteSize: BYTE,
Parity: Parity,
StopBits: Stop_Bits,
XonChar: byte,
XoffChar: byte,
ErrorChar: byte,
EofChar: byte,
EvtChar: byte,
wReserved1: WORD,
}
@(default_calling_convention="stdcall")
foreign kernel32 {
GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
}
LPFIBER_START_ROUTINE :: #type proc "stdcall" (lpFiberParameter: LPVOID)
@(default_calling_convention = "stdcall")
foreign kernel32 {
CreateFiber :: proc(dwStackSize: SIZE_T, lpStartAddress: LPFIBER_START_ROUTINE, lpParameter: LPVOID) -> LPVOID ---
DeleteFiber :: proc(lpFiber: LPVOID) ---
ConvertThreadToFiber :: proc(lpParameter: LPVOID) -> LPVOID ---
SwitchToFiber :: proc(lpFiber: LPVOID) ---
}
+6
View File
@@ -14,4 +14,10 @@ foreign shell32 {
lpDirectory: LPCWSTR,
nShowCmd: INT,
) -> HINSTANCE ---
SHCreateDirectoryExW :: proc(
hwnd: HWND,
pszPath: LPCWSTR,
psa: ^SECURITY_ATTRIBUTES,
) -> c_int ---
SHFileOperationW :: proc(lpFileOp: LPSHFILEOPSTRUCTW) -> c_int ---
}
+1
View File
@@ -8,4 +8,5 @@ foreign shlwapi {
PathFileExistsW :: proc(pszPath: wstring) -> BOOL ---
PathFindExtensionW :: proc(pszPath: wstring) -> wstring ---
PathFindFileNameW :: proc(pszPath: wstring) -> wstring ---
SHAutoComplete :: proc(hwndEdit: HWND, dwFlags: DWORD) -> LWSTDAPI ---
}
+280 -4
View File
@@ -20,6 +20,7 @@ DWORD :: c_ulong
DWORDLONG :: c.ulonglong
QWORD :: c.ulonglong
HANDLE :: distinct LPVOID
PHANDLE :: ^HANDLE
HINSTANCE :: HANDLE
HMODULE :: distinct HINSTANCE
HRESULT :: distinct LONG
@@ -43,6 +44,7 @@ BOOLEAN :: distinct b8
GROUP :: distinct c_uint
LARGE_INTEGER :: distinct c_longlong
ULARGE_INTEGER :: distinct c_ulonglong
PULARGE_INTEGER :: ^ULARGE_INTEGER
LONG :: c_long
UINT :: c_uint
INT :: c_int
@@ -133,6 +135,11 @@ LPWSAOVERLAPPED :: distinct rawptr
LPWSAOVERLAPPED_COMPLETION_ROUTINE :: distinct rawptr
LPCVOID :: rawptr
PACCESS_TOKEN :: PVOID
PSECURITY_DESCRIPTOR :: PVOID
PSID :: PVOID
PCLAIMS_BLOB :: PVOID
PCONDITION_VARIABLE :: ^CONDITION_VARIABLE
PLARGE_INTEGER :: ^LARGE_INTEGER
PSRWLOCK :: ^SRWLOCK
@@ -175,6 +182,7 @@ FILE_SHARE_DELETE: DWORD : 0x00000004
FILE_GENERIC_ALL: DWORD : 0x10000000
FILE_GENERIC_EXECUTE: DWORD : 0x20000000
FILE_GENERIC_READ: DWORD : 0x80000000
FILE_ALL_ACCESS :: STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF
FILE_ACTION_ADDED :: 0x00000001
FILE_ACTION_REMOVED :: 0x00000002
@@ -230,6 +238,20 @@ SECURITY_SQOS_PRESENT: DWORD : 0x00100000
FIONBIO: c_ulong : 0x8004667e
OWNER_SECURITY_INFORMATION :: 0x00000001
GROUP_SECURITY_INFORMATION :: 0x00000002
DACL_SECURITY_INFORMATION :: 0x00000004
SACL_SECURITY_INFORMATION :: 0x00000008
LABEL_SECURITY_INFORMATION :: 0x00000010
ATTRIBUTE_SECURITY_INFORMATION :: 0x00000020
SCOPE_SECURITY_INFORMATION :: 0x00000040
PROCESS_TRUST_LABEL_SECURITY_INFORMATION :: 0x00000080
ACCESS_FILTER_SECURITY_INFORMATION :: 0x00000100
BACKUP_SECURITY_INFORMATION :: 0x00010000
PROTECTED_DACL_SECURITY_INFORMATION :: 0x80000000
PROTECTED_SACL_SECURITY_INFORMATION :: 0x40000000
UNPROTECTED_DACL_SECURITY_INFORMATION :: 0x20000000
UNPROTECTED_SACL_SECURITY_INFORMATION :: 0x10000000
GET_FILEEX_INFO_LEVELS :: distinct i32
GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0
@@ -773,6 +795,30 @@ MSG :: struct {
LPMSG :: ^MSG
TEXTMETRICW :: struct {
tmHeight: LONG,
tmAscent: LONG,
tmDescent: LONG,
tmInternalLeading: LONG,
tmExternalLeading: LONG,
tmAveCharWidth: LONG,
tmMaxCharWidth: LONG,
tmWeight: LONG,
tmOverhang: LONG,
tmDigitizedAspectX: LONG,
tmDigitizedAspectY: LONG,
tmFirstChar: WCHAR,
tmLastChar: WCHAR,
tmDefaultChar: WCHAR,
tmBreakChar: WCHAR,
tmItalic: BYTE,
tmUnderlined: BYTE,
tmStruckOut: BYTE,
tmPitchAndFamily: BYTE,
tmCharSet: BYTE,
}
LPTEXTMETRICW :: ^TEXTMETRICW
PAINTSTRUCT :: struct {
hdc: HDC,
fErase: BOOL,
@@ -879,6 +925,48 @@ NM_FONTCHANGED :: NM_OUTOFMEMORY-22
NM_CUSTOMTEXT :: NM_OUTOFMEMORY-23 // uses NMCUSTOMTEXT struct
NM_TVSTATEIMAGECHANGING :: NM_OUTOFMEMORY-23 // uses NMTVSTATEIMAGECHANGING struct, defined after HTREEITEM
PCZZWSTR :: ^WCHAR
SHFILEOPSTRUCTW :: struct {
hwnd: HWND,
wFunc: UINT,
pFrom: PCZZWSTR,
pTo: PCZZWSTR,
fFlags: FILEOP_FLAGS,
fAnyOperationsAborted: BOOL,
hNameMappings: LPVOID,
lpszProgressTitle: PCWSTR, // only used if FOF_SIMPLEPROGRESS
}
LPSHFILEOPSTRUCTW :: ^SHFILEOPSTRUCTW
// Shell File Operations
FO_MOVE :: 0x0001
FO_COPY :: 0x0002
FO_DELETE :: 0x0003
FO_RENAME :: 0x0004
// SHFILEOPSTRUCT.fFlags and IFileOperation::SetOperationFlags() flag values
FOF_MULTIDESTFILES :: 0x0001
FOF_CONFIRMMOUSE :: 0x0002
FOF_SILENT :: 0x0004 // don't display progress UI (confirm prompts may be displayed still)
FOF_RENAMEONCOLLISION :: 0x0008 // automatically rename the source files to avoid the collisions
FOF_NOCONFIRMATION :: 0x0010 // don't display confirmation UI, assume "yes" for cases that can be bypassed, "no" for those that can not
FOF_WANTMAPPINGHANDLE :: 0x0020 // Fill in SHFILEOPSTRUCT.hNameMappings
// Must be freed using SHFreeNameMappings
FOF_ALLOWUNDO :: 0x0040 // enable undo including Recycle behavior for IFileOperation::Delete()
FOF_FILESONLY :: 0x0080 // only operate on the files (non folders), both files and folders are assumed without this
FOF_SIMPLEPROGRESS :: 0x0100 // means don't show names of files
FOF_NOCONFIRMMKDIR :: 0x0200 // don't dispplay confirmatino UI before making any needed directories, assume "Yes" in these cases
FOF_NOERRORUI :: 0x0400 // don't put up error UI, other UI may be displayed, progress, confirmations
FOF_NOCOPYSECURITYATTRIBS :: 0x0800 // dont copy file security attributes (ACLs)
FOF_NORECURSION :: 0x1000 // don't recurse into directories for operations that would recurse
FOF_NO_CONNECTED_ELEMENTS :: 0x2000 // don't operate on connected elements ("xxx_files" folders that go with .htm files)
FOF_WANTNUKEWARNING :: 0x4000 // during delete operation, warn if object is being permanently destroyed instead of recycling (partially overrides FOF_NOCONFIRMATION)
FOF_NORECURSEREPARSE :: 0x8000 // deprecated; the operations engine always does the right thing on FolderLink objects (symlinks, reparse points, folder shortcuts)
FOF_NO_UI :: (FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR) // don't display any UI at all
FILEOP_FLAGS :: WORD
DEVMODEW :: struct {
dmDeviceName: [32]wchar_t,
dmSpecVersion: WORD,
@@ -1066,8 +1154,14 @@ WS_EX_TOPMOST : UINT : 0x0000_0008
WS_EX_TRANSPARENT : UINT : 0x0000_0020
WS_EX_WINDOWEDGE : UINT : 0x0000_0100
PBS_SMOOTH :: 0x01
PBS_VERTICAL :: 0x04
PBS_SMOOTH :: 0x01
PBS_VERTICAL :: 0x04
PBS_MARQUEE :: 0x08
PBS_SMOOTHREVERSE :: 0x10
PBST_NORMAL :: 0x0001
PBST_ERROR :: 0x0002
PBST_PAUSED :: 0x0003
QS_ALLEVENTS : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY
QS_ALLINPUT : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE
@@ -1462,6 +1556,24 @@ IDI_WARNING := IDI_EXCLAMATION
IDI_ERROR := IDI_HAND
IDI_INFORMATION := IDI_ASTERISK
IMAGE_BITMAP :: 0
IMAGE_ICON :: 1
IMAGE_CURSOR :: 2
IMAGE_ENHMETAFILE :: 3
LR_DEFAULTCOLOR :: 0x00000000
LR_MONOCHROME :: 0x00000001
LR_COLOR :: 0x00000002
LR_COPYRETURNORG :: 0x00000004
LR_COPYDELETEORG :: 0x00000008
LR_LOADFROMFILE :: 0x00000010
LR_LOADTRANSPARENT :: 0x00000020
LR_DEFAULTSIZE :: 0x00000040
LR_VGACOLOR :: 0x00000080
LR_LOADMAP3DCOLORS :: 0x00001000
LR_CREATEDIBSECTION :: 0x00002000
LR_COPYFROMRESOURCE :: 0x00004000
LR_SHARED :: 0x00008000
// DIB color table identifiers
DIB_RGB_COLORS :: 0
@@ -1662,6 +1774,7 @@ WSAENOTCONN: c_int : 10057
WSAESHUTDOWN: c_int : 10058
WSAETIMEDOUT: c_int : 10060
WSAECONNREFUSED: c_int : 10061
WSATRY_AGAIN: c_int : 11002
MAX_PROTOCOL_CHAIN: DWORD : 7
@@ -1773,12 +1886,15 @@ WAIT_FAILED: DWORD : 0xFFFFFFFF
PIPE_ACCESS_INBOUND: DWORD : 0x00000001
PIPE_ACCESS_OUTBOUND: DWORD : 0x00000002
PIPE_ACCESS_DUPLEX: DWORD : 0x00000003
FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD : 0x00080000
FILE_FLAG_OVERLAPPED: DWORD : 0x40000000
PIPE_WAIT: DWORD : 0x00000000
PIPE_TYPE_BYTE: DWORD : 0x00000000
PIPE_TYPE_MESSAGE: DWORD : 0x00000004
PIPE_REJECT_REMOTE_CLIENTS: DWORD : 0x00000008
PIPE_READMODE_BYTE: DWORD : 0x00000000
PIPE_READMODE_MESSAGE: DWORD : 0x00000002
PIPE_ACCEPT_REMOTE_CLIENTS: DWORD : 0x00000000
FD_SETSIZE :: 64
@@ -1792,7 +1908,58 @@ HEAP_ZERO_MEMORY: DWORD : 0x00000008
HANDLE_FLAG_INHERIT: DWORD : 0x00000001
HANDLE_FLAG_PROTECT_FROM_CLOSE :: 0x00000002
TOKEN_READ: DWORD : 0x20008
GENERIC_MAPPING :: struct {
GenericRead: ACCESS_MASK,
GenericWrite: ACCESS_MASK,
GenericExecute: ACCESS_MASK,
GenericAll: ACCESS_MASK,
}
PGENERIC_MAPPING :: ^GENERIC_MAPPING
SECURITY_IMPERSONATION_LEVEL :: enum {
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation,
}
SECURITY_INFORMATION :: DWORD
ANYSIZE_ARRAY :: 1
LUID_AND_ATTRIBUTES :: struct {
Luid: LUID,
Attributes: DWORD,
}
PRIVILEGE_SET :: struct {
PrivilegeCount: DWORD,
Control: DWORD,
Privilege: [ANYSIZE_ARRAY]LUID_AND_ATTRIBUTES,
}
PPRIVILEGE_SET :: ^PRIVILEGE_SET
// Token Specific Access Rights.
TOKEN_ASSIGN_PRIMARY :: 0x0001
TOKEN_DUPLICATE :: 0x0002
TOKEN_IMPERSONATE :: 0x0004
TOKEN_QUERY :: 0x0008
TOKEN_QUERY_SOURCE :: 0x0010
TOKEN_ADJUST_PRIVILEGES :: 0x0020
TOKEN_ADJUST_GROUPS :: 0x0040
TOKEN_ADJUST_DEFAULT :: 0x0080
TOKEN_ADJUST_SESSIONID :: 0x0100
TOKEN_ALL_ACCESS_P :: STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY |\
TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT
TOKEN_ALL_ACCESS :: TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID
TOKEN_READ :: STANDARD_RIGHTS_READ | TOKEN_QUERY
TOKEN_WRITE :: STANDARD_RIGHTS_WRITE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT
TOKEN_EXECUTE :: STANDARD_RIGHTS_EXECUTE
TOKEN_TRUST_CONSTRAINT_MASK :: STANDARD_RIGHTS_READ | TOKEN_QUERY | TOKEN_QUERY_SOURCE
TOKEN_ACCESS_PSEUDO_HANDLE_WIN8 :: TOKEN_QUERY | TOKEN_QUERY_SOURCE
TOKEN_ACCESS_PSEUDO_HANDLE :: TOKEN_ACCESS_PSEUDO_HANDLE_WIN8
CP_ACP :: 0 // default to ANSI code page
CP_OEMCP :: 1 // default to OEM code page
@@ -2100,9 +2267,10 @@ FILETIME :: struct {
FILETIME_as_unix_nanoseconds :: proc "contextless" (ft: FILETIME) -> i64 {
t := i64(u64(ft.dwLowDateTime) | u64(ft.dwHighDateTime) << 32)
return (t - 0x019db1ded53e8000) * 100
return (t - 116444736000000000) * 100
}
OVERLAPPED :: struct {
Internal: ^c_ulong,
InternalHigh: ^c_ulong,
@@ -2776,6 +2944,16 @@ SYSTEMTIME :: struct {
milliseconds: WORD,
}
TIME_ZONE_INFORMATION :: struct {
Bias: LONG,
StandardName: [32]WCHAR,
StandardDate: SYSTEMTIME,
StandardBias: LONG,
DaylightName: [32]WCHAR,
DaylightDate: SYSTEMTIME,
DaylightBias: LONG,
}
@(private="file")
IMAGE_DOS_HEADER :: struct {
@@ -3047,12 +3225,32 @@ SHCONTF_FLATLIST :: 0x4000
SHCONTF_ENABLE_ASYNC :: 0x8000
SHCONTF_INCLUDESUPERHIDDEN :: 0x10000
SHACF_DEFAULT :: 0x00000000 // Currently (SHACF_FILESYSTEM | SHACF_URLALL)
SHACF_FILESYSTEM :: 0x00000001 // This includes the File System as well as the rest of the shell (Desktop\My Computer\Control Panel\)
SHACF_URLALL :: (SHACF_URLHISTORY | SHACF_URLMRU)
SHACF_URLHISTORY :: 0x00000002 // URLs in the User's History
SHACF_URLMRU :: 0x00000004 // URLs in the User's Recently Used list.
SHACF_USETAB :: 0x00000008 // Use the tab to move thru the autocomplete possibilities instead of to the next dialog/window control.
SHACF_FILESYS_ONLY :: 0x00000010 // This includes the File System
SHACF_FILESYS_DIRS :: 0x00000020 // Same as SHACF_FILESYS_ONLY except it only includes directories, UNC servers, and UNC server shares.
SHACF_VIRTUAL_NAMESPACE :: 0x00000040 // Also include the virtual namespace
SHACF_AUTOSUGGEST_FORCE_ON :: 0x10000000 // Ignore the registry default and force the feature on.
SHACF_AUTOSUGGEST_FORCE_OFF :: 0x20000000 // Ignore the registry default and force the feature off.
SHACF_AUTOAPPEND_FORCE_ON :: 0x40000000 // Ignore the registry default and force the feature on. (Also know as AutoComplete)
SHACF_AUTOAPPEND_FORCE_OFF :: 0x80000000 // Ignore the registry default and force the feature off. (Also know as AutoComplete)
LWSTDAPI :: HRESULT
CLSID_FileOpenDialog := &GUID{0xDC1C5A9C, 0xE88A, 0x4DDE, {0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7}}
CLSID_FileSaveDialog := &GUID{0xC0B4E2F3, 0xBA21, 0x4773, {0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B}}
CLSID_TaskbarList := &GUID{0x56FDF344, 0xFD6D, 0x11d0, {0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90}}
IID_IFileDialog := &GUID{0x42F85136, 0xDB7E, 0x439C, {0x85, 0xF1, 0xE4, 0x07, 0x5D, 0x13, 0x5F, 0xC8}}
IID_IFileSaveDialog := &GUID{0x84BCCD23, 0x5FDE, 0x4CDB, {0xAE, 0xA4, 0xAF, 0x64, 0xB8, 0x3D, 0x78, 0xAB}}
IID_IFileOpenDialog := &GUID{0xD57C7288, 0xD4AD, 0x4768, {0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60}}
IID_ITaskbarList := &GUID{0x56FDF342, 0xFD6D, 0x11d0, {0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90}}
IID_ITaskbarList2 := &GUID{0x602D4995, 0xB13A, 0x429b, {0xA6, 0x6E, 0x19, 0x35, 0xE4, 0x4F, 0x43, 0x17}}
IID_ITaskbarList3 := &GUID{0xea1afb91, 0x9e28, 0x4b86, {0x90, 0xe9, 0x9e, 0x9f, 0x8a, 0x5e, 0xef, 0xaf}}
IModalWindow :: struct #raw_union {
#subtype IUnknown: IUnknown,
@@ -3357,6 +3555,84 @@ IFileSaveDialogVtbl :: struct {
ApplyProperties: proc "stdcall" (this: ^IFileSaveDialog, psi: ^IShellItem, pStore: ^IPropertyStore, hwnd: HWND, pSink: ^IFileOperationProgressSink) -> HRESULT,
}
ITaskbarList :: struct #raw_union {
#subtype IUnknown: IUnknown,
using Vtbl: ^ITaskbarListVtbl,
}
ITaskbarListVtbl :: struct {
using IUnknownVtbl: IUnknownVtbl,
HrInit: proc "stdcall" (this: ^ITaskbarList) -> HRESULT,
AddTab: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
DeleteTab: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
ActivateTab: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
SetActiveAlt: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
}
ITaskbarList2 :: struct #raw_union {
#subtype ITaskbarList: ITaskbarList,
using Vtbl: ^ITaskbarList2Vtbl,
}
ITaskbarList2Vtbl :: struct {
using ITaskbarListVtbl: ITaskbarListVtbl,
MarkFullscreenWindow: proc "stdcall" (this: ^ITaskbarList2, hwnd: HWND, fFullscreen: BOOL) -> HRESULT,
}
TBPFLAG :: enum c_int {
NOPROGRESS = 0,
INDETERMINATE = 0x1,
NORMAL = 0x2,
ERROR = 0x4,
PAUSED = 0x8,
}
THUMBBUTTONFLAGS :: enum c_int {
ENABLED = 0,
DISABLED = 0x1,
DISMISSONCLICK = 0x2,
NOBACKGROUND = 0x4,
HIDDEN = 0x8,
NONINTERACTIVE = 0x10,
}
THUMBBUTTONMASK :: enum c_int {
BITMAP = 0x1,
ICON = 0x2,
TOOLTIP = 0x4,
FLAGS = 0x8,
}
THUMBBUTTON :: struct {
dwMask: THUMBBUTTONMASK,
iId: UINT,
iBitmap: UINT,
hIcon: HICON,
szTip: [260]WCHAR,
dwFlags: THUMBBUTTONFLAGS,
}
LPTHUMBBUTTON :: ^THUMBBUTTON
HIMAGELIST :: ^IUnknown
ITaskbarList3 :: struct #raw_union {
#subtype ITaskbarList2: ITaskbarList2,
using Vtbl: ^ITaskbarList3Vtbl,
}
ITaskbarList3Vtbl :: struct {
using ITaskbarList2Vtbl: ITaskbarList2Vtbl,
SetProgressValue: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, ullCompleted: ULONGLONG, ullTotal: ULONGLONG) -> HRESULT,
SetProgressState: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, tbpFlags: TBPFLAG) -> HRESULT,
RegisterTab: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND, hwndMDI: HWND) -> HRESULT,
UnregisterTab: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND) -> HRESULT,
SetTabOrder: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND, hwndInsertBefore: HWND) -> HRESULT,
SetTabActive: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND, hwndMDI: HWND, dwReserved: DWORD) -> HRESULT,
ThumbBarAddButtons: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, cButtons: UINT, pButton: LPTHUMBBUTTON) -> HRESULT,
ThumbBarUpdateButtons: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, cButtons: UINT, pButton: LPTHUMBBUTTON) -> HRESULT,
ThumbBarSetImageList: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, himl: HIMAGELIST) -> HRESULT,
SetOverlayIcon: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, hIcon: HICON, pszDescription: LPCWSTR) -> HRESULT,
SetThumbnailTooltip: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, pszTip: LPCWSTR) -> HRESULT,
SetThumbnailClip: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, prcClip: ^RECT) -> HRESULT,
}
MEMORYSTATUSEX :: struct {
dwLength: DWORD,
dwMemoryLoad: DWORD,
+4
View File
@@ -78,6 +78,7 @@ foreign user32 {
LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON ---
LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR ---
LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR ---
LoadImageW :: proc(hInst: HINSTANCE, name: LPCWSTR, type: UINT, cx: c_int, cy: c_int, fuLoad: UINT) -> HANDLE ---
GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
@@ -104,6 +105,9 @@ foreign user32 {
GetDC :: proc(hWnd: HWND) -> HDC ---
ReleaseDC :: proc(hWnd: HWND, hDC: HDC) -> c_int ---
GetDlgCtrlID :: proc(hWnd: HWND) -> c_int ---
GetDlgItem :: proc(hDlg: HWND, nIDDlgItem: c_int) -> HWND ---
GetUpdateRect :: proc(hWnd: HWND, lpRect: LPRECT, bErase: BOOL) -> BOOL ---
ValidateRect :: proc(hWnd: HWND, lpRect: ^RECT) -> BOOL ---
InvalidateRect :: proc(hWnd: HWND, lpRect: ^RECT, bErase: BOOL) -> BOOL ---
+4 -4
View File
@@ -62,19 +62,19 @@ utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> wstri
wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
context.allocator = allocator
if N <= 0 {
if N == 0 {
return
}
n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil)
n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N) if N > 0 else -1, nil, 0, nil, nil)
if n == 0 {
return
}
// If N == -1 the call to WideCharToMultiByte assume the wide string is null terminated
// If N < 0 the call to WideCharToMultiByte assume the wide string is null terminated
// and will scan it to find the first null terminated character. The resulting string will
// also be null terminated.
// If N != -1 it assumes the wide string is not null terminated and the resulting string
// If N > 0 it assumes the wide string is not null terminated and the resulting string
// will not be null terminated.
text := make([]byte, n) or_return
File diff suppressed because it is too large Load Diff
+34 -32
View File
@@ -17,7 +17,7 @@ MAX_DURATION :: Duration(1<<63 - 1)
IS_SUPPORTED :: _IS_SUPPORTED
Time :: struct {
_nsec: i64, // zero is 1970-01-01 00:00:00
_nsec: i64, // Measured in UNIX nanonseconds
}
Month :: enum int {
@@ -59,36 +59,36 @@ sleep :: proc "contextless" (d: Duration) {
_sleep(d)
}
stopwatch_start :: proc(using stopwatch: ^Stopwatch) {
stopwatch_start :: proc "contextless" (using stopwatch: ^Stopwatch) {
if !running {
_start_time = tick_now()
running = true
}
}
stopwatch_stop :: proc(using stopwatch: ^Stopwatch) {
stopwatch_stop :: proc "contextless" (using stopwatch: ^Stopwatch) {
if running {
_accumulation += tick_diff(_start_time, tick_now())
running = false
}
}
stopwatch_reset :: proc(using stopwatch: ^Stopwatch) {
stopwatch_reset :: proc "contextless" (using stopwatch: ^Stopwatch) {
_accumulation = {}
running = false
}
stopwatch_duration :: proc(using stopwatch: Stopwatch) -> Duration {
stopwatch_duration :: proc "contextless" (using stopwatch: Stopwatch) -> Duration {
if !running { return _accumulation }
return _accumulation + tick_diff(_start_time, tick_now())
}
diff :: proc(start, end: Time) -> Duration {
diff :: proc "contextless" (start, end: Time) -> Duration {
d := end._nsec - start._nsec
return Duration(d)
}
since :: proc(start: Time) -> Duration {
since :: proc "contextless" (start: Time) -> Duration {
return diff(start, now())
}
@@ -117,8 +117,8 @@ duration_hours :: proc "contextless" (d: Duration) -> f64 {
return f64(hour) + f64(nsec)/(60*60*1e9)
}
duration_round :: proc(d, m: Duration) -> Duration {
_less_than_half :: #force_inline proc(x, y: Duration) -> bool {
duration_round :: proc "contextless" (d, m: Duration) -> Duration {
_less_than_half :: #force_inline proc "contextless" (x, y: Duration) -> bool {
return u64(x)+u64(x) < u64(y)
}
@@ -146,45 +146,45 @@ duration_round :: proc(d, m: Duration) -> Duration {
return MAX_DURATION
}
duration_truncate :: proc(d, m: Duration) -> Duration {
duration_truncate :: proc "contextless" (d, m: Duration) -> Duration {
return d if m <= 0 else d - d%m
}
date :: proc(t: Time) -> (year: int, month: Month, day: int) {
date :: proc "contextless" (t: Time) -> (year: int, month: Month, day: int) {
year, month, day, _ = _abs_date(_time_abs(t), true)
return
}
year :: proc(t: Time) -> (year: int) {
year :: proc "contextless" (t: Time) -> (year: int) {
year, _, _, _ = _date(t, true)
return
}
month :: proc(t: Time) -> (month: Month) {
month :: proc "contextless" (t: Time) -> (month: Month) {
_, month, _, _ = _date(t, true)
return
}
day :: proc(t: Time) -> (day: int) {
day :: proc "contextless" (t: Time) -> (day: int) {
_, _, day, _ = _date(t, true)
return
}
clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
clock_from_time :: proc(t: Time) -> (hour, min, sec: int) {
clock_from_time :: proc "contextless" (t: Time) -> (hour, min, sec: int) {
return clock_from_seconds(_time_abs(t))
}
clock_from_duration :: proc(d: Duration) -> (hour, min, sec: int) {
clock_from_duration :: proc "contextless" (d: Duration) -> (hour, min, sec: int) {
return clock_from_seconds(u64(d/1e9))
}
clock_from_stopwatch :: proc(s: Stopwatch) -> (hour, min, sec: int) {
clock_from_stopwatch :: proc "contextless" (s: Stopwatch) -> (hour, min, sec: int) {
return clock_from_duration(stopwatch_duration(s))
}
clock_from_seconds :: proc(nsec: u64) -> (hour, min, sec: int) {
clock_from_seconds :: proc "contextless" (nsec: u64) -> (hour, min, sec: int) {
sec = int(nsec % SECONDS_PER_DAY)
hour = sec / SECONDS_PER_HOUR
sec -= hour * SECONDS_PER_HOUR
@@ -193,11 +193,11 @@ clock_from_seconds :: proc(nsec: u64) -> (hour, min, sec: int) {
return
}
read_cycle_counter :: proc() -> u64 {
read_cycle_counter :: proc "contextless" () -> u64 {
return u64(intrinsics.read_cycle_counter())
}
unix :: proc(sec: i64, nsec: i64) -> Time {
unix :: proc "contextless" (sec: i64, nsec: i64) -> Time {
sec, nsec := sec, nsec
if nsec < 0 || nsec >= 1e9 {
n := nsec / 1e9
@@ -208,20 +208,20 @@ unix :: proc(sec: i64, nsec: i64) -> Time {
sec -= 1
}
}
return Time{(sec*1e9 + nsec) + UNIX_TO_INTERNAL}
return Time{(sec*1e9 + nsec)}
}
to_unix_seconds :: time_to_unix
time_to_unix :: proc(t: Time) -> i64 {
time_to_unix :: proc "contextless" (t: Time) -> i64 {
return t._nsec/1e9
}
to_unix_nanoseconds :: time_to_unix_nano
time_to_unix_nano :: proc(t: Time) -> i64 {
time_to_unix_nano :: proc "contextless" (t: Time) -> i64 {
return t._nsec
}
time_add :: proc(t: Time, d: Duration) -> Time {
time_add :: proc "contextless" (t: Time, d: Duration) -> Time {
return Time{t._nsec + i64(d)}
}
@@ -231,7 +231,7 @@ time_add :: proc(t: Time, d: Duration) -> Time {
// On Windows it depends but is comparable with regular sleep in the worst case.
// To get the same kind of accuracy as on Linux, have your program call `win32.time_begin_period(1)` to
// tell Windows to use a more accurate timer for your process.
accurate_sleep :: proc(d: Duration) {
accurate_sleep :: proc "contextless" (d: Duration) {
to_sleep, estimate, mean, m2, count: Duration
to_sleep = d
@@ -279,19 +279,19 @@ ABSOLUTE_TO_UNIX :: -UNIX_TO_ABSOLUTE
@(private)
_date :: proc(t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
_date :: proc "contextless" (t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
year, month, day, yday = _abs_date(_time_abs(t), full)
return
}
@(private)
_time_abs :: proc(t: Time) -> u64 {
_time_abs :: proc "contextless" (t: Time) -> u64 {
return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE)
}
@(private)
_abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
_is_leap_year :: proc(year: int) -> bool {
_abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
_is_leap_year :: proc "contextless" (year: int) -> bool {
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
}
@@ -352,9 +352,11 @@ _abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, y
return
}
datetime_to_time :: proc(year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
divmod :: proc(year: int, divisor: int) -> (div: int, mod: int) {
assert(divisor > 0)
datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
divmod :: proc "contextless" (year: int, divisor: int) -> (div: int, mod: int) {
if divisor <= 0 {
intrinsics.debug_trap()
}
div = int(year / divisor)
mod = year % divisor
return
+2
View File
@@ -22,3 +22,5 @@ _tick_now :: proc "contextless" () -> Tick {
return {}
}
_yield :: proc "contextless" () {
}
+10 -3
View File
@@ -7,9 +7,16 @@ _IS_SUPPORTED :: true
_now :: proc "contextless" () -> Time {
file_time: win32.FILETIME
win32.GetSystemTimeAsFileTime(&file_time)
ns := win32.FILETIME_as_unix_nanoseconds(file_time)
return Time{_nsec=ns}
ns: i64
// monotonic
win32.GetSystemTimePreciseAsFileTime(&file_time)
dt := u64(transmute(u64le)file_time) // in 100ns units
ns = i64((dt - 116444736000000000) * 100) // convert to ns
return unix(0, ns)
}
_sleep :: proc "contextless" (d: Duration) {