mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-31 11:50:07 +00:00
Merge tag 'dev-2024-07'
This commit is contained in:
+1
-14
@@ -34,20 +34,7 @@ when ODIN_OS == .Windows {
|
||||
SIGTERM :: 15
|
||||
}
|
||||
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
|
||||
SIG_ERR :: rawptr(~uintptr(0))
|
||||
SIG_DFL :: rawptr(uintptr(0))
|
||||
SIG_IGN :: rawptr(uintptr(1))
|
||||
|
||||
SIGABRT :: 6
|
||||
SIGFPE :: 8
|
||||
SIGILL :: 4
|
||||
SIGINT :: 2
|
||||
SIGSEGV :: 11
|
||||
SIGTERM :: 15
|
||||
}
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Darwin {
|
||||
SIG_ERR :: rawptr(~uintptr(0))
|
||||
SIG_DFL :: rawptr(uintptr(0))
|
||||
SIG_IGN :: rawptr(uintptr(1))
|
||||
|
||||
@@ -34,13 +34,13 @@ COMPRESS_OUTPUT_ALLOCATE_MIN :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MIN, 1 << 2
|
||||
*/
|
||||
when size_of(uintptr) == 8 {
|
||||
|
||||
// For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
||||
// which is GZIP and PKZIP's max payload size.
|
||||
// For 64-bit platforms, we set the default max buffer size to 4 GiB,
|
||||
// which is GZIP and PKZIP's max payload size.
|
||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 32))
|
||||
} else {
|
||||
|
||||
// For 32-bit platforms, we set the default max buffer size to 512 MiB.
|
||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
|
||||
COMPRESS_OUTPUT_ALLOCATE_MAX :: int(#config(COMPRESS_OUTPUT_ALLOCATE_MAX, 1 << 29))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ decompress_slice_to_output_buffer :: proc(input: []u8, output: []u8, model := DE
|
||||
validate_model(model) or_return
|
||||
|
||||
for inp < inp_end {
|
||||
val := transmute(i8)input[inp]
|
||||
val := i8(input[inp])
|
||||
mark := int(-1)
|
||||
|
||||
for val < 0 {
|
||||
@@ -274,12 +274,9 @@ compress_string_to_buffer :: proc(input: string, output: []u8, model := DEFAULT_
|
||||
out_ptr := raw_data(output[out:])
|
||||
|
||||
switch pack.bytes_packed {
|
||||
case 4:
|
||||
intrinsics.unaligned_store(transmute(^u32)out_ptr, code)
|
||||
case 2:
|
||||
intrinsics.unaligned_store(transmute(^u16)out_ptr, u16(code))
|
||||
case 1:
|
||||
intrinsics.unaligned_store(transmute(^u8)out_ptr, u8(code))
|
||||
case 4: intrinsics.unaligned_store((^u32)(out_ptr), code)
|
||||
case 2: intrinsics.unaligned_store((^u16)(out_ptr), u16(code))
|
||||
case 1: intrinsics.unaligned_store( (^u8)(out_ptr), u8(code))
|
||||
case:
|
||||
return out, .Unknown_Compression_Method
|
||||
}
|
||||
|
||||
@@ -210,8 +210,11 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, set_to: bool = true, allocator
|
||||
|
||||
ba.max_index = max(idx, ba.max_index)
|
||||
|
||||
if set_to{ ba.bits[leg_index] |= 1 << uint(bit_index) }
|
||||
else { ba.bits[leg_index] &= ~(1 << uint(bit_index)) }
|
||||
if set_to {
|
||||
ba.bits[leg_index] |= 1 << uint(bit_index)
|
||||
} else {
|
||||
ba.bits[leg_index] &~= 1 << uint(bit_index)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -253,7 +256,7 @@ Inputs:
|
||||
- index: Which bit in the array
|
||||
*/
|
||||
unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
|
||||
b.bits[bit >> INDEX_SHIFT] &= ~(1 << uint(bit & INDEX_MASK))
|
||||
b.bits[bit >> INDEX_SHIFT] &~= 1 << uint(bit & INDEX_MASK)
|
||||
}
|
||||
/*
|
||||
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||
|
||||
@@ -70,8 +70,7 @@ set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Alloc
|
||||
if c.count == c.capacity {
|
||||
e = c.tail
|
||||
_remove_node(c, e)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
c.count += 1
|
||||
e = new(Node(Key, Value), c.node_allocator) or_return
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ pop_front_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
// Push multiple elements to the front of the queue
|
||||
// Push multiple elements to the back of the queue
|
||||
push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> (ok: bool, err: runtime.Allocator_Error) {
|
||||
n := uint(builtin.len(elems))
|
||||
if space(q^) < int(n) {
|
||||
@@ -241,7 +241,7 @@ clear :: proc(q: ^$Q/Queue($T)) {
|
||||
}
|
||||
|
||||
|
||||
// Internal growinh procedure
|
||||
// Internal growing procedure
|
||||
_grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0) -> runtime.Allocator_Error {
|
||||
new_capacity := max(min_capacity, uint(8), uint(builtin.len(q.data))*2)
|
||||
n := uint(builtin.len(q.data))
|
||||
|
||||
@@ -29,7 +29,7 @@ Tree :: struct($Key: typeid, $Value: typeid) {
|
||||
|
||||
_root: ^Node(Key, Value),
|
||||
_node_allocator: runtime.Allocator,
|
||||
_cmp_fn: proc(Key, Key) -> Ordering,
|
||||
_cmp_fn: proc(Key, Key) -> Ordering,
|
||||
_size: int,
|
||||
}
|
||||
|
||||
|
||||
@@ -119,20 +119,20 @@ consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_l
|
||||
}
|
||||
|
||||
ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index, a.len)
|
||||
if index+1 < a.len {
|
||||
runtime.bounds_check_error_loc(loc, index, a.len)
|
||||
if index+1 < a.len {
|
||||
copy(a.data[index:], a.data[index+1:])
|
||||
}
|
||||
a.len -= 1
|
||||
}
|
||||
|
||||
unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index, a.len)
|
||||
runtime.bounds_check_error_loc(loc, index, a.len)
|
||||
n := a.len-1
|
||||
if index != n {
|
||||
a.data[index] = a.data[n]
|
||||
}
|
||||
a.len -= 1
|
||||
a.len -= 1
|
||||
}
|
||||
|
||||
clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
|
||||
|
||||
@@ -61,7 +61,7 @@ add_dependency :: proc(sorter: ^$S/Sorter($K), key, dependency: K) -> bool {
|
||||
}
|
||||
find.dependents[key] = true
|
||||
|
||||
find = &sorter.relations[key]
|
||||
find = &sorter.relations[key]
|
||||
if find == nil {
|
||||
find = map_insert(&sorter.relations, key, make_relations(sorter))
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
|
||||
if len(b) != 32 {
|
||||
intrinsics.trap()
|
||||
}
|
||||
b_ := transmute(^[32]byte)(raw_data(b))
|
||||
b_ := (^[32]byte)(raw_data(b))
|
||||
|
||||
// Do the work in a scratch element, so that ge is unchanged on
|
||||
// failure.
|
||||
@@ -169,7 +169,7 @@ ge_bytes :: proc "contextless" (ge: ^Group_Element, dst: []byte) {
|
||||
if len(dst) != 32 {
|
||||
intrinsics.trap()
|
||||
}
|
||||
dst_ := transmute(^[32]byte)(raw_data(dst))
|
||||
dst_ := (^[32]byte)(raw_data(dst))
|
||||
|
||||
// Convert the element to affine (x, y) representation.
|
||||
x, y, z_inv: field.Tight_Field_Element = ---, ---, ---
|
||||
|
||||
@@ -28,7 +28,7 @@ sc_set_bytes :: proc "contextless" (sc: ^Scalar, b: []byte) -> bool {
|
||||
if len(b) != 32 {
|
||||
intrinsics.trap()
|
||||
}
|
||||
b_ := transmute(^[32]byte)(raw_data(b))
|
||||
b_ := (^[32]byte)(raw_data(b))
|
||||
return field.fe_from_bytes(sc, b_)
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ sc_set_bytes_rfc8032 :: proc "contextless" (sc: ^Scalar, b: []byte) {
|
||||
if len(b) != 32 {
|
||||
intrinsics.trap()
|
||||
}
|
||||
b_ := transmute(^[32]byte)(raw_data(b))
|
||||
b_ := (^[32]byte)(raw_data(b))
|
||||
field.fe_from_bytes_rfc8032(sc, b_)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,13 +6,13 @@ import "core:mem"
|
||||
fe_relax_cast :: #force_inline proc "contextless" (
|
||||
arg1: ^Tight_Field_Element,
|
||||
) -> ^Loose_Field_Element {
|
||||
return transmute(^Loose_Field_Element)(arg1)
|
||||
return (^Loose_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
fe_tighten_cast :: #force_inline proc "contextless" (
|
||||
arg1: ^Loose_Field_Element,
|
||||
) -> ^Tight_Field_Element {
|
||||
return transmute(^Tight_Field_Element)(arg1)
|
||||
return (^Tight_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
fe_clear :: proc "contextless" (
|
||||
|
||||
@@ -7,13 +7,13 @@ import "core:mem"
|
||||
fe_relax_cast :: #force_inline proc "contextless" (
|
||||
arg1: ^Tight_Field_Element,
|
||||
) -> ^Loose_Field_Element {
|
||||
return transmute(^Loose_Field_Element)(arg1)
|
||||
return (^Loose_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
fe_tighten_cast :: #force_inline proc "contextless" (
|
||||
arg1: ^Loose_Field_Element,
|
||||
) -> ^Tight_Field_Element {
|
||||
return transmute(^Tight_Field_Element)(arg1)
|
||||
return (^Tight_Field_Element)(arg1)
|
||||
}
|
||||
|
||||
fe_from_bytes :: #force_inline proc "contextless" (
|
||||
|
||||
@@ -61,7 +61,7 @@ init_256 :: proc(ctx: ^Context, key, domain_sep: []byte) {
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
assert(ctx.is_initialized)
|
||||
|
||||
shake.write(transmute(^shake.Context)(ctx), data)
|
||||
shake.write((^shake.Context)(ctx), data)
|
||||
}
|
||||
|
||||
// final finalizes the Context, writes the tag to dst, and calls reset
|
||||
@@ -75,7 +75,7 @@ final :: proc(ctx: ^Context, dst: []byte) {
|
||||
panic("crypto/kmac: invalid KMAC tag_size, too short")
|
||||
}
|
||||
|
||||
_sha3.final_cshake(transmute(^_sha3.Context)(ctx), dst)
|
||||
_sha3.final_cshake((^_sha3.Context)(ctx), dst)
|
||||
}
|
||||
|
||||
// clone clones the Context other into ctx.
|
||||
@@ -84,7 +84,7 @@ clone :: proc(ctx, other: ^Context) {
|
||||
return
|
||||
}
|
||||
|
||||
shake.clone(transmute(^shake.Context)(ctx), transmute(^shake.Context)(other))
|
||||
shake.clone((^shake.Context)(ctx), (^shake.Context)(other))
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
@@ -94,7 +94,7 @@ reset :: proc(ctx: ^Context) {
|
||||
return
|
||||
}
|
||||
|
||||
shake.reset(transmute(^shake.Context)(ctx))
|
||||
shake.reset((^shake.Context)(ctx))
|
||||
}
|
||||
|
||||
@(private)
|
||||
@@ -107,7 +107,7 @@ _init_kmac :: proc(ctx: ^Context, key, s: []byte, sec_strength: int) {
|
||||
panic("crypto/kmac: invalid KMAC key, too short")
|
||||
}
|
||||
|
||||
ctx_ := transmute(^_sha3.Context)(ctx)
|
||||
ctx_ := (^_sha3.Context)(ctx)
|
||||
_sha3.init_cshake(ctx_, N_KMAC, s, sec_strength)
|
||||
_sha3.bytepad(ctx_, [][]byte{key}, _sha3.rate_cshake(sec_strength))
|
||||
}
|
||||
|
||||
@@ -66,12 +66,12 @@ init_512 :: proc(ctx: ^Context) {
|
||||
@(private)
|
||||
_init :: proc(ctx: ^Context) {
|
||||
ctx.dsbyte = _sha3.DS_KECCAK
|
||||
_sha3.init(transmute(^_sha3.Context)(ctx))
|
||||
_sha3.init((^_sha3.Context)(ctx))
|
||||
}
|
||||
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
_sha3.update(transmute(^_sha3.Context)(ctx), data)
|
||||
_sha3.update((^_sha3.Context)(ctx), data)
|
||||
}
|
||||
|
||||
// final finalizes the Context, writes the digest to hash, and calls
|
||||
@@ -80,16 +80,16 @@ update :: proc(ctx: ^Context, data: []byte) {
|
||||
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||
// which is useful for for calculating rolling digests.
|
||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||
_sha3.final(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
|
||||
_sha3.final((^_sha3.Context)(ctx), hash, finalize_clone)
|
||||
}
|
||||
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
|
||||
_sha3.clone((^_sha3.Context)(ctx), (^_sha3.Context)(other))
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
_sha3.reset(transmute(^_sha3.Context)(ctx))
|
||||
_sha3.reset((^_sha3.Context)(ctx))
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ HAS_RAND_BYTES :: true
|
||||
_rand_bytes :: proc(dst: []byte) {
|
||||
err := Sec.RandomCopyBytes(count=len(dst), bytes=raw_data(dst))
|
||||
if err != .Success {
|
||||
msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err))
|
||||
fmt.panicf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg)
|
||||
msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err))
|
||||
fmt.panicf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ ge_set_bytes :: proc "contextless" (ge: ^Group_Element, b: []byte) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
b_ := transmute(^[32]byte)(raw_data(b))
|
||||
b_ := (^[32]byte)(raw_data(b))
|
||||
|
||||
s: field.Tight_Field_Element = ---
|
||||
defer field.fe_clear(&s)
|
||||
@@ -297,7 +297,7 @@ ge_bytes :: proc(ge: ^Group_Element, dst: []byte) {
|
||||
// 2. Return the 32-byte little-endian encoding of s. More
|
||||
// specifically, this is the encoding of the canonical
|
||||
// representation of s as an integer between 0 and p-1, inclusive.
|
||||
dst_ := transmute(^[32]byte)(raw_data(dst))
|
||||
dst_ := (^[32]byte)(raw_data(dst))
|
||||
field.fe_to_bytes(dst_, &tmp)
|
||||
|
||||
field.fe_clear_vec([]^field.Tight_Field_Element{&u1, &u2, &tmp, &z_inv, &ix0, &iy0, &x, &y})
|
||||
@@ -417,7 +417,7 @@ ge_is_identity :: proc(ge: ^Group_Element) -> int {
|
||||
|
||||
@(private)
|
||||
ge_map :: proc "contextless" (ge: ^Group_Element, b: []byte) {
|
||||
b_ := transmute(^[32]byte)(raw_data(b))
|
||||
b_ := (^[32]byte)(raw_data(b))
|
||||
|
||||
// The MAP function is defined on 32-byte strings as:
|
||||
//
|
||||
|
||||
@@ -46,7 +46,7 @@ sc_set_bytes_wide :: proc(sc: ^Scalar, b: []byte) {
|
||||
panic("crypto/ristretto255: invalid wide input size")
|
||||
}
|
||||
|
||||
b_ := transmute(^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
|
||||
b_ := (^[WIDE_SCALAR_SIZE]byte)(raw_data(b))
|
||||
grp.sc_set_bytes_wide(sc, b_)
|
||||
}
|
||||
|
||||
|
||||
@@ -68,12 +68,12 @@ init_512 :: proc(ctx: ^Context) {
|
||||
@(private)
|
||||
_init :: proc(ctx: ^Context) {
|
||||
ctx.dsbyte = _sha3.DS_SHA3
|
||||
_sha3.init(transmute(^_sha3.Context)(ctx))
|
||||
_sha3.init((^_sha3.Context)(ctx))
|
||||
}
|
||||
|
||||
// update adds more data to the Context.
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
_sha3.update(transmute(^_sha3.Context)(ctx), data)
|
||||
_sha3.update((^_sha3.Context)(ctx), data)
|
||||
}
|
||||
|
||||
// final finalizes the Context, writes the digest to hash, and calls
|
||||
@@ -82,16 +82,16 @@ update :: proc(ctx: ^Context, data: []byte) {
|
||||
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||
// which is useful for for calculating rolling digests.
|
||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||
_sha3.final(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
|
||||
_sha3.final((^_sha3.Context)(ctx), hash, finalize_clone)
|
||||
}
|
||||
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
|
||||
_sha3.clone((^_sha3.Context)(ctx), (^_sha3.Context)(other))
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
_sha3.reset(transmute(^_sha3.Context)(ctx))
|
||||
_sha3.reset((^_sha3.Context)(ctx))
|
||||
}
|
||||
|
||||
@@ -24,35 +24,35 @@ Context :: distinct _sha3.Context
|
||||
|
||||
// init_128 initializes a Context for SHAKE128.
|
||||
init_128 :: proc(ctx: ^Context) {
|
||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, nil, 128)
|
||||
_sha3.init_cshake((^_sha3.Context)(ctx), nil, nil, 128)
|
||||
}
|
||||
|
||||
// init_256 initializes a Context for SHAKE256.
|
||||
init_256 :: proc(ctx: ^Context) {
|
||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, nil, 256)
|
||||
_sha3.init_cshake((^_sha3.Context)(ctx), nil, nil, 256)
|
||||
}
|
||||
|
||||
// init_cshake_128 initializes a Context for cSHAKE128.
|
||||
init_cshake_128 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, domain_sep, 128)
|
||||
_sha3.init_cshake((^_sha3.Context)(ctx), nil, domain_sep, 128)
|
||||
}
|
||||
|
||||
// init_cshake_256 initializes a Context for cSHAKE256.
|
||||
init_cshake_256 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), nil, domain_sep, 256)
|
||||
_sha3.init_cshake((^_sha3.Context)(ctx), nil, domain_sep, 256)
|
||||
}
|
||||
|
||||
// write writes more data into the SHAKE instance. This MUST not be called
|
||||
// after any reads have been done, and attempts to do so will panic.
|
||||
write :: proc(ctx: ^Context, data: []byte) {
|
||||
_sha3.update(transmute(^_sha3.Context)(ctx), data)
|
||||
_sha3.update((^_sha3.Context)(ctx), data)
|
||||
}
|
||||
|
||||
// read reads output from the SHAKE instance. There is no practical upper
|
||||
// limit to the amount of data that can be read from SHAKE. After read has
|
||||
// been called one or more times, further calls to write will panic.
|
||||
read :: proc(ctx: ^Context, dst: []byte) {
|
||||
ctx_ := transmute(^_sha3.Context)(ctx)
|
||||
ctx_ := (^_sha3.Context)(ctx)
|
||||
if !ctx.is_finalized {
|
||||
_sha3.shake_xof(ctx_)
|
||||
}
|
||||
@@ -62,11 +62,11 @@ read :: proc(ctx: ^Context, dst: []byte) {
|
||||
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
|
||||
_sha3.clone((^_sha3.Context)(ctx), (^_sha3.Context)(other))
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
_sha3.reset(transmute(^_sha3.Context)(ctx))
|
||||
_sha3.reset((^_sha3.Context)(ctx))
|
||||
}
|
||||
|
||||
@@ -13,19 +13,19 @@ Context :: distinct _sha3.Context
|
||||
|
||||
// init_128 initializes a Context for TupleHash128 or TupleHashXOF128.
|
||||
init_128 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 128)
|
||||
_sha3.init_cshake((^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 128)
|
||||
}
|
||||
|
||||
// init_256 initializes a Context for TupleHash256 or TupleHashXOF256.
|
||||
init_256 :: proc(ctx: ^Context, domain_sep: []byte) {
|
||||
_sha3.init_cshake(transmute(^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 256)
|
||||
_sha3.init_cshake((^_sha3.Context)(ctx), N_TUPLEHASH, domain_sep, 256)
|
||||
}
|
||||
|
||||
// write_element writes a tuple element into the TupleHash or TupleHashXOF
|
||||
// instance. This MUST not be called after any reads have been done, and
|
||||
// any attempts to do so will panic.
|
||||
write_element :: proc(ctx: ^Context, data: []byte) {
|
||||
_, _ = _sha3.encode_string(transmute(^_sha3.Context)(ctx), data)
|
||||
_, _ = _sha3.encode_string((^_sha3.Context)(ctx), data)
|
||||
}
|
||||
|
||||
// final finalizes the Context, writes the digest to hash, and calls
|
||||
@@ -34,7 +34,7 @@ write_element :: proc(ctx: ^Context, data: []byte) {
|
||||
// Iff finalize_clone is set, final will work on a copy of the Context,
|
||||
// which is useful for for calculating rolling digests.
|
||||
final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||
_sha3.final_cshake(transmute(^_sha3.Context)(ctx), hash, finalize_clone)
|
||||
_sha3.final_cshake((^_sha3.Context)(ctx), hash, finalize_clone)
|
||||
}
|
||||
|
||||
// read reads output from the TupleHashXOF instance. There is no practical
|
||||
@@ -42,7 +42,7 @@ final :: proc(ctx: ^Context, hash: []byte, finalize_clone: bool = false) {
|
||||
// After read has been called one or more times, further calls to
|
||||
// write_element will panic.
|
||||
read :: proc(ctx: ^Context, dst: []byte) {
|
||||
ctx_ := transmute(^_sha3.Context)(ctx)
|
||||
ctx_ := (^_sha3.Context)(ctx)
|
||||
if !ctx.is_finalized {
|
||||
_sha3.encode_byte_len(ctx_, 0, false) // right_encode
|
||||
_sha3.shake_xof(ctx_)
|
||||
@@ -53,13 +53,13 @@ read :: proc(ctx: ^Context, dst: []byte) {
|
||||
|
||||
// clone clones the Context other into ctx.
|
||||
clone :: proc(ctx, other: ^Context) {
|
||||
_sha3.clone(transmute(^_sha3.Context)(ctx), transmute(^_sha3.Context)(other))
|
||||
_sha3.clone((^_sha3.Context)(ctx), (^_sha3.Context)(other))
|
||||
}
|
||||
|
||||
// reset sanitizes the Context. The Context must be re-initialized to
|
||||
// be used again.
|
||||
reset :: proc(ctx: ^Context) {
|
||||
_sha3.reset(transmute(^_sha3.Context)(ctx))
|
||||
_sha3.reset((^_sha3.Context)(ctx))
|
||||
}
|
||||
|
||||
@(private)
|
||||
|
||||
@@ -78,7 +78,7 @@ _Context :: struct {
|
||||
|
||||
@(private="package")
|
||||
_init :: proc(ctx: ^Context) -> (ok: bool) {
|
||||
defer if !ok do destroy(ctx)
|
||||
defer if !ok { destroy(ctx) }
|
||||
|
||||
ctx.impl.state = backtrace_create_state("odin-debug-trace", 1, nil, ctx)
|
||||
return ctx.impl.state != nil
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//+build !windows !linux !darwin
|
||||
package debug_trace
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
_Context :: struct {
|
||||
}
|
||||
|
||||
@@ -10,9 +12,9 @@ _init :: proc(ctx: ^Context) -> (ok: bool) {
|
||||
_destroy :: proc(ctx: ^Context) -> bool {
|
||||
return true
|
||||
}
|
||||
_frames :: proc(ctx: ^Context, skip: uint, allocator: runtime.Allocator) -> []Frame {
|
||||
_frames :: proc(ctx: ^Context, skip: uint, frames_buffer: []Frame) -> []Frame {
|
||||
return nil
|
||||
}
|
||||
_resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> (result: runtime.Source_Code_Location) {
|
||||
_resolve :: proc(ctx: ^Context, frame: Frame, allocator: runtime.Allocator) -> (result: Frame_Location) {
|
||||
return
|
||||
}
|
||||
|
||||
+116
-116
@@ -8,141 +8,141 @@ package encoding_base32
|
||||
// truncate it from the encoded output.
|
||||
|
||||
ENC_TABLE := [32]byte {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', '2', '3', '4', '5', '6', '7',
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', '2', '3', '4', '5', '6', '7',
|
||||
}
|
||||
|
||||
PADDING :: '='
|
||||
|
||||
DEC_TABLE := [?]u8 {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
}
|
||||
|
||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
|
||||
out_length := (len(data) + 4) / 5 * 8
|
||||
out := make([]byte, out_length)
|
||||
_encode(out, data)
|
||||
return string(out)
|
||||
out_length := (len(data) + 4) / 5 * 8
|
||||
out := make([]byte, out_length)
|
||||
_encode(out, data)
|
||||
return string(out)
|
||||
}
|
||||
|
||||
@private
|
||||
_encode :: proc(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
|
||||
out := out
|
||||
data := data
|
||||
out := out
|
||||
data := data
|
||||
|
||||
for len(data) > 0 {
|
||||
carry: byte
|
||||
switch len(data) {
|
||||
case:
|
||||
out[7] = ENC_TABLE[data[4] & 0x1f]
|
||||
carry = data[4] >> 5
|
||||
fallthrough
|
||||
case 4:
|
||||
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f]
|
||||
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f]
|
||||
carry = data[3] >> 7
|
||||
fallthrough
|
||||
case 3:
|
||||
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f]
|
||||
carry = (data[2] >> 4) & 0x1f
|
||||
fallthrough
|
||||
case 2:
|
||||
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f]
|
||||
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f]
|
||||
carry = (data[1] >> 6) & 0x1f
|
||||
fallthrough
|
||||
case 1:
|
||||
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f]
|
||||
out[0] = ENC_TABLE[data[0] >> 3]
|
||||
}
|
||||
for len(data) > 0 {
|
||||
carry: byte
|
||||
switch len(data) {
|
||||
case:
|
||||
out[7] = ENC_TABLE[data[4] & 0x1f]
|
||||
carry = data[4] >> 5
|
||||
fallthrough
|
||||
case 4:
|
||||
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f]
|
||||
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f]
|
||||
carry = data[3] >> 7
|
||||
fallthrough
|
||||
case 3:
|
||||
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f]
|
||||
carry = (data[2] >> 4) & 0x1f
|
||||
fallthrough
|
||||
case 2:
|
||||
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f]
|
||||
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f]
|
||||
carry = (data[1] >> 6) & 0x1f
|
||||
fallthrough
|
||||
case 1:
|
||||
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f]
|
||||
out[0] = ENC_TABLE[data[0] >> 3]
|
||||
}
|
||||
|
||||
if len(data) < 5 {
|
||||
out[7] = byte(PADDING)
|
||||
if len(data) < 4 {
|
||||
out[6] = byte(PADDING)
|
||||
out[5] = byte(PADDING)
|
||||
if len(data) < 3 {
|
||||
out[4] = byte(PADDING)
|
||||
if len(data) < 2 {
|
||||
out[3] = byte(PADDING)
|
||||
out[2] = byte(PADDING)
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
data = data[5:]
|
||||
out = out[8:]
|
||||
}
|
||||
if len(data) < 5 {
|
||||
out[7] = byte(PADDING)
|
||||
if len(data) < 4 {
|
||||
out[6] = byte(PADDING)
|
||||
out[5] = byte(PADDING)
|
||||
if len(data) < 3 {
|
||||
out[4] = byte(PADDING)
|
||||
if len(data) < 2 {
|
||||
out[3] = byte(PADDING)
|
||||
out[2] = byte(PADDING)
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
data = data[5:]
|
||||
out = out[8:]
|
||||
}
|
||||
}
|
||||
|
||||
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
outi := 0
|
||||
data := data
|
||||
outi := 0
|
||||
data := data
|
||||
|
||||
out := make([]byte, len(data) / 8 * 5, allocator)
|
||||
end := false
|
||||
for len(data) > 0 && !end {
|
||||
dbuf : [8]byte
|
||||
dlen := 8
|
||||
out := make([]byte, len(data) / 8 * 5, allocator)
|
||||
end := false
|
||||
for len(data) > 0 && !end {
|
||||
dbuf : [8]byte
|
||||
dlen := 8
|
||||
|
||||
for j := 0; j < 8; {
|
||||
if len(data) == 0 {
|
||||
dlen, end = j, true
|
||||
break
|
||||
}
|
||||
input := data[0]
|
||||
data = data[1:]
|
||||
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
|
||||
assert(!(len(data) + j < 8 - 1), "Corrupted input")
|
||||
for k := 0; k < 8-1-j; k +=1 {
|
||||
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
|
||||
}
|
||||
dlen, end = j, true
|
||||
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
|
||||
break
|
||||
}
|
||||
dbuf[j] = DEC_TABLE[input]
|
||||
assert(dbuf[j] != 0xff, "Corrupted input")
|
||||
j += 1
|
||||
}
|
||||
for j := 0; j < 8; {
|
||||
if len(data) == 0 {
|
||||
dlen, end = j, true
|
||||
break
|
||||
}
|
||||
input := data[0]
|
||||
data = data[1:]
|
||||
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
|
||||
assert(!(len(data) + j < 8 - 1), "Corrupted input")
|
||||
for k := 0; k < 8-1-j; k +=1 {
|
||||
assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input")
|
||||
}
|
||||
dlen, end = j, true
|
||||
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input")
|
||||
break
|
||||
}
|
||||
dbuf[j] = DEC_TABLE[input]
|
||||
assert(dbuf[j] != 0xff, "Corrupted input")
|
||||
j += 1
|
||||
}
|
||||
|
||||
switch dlen {
|
||||
case 8:
|
||||
out[outi + 4] = dbuf[6] << 5 | dbuf[7]
|
||||
fallthrough
|
||||
case 7:
|
||||
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
|
||||
fallthrough
|
||||
case 5:
|
||||
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
|
||||
fallthrough
|
||||
case 4:
|
||||
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
|
||||
fallthrough
|
||||
case 2:
|
||||
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
|
||||
}
|
||||
outi += 5
|
||||
}
|
||||
return out
|
||||
switch dlen {
|
||||
case 8:
|
||||
out[outi + 4] = dbuf[6] << 5 | dbuf[7]
|
||||
fallthrough
|
||||
case 7:
|
||||
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3
|
||||
fallthrough
|
||||
case 5:
|
||||
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1
|
||||
fallthrough
|
||||
case 4:
|
||||
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4
|
||||
fallthrough
|
||||
case 2:
|
||||
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2
|
||||
}
|
||||
outi += 5
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ encode_into_encoder :: proc(e: Encoder, v: Value, loc := #caller_location) -> En
|
||||
|
||||
if .Self_Described_CBOR in e.flags {
|
||||
_encode_u64(e, TAG_SELF_DESCRIBED_CBOR, .Tag) or_return
|
||||
e.flags &~= { .Self_Described_CBOR }
|
||||
e.flags -= { .Self_Described_CBOR }
|
||||
}
|
||||
|
||||
switch v_spec in v {
|
||||
@@ -423,7 +423,7 @@ _decode_bytes :: proc(d: Decoder, add: Add, type: Major = .Bytes, allocator := c
|
||||
_encode_bytes :: proc(e: Encoder, val: Bytes, major: Major = .Bytes) -> (err: Encode_Error) {
|
||||
assert(len(val) >= 0)
|
||||
_encode_u64(e, u64(len(val)), major) or_return
|
||||
_, err = io.write_full(e.writer, val[:])
|
||||
_, err = io.write_full(e.writer, val[:])
|
||||
return
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ _decode_text :: proc(d: Decoder, add: Add, allocator := context.allocator, loc :
|
||||
}
|
||||
|
||||
_encode_text :: proc(e: Encoder, val: Text) -> Encode_Error {
|
||||
return _encode_bytes(e, transmute([]byte)val, .Text)
|
||||
return _encode_bytes(e, transmute([]byte)val, .Text)
|
||||
}
|
||||
|
||||
_decode_array_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Array, err: Decode_Error) {
|
||||
@@ -480,10 +480,10 @@ _decode_array :: proc(d: Decoder, add: Add, allocator := context.allocator, loc
|
||||
_encode_array :: proc(e: Encoder, arr: Array) -> Encode_Error {
|
||||
assert(len(arr) >= 0)
|
||||
_encode_u64(e, u64(len(arr)), .Array)
|
||||
for val in arr {
|
||||
encode(e, val) or_return
|
||||
}
|
||||
return nil
|
||||
for val in arr {
|
||||
encode(e, val) or_return
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_decode_map_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: ^Map, err: Decode_Error) {
|
||||
@@ -576,7 +576,7 @@ _encode_map :: proc(e: Encoder, m: Map) -> (err: Encode_Error) {
|
||||
encode(e, entry.entry.value) or_return
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
_decode_tag_ptr :: proc(d: Decoder, add: Add, allocator := context.allocator, loc := #caller_location) -> (v: Value, err: Decode_Error) {
|
||||
@@ -626,7 +626,7 @@ _decode_uint_as_u64 :: proc(r: io.Reader, add: Add) -> (nr: u64, err: Decode_Err
|
||||
|
||||
_encode_tag :: proc(e: Encoder, val: Tag) -> Encode_Error {
|
||||
_encode_u64(e, val.number, .Tag) or_return
|
||||
return encode(e, val.value)
|
||||
return encode(e, val.value)
|
||||
}
|
||||
|
||||
_decode_simple :: proc(r: io.Reader) -> (v: Simple, err: io.Error) {
|
||||
@@ -739,16 +739,16 @@ _encode_nil :: proc(w: io.Writer) -> io.Error {
|
||||
// Streaming
|
||||
|
||||
encode_stream_begin :: proc(w: io.Writer, major: Major) -> (err: io.Error) {
|
||||
assert(major >= Major(.Bytes) && major <= Major(.Map), "illegal stream type")
|
||||
assert(major >= Major(.Bytes) && major <= Major(.Map), "illegal stream type")
|
||||
|
||||
header := (u8(major) << 5) | u8(Add.Length_Unknown)
|
||||
_, err = io.write_full(w, {header})
|
||||
header := (u8(major) << 5) | u8(Add.Length_Unknown)
|
||||
_, err = io.write_full(w, {header})
|
||||
return
|
||||
}
|
||||
|
||||
encode_stream_end :: proc(w: io.Writer) -> io.Error {
|
||||
header := (u8(Major.Other) << 5) | u8(Add.Break)
|
||||
_, err := io.write_full(w, {header})
|
||||
header := (u8(Major.Other) << 5) | u8(Add.Break)
|
||||
_, err := io.write_full(w, {header})
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -757,8 +757,8 @@ encode_stream_text :: _encode_text
|
||||
encode_stream_array_item :: encode
|
||||
|
||||
encode_stream_map_entry :: proc(e: Encoder, key: Value, val: Value) -> Encode_Error {
|
||||
encode(e, key) or_return
|
||||
return encode(e, val)
|
||||
encode(e, key) or_return
|
||||
return encode(e, val)
|
||||
}
|
||||
|
||||
// For `Bytes` and `Text` strings: Decodes the number of items the header says follows.
|
||||
|
||||
@@ -85,7 +85,7 @@ marshal_into_encoder :: proc(e: Encoder, v: any, loc := #caller_location) -> (e
|
||||
|
||||
if .Self_Described_CBOR in e.flags {
|
||||
err_conv(_encode_u64(e, TAG_SELF_DESCRIBED_CBOR, .Tag)) or_return
|
||||
e.flags &~= { .Self_Described_CBOR }
|
||||
e.flags -= { .Self_Described_CBOR }
|
||||
}
|
||||
|
||||
if v == nil {
|
||||
|
||||
@@ -95,7 +95,6 @@ tag_register_number :: proc(impl: Tag_Implementation, nr: Tag_Number, id: string
|
||||
}
|
||||
|
||||
// Controls initialization of default tag implementations.
|
||||
// JS and WASI default to a panic allocator so we don't want to do it on those.
|
||||
INITIALIZE_DEFAULT_TAGS :: #config(CBOR_INITIALIZE_DEFAULT_TAGS, !ODIN_DEFAULT_TO_PANIC_ALLOCATOR && !ODIN_DEFAULT_TO_NIL_ALLOCATOR)
|
||||
|
||||
@(private, init, disabled=!INITIALIZE_DEFAULT_TAGS)
|
||||
|
||||
@@ -273,13 +273,13 @@ _unmarshal_value :: proc(d: Decoder, v: any, hdr: Header, allocator := context.a
|
||||
|
||||
// NOTE: Because this is a special type and not to be treated as a general integer,
|
||||
// We only put the value of it in fields that are explicitly of type `Simple`.
|
||||
switch &dst in v {
|
||||
case Simple:
|
||||
dst = decoded
|
||||
return
|
||||
case:
|
||||
return _unsupported(v, hdr, add)
|
||||
}
|
||||
switch &dst in v {
|
||||
case Simple:
|
||||
dst = decoded
|
||||
return
|
||||
case:
|
||||
return _unsupported(v, hdr, add)
|
||||
}
|
||||
|
||||
case .Tag:
|
||||
switch &dst in v {
|
||||
|
||||
+4816
-4816
File diff suppressed because it is too large
Load Diff
@@ -25,7 +25,7 @@ Marshal_Options :: struct {
|
||||
spec: Specification,
|
||||
|
||||
// Use line breaks & tabs/spaces
|
||||
pretty: bool,
|
||||
pretty: bool,
|
||||
|
||||
// Use spaces for indentation instead of tabs
|
||||
use_spaces: bool,
|
||||
@@ -34,7 +34,7 @@ Marshal_Options :: struct {
|
||||
spaces: int,
|
||||
|
||||
// Output uint as hex in JSON5 & MJSON
|
||||
write_uint_as_hex: bool,
|
||||
write_uint_as_hex: bool,
|
||||
|
||||
// If spec is MJSON and this is true, then keys will be quoted.
|
||||
//
|
||||
@@ -138,7 +138,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
// allow uints to be printed as hex
|
||||
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
|
||||
switch i in a {
|
||||
case u8, u16, u32, u64, u128:
|
||||
case u8, u16, u32, u64, u128:
|
||||
s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
|
||||
|
||||
case:
|
||||
@@ -239,7 +239,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
case runtime.Type_Info_Array:
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
for i in 0..<info.count {
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
opt_write_iteration(w, opt, i == 0) or_return
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
@@ -248,7 +248,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
case runtime.Type_Info_Enumerated_Array:
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
for i in 0..<info.count {
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
opt_write_iteration(w, opt, i == 0) or_return
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
@@ -258,7 +258,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
array := cast(^mem.Raw_Dynamic_Array)v.data
|
||||
for i in 0..<array.len {
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
opt_write_iteration(w, opt, i == 0) or_return
|
||||
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
@@ -268,7 +268,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
slice := cast(^mem.Raw_Slice)v.data
|
||||
for i in 0..<slice.len {
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
opt_write_iteration(w, opt, i == 0) or_return
|
||||
data := uintptr(slice.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
@@ -290,7 +290,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
for bucket_index in 0..<map_cap {
|
||||
runtime.map_hash_is_valid(hs[bucket_index]) or_continue
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
opt_write_iteration(w, opt, i == 0) or_return
|
||||
i += 1
|
||||
|
||||
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
|
||||
@@ -356,7 +356,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
slice.sort_by(sorted[:], proc(i, j: Entry) -> bool { return i.key < j.key })
|
||||
|
||||
for s, i in sorted {
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
opt_write_iteration(w, opt, i == 0) or_return
|
||||
opt_write_key(w, opt, s.key) or_return
|
||||
marshal_to_writer(w, s.value, opt) or_return
|
||||
}
|
||||
@@ -387,17 +387,17 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
case runtime.Type_Info_Pointer,
|
||||
runtime.Type_Info_Multi_Pointer,
|
||||
runtime.Type_Info_Procedure:
|
||||
return (^rawptr)(v.data)^ == nil
|
||||
return (^rawptr)(v.data)^ == nil
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
return (^runtime.Raw_Dynamic_Array)(v.data).len == 0
|
||||
return (^runtime.Raw_Dynamic_Array)(v.data).len == 0
|
||||
case runtime.Type_Info_Slice:
|
||||
return (^runtime.Raw_Slice)(v.data).len == 0
|
||||
return (^runtime.Raw_Slice)(v.data).len == 0
|
||||
case runtime.Type_Info_Union,
|
||||
runtime.Type_Info_Bit_Set,
|
||||
runtime.Type_Info_Soa_Pointer:
|
||||
return reflect.is_nil(v)
|
||||
case runtime.Type_Info_Map:
|
||||
return (^runtime.Raw_Map)(v.data).len == 0
|
||||
return (^runtime.Raw_Map)(v.data).len == 0
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -405,6 +405,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
marshal_struct_fields :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
|
||||
ti := runtime.type_info_base(type_info_of(v.id))
|
||||
info := ti.variant.(runtime.Type_Info_Struct)
|
||||
first_iteration := true
|
||||
for name, i in info.names {
|
||||
omitempty := false
|
||||
|
||||
@@ -424,7 +425,8 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
continue
|
||||
}
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
opt_write_iteration(w, opt, first_iteration) or_return
|
||||
first_iteration = false
|
||||
if json_name != "" {
|
||||
opt_write_key(w, opt, json_name) or_return
|
||||
} else {
|
||||
@@ -588,10 +590,10 @@ opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: i
|
||||
}
|
||||
|
||||
// insert comma separation and write indentations
|
||||
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int) -> (err: io.Error) {
|
||||
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, first_iteration: bool) -> (err: io.Error) {
|
||||
switch opt.spec {
|
||||
case .JSON, .JSON5:
|
||||
if iteration > 0 {
|
||||
case .JSON, .JSON5:
|
||||
if !first_iteration {
|
||||
io.write_byte(w, ',') or_return
|
||||
|
||||
if opt.pretty {
|
||||
@@ -601,8 +603,8 @@ opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int)
|
||||
|
||||
opt_write_indentation(w, opt) or_return
|
||||
|
||||
case .MJSON:
|
||||
if iteration > 0 {
|
||||
case .MJSON:
|
||||
if !first_iteration {
|
||||
// on pretty no commas necessary
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2024, Feoramund
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,67 @@
|
||||
package uuid
|
||||
|
||||
// A RFC 4122 Universally Unique Identifier
|
||||
Identifier :: distinct [16]u8
|
||||
|
||||
EXPECTED_LENGTH :: 8 + 4 + 4 + 4 + 12 + 4
|
||||
|
||||
VERSION_BYTE_INDEX :: 6
|
||||
VARIANT_BYTE_INDEX :: 8
|
||||
|
||||
// The number of 100-nanosecond intervals between 1582-10-15 and 1970-01-01.
|
||||
HNS_INTERVALS_BETWEEN_GREG_AND_UNIX :: 141427 * 24 * 60 * 60 * 1000 * 1000 * 10
|
||||
|
||||
VERSION_7_TIME_MASK :: 0xffffffff_ffff0000_00000000_00000000
|
||||
VERSION_7_TIME_SHIFT :: 80
|
||||
VERSION_7_COUNTER_MASK :: 0x00000000_00000fff_00000000_00000000
|
||||
VERSION_7_COUNTER_SHIFT :: 64
|
||||
|
||||
@(private)
|
||||
NO_CSPRNG_ERROR :: "The context random generator is not cryptographic. See the documentation for an example of how to set one up."
|
||||
@(private)
|
||||
BIG_CLOCK_ERROR :: "The clock sequence can only hold 14 bits of data, therefore no number greater than 16,383 (0x3FFF)."
|
||||
@(private)
|
||||
VERSION_7_BIG_COUNTER_ERROR :: "This implementation of the version 7 UUID counter can only hold 12 bits of data, therefore no number greater than 4,095 (0xFFF)."
|
||||
|
||||
Read_Error :: enum {
|
||||
None,
|
||||
Invalid_Length,
|
||||
Invalid_Hexadecimal,
|
||||
Invalid_Separator,
|
||||
}
|
||||
|
||||
Variant_Type :: enum {
|
||||
Unknown,
|
||||
Reserved_Apollo_NCS, // 0b0xx
|
||||
RFC_4122, // 0b10x
|
||||
Reserved_Microsoft_COM, // 0b110
|
||||
Reserved_Future, // 0b111
|
||||
}
|
||||
|
||||
// Name string is a fully-qualified domain name.
|
||||
@(rodata)
|
||||
Namespace_DNS := Identifier {
|
||||
0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1,
|
||||
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
|
||||
}
|
||||
|
||||
// Name string is a URL.
|
||||
@(rodata)
|
||||
Namespace_URL := Identifier {
|
||||
0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1,
|
||||
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
|
||||
}
|
||||
|
||||
// Name string is an ISO OID.
|
||||
@(rodata)
|
||||
Namespace_OID := Identifier {
|
||||
0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1,
|
||||
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
|
||||
}
|
||||
|
||||
// Name string is an X.500 DN (in DER or a text output format).
|
||||
@(rodata)
|
||||
Namespace_X500 := Identifier {
|
||||
0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1,
|
||||
0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8,
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
package uuid implements Universally Unique Identifiers according to the
|
||||
standard originally outlined in RFC 4122 with additions from RFC 9562.
|
||||
|
||||
The UUIDs are textually represented and read in the following string format:
|
||||
`00000000-0000-v000-V000-000000000000`
|
||||
|
||||
`v` is where the version bits reside, and `V` is where the variant bits reside.
|
||||
The meaning of the other bits is version-dependent.
|
||||
|
||||
Outside of string representations, UUIDs are represented in memory by a 128-bit
|
||||
structure organized as an array of 16 bytes.
|
||||
|
||||
|
||||
Of the UUID versions which may make use of random number generation, a
|
||||
requirement is placed upon them that the underlying generator be
|
||||
cryptographically-secure, per RFC 9562's suggestion.
|
||||
|
||||
- Version 1 without a node argument.
|
||||
- Version 4 in all cases.
|
||||
- Version 6 without either a clock or node argument.
|
||||
- Version 7 in all cases.
|
||||
|
||||
Here's an example of how to set up one:
|
||||
|
||||
import "core:crypto"
|
||||
import "core:encoding/uuid"
|
||||
|
||||
main :: proc() {
|
||||
my_uuid: uuid.Identifier
|
||||
|
||||
{
|
||||
// This scope will have a CSPRNG.
|
||||
context.random_generator = crypto.random_generator()
|
||||
my_uuid = uuid.generate_v7()
|
||||
}
|
||||
|
||||
// Back to the default random number generator.
|
||||
}
|
||||
|
||||
|
||||
For more information on the specifications, see here:
|
||||
- https://www.rfc-editor.org/rfc/rfc4122.html
|
||||
- https://www.rfc-editor.org/rfc/rfc9562.html
|
||||
*/
|
||||
package uuid
|
||||
@@ -0,0 +1,333 @@
|
||||
package uuid
|
||||
|
||||
import "base:runtime"
|
||||
import "core:crypto/hash"
|
||||
import "core:math/rand"
|
||||
import "core:time"
|
||||
|
||||
/*
|
||||
Generate a version 1 UUID.
|
||||
|
||||
Inputs:
|
||||
- clock_seq: The clock sequence, a number which must be initialized to a random number once in the lifetime of a system.
|
||||
- node: An optional 48-bit spatially unique identifier, specified to be the IEEE 802 address of the system.
|
||||
If one is not provided or available, 48 bits of random state will take its place.
|
||||
- timestamp: A timestamp from the `core:time` package, or `nil` to use the current time.
|
||||
|
||||
Returns:
|
||||
- result: The generated UUID.
|
||||
*/
|
||||
generate_v1 :: proc(clock_seq: u16, node: Maybe([6]u8) = nil, timestamp: Maybe(time.Time) = nil) -> (result: Identifier) {
|
||||
assert(clock_seq <= 0x3FFF, BIG_CLOCK_ERROR)
|
||||
unix_time_in_hns_intervals := time.to_unix_nanoseconds(timestamp.? or_else time.now()) / 100
|
||||
|
||||
uuid_timestamp := cast(u64le)(HNS_INTERVALS_BETWEEN_GREG_AND_UNIX + unix_time_in_hns_intervals)
|
||||
uuid_timestamp_octets := transmute([8]u8)uuid_timestamp
|
||||
|
||||
result[0] = uuid_timestamp_octets[0]
|
||||
result[1] = uuid_timestamp_octets[1]
|
||||
result[2] = uuid_timestamp_octets[2]
|
||||
result[3] = uuid_timestamp_octets[3]
|
||||
result[4] = uuid_timestamp_octets[4]
|
||||
result[5] = uuid_timestamp_octets[5]
|
||||
|
||||
result[6] = uuid_timestamp_octets[6] >> 4
|
||||
result[7] = uuid_timestamp_octets[6] << 4 | uuid_timestamp_octets[7]
|
||||
|
||||
if realized_node, ok := node.?; ok {
|
||||
mutable_node := realized_node
|
||||
runtime.mem_copy_non_overlapping(&result[10], &mutable_node[0], 6)
|
||||
} else {
|
||||
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
|
||||
bytes_generated := rand.read(result[10:])
|
||||
assert(bytes_generated == 6, "RNG failed to generate 6 bytes for UUID v1.")
|
||||
}
|
||||
|
||||
result[VERSION_BYTE_INDEX] |= 0x10
|
||||
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||
|
||||
result[8] |= cast(u8)(clock_seq & 0x3F00 >> 8)
|
||||
result[9] = cast(u8)clock_seq
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Generate a version 4 UUID.
|
||||
|
||||
This UUID will be pseudorandom, save for 6 pre-determined version and variant bits.
|
||||
|
||||
Returns:
|
||||
- result: The generated UUID.
|
||||
*/
|
||||
generate_v4 :: proc() -> (result: Identifier) {
|
||||
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
|
||||
bytes_generated := rand.read(result[:])
|
||||
assert(bytes_generated == 16, "RNG failed to generate 16 bytes for UUID v4.")
|
||||
|
||||
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||
result[VERSION_BYTE_INDEX] |= 0x40
|
||||
|
||||
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Generate a version 6 UUID.
|
||||
|
||||
Inputs:
|
||||
- clock_seq: The clock sequence from version 1, now made optional.
|
||||
If unspecified, it will be replaced with random bits.
|
||||
- node: An optional 48-bit spatially unique identifier, specified to be the IEEE 802 address of the system.
|
||||
If one is not provided or available, 48 bits of random state will take its place.
|
||||
- timestamp: A timestamp from the `core:time` package, or `nil` to use the current time.
|
||||
|
||||
Returns:
|
||||
- result: The generated UUID.
|
||||
*/
|
||||
generate_v6 :: proc(clock_seq: Maybe(u16) = nil, node: Maybe([6]u8) = nil, timestamp: Maybe(time.Time) = nil) -> (result: Identifier) {
|
||||
unix_time_in_hns_intervals := time.to_unix_nanoseconds(timestamp.? or_else time.now()) / 100
|
||||
|
||||
uuid_timestamp := cast(u128be)(HNS_INTERVALS_BETWEEN_GREG_AND_UNIX + unix_time_in_hns_intervals)
|
||||
|
||||
result = transmute(Identifier)(
|
||||
uuid_timestamp & 0x0FFFFFFF_FFFFF000 << 68 |
|
||||
uuid_timestamp & 0x00000000_00000FFF << 64
|
||||
)
|
||||
|
||||
if realized_clock_seq, ok := clock_seq.?; ok {
|
||||
assert(realized_clock_seq <= 0x3FFF, BIG_CLOCK_ERROR)
|
||||
result[8] |= cast(u8)(realized_clock_seq & 0x3F00 >> 8)
|
||||
result[9] = cast(u8)realized_clock_seq
|
||||
} else {
|
||||
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
|
||||
temporary: [2]u8
|
||||
bytes_generated := rand.read(temporary[:])
|
||||
assert(bytes_generated == 2, "RNG failed to generate 2 bytes for UUID v1.")
|
||||
result[8] |= temporary[0] & 0x3F
|
||||
result[9] = temporary[1]
|
||||
}
|
||||
|
||||
if realized_node, ok := node.?; ok {
|
||||
mutable_node := realized_node
|
||||
runtime.mem_copy_non_overlapping(&result[10], &mutable_node[0], 6)
|
||||
} else {
|
||||
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
|
||||
bytes_generated := rand.read(result[10:])
|
||||
assert(bytes_generated == 6, "RNG failed to generate 6 bytes for UUID v1.")
|
||||
}
|
||||
|
||||
result[VERSION_BYTE_INDEX] |= 0x60
|
||||
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Generate a version 7 UUID.
|
||||
|
||||
This UUID will be pseudorandom, save for 6 pre-determined version and variant
|
||||
bits and a 48-bit timestamp.
|
||||
|
||||
It is designed with time-based sorting in mind, such as for database usage, as
|
||||
the highest bits are allocated from the timestamp of when it is created.
|
||||
|
||||
Inputs:
|
||||
- timestamp: A timestamp from the `core:time` package, or `nil` to use the current time.
|
||||
|
||||
Returns:
|
||||
- result: The generated UUID.
|
||||
*/
|
||||
generate_v7_basic :: proc(timestamp: Maybe(time.Time) = nil) -> (result: Identifier) {
|
||||
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
|
||||
unix_time_in_milliseconds := time.to_unix_nanoseconds(timestamp.? or_else time.now()) / 1e6
|
||||
|
||||
result = transmute(Identifier)(cast(u128be)unix_time_in_milliseconds << VERSION_7_TIME_SHIFT)
|
||||
|
||||
bytes_generated := rand.read(result[6:])
|
||||
assert(bytes_generated == 10, "RNG failed to generate 10 bytes for UUID v7.")
|
||||
|
||||
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||
result[VERSION_BYTE_INDEX] |= 0x70
|
||||
|
||||
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Generate a version 7 UUID that has an incremented counter.
|
||||
|
||||
This UUID will be pseudorandom, save for 6 pre-determined version and variant
|
||||
bits, a 48-bit timestamp, and 12 bits of counter state.
|
||||
|
||||
It is designed with time-based sorting in mind, such as for database usage, as
|
||||
the highest bits are allocated from the timestamp of when it is created.
|
||||
|
||||
This procedure is preferable if you are generating hundreds or thousands of
|
||||
UUIDs as a batch within the span of a millisecond. Do note that the counter
|
||||
only has 12 bits of state, thus `counter` cannot exceed the number 4,095.
|
||||
|
||||
Example:
|
||||
|
||||
import "core:uuid"
|
||||
|
||||
// Create a batch of UUIDs all at once.
|
||||
batch: [dynamic]uuid.Identifier
|
||||
|
||||
for i: u16 = 0; i < 1000; i += 1 {
|
||||
my_uuid := uuid.generate_v7_counter(i)
|
||||
append(&batch, my_uuid)
|
||||
}
|
||||
|
||||
Inputs:
|
||||
- counter: A 12-bit value which should be incremented each time a UUID is generated in a batch.
|
||||
- timestamp: A timestamp from the `core:time` package, or `nil` to use the current time.
|
||||
|
||||
Returns:
|
||||
- result: The generated UUID.
|
||||
*/
|
||||
generate_v7_with_counter :: proc(counter: u16, timestamp: Maybe(time.Time) = nil) -> (result: Identifier) {
|
||||
assert(.Cryptographic in runtime.random_generator_query_info(context.random_generator), NO_CSPRNG_ERROR)
|
||||
assert(counter <= 0x0fff, VERSION_7_BIG_COUNTER_ERROR)
|
||||
unix_time_in_milliseconds := time.to_unix_nanoseconds(timestamp.? or_else time.now()) / 1e6
|
||||
|
||||
result = transmute(Identifier)(
|
||||
cast(u128be)unix_time_in_milliseconds << VERSION_7_TIME_SHIFT |
|
||||
cast(u128be)counter << VERSION_7_COUNTER_SHIFT
|
||||
)
|
||||
|
||||
bytes_generated := rand.read(result[8:])
|
||||
assert(bytes_generated == 8, "RNG failed to generate 8 bytes for UUID v7.")
|
||||
|
||||
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||
result[VERSION_BYTE_INDEX] |= 0x70
|
||||
|
||||
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
generate_v7 :: proc {
|
||||
generate_v7_basic,
|
||||
generate_v7_with_counter,
|
||||
}
|
||||
|
||||
/*
|
||||
Generate a version 8 UUID using a specific hashing algorithm.
|
||||
|
||||
This UUID is generated by hashing a name with a namespace.
|
||||
|
||||
Note that all version 8 UUIDs are for experimental or vendor-specific use
|
||||
cases, per the specification. This use case in particular is for offering a
|
||||
non-legacy alternative to UUID versions 3 and 5.
|
||||
|
||||
Inputs:
|
||||
- namespace: An `Identifier` that is used to represent the underlying namespace.
|
||||
This can be any one of the `Namespace_*` values provided in this package.
|
||||
- name: The byte slice which will be hashed with the namespace.
|
||||
- algorithm: A hashing algorithm from `core:crypto/hash`.
|
||||
|
||||
Returns:
|
||||
- result: The generated UUID.
|
||||
|
||||
Example:
|
||||
import "core:crypto/hash"
|
||||
import "core:encoding/uuid"
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
my_uuid := uuid.generate_v8_hash(uuid.Namespace_DNS, "www.odin-lang.org", .SHA256)
|
||||
my_uuid_string := uuid.to_string(my_uuid, context.temp_allocator)
|
||||
fmt.println(my_uuid_string)
|
||||
}
|
||||
|
||||
Output:
|
||||
|
||||
3730f688-4bff-8dce-9cbf-74a3960c5703
|
||||
|
||||
*/
|
||||
generate_v8_hash_bytes :: proc(
|
||||
namespace: Identifier,
|
||||
name: []byte,
|
||||
algorithm: hash.Algorithm,
|
||||
) -> (
|
||||
result: Identifier,
|
||||
) {
|
||||
// 128 bytes should be enough for the foreseeable future.
|
||||
digest: [128]byte
|
||||
|
||||
assert(hash.DIGEST_SIZES[algorithm] >= 16, "Per RFC 9562, the hashing algorithm used must generate a digest of 128 bits or larger.")
|
||||
assert(hash.DIGEST_SIZES[algorithm] < len(digest), "Digest size is too small for this algorithm. The buffer must be increased.")
|
||||
|
||||
hash_context: hash.Context
|
||||
hash.init(&hash_context, algorithm)
|
||||
|
||||
mutable_namespace := namespace
|
||||
hash.update(&hash_context, mutable_namespace[:])
|
||||
hash.update(&hash_context, name[:])
|
||||
hash.final(&hash_context, digest[:])
|
||||
|
||||
runtime.mem_copy_non_overlapping(&result, &digest, 16)
|
||||
|
||||
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||
result[VERSION_BYTE_INDEX] |= 0x80
|
||||
|
||||
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Generate a version 8 UUID using a specific hashing algorithm.
|
||||
|
||||
This UUID is generated by hashing a name with a namespace.
|
||||
|
||||
Note that all version 8 UUIDs are for experimental or vendor-specific use
|
||||
cases, per the specification. This use case in particular is for offering a
|
||||
non-legacy alternative to UUID versions 3 and 5.
|
||||
|
||||
Inputs:
|
||||
- namespace: An `Identifier` that is used to represent the underlying namespace.
|
||||
This can be any one of the `Namespace_*` values provided in this package.
|
||||
- name: The string which will be hashed with the namespace.
|
||||
- algorithm: A hashing algorithm from `core:crypto/hash`.
|
||||
|
||||
Returns:
|
||||
- result: The generated UUID.
|
||||
|
||||
Example:
|
||||
import "core:crypto/hash"
|
||||
import "core:encoding/uuid"
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
my_uuid := uuid.generate_v8_hash(uuid.Namespace_DNS, "www.odin-lang.org", .SHA256)
|
||||
my_uuid_string := uuid.to_string(my_uuid, context.temp_allocator)
|
||||
fmt.println(my_uuid_string)
|
||||
}
|
||||
|
||||
Output:
|
||||
|
||||
3730f688-4bff-8dce-9cbf-74a3960c5703
|
||||
|
||||
*/
|
||||
generate_v8_hash_string :: proc(
|
||||
namespace: Identifier,
|
||||
name: string,
|
||||
algorithm: hash.Algorithm,
|
||||
) -> (
|
||||
result: Identifier,
|
||||
) {
|
||||
return generate_v8_hash_bytes(namespace, transmute([]byte)name, algorithm)
|
||||
}
|
||||
|
||||
generate_v8_hash :: proc {
|
||||
generate_v8_hash_bytes,
|
||||
generate_v8_hash_string,
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
package uuid/legacy implements versions 3 and 5 of UUID generation, both of
|
||||
which are using hashing algorithms (MD5 and SHA1, respectively) that are known
|
||||
these days to no longer be secure.
|
||||
*/
|
||||
package uuid_legacy
|
||||
|
||||
import "base:runtime"
|
||||
import "core:crypto/legacy/md5"
|
||||
import "core:crypto/legacy/sha1"
|
||||
import "core:encoding/uuid"
|
||||
|
||||
Identifier :: uuid.Identifier
|
||||
VERSION_BYTE_INDEX :: uuid.VERSION_BYTE_INDEX
|
||||
VARIANT_BYTE_INDEX :: uuid.VARIANT_BYTE_INDEX
|
||||
|
||||
|
||||
/*
|
||||
Generate a version 3 UUID.
|
||||
|
||||
This UUID is generated with a MD5 hash of a name and a namespace.
|
||||
|
||||
Inputs:
|
||||
- namespace: An `Identifier` that is used to represent the underlying namespace.
|
||||
This can be any one of the `Namespace_*` values provided in the `uuid` package.
|
||||
- name: The byte slice which will be hashed with the namespace.
|
||||
|
||||
Returns:
|
||||
- result: The generated UUID.
|
||||
*/
|
||||
generate_v3_bytes :: proc(
|
||||
namespace: Identifier,
|
||||
name: []byte,
|
||||
) -> (
|
||||
result: Identifier,
|
||||
) {
|
||||
namespace := namespace
|
||||
|
||||
ctx: md5.Context
|
||||
md5.init(&ctx)
|
||||
md5.update(&ctx, namespace[:])
|
||||
md5.update(&ctx, name)
|
||||
md5.final(&ctx, result[:])
|
||||
|
||||
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||
result[VERSION_BYTE_INDEX] |= 0x30
|
||||
|
||||
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Generate a version 3 UUID.
|
||||
|
||||
This UUID is generated with a MD5 hash of a name and a namespace.
|
||||
|
||||
Inputs:
|
||||
- namespace: An `Identifier` that is used to represent the underlying namespace.
|
||||
This can be any one of the `Namespace_*` values provided in the `uuid` package.
|
||||
- name: The string which will be hashed with the namespace.
|
||||
|
||||
Returns:
|
||||
- result: The generated UUID.
|
||||
*/
|
||||
generate_v3_string :: proc(
|
||||
namespace: Identifier,
|
||||
name: string,
|
||||
) -> (
|
||||
result: Identifier,
|
||||
) {
|
||||
return generate_v3_bytes(namespace, transmute([]byte)name)
|
||||
}
|
||||
|
||||
generate_v3 :: proc {
|
||||
generate_v3_bytes,
|
||||
generate_v3_string,
|
||||
}
|
||||
|
||||
/*
|
||||
Generate a version 5 UUID.
|
||||
|
||||
This UUID is generated with a SHA1 hash of a name and a namespace.
|
||||
|
||||
Inputs:
|
||||
- namespace: An `Identifier` that is used to represent the underlying namespace.
|
||||
This can be any one of the `Namespace_*` values provided in the `uuid` package.
|
||||
- name: The byte slice which will be hashed with the namespace.
|
||||
|
||||
Returns:
|
||||
- result: The generated UUID.
|
||||
*/
|
||||
generate_v5_bytes :: proc(
|
||||
namespace: Identifier,
|
||||
name: []byte,
|
||||
) -> (
|
||||
result: Identifier,
|
||||
) {
|
||||
namespace := namespace
|
||||
digest: [sha1.DIGEST_SIZE]byte
|
||||
|
||||
ctx: sha1.Context
|
||||
sha1.init(&ctx)
|
||||
sha1.update(&ctx, namespace[:])
|
||||
sha1.update(&ctx, name)
|
||||
sha1.final(&ctx, digest[:])
|
||||
|
||||
runtime.mem_copy_non_overlapping(&result, &digest, 16)
|
||||
|
||||
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||
result[VERSION_BYTE_INDEX] |= 0x50
|
||||
|
||||
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Generate a version 5 UUID.
|
||||
|
||||
This UUID is generated with a SHA1 hash of a name and a namespace.
|
||||
|
||||
Inputs:
|
||||
- namespace: An `Identifier` that is used to represent the underlying namespace.
|
||||
This can be any one of the `Namespace_*` values provided in the `uuid` package.
|
||||
- name: The string which will be hashed with the namespace.
|
||||
|
||||
Returns:
|
||||
- result: The generated UUID.
|
||||
*/
|
||||
generate_v5_string :: proc(
|
||||
namespace: Identifier,
|
||||
name: string,
|
||||
) -> (
|
||||
result: Identifier,
|
||||
) {
|
||||
return generate_v5_bytes(namespace, transmute([]byte)name)
|
||||
}
|
||||
|
||||
generate_v5 :: proc {
|
||||
generate_v5_bytes,
|
||||
generate_v5_string,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,242 @@
|
||||
package uuid
|
||||
|
||||
import "base:runtime"
|
||||
import "core:time"
|
||||
|
||||
/*
|
||||
Convert a string to a UUID.
|
||||
|
||||
Inputs:
|
||||
- str: A string in the 8-4-4-4-12 format.
|
||||
|
||||
Returns:
|
||||
- id: The converted identifier, or `nil` if there is an error.
|
||||
- error: A description of the error, or `nil` if successful.
|
||||
*/
|
||||
read :: proc "contextless" (str: string) -> (id: Identifier, error: Read_Error) #no_bounds_check {
|
||||
// Only exact-length strings are acceptable.
|
||||
if len(str) != EXPECTED_LENGTH {
|
||||
return {}, .Invalid_Length
|
||||
}
|
||||
|
||||
// Check ahead to see if the separators are in the right places.
|
||||
if str[8] != '-' || str[13] != '-' || str[18] != '-' || str[23] != '-' {
|
||||
return {}, .Invalid_Separator
|
||||
}
|
||||
|
||||
read_nibble :: proc "contextless" (nibble: u8) -> u8 {
|
||||
switch nibble {
|
||||
case '0' ..= '9':
|
||||
return nibble - '0'
|
||||
case 'A' ..= 'F':
|
||||
return nibble - 'A' + 10
|
||||
case 'a' ..= 'f':
|
||||
return nibble - 'a' + 10
|
||||
case:
|
||||
// Return an error value.
|
||||
return 0xFF
|
||||
}
|
||||
}
|
||||
|
||||
index := 0
|
||||
octet_index := 0
|
||||
|
||||
CHUNKS :: [5]int{8, 4, 4, 4, 12}
|
||||
|
||||
for chunk in CHUNKS {
|
||||
for i := index; i < index + chunk; i += 2 {
|
||||
high := read_nibble(str[i])
|
||||
low := read_nibble(str[i + 1])
|
||||
|
||||
if high | low > 0xF {
|
||||
return {}, .Invalid_Hexadecimal
|
||||
}
|
||||
|
||||
id[octet_index] = low | high << 4
|
||||
octet_index += 1
|
||||
}
|
||||
|
||||
index += chunk + 1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Get the version of a UUID.
|
||||
|
||||
Inputs:
|
||||
- id: The identifier.
|
||||
|
||||
Returns:
|
||||
- number: The version number.
|
||||
*/
|
||||
version :: proc "contextless" (id: Identifier) -> (number: int) #no_bounds_check {
|
||||
return cast(int)(id[VERSION_BYTE_INDEX] & 0xF0 >> 4)
|
||||
}
|
||||
|
||||
/*
|
||||
Get the variant of a UUID.
|
||||
|
||||
Inputs:
|
||||
- id: The identifier.
|
||||
|
||||
Returns:
|
||||
- variant: The variant type.
|
||||
*/
|
||||
variant :: proc "contextless" (id: Identifier) -> (variant: Variant_Type) #no_bounds_check {
|
||||
switch {
|
||||
case id[VARIANT_BYTE_INDEX] & 0x80 == 0:
|
||||
return .Reserved_Apollo_NCS
|
||||
case id[VARIANT_BYTE_INDEX] & 0xC0 == 0x80:
|
||||
return .RFC_4122
|
||||
case id[VARIANT_BYTE_INDEX] & 0xE0 == 0xC0:
|
||||
return .Reserved_Microsoft_COM
|
||||
case id[VARIANT_BYTE_INDEX] & 0xF0 == 0xE0:
|
||||
return .Reserved_Future
|
||||
case:
|
||||
return .Unknown
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Get the clock sequence of a version 1 or version 6 UUID.
|
||||
|
||||
Inputs:
|
||||
- id: The identifier.
|
||||
|
||||
Returns:
|
||||
- clock_seq: The 14-bit clock sequence field.
|
||||
*/
|
||||
clock_seq :: proc "contextless" (id: Identifier) -> (clock_seq: u16) {
|
||||
return cast(u16)id[9] | cast(u16)id[8] & 0x3F << 8
|
||||
}
|
||||
|
||||
/*
|
||||
Get the node of a version 1 or version 6 UUID.
|
||||
|
||||
Inputs:
|
||||
- id: The identifier.
|
||||
|
||||
Returns:
|
||||
- node: The 48-bit spatially unique identifier.
|
||||
*/
|
||||
node :: proc "contextless" (id: Identifier) -> (node: [6]u8) {
|
||||
mutable_id := id
|
||||
runtime.mem_copy_non_overlapping(&node, &mutable_id[10], 6)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Get the raw timestamp of a version 1 UUID.
|
||||
|
||||
Inputs:
|
||||
- id: The identifier.
|
||||
|
||||
Returns:
|
||||
- timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
|
||||
*/
|
||||
raw_time_v1 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
|
||||
timestamp_octets: [8]u8
|
||||
|
||||
timestamp_octets[0] = id[0]
|
||||
timestamp_octets[1] = id[1]
|
||||
timestamp_octets[2] = id[2]
|
||||
timestamp_octets[3] = id[3]
|
||||
timestamp_octets[4] = id[4]
|
||||
timestamp_octets[5] = id[5]
|
||||
|
||||
timestamp_octets[6] = id[6] << 4 | id[7] >> 4
|
||||
timestamp_octets[7] = id[7] & 0xF
|
||||
|
||||
return cast(u64)transmute(u64le)timestamp_octets
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Get the timestamp of a version 1 UUID.
|
||||
|
||||
Inputs:
|
||||
- id: The identifier.
|
||||
|
||||
Returns:
|
||||
- timestamp: The timestamp of the UUID.
|
||||
*/
|
||||
time_v1 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
|
||||
return time.from_nanoseconds(cast(i64)(raw_time_v1(id) - HNS_INTERVALS_BETWEEN_GREG_AND_UNIX) * 100)
|
||||
}
|
||||
|
||||
/*
|
||||
Get the raw timestamp of a version 6 UUID.
|
||||
|
||||
Inputs:
|
||||
- id: The identifier.
|
||||
|
||||
Returns:
|
||||
- timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
|
||||
*/
|
||||
raw_time_v6 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
|
||||
temporary := transmute(u128be)id
|
||||
|
||||
timestamp |= cast(u64)(temporary & 0xFFFFFFFF_FFFF0000_00000000_00000000 >> 68)
|
||||
timestamp |= cast(u64)(temporary & 0x00000000_00000FFF_00000000_00000000 >> 64)
|
||||
|
||||
return timestamp
|
||||
}
|
||||
|
||||
/*
|
||||
Get the timestamp of a version 6 UUID.
|
||||
|
||||
Inputs:
|
||||
- id: The identifier.
|
||||
|
||||
Returns:
|
||||
- timestamp: The timestamp, in 100-nanosecond intervals since 1582-10-15.
|
||||
*/
|
||||
time_v6 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
|
||||
return time.from_nanoseconds(cast(i64)(raw_time_v6(id) - HNS_INTERVALS_BETWEEN_GREG_AND_UNIX) * 100)
|
||||
}
|
||||
|
||||
/*
|
||||
Get the raw timestamp of a version 7 UUID.
|
||||
|
||||
Inputs:
|
||||
- id: The identifier.
|
||||
|
||||
Returns:
|
||||
- timestamp: The timestamp, in milliseconds since the UNIX epoch.
|
||||
*/
|
||||
raw_time_v7 :: proc "contextless" (id: Identifier) -> (timestamp: u64) {
|
||||
time_bits := transmute(u128be)id & VERSION_7_TIME_MASK
|
||||
return cast(u64)(time_bits >> VERSION_7_TIME_SHIFT)
|
||||
}
|
||||
|
||||
/*
|
||||
Get the timestamp of a version 7 UUID.
|
||||
|
||||
Inputs:
|
||||
- id: The identifier.
|
||||
|
||||
Returns:
|
||||
- timestamp: The timestamp, in milliseconds since the UNIX epoch.
|
||||
*/
|
||||
time_v7 :: proc "contextless" (id: Identifier) -> (timestamp: time.Time) {
|
||||
return time.from_nanoseconds(cast(i64)raw_time_v7(id) * 1e6)
|
||||
}
|
||||
|
||||
/*
|
||||
Get the 12-bit counter value of a version 7 UUID.
|
||||
|
||||
The UUID must have been generated with a counter, otherwise this procedure will
|
||||
return random bits.
|
||||
|
||||
Inputs:
|
||||
- id: The identifier.
|
||||
|
||||
Returns:
|
||||
- counter: The 12-bit counter value.
|
||||
*/
|
||||
counter_v7 :: proc "contextless" (id: Identifier) -> (counter: u16) {
|
||||
counter_bits := transmute(u128be)id & VERSION_7_COUNTER_MASK
|
||||
return cast(u16)(counter_bits >> VERSION_7_COUNTER_SHIFT)
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package uuid
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
/*
|
||||
Stamp a 128-bit integer as being a valid version 8 UUID.
|
||||
|
||||
Per the specification, all version 8 UUIDs are either for experimental or
|
||||
vendor-specific purposes. This procedure allows for converting arbitrary data
|
||||
into custom UUIDs.
|
||||
|
||||
Inputs:
|
||||
- integer: Any integer type.
|
||||
|
||||
Returns:
|
||||
- result: A valid version 8 UUID.
|
||||
*/
|
||||
stamp_v8_int :: proc(#any_int integer: u128) -> (result: Identifier) {
|
||||
result = transmute(Identifier)cast(u128be)integer
|
||||
|
||||
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||
result[VERSION_BYTE_INDEX] |= 0x80
|
||||
|
||||
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Stamp an array of 16 bytes as being a valid version 8 UUID.
|
||||
|
||||
Per the specification, all version 8 UUIDs are either for experimental or
|
||||
vendor-specific purposes. This procedure allows for converting arbitrary data
|
||||
into custom UUIDs.
|
||||
|
||||
Inputs:
|
||||
- array: An array of 16 bytes.
|
||||
|
||||
Returns:
|
||||
- result: A valid version 8 UUID.
|
||||
*/
|
||||
stamp_v8_array :: proc(array: [16]u8) -> (result: Identifier) {
|
||||
result = Identifier(array)
|
||||
|
||||
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||
result[VERSION_BYTE_INDEX] |= 0x80
|
||||
|
||||
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Stamp a slice of bytes as being a valid version 8 UUID.
|
||||
|
||||
If the slice is less than 16 bytes long, the data available will be used.
|
||||
If it is longer than 16 bytes, only the first 16 will be used.
|
||||
|
||||
This procedure does not modify the underlying slice.
|
||||
|
||||
Per the specification, all version 8 UUIDs are either for experimental or
|
||||
vendor-specific purposes. This procedure allows for converting arbitrary data
|
||||
into custom UUIDs.
|
||||
|
||||
Inputs:
|
||||
- slice: A slice of bytes.
|
||||
|
||||
Returns:
|
||||
- result: A valid version 8 UUID.
|
||||
*/
|
||||
stamp_v8_slice :: proc(slice: []u8) -> (result: Identifier) {
|
||||
runtime.mem_copy_non_overlapping(&result, &slice[0], min(16, len(slice)))
|
||||
|
||||
result[VERSION_BYTE_INDEX] &= 0x0F
|
||||
result[VERSION_BYTE_INDEX] |= 0x80
|
||||
|
||||
result[VARIANT_BYTE_INDEX] &= 0x3F
|
||||
result[VARIANT_BYTE_INDEX] |= 0x80
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
stamp_v8 :: proc {
|
||||
stamp_v8_int,
|
||||
stamp_v8_array,
|
||||
stamp_v8_slice,
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package uuid
|
||||
|
||||
import "base:runtime"
|
||||
import "core:io"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
|
||||
/*
|
||||
Write a UUID in the 8-4-4-4-12 format.
|
||||
|
||||
This procedure performs error checking with every byte written.
|
||||
|
||||
If you can guarantee beforehand that your stream has enough space to hold the
|
||||
UUID (32 bytes), then it is better to use `unsafe_write` instead as that will
|
||||
be faster.
|
||||
|
||||
Inputs:
|
||||
- w: A writable stream.
|
||||
- id: The identifier to convert.
|
||||
|
||||
Returns:
|
||||
- error: An `io` error, if one occurred, otherwise `nil`.
|
||||
*/
|
||||
write :: proc(w: io.Writer, id: Identifier) -> (error: io.Error) #no_bounds_check {
|
||||
write_octet :: proc (w: io.Writer, octet: u8) -> io.Error #no_bounds_check {
|
||||
high_nibble := octet >> 4
|
||||
low_nibble := octet & 0xF
|
||||
|
||||
io.write_byte(w, strconv.digits[high_nibble]) or_return
|
||||
io.write_byte(w, strconv.digits[low_nibble]) or_return
|
||||
return nil
|
||||
}
|
||||
|
||||
for index in 0 ..< 4 { write_octet(w, id[index]) or_return }
|
||||
io.write_byte(w, '-') or_return
|
||||
for index in 4 ..< 6 { write_octet(w, id[index]) or_return }
|
||||
io.write_byte(w, '-') or_return
|
||||
for index in 6 ..< 8 { write_octet(w, id[index]) or_return }
|
||||
io.write_byte(w, '-') or_return
|
||||
for index in 8 ..< 10 { write_octet(w, id[index]) or_return }
|
||||
io.write_byte(w, '-') or_return
|
||||
for index in 10 ..< 16 { write_octet(w, id[index]) or_return }
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
Write a UUID in the 8-4-4-4-12 format.
|
||||
|
||||
This procedure performs no error checking on the underlying stream.
|
||||
|
||||
Inputs:
|
||||
- w: A writable stream.
|
||||
- id: The identifier to convert.
|
||||
*/
|
||||
unsafe_write :: proc(w: io.Writer, id: Identifier) #no_bounds_check {
|
||||
write_octet :: proc (w: io.Writer, octet: u8) #no_bounds_check {
|
||||
high_nibble := octet >> 4
|
||||
low_nibble := octet & 0xF
|
||||
|
||||
io.write_byte(w, strconv.digits[high_nibble])
|
||||
io.write_byte(w, strconv.digits[low_nibble])
|
||||
}
|
||||
|
||||
for index in 0 ..< 4 { write_octet(w, id[index]) }
|
||||
io.write_byte(w, '-')
|
||||
for index in 4 ..< 6 { write_octet(w, id[index]) }
|
||||
io.write_byte(w, '-')
|
||||
for index in 6 ..< 8 { write_octet(w, id[index]) }
|
||||
io.write_byte(w, '-')
|
||||
for index in 8 ..< 10 { write_octet(w, id[index]) }
|
||||
io.write_byte(w, '-')
|
||||
for index in 10 ..< 16 { write_octet(w, id[index]) }
|
||||
}
|
||||
|
||||
/*
|
||||
Convert a UUID to a string in the 8-4-4-4-12 format.
|
||||
|
||||
*Allocates Using Provided Allocator*
|
||||
|
||||
Inputs:
|
||||
- id: The identifier to convert.
|
||||
- allocator: (default: context.allocator)
|
||||
- loc: The caller location for debugging purposes (default: #caller_location)
|
||||
|
||||
Returns:
|
||||
- str: The allocated and converted string.
|
||||
- error: An optional allocator error if one occured, `nil` otherwise.
|
||||
*/
|
||||
to_string_allocated :: proc(
|
||||
id: Identifier,
|
||||
allocator := context.allocator,
|
||||
loc := #caller_location,
|
||||
) -> (
|
||||
str: string,
|
||||
error: runtime.Allocator_Error,
|
||||
) #optional_allocator_error {
|
||||
buf := make([]byte, EXPECTED_LENGTH, allocator, loc) or_return
|
||||
builder := strings.builder_from_bytes(buf[:])
|
||||
unsafe_write(strings.to_writer(&builder), id)
|
||||
return strings.to_string(builder), nil
|
||||
}
|
||||
|
||||
/*
|
||||
Convert a UUID to a string in the 8-4-4-4-12 format.
|
||||
|
||||
Inputs:
|
||||
- id: The identifier to convert.
|
||||
- buffer: A byte buffer to store the result. Must be at least 32 bytes large.
|
||||
- loc: The caller location for debugging purposes (default: #caller_location)
|
||||
|
||||
Returns:
|
||||
- str: The converted string which will be stored in `buffer`.
|
||||
*/
|
||||
to_string_buffer :: proc(
|
||||
id: Identifier,
|
||||
buffer: []byte,
|
||||
loc := #caller_location,
|
||||
) -> (
|
||||
str: string,
|
||||
) {
|
||||
assert(len(buffer) >= EXPECTED_LENGTH, "The buffer provided is not at least 32 bytes large.", loc)
|
||||
builder := strings.builder_from_bytes(buffer)
|
||||
unsafe_write(strings.to_writer(&builder), id)
|
||||
return strings.to_string(builder)
|
||||
}
|
||||
|
||||
to_string :: proc {
|
||||
to_string_allocated,
|
||||
to_string_buffer,
|
||||
}
|
||||
@@ -33,7 +33,7 @@ print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error
|
||||
written += fmt.wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident)
|
||||
|
||||
if len(doc.doctype.rest) > 0 {
|
||||
fmt.wprintf(writer, "\t%v\n", doc.doctype.rest)
|
||||
fmt.wprintf(writer, "\t%v\n", doc.doctype.rest)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,10 +42,10 @@ print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error
|
||||
}
|
||||
|
||||
if len(doc.elements) > 0 {
|
||||
fmt.wprintln(writer, " --- ")
|
||||
print_element(writer, doc, 0)
|
||||
fmt.wprintln(writer, " --- ")
|
||||
}
|
||||
fmt.wprintln(writer, " --- ")
|
||||
print_element(writer, doc, 0)
|
||||
fmt.wprintln(writer, " --- ")
|
||||
}
|
||||
|
||||
return written, .None
|
||||
}
|
||||
|
||||
+101
-108
@@ -45,45 +45,45 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
|
||||
if specific_type_info.signed {
|
||||
value := strconv.parse_i128(str) or_return
|
||||
switch type_info.id {
|
||||
case i8: (cast(^i8) ptr)^ = cast(i8) bounded_int(value, cast(i128)min(i8), cast(i128)max(i8) ) or_return
|
||||
case i16: (cast(^i16) ptr)^ = cast(i16) bounded_int(value, cast(i128)min(i16), cast(i128)max(i16) ) or_return
|
||||
case i32: (cast(^i32) ptr)^ = cast(i32) bounded_int(value, cast(i128)min(i32), cast(i128)max(i32) ) or_return
|
||||
case i64: (cast(^i64) ptr)^ = cast(i64) bounded_int(value, cast(i128)min(i64), cast(i128)max(i64) ) or_return
|
||||
case i128: (cast(^i128) ptr)^ = value
|
||||
case i8: (^i8) (ptr)^ = cast(i8) bounded_int(value, cast(i128)min(i8), cast(i128)max(i8) ) or_return
|
||||
case i16: (^i16) (ptr)^ = cast(i16) bounded_int(value, cast(i128)min(i16), cast(i128)max(i16) ) or_return
|
||||
case i32: (^i32) (ptr)^ = cast(i32) bounded_int(value, cast(i128)min(i32), cast(i128)max(i32) ) or_return
|
||||
case i64: (^i64) (ptr)^ = cast(i64) bounded_int(value, cast(i128)min(i64), cast(i128)max(i64) ) or_return
|
||||
case i128: (^i128) (ptr)^ = value
|
||||
|
||||
case int: (cast(^int) ptr)^ = cast(int) bounded_int(value, cast(i128)min(int), cast(i128)max(int) ) or_return
|
||||
case int: (^int) (ptr)^ = cast(int) bounded_int(value, cast(i128)min(int), cast(i128)max(int) ) or_return
|
||||
|
||||
case i16le: (cast(^i16le) ptr)^ = cast(i16le) bounded_int(value, cast(i128)min(i16le), cast(i128)max(i16le) ) or_return
|
||||
case i32le: (cast(^i32le) ptr)^ = cast(i32le) bounded_int(value, cast(i128)min(i32le), cast(i128)max(i32le) ) or_return
|
||||
case i64le: (cast(^i64le) ptr)^ = cast(i64le) bounded_int(value, cast(i128)min(i64le), cast(i128)max(i64le) ) or_return
|
||||
case i128le: (cast(^i128le)ptr)^ = cast(i128le) bounded_int(value, cast(i128)min(i128le), cast(i128)max(i128le)) or_return
|
||||
case i16le: (^i16le) (ptr)^ = cast(i16le) bounded_int(value, cast(i128)min(i16le), cast(i128)max(i16le) ) or_return
|
||||
case i32le: (^i32le) (ptr)^ = cast(i32le) bounded_int(value, cast(i128)min(i32le), cast(i128)max(i32le) ) or_return
|
||||
case i64le: (^i64le) (ptr)^ = cast(i64le) bounded_int(value, cast(i128)min(i64le), cast(i128)max(i64le) ) or_return
|
||||
case i128le: (^i128le)(ptr)^ = cast(i128le) bounded_int(value, cast(i128)min(i128le), cast(i128)max(i128le)) or_return
|
||||
|
||||
case i16be: (cast(^i16be) ptr)^ = cast(i16be) bounded_int(value, cast(i128)min(i16be), cast(i128)max(i16be) ) or_return
|
||||
case i32be: (cast(^i32be) ptr)^ = cast(i32be) bounded_int(value, cast(i128)min(i32be), cast(i128)max(i32be) ) or_return
|
||||
case i64be: (cast(^i64be) ptr)^ = cast(i64be) bounded_int(value, cast(i128)min(i64be), cast(i128)max(i64be) ) or_return
|
||||
case i128be: (cast(^i128be)ptr)^ = cast(i128be) bounded_int(value, cast(i128)min(i128be), cast(i128)max(i128be)) or_return
|
||||
case i16be: (^i16be) (ptr)^ = cast(i16be) bounded_int(value, cast(i128)min(i16be), cast(i128)max(i16be) ) or_return
|
||||
case i32be: (^i32be) (ptr)^ = cast(i32be) bounded_int(value, cast(i128)min(i32be), cast(i128)max(i32be) ) or_return
|
||||
case i64be: (^i64be) (ptr)^ = cast(i64be) bounded_int(value, cast(i128)min(i64be), cast(i128)max(i64be) ) or_return
|
||||
case i128be: (^i128be)(ptr)^ = cast(i128be) bounded_int(value, cast(i128)min(i128be), cast(i128)max(i128be)) or_return
|
||||
}
|
||||
} else {
|
||||
value := strconv.parse_u128(str) or_return
|
||||
switch type_info.id {
|
||||
case u8: (cast(^u8) ptr)^ = cast(u8) bounded_uint(value, cast(u128)max(u8) ) or_return
|
||||
case u16: (cast(^u16) ptr)^ = cast(u16) bounded_uint(value, cast(u128)max(u16) ) or_return
|
||||
case u32: (cast(^u32) ptr)^ = cast(u32) bounded_uint(value, cast(u128)max(u32) ) or_return
|
||||
case u64: (cast(^u64) ptr)^ = cast(u64) bounded_uint(value, cast(u128)max(u64) ) or_return
|
||||
case u128: (cast(^u128) ptr)^ = value
|
||||
case u8: (^u8) (ptr)^ = cast(u8) bounded_uint(value, cast(u128)max(u8) ) or_return
|
||||
case u16: (^u16) (ptr)^ = cast(u16) bounded_uint(value, cast(u128)max(u16) ) or_return
|
||||
case u32: (^u32) (ptr)^ = cast(u32) bounded_uint(value, cast(u128)max(u32) ) or_return
|
||||
case u64: (^u64) (ptr)^ = cast(u64) bounded_uint(value, cast(u128)max(u64) ) or_return
|
||||
case u128: (^u128) (ptr)^ = value
|
||||
|
||||
case uint: (cast(^uint) ptr)^ = cast(uint) bounded_uint(value, cast(u128)max(uint) ) or_return
|
||||
case uintptr: (cast(^uintptr)ptr)^ = cast(uintptr) bounded_uint(value, cast(u128)max(uintptr)) or_return
|
||||
case uint: (^uint) (ptr)^ = cast(uint) bounded_uint(value, cast(u128)max(uint) ) or_return
|
||||
case uintptr: (^uintptr)(ptr)^ = cast(uintptr) bounded_uint(value, cast(u128)max(uintptr)) or_return
|
||||
|
||||
case u16le: (cast(^u16le) ptr)^ = cast(u16le) bounded_uint(value, cast(u128)max(u16le) ) or_return
|
||||
case u32le: (cast(^u32le) ptr)^ = cast(u32le) bounded_uint(value, cast(u128)max(u32le) ) or_return
|
||||
case u64le: (cast(^u64le) ptr)^ = cast(u64le) bounded_uint(value, cast(u128)max(u64le) ) or_return
|
||||
case u128le: (cast(^u128le) ptr)^ = cast(u128le) bounded_uint(value, cast(u128)max(u128le) ) or_return
|
||||
case u16le: (^u16le) (ptr)^ = cast(u16le) bounded_uint(value, cast(u128)max(u16le) ) or_return
|
||||
case u32le: (^u32le) (ptr)^ = cast(u32le) bounded_uint(value, cast(u128)max(u32le) ) or_return
|
||||
case u64le: (^u64le) (ptr)^ = cast(u64le) bounded_uint(value, cast(u128)max(u64le) ) or_return
|
||||
case u128le: (^u128le) (ptr)^ = cast(u128le) bounded_uint(value, cast(u128)max(u128le) ) or_return
|
||||
|
||||
case u16be: (cast(^u16be) ptr)^ = cast(u16be) bounded_uint(value, cast(u128)max(u16be) ) or_return
|
||||
case u32be: (cast(^u32be) ptr)^ = cast(u32be) bounded_uint(value, cast(u128)max(u32be) ) or_return
|
||||
case u64be: (cast(^u64be) ptr)^ = cast(u64be) bounded_uint(value, cast(u128)max(u64be) ) or_return
|
||||
case u128be: (cast(^u128be) ptr)^ = cast(u128be) bounded_uint(value, cast(u128)max(u128be) ) or_return
|
||||
case u16be: (^u16be) (ptr)^ = cast(u16be) bounded_uint(value, cast(u128)max(u16be) ) or_return
|
||||
case u32be: (^u32be) (ptr)^ = cast(u32be) bounded_uint(value, cast(u128)max(u32be) ) or_return
|
||||
case u64be: (^u64be) (ptr)^ = cast(u64be) bounded_uint(value, cast(u128)max(u64be) ) or_return
|
||||
case u128be: (^u128be) (ptr)^ = cast(u128be) bounded_uint(value, cast(u128)max(u128be) ) or_return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,60 +92,60 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
|
||||
return false
|
||||
}
|
||||
|
||||
(cast(^rune)ptr)^ = utf8.rune_at_pos(str, 0)
|
||||
(^rune)(ptr)^ = utf8.rune_at_pos(str, 0)
|
||||
|
||||
case runtime.Type_Info_Float:
|
||||
value := strconv.parse_f64(str) or_return
|
||||
switch type_info.id {
|
||||
case f16: (cast(^f16) ptr)^ = cast(f16) value
|
||||
case f32: (cast(^f32) ptr)^ = cast(f32) value
|
||||
case f64: (cast(^f64) ptr)^ = value
|
||||
case f16: (^f16) (ptr)^ = cast(f16) value
|
||||
case f32: (^f32) (ptr)^ = cast(f32) value
|
||||
case f64: (^f64) (ptr)^ = value
|
||||
|
||||
case f16le: (cast(^f16le)ptr)^ = cast(f16le) value
|
||||
case f32le: (cast(^f32le)ptr)^ = cast(f32le) value
|
||||
case f64le: (cast(^f64le)ptr)^ = cast(f64le) value
|
||||
case f16le: (^f16le)(ptr)^ = cast(f16le) value
|
||||
case f32le: (^f32le)(ptr)^ = cast(f32le) value
|
||||
case f64le: (^f64le)(ptr)^ = cast(f64le) value
|
||||
|
||||
case f16be: (cast(^f16be)ptr)^ = cast(f16be) value
|
||||
case f32be: (cast(^f32be)ptr)^ = cast(f32be) value
|
||||
case f64be: (cast(^f64be)ptr)^ = cast(f64be) value
|
||||
case f16be: (^f16be)(ptr)^ = cast(f16be) value
|
||||
case f32be: (^f32be)(ptr)^ = cast(f32be) value
|
||||
case f64be: (^f64be)(ptr)^ = cast(f64be) value
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Complex:
|
||||
value := strconv.parse_complex128(str) or_return
|
||||
switch type_info.id {
|
||||
case complex128: (cast(^complex128)ptr)^ = value
|
||||
case complex64: (cast(^complex64) ptr)^ = cast(complex64)value
|
||||
case complex32: (cast(^complex32) ptr)^ = cast(complex32)value
|
||||
case complex32: (^complex32) (ptr)^ = (complex32)(value)
|
||||
case complex64: (^complex64) (ptr)^ = (complex64)(value)
|
||||
case complex128: (^complex128)(ptr)^ = value
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Quaternion:
|
||||
value := strconv.parse_quaternion256(str) or_return
|
||||
switch type_info.id {
|
||||
case quaternion256: (cast(^quaternion256)ptr)^ = value
|
||||
case quaternion128: (cast(^quaternion128)ptr)^ = cast(quaternion128)value
|
||||
case quaternion64: (cast(^quaternion64) ptr)^ = cast(quaternion64)value
|
||||
case quaternion64: (^quaternion64) (ptr)^ = (quaternion64)(value)
|
||||
case quaternion128: (^quaternion128)(ptr)^ = (quaternion128)(value)
|
||||
case quaternion256: (^quaternion256)(ptr)^ = value
|
||||
}
|
||||
|
||||
case runtime.Type_Info_String:
|
||||
if specific_type_info.is_cstring {
|
||||
cstr_ptr := cast(^cstring)ptr
|
||||
cstr_ptr := (^cstring)(ptr)
|
||||
if cstr_ptr != nil {
|
||||
// Prevent memory leaks from us setting this value multiple times.
|
||||
delete(cstr_ptr^)
|
||||
}
|
||||
cstr_ptr^ = strings.clone_to_cstring(str)
|
||||
} else {
|
||||
(cast(^string)ptr)^ = str
|
||||
(^string)(ptr)^ = str
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Boolean:
|
||||
value := strconv.parse_bool(str) or_return
|
||||
switch type_info.id {
|
||||
case bool: (cast(^bool) ptr)^ = value
|
||||
case b8: (cast(^b8) ptr)^ = cast(b8) value
|
||||
case b16: (cast(^b16) ptr)^ = cast(b16) value
|
||||
case b32: (cast(^b32) ptr)^ = cast(b32) value
|
||||
case b64: (cast(^b64) ptr)^ = cast(b64) value
|
||||
case bool: (^bool)(ptr)^ = value
|
||||
case b8: (^b8) (ptr)^ = b8(value)
|
||||
case b16: (^b16) (ptr)^ = b16(value)
|
||||
case b32: (^b32) (ptr)^ = b32(value)
|
||||
case b64: (^b64) (ptr)^ = b64(value)
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Bit_Set:
|
||||
@@ -154,9 +154,9 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
|
||||
value: u128
|
||||
|
||||
// NOTE: `upper` is inclusive, i.e: `0..=31`
|
||||
max_bit_index := cast(u128)(1 + specific_type_info.upper - specific_type_info.lower)
|
||||
bit_index : u128 = 0
|
||||
#no_bounds_check for string_index : uint = 0; string_index < len(str); string_index += 1 {
|
||||
max_bit_index := u128(1 + specific_type_info.upper - specific_type_info.lower)
|
||||
bit_index := u128(0)
|
||||
#no_bounds_check for string_index in 0..<uint(len(str)) {
|
||||
if bit_index == max_bit_index {
|
||||
// The string's too long for this bit_set.
|
||||
return false
|
||||
@@ -180,11 +180,11 @@ parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info:
|
||||
set_unbounded_integer_by_type(ptr, value, specific_type_info.underlying.id)
|
||||
} else {
|
||||
switch 8*type_info.size {
|
||||
case 8: (cast(^u8) ptr)^ = cast(u8) value
|
||||
case 16: (cast(^u16) ptr)^ = cast(u16) value
|
||||
case 32: (cast(^u32) ptr)^ = cast(u32) value
|
||||
case 64: (cast(^u64) ptr)^ = cast(u64) value
|
||||
case 128: (cast(^u128) ptr)^ = cast(u128) value
|
||||
case 8: (^u8) (ptr)^ = cast(u8) value
|
||||
case 16: (^u16) (ptr)^ = cast(u16) value
|
||||
case 32: (^u32) (ptr)^ = cast(u32) value
|
||||
case 64: (^u64) (ptr)^ = cast(u64) value
|
||||
case 128: (^u128)(ptr)^ = value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,7 +222,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
||||
mode: int
|
||||
|
||||
if file, ok := get_struct_subtag(arg_tag, SUBTAG_FILE); ok {
|
||||
for i := 0; i < len(file); i += 1 {
|
||||
for i in 0..<len(file) {
|
||||
#no_bounds_check switch file[i] {
|
||||
case 'r': wants_read = true
|
||||
case 'w': wants_write = true
|
||||
@@ -249,7 +249,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
||||
|
||||
if permstr, ok := get_struct_subtag(arg_tag, SUBTAG_PERMS); ok {
|
||||
if value, parse_ok := strconv.parse_u64_of_base(permstr, 8); parse_ok {
|
||||
perms = cast(int)value
|
||||
perms = int(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
||||
return
|
||||
}
|
||||
|
||||
(cast(^os.Handle)ptr)^ = handle
|
||||
(^os.Handle)(ptr)^ = handle
|
||||
return
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
||||
return
|
||||
}
|
||||
|
||||
(cast(^time.Time)ptr)^ = res
|
||||
(^time.Time)(ptr)^ = res
|
||||
return
|
||||
} else if data_type == datetime.DateTime {
|
||||
// NOTE: The UTC offset and leap second data are discarded.
|
||||
@@ -302,7 +302,7 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
||||
return
|
||||
}
|
||||
|
||||
(cast(^datetime.DateTime)ptr)^ = res
|
||||
(^datetime.DateTime)(ptr)^ = res
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -323,44 +323,44 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
|
||||
@(optimization_mode="size")
|
||||
set_unbounded_integer_by_type :: proc(ptr: rawptr, value: $T, data_type: typeid) where intrinsics.type_is_integer(T) {
|
||||
switch data_type {
|
||||
case i8: (cast(^i8) ptr)^ = cast(i8) value
|
||||
case i16: (cast(^i16) ptr)^ = cast(i16) value
|
||||
case i32: (cast(^i32) ptr)^ = cast(i32) value
|
||||
case i64: (cast(^i64) ptr)^ = cast(i64) value
|
||||
case i128: (cast(^i128) ptr)^ = cast(i128) value
|
||||
case i8: (^i8) (ptr)^ = cast(i8) value
|
||||
case i16: (^i16) (ptr)^ = cast(i16) value
|
||||
case i32: (^i32) (ptr)^ = cast(i32) value
|
||||
case i64: (^i64) (ptr)^ = cast(i64) value
|
||||
case i128: (^i128) (ptr)^ = cast(i128) value
|
||||
|
||||
case int: (cast(^int) ptr)^ = cast(int) value
|
||||
case int: (^int) (ptr)^ = cast(int) value
|
||||
|
||||
case i16le: (cast(^i16le) ptr)^ = cast(i16le) value
|
||||
case i32le: (cast(^i32le) ptr)^ = cast(i32le) value
|
||||
case i64le: (cast(^i64le) ptr)^ = cast(i64le) value
|
||||
case i128le: (cast(^i128le) ptr)^ = cast(i128le) value
|
||||
case i16le: (^i16le) (ptr)^ = cast(i16le) value
|
||||
case i32le: (^i32le) (ptr)^ = cast(i32le) value
|
||||
case i64le: (^i64le) (ptr)^ = cast(i64le) value
|
||||
case i128le: (^i128le) (ptr)^ = cast(i128le) value
|
||||
|
||||
case i16be: (cast(^i16be) ptr)^ = cast(i16be) value
|
||||
case i32be: (cast(^i32be) ptr)^ = cast(i32be) value
|
||||
case i64be: (cast(^i64be) ptr)^ = cast(i64be) value
|
||||
case i128be: (cast(^i128be) ptr)^ = cast(i128be) value
|
||||
case i16be: (^i16be) (ptr)^ = cast(i16be) value
|
||||
case i32be: (^i32be) (ptr)^ = cast(i32be) value
|
||||
case i64be: (^i64be) (ptr)^ = cast(i64be) value
|
||||
case i128be: (^i128be) (ptr)^ = cast(i128be) value
|
||||
|
||||
case u8: (cast(^u8) ptr)^ = cast(u8) value
|
||||
case u16: (cast(^u16) ptr)^ = cast(u16) value
|
||||
case u32: (cast(^u32) ptr)^ = cast(u32) value
|
||||
case u64: (cast(^u64) ptr)^ = cast(u64) value
|
||||
case u128: (cast(^u128) ptr)^ = cast(u128) value
|
||||
case u8: (^u8) (ptr)^ = cast(u8) value
|
||||
case u16: (^u16) (ptr)^ = cast(u16) value
|
||||
case u32: (^u32) (ptr)^ = cast(u32) value
|
||||
case u64: (^u64) (ptr)^ = cast(u64) value
|
||||
case u128: (^u128) (ptr)^ = cast(u128) value
|
||||
|
||||
case uint: (cast(^uint) ptr)^ = cast(uint) value
|
||||
case uintptr: (cast(^uintptr)ptr)^ = cast(uintptr) value
|
||||
case uint: (^uint) (ptr)^ = cast(uint) value
|
||||
case uintptr: (^uintptr)(ptr)^ = cast(uintptr) value
|
||||
|
||||
case u16le: (cast(^u16le) ptr)^ = cast(u16le) value
|
||||
case u32le: (cast(^u32le) ptr)^ = cast(u32le) value
|
||||
case u64le: (cast(^u64le) ptr)^ = cast(u64le) value
|
||||
case u128le: (cast(^u128le) ptr)^ = cast(u128le) value
|
||||
case u16le: (^u16le) (ptr)^ = cast(u16le) value
|
||||
case u32le: (^u32le) (ptr)^ = cast(u32le) value
|
||||
case u64le: (^u64le) (ptr)^ = cast(u64le) value
|
||||
case u128le: (^u128le) (ptr)^ = cast(u128le) value
|
||||
|
||||
case u16be: (cast(^u16be) ptr)^ = cast(u16be) value
|
||||
case u32be: (cast(^u32be) ptr)^ = cast(u32be) value
|
||||
case u64be: (cast(^u64be) ptr)^ = cast(u64be) value
|
||||
case u128be: (cast(^u128be) ptr)^ = cast(u128be) value
|
||||
case u16be: (^u16be) (ptr)^ = cast(u16be) value
|
||||
case u32be: (^u32be) (ptr)^ = cast(u32be) value
|
||||
case u64be: (^u64be) (ptr)^ = cast(u64be) value
|
||||
case u128be: (^u128be) (ptr)^ = cast(u128be) value
|
||||
|
||||
case rune: (cast(^rune) ptr)^ = cast(rune) value
|
||||
case rune: (^rune) (ptr)^ = cast(rune) value
|
||||
|
||||
case:
|
||||
fmt.panicf("Unsupported integer backing type: %v", data_type)
|
||||
@@ -443,9 +443,9 @@ parse_and_set_pointer_by_type :: proc(ptr: rawptr, str: string, type_info: ^runt
|
||||
}
|
||||
}
|
||||
|
||||
subptr := cast(rawptr)(
|
||||
cast(uintptr)ptr.data +
|
||||
cast(uintptr)((ptr.len - 1) * specific_type_info.elem.size))
|
||||
subptr := rawptr(
|
||||
uintptr(ptr.data) +
|
||||
uintptr((ptr.len - 1) * specific_type_info.elem.size))
|
||||
mem.copy(subptr, raw_data(elem_backing), len(elem_backing))
|
||||
|
||||
case runtime.Type_Info_Enum:
|
||||
@@ -490,7 +490,7 @@ get_field_pos :: proc(field: reflect.Struct_Field) -> (int, bool) {
|
||||
if args_tag, ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS); ok {
|
||||
if pos_subtag, pos_ok := get_struct_subtag(args_tag, SUBTAG_POS); pos_ok {
|
||||
if value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10); parse_ok {
|
||||
return cast(int)value, true
|
||||
return int(value), true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -516,15 +516,8 @@ get_field_by_name :: proc(model: ^$T, name: string) -> (result: reflect.Struct_F
|
||||
// Get a struct field by its `pos` subtag.
|
||||
get_field_by_pos :: proc(model: ^$T, pos: int) -> (result: reflect.Struct_Field, index: int, ok: bool) {
|
||||
for field, i in reflect.struct_fields_zipped(T) {
|
||||
args_tag, tag_ok := reflect.struct_tag_lookup(field.tag, TAG_ARGS)
|
||||
if !tag_ok {
|
||||
continue
|
||||
}
|
||||
|
||||
pos_subtag, pos_ok := get_struct_subtag(args_tag, SUBTAG_POS)
|
||||
if !pos_ok {
|
||||
continue
|
||||
}
|
||||
args_tag := reflect.struct_tag_lookup(field.tag, TAG_ARGS) or_continue
|
||||
pos_subtag := get_struct_subtag(args_tag, SUBTAG_POS) or_continue
|
||||
|
||||
value, parse_ok := strconv.parse_u64_of_base(pos_subtag, 10)
|
||||
if parse_ok && cast(int)value == pos {
|
||||
|
||||
+14
-17
@@ -92,7 +92,7 @@ _user_formatters: ^map[typeid]User_Formatter
|
||||
//
|
||||
set_user_formatters :: proc(m: ^map[typeid]User_Formatter) {
|
||||
assert(_user_formatters == nil, "set_user_formatters must not be called more than once.")
|
||||
_user_formatters = m
|
||||
_user_formatters = m
|
||||
}
|
||||
// Registers a user-defined formatter for a specific typeid
|
||||
//
|
||||
@@ -1072,8 +1072,8 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
|
||||
}
|
||||
|
||||
flags: strconv.Int_Flags
|
||||
if fi.hash && !fi.zero && start == 0 { flags |= {.Prefix} }
|
||||
if fi.plus { flags |= {.Plus} }
|
||||
if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
|
||||
if fi.plus { flags += {.Plus} }
|
||||
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
||||
prev_zero := fi.zero
|
||||
defer fi.zero = prev_zero
|
||||
@@ -1157,8 +1157,8 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
|
||||
}
|
||||
|
||||
flags: strconv.Int_Flags
|
||||
if fi.hash && !fi.zero && start == 0 { flags |= {.Prefix} }
|
||||
if fi.plus { flags |= {.Plus} }
|
||||
if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
|
||||
if fi.plus { flags += {.Plus} }
|
||||
s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
||||
|
||||
if fi.hash && fi.zero && fi.indent == 0 {
|
||||
@@ -1229,10 +1229,10 @@ _fmt_memory :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, units: st
|
||||
// Add the unit at the end.
|
||||
copy(buf[len(str):], units[off:off+unit_len])
|
||||
str = string(buf[:len(str)+unit_len])
|
||||
|
||||
if !fi.plus {
|
||||
// Strip sign from "+<value>" but not "+Inf".
|
||||
if str[0] == '+' && str[1] != 'I' {
|
||||
|
||||
if !fi.plus {
|
||||
// Strip sign from "+<value>" but not "+Inf".
|
||||
if str[0] == '+' && str[1] != 'I' {
|
||||
str = str[1:]
|
||||
}
|
||||
}
|
||||
@@ -1460,13 +1460,10 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
|
||||
if !fi.minus {
|
||||
io.write_string(fi.writer, s, &fi.n)
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
io.write_string(fi.writer, s, &fi.n)
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
io.write_string(fi.writer, s, &fi.n)
|
||||
}
|
||||
|
||||
@@ -1768,7 +1765,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') {
|
||||
|
||||
if is_enum {
|
||||
enum_name: string
|
||||
if ti_named, is_named := info.elem.variant.(runtime.Type_Info_Named); is_named {
|
||||
if ti_named, is_named := info.elem.variant.(runtime.Type_Info_Named); is_named {
|
||||
enum_name = ti_named.name
|
||||
}
|
||||
for ev, evi in e.values {
|
||||
@@ -2712,7 +2709,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
return
|
||||
}
|
||||
if fi.indirection_level < 1 {
|
||||
fi.indirection_level += 1
|
||||
fi.indirection_level += 1
|
||||
defer fi.indirection_level -= 1
|
||||
io.write_byte(fi.writer, '&')
|
||||
fmt_value(fi, a, verb)
|
||||
@@ -2781,7 +2778,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
runtime.Type_Info_Dynamic_Array,
|
||||
runtime.Type_Info_Map:
|
||||
if fi.indirection_level < 1 {
|
||||
fi.indirection_level += 1
|
||||
fi.indirection_level += 1
|
||||
defer fi.indirection_level -= 1
|
||||
io.write_byte(fi.writer, '&', &fi.n)
|
||||
fmt_value(fi, a, verb)
|
||||
|
||||
@@ -897,19 +897,20 @@ XXH3_hashLong_64b_default :: #force_no_inline proc(input: []u8, seed64: xxh_u64,
|
||||
why (uop cache maybe?), but the difference is large and easily measurable.
|
||||
*/
|
||||
@(optimization_mode="speed")
|
||||
XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(input: []u8,
|
||||
seed: xxh_u64,
|
||||
f_acc512: XXH3_accumulate_512_f,
|
||||
f_scramble: XXH3_scramble_accumulator_f,
|
||||
f_init_sec: XXH3_init_custom_secret_f) -> (hash: xxh_u64) {
|
||||
XXH3_hashLong_64b_withSeed_internal :: #force_no_inline proc(
|
||||
input: []u8,
|
||||
seed: xxh_u64,
|
||||
f_acc512: XXH3_accumulate_512_f,
|
||||
f_scramble: XXH3_scramble_accumulator_f,
|
||||
f_init_sec: XXH3_init_custom_secret_f,
|
||||
) -> (hash: xxh_u64) {
|
||||
if seed == 0 {
|
||||
return XXH3_hashLong_64b_internal(input, XXH3_kSecret[:], f_acc512, f_scramble)
|
||||
}
|
||||
{
|
||||
secret: [XXH_SECRET_DEFAULT_SIZE]u8
|
||||
f_init_sec(secret[:], seed)
|
||||
return XXH3_hashLong_64b_internal(input, secret[:], f_acc512, f_scramble)
|
||||
}
|
||||
|
||||
secret: [XXH_SECRET_DEFAULT_SIZE]u8
|
||||
f_init_sec(secret[:], seed)
|
||||
return XXH3_hashLong_64b_internal(input, secret[:], f_acc512, f_scramble)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -71,7 +71,7 @@ save_to_buffer :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
|
||||
written := 0
|
||||
|
||||
if resize(&output.buf, int(header.size)) != nil {
|
||||
return .Unable_To_Allocate_Or_Resize
|
||||
return .Unable_To_Allocate_Or_Resize
|
||||
}
|
||||
|
||||
header_bytes := transmute([size_of(image.BMP_Header)]u8)header
|
||||
@@ -131,7 +131,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
|
||||
if .info in options {
|
||||
options |= {.return_metadata, .do_not_decompress_image}
|
||||
options += {.return_metadata, .do_not_decompress_image}
|
||||
options -= {.info}
|
||||
}
|
||||
|
||||
@@ -735,7 +735,7 @@ destroy :: proc(img: ^Image) {
|
||||
|
||||
bytes.buffer_destroy(&img.pixels)
|
||||
if v, ok := img.metadata.(^image.BMP_Info); ok {
|
||||
free(v)
|
||||
free(v)
|
||||
}
|
||||
free(img)
|
||||
}
|
||||
|
||||
+41
-41
@@ -1313,55 +1313,55 @@ expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bo
|
||||
}
|
||||
|
||||
switch img.depth {
|
||||
case 8:
|
||||
switch img.channels {
|
||||
case 1: // Turn Gray into RGB
|
||||
out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
|
||||
case 8:
|
||||
switch img.channels {
|
||||
case 1: // Turn Gray into RGB
|
||||
out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
|
||||
|
||||
for p in img.pixels.buf {
|
||||
out[0] = p // Broadcast gray value into RGB components.
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
case 2: // Turn Gray + Alpha into RGBA
|
||||
inp := mem.slice_data_cast([]GA_Pixel, img.pixels.buf[:])
|
||||
out := mem.slice_data_cast([]RGBA_Pixel, buf.buf[:])
|
||||
|
||||
for p in inp {
|
||||
out[0].rgb = p.r // Gray component.
|
||||
out[0].a = p.g // Alpha component.
|
||||
}
|
||||
|
||||
case:
|
||||
unreachable()
|
||||
for p in img.pixels.buf {
|
||||
out[0] = p // Broadcast gray value into RGB components.
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
case 16:
|
||||
switch img.channels {
|
||||
case 1: // Turn Gray into RGB
|
||||
inp := mem.slice_data_cast([]u16, img.pixels.buf[:])
|
||||
out := mem.slice_data_cast([]RGB_Pixel_16, buf.buf[:])
|
||||
case 2: // Turn Gray + Alpha into RGBA
|
||||
inp := mem.slice_data_cast([]GA_Pixel, img.pixels.buf[:])
|
||||
out := mem.slice_data_cast([]RGBA_Pixel, buf.buf[:])
|
||||
|
||||
for p in inp {
|
||||
out[0] = p // Broadcast gray value into RGB components.
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
case 2: // Turn Gray + Alpha into RGBA
|
||||
inp := mem.slice_data_cast([]GA_Pixel_16, img.pixels.buf[:])
|
||||
out := mem.slice_data_cast([]RGBA_Pixel_16, buf.buf[:])
|
||||
|
||||
for p in inp {
|
||||
out[0].rgb = p.r // Gray component.
|
||||
out[0].a = p.g // Alpha component.
|
||||
}
|
||||
|
||||
case:
|
||||
unreachable()
|
||||
for p in inp {
|
||||
out[0].rgb = p.r // Gray component.
|
||||
out[0].a = p.g // Alpha component.
|
||||
}
|
||||
|
||||
case:
|
||||
unreachable()
|
||||
}
|
||||
|
||||
case 16:
|
||||
switch img.channels {
|
||||
case 1: // Turn Gray into RGB
|
||||
inp := mem.slice_data_cast([]u16, img.pixels.buf[:])
|
||||
out := mem.slice_data_cast([]RGB_Pixel_16, buf.buf[:])
|
||||
|
||||
for p in inp {
|
||||
out[0] = p // Broadcast gray value into RGB components.
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
case 2: // Turn Gray + Alpha into RGBA
|
||||
inp := mem.slice_data_cast([]GA_Pixel_16, img.pixels.buf[:])
|
||||
out := mem.slice_data_cast([]RGBA_Pixel_16, buf.buf[:])
|
||||
|
||||
for p in inp {
|
||||
out[0].rgb = p.r // Gray component.
|
||||
out[0].a = p.g // Alpha component.
|
||||
}
|
||||
|
||||
case:
|
||||
unreachable()
|
||||
}
|
||||
|
||||
case:
|
||||
unreachable()
|
||||
}
|
||||
|
||||
|
||||
|
||||
+24
-24
@@ -341,7 +341,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
options := options
|
||||
|
||||
if .info in options {
|
||||
options |= {.return_metadata, .do_not_decompress_image}
|
||||
options += {.return_metadata, .do_not_decompress_image}
|
||||
options -= {.info}
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
}
|
||||
|
||||
if .do_not_expand_channels in options {
|
||||
options |= {.do_not_expand_grayscale, .do_not_expand_indexed}
|
||||
options += {.do_not_expand_grayscale, .do_not_expand_indexed}
|
||||
}
|
||||
|
||||
if img == nil {
|
||||
@@ -535,28 +535,28 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
|
||||
ct := transmute(u8)info.header.color_type
|
||||
switch ct {
|
||||
case 3: // Indexed color
|
||||
if c.header.length != 1 {
|
||||
return {}, .BKGD_Invalid_Length
|
||||
}
|
||||
col := _plte.entries[c.data[0]]
|
||||
img.background = [3]u16{
|
||||
u16(col[0]) << 8 | u16(col[0]),
|
||||
u16(col[1]) << 8 | u16(col[1]),
|
||||
u16(col[2]) << 8 | u16(col[2]),
|
||||
}
|
||||
case 0, 4: // Grayscale, with and without Alpha
|
||||
if c.header.length != 2 {
|
||||
return {}, .BKGD_Invalid_Length
|
||||
}
|
||||
col := u16(mem.slice_data_cast([]u16be, c.data[:])[0])
|
||||
img.background = [3]u16{col, col, col}
|
||||
case 2, 6: // Color, with and without Alpha
|
||||
if c.header.length != 6 {
|
||||
return {}, .BKGD_Invalid_Length
|
||||
}
|
||||
col := mem.slice_data_cast([]u16be, c.data[:])
|
||||
img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])}
|
||||
case 3: // Indexed color
|
||||
if c.header.length != 1 {
|
||||
return {}, .BKGD_Invalid_Length
|
||||
}
|
||||
col := _plte.entries[c.data[0]]
|
||||
img.background = [3]u16{
|
||||
u16(col[0]) << 8 | u16(col[0]),
|
||||
u16(col[1]) << 8 | u16(col[1]),
|
||||
u16(col[2]) << 8 | u16(col[2]),
|
||||
}
|
||||
case 0, 4: // Grayscale, with and without Alpha
|
||||
if c.header.length != 2 {
|
||||
return {}, .BKGD_Invalid_Length
|
||||
}
|
||||
col := u16(mem.slice_data_cast([]u16be, c.data[:])[0])
|
||||
img.background = [3]u16{col, col, col}
|
||||
case 2, 6: // Color, with and without Alpha
|
||||
if c.header.length != 6 {
|
||||
return {}, .BKGD_Invalid_Length
|
||||
}
|
||||
col := mem.slice_data_cast([]u16be, c.data[:])
|
||||
img.background = [3]u16{u16(col[0]), u16(col[1]), u16(col[2])}
|
||||
}
|
||||
|
||||
case .tRNS:
|
||||
|
||||
@@ -139,15 +139,13 @@ save_to_buffer :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
|
||||
} else {
|
||||
// Write RGB literal
|
||||
output.buf[written] = u8(QOI_Opcode_Tag.RGB)
|
||||
pix_bytes := transmute([4]u8)pix
|
||||
copy(output.buf[written + 1:], pix_bytes[:3])
|
||||
copy(output.buf[written + 1:], pix[:3])
|
||||
written += 4
|
||||
}
|
||||
} else {
|
||||
// Write RGBA literal
|
||||
output.buf[written] = u8(QOI_Opcode_Tag.RGBA)
|
||||
pix_bytes := transmute([4]u8)pix
|
||||
copy(output.buf[written + 1:], pix_bytes[:])
|
||||
copy(output.buf[written + 1:], pix[:])
|
||||
written += 5
|
||||
}
|
||||
}
|
||||
@@ -178,7 +176,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
options := options
|
||||
|
||||
if .info in options {
|
||||
options |= {.return_metadata, .do_not_decompress_image}
|
||||
options += {.return_metadata, .do_not_decompress_image}
|
||||
options -= {.info}
|
||||
}
|
||||
|
||||
@@ -232,7 +230,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
bytes_needed := image.compute_buffer_size(int(header.width), int(header.height), img.channels, 8)
|
||||
|
||||
if resize(&img.pixels.buf, bytes_needed) != nil {
|
||||
return img, .Unable_To_Allocate_Or_Resize
|
||||
return img, .Unable_To_Allocate_Or_Resize
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -343,7 +341,7 @@ destroy :: proc(img: ^Image) {
|
||||
bytes.buffer_destroy(&img.pixels)
|
||||
|
||||
if v, ok := img.metadata.(^image.QOI_Info); ok {
|
||||
free(v)
|
||||
free(v)
|
||||
}
|
||||
free(img)
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
}
|
||||
|
||||
if .info in options {
|
||||
options |= {.return_metadata, .do_not_decompress_image}
|
||||
options += {.return_metadata, .do_not_decompress_image}
|
||||
options -= {.info}
|
||||
}
|
||||
|
||||
|
||||
+24
-24
@@ -787,8 +787,8 @@ _private_int_sqr_comba :: proc(dest, src: ^Int, allocator := context.allocator)
|
||||
/*
|
||||
Karatsuba squaring, computes `dest` = `src` * `src` using three half-size squarings.
|
||||
|
||||
See comments of `_private_int_mul_karatsuba` for details.
|
||||
It is essentially the same algorithm but merely tuned to perform recursive squarings.
|
||||
See comments of `_private_int_mul_karatsuba` for details.
|
||||
It is essentially the same algorithm but merely tuned to perform recursive squarings.
|
||||
*/
|
||||
_private_int_sqr_karatsuba :: proc(dest, src: ^Int, allocator := context.allocator) -> (err: Error) {
|
||||
context.allocator = allocator
|
||||
@@ -967,7 +967,7 @@ _private_int_div_3 :: proc(quotient, numerator: ^Int, allocator := context.alloc
|
||||
/*
|
||||
b = 2^_DIGIT_BITS / 3
|
||||
*/
|
||||
b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3)
|
||||
b := _WORD(1) << _WORD(_DIGIT_BITS) / _WORD(3)
|
||||
|
||||
q := &Int{}
|
||||
internal_grow(q, numerator.used) or_return
|
||||
@@ -975,7 +975,7 @@ _private_int_div_3 :: proc(quotient, numerator: ^Int, allocator := context.alloc
|
||||
q.sign = numerator.sign
|
||||
|
||||
w, t: _WORD
|
||||
#no_bounds_check for ix := numerator.used; ix >= 0; ix -= 1 {
|
||||
#no_bounds_check for ix := numerator.used - 1; ix >= 0; ix -= 1 {
|
||||
w = (w << _WORD(_DIGIT_BITS)) | _WORD(numerator.digit[ix])
|
||||
if w >= 3 {
|
||||
/*
|
||||
@@ -1007,8 +1007,8 @@ _private_int_div_3 :: proc(quotient, numerator: ^Int, allocator := context.alloc
|
||||
*/
|
||||
if quotient != nil {
|
||||
err = clamp(q)
|
||||
internal_swap(q, quotient)
|
||||
}
|
||||
internal_swap(q, quotient)
|
||||
}
|
||||
internal_destroy(q)
|
||||
return remainder, nil
|
||||
}
|
||||
@@ -1555,24 +1555,24 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int, allocator := context.
|
||||
|
||||
/*
|
||||
If neither `a` or `b` was zero, we need to compute `gcd`.
|
||||
Get copies of `a` and `b` we can modify.
|
||||
*/
|
||||
Get copies of `a` and `b` we can modify.
|
||||
*/
|
||||
u, v := &Int{}, &Int{}
|
||||
defer internal_destroy(u, v)
|
||||
internal_copy(u, a) or_return
|
||||
internal_copy(v, b) or_return
|
||||
|
||||
/*
|
||||
Must be positive for the remainder of the algorithm.
|
||||
*/
|
||||
/*
|
||||
Must be positive for the remainder of the algorithm.
|
||||
*/
|
||||
u.sign = .Zero_or_Positive; v.sign = .Zero_or_Positive
|
||||
|
||||
/*
|
||||
B1. Find the common power of two for `u` and `v`.
|
||||
*/
|
||||
u_lsb, _ := internal_count_lsb(u)
|
||||
v_lsb, _ := internal_count_lsb(v)
|
||||
k := min(u_lsb, v_lsb)
|
||||
/*
|
||||
B1. Find the common power of two for `u` and `v`.
|
||||
*/
|
||||
u_lsb, _ := internal_count_lsb(u)
|
||||
v_lsb, _ := internal_count_lsb(v)
|
||||
k := min(u_lsb, v_lsb)
|
||||
|
||||
if k > 0 {
|
||||
/*
|
||||
@@ -1615,11 +1615,11 @@ _private_int_gcd_lcm :: proc(res_gcd, res_lcm, a, b: ^Int, allocator := context.
|
||||
internal_shr(v, v, b) or_return
|
||||
}
|
||||
|
||||
/*
|
||||
Multiply by 2**k which we divided out at the beginning.
|
||||
*/
|
||||
internal_shl(temp_gcd_res, u, k) or_return
|
||||
temp_gcd_res.sign = .Zero_or_Positive
|
||||
/*
|
||||
Multiply by 2**k which we divided out at the beginning.
|
||||
*/
|
||||
internal_shl(temp_gcd_res, u, k) or_return
|
||||
temp_gcd_res.sign = .Zero_or_Positive
|
||||
|
||||
/*
|
||||
We've computed `gcd`, either the long way, or because one of the inputs was zero.
|
||||
@@ -1786,8 +1786,8 @@ _private_montgomery_reduce_comba :: proc(x, n: ^Int, rho: DIGIT, allocator := co
|
||||
`a = a + mu * m * b**i`
|
||||
|
||||
This is computed in place and on the fly. The multiplication
|
||||
by b**i is handled by offseting which columns the results
|
||||
are added to.
|
||||
by b**i is handled by offseting which columns the results
|
||||
are added to.
|
||||
|
||||
Note the comba method normally doesn't handle carries in the
|
||||
inner loop In this case we fix the carry from the previous
|
||||
|
||||
@@ -3,7 +3,7 @@ package linalg
|
||||
import "core:math"
|
||||
import "base:builtin"
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
@require import "base:runtime"
|
||||
|
||||
// Generic
|
||||
|
||||
|
||||
@@ -637,22 +637,20 @@ _internal_noise_4d_unskewed_base :: proc(seed: i64, coord: Vec4) -> (value: f32)
|
||||
|
||||
// Next point is the closest vertex on the 4-simplex whose base vertex is the aforementioned vertex.
|
||||
score := 1.0 + ssi * (-1.0 / UNSKEW_4D) // Seems slightly faster than 1.0-xsi-ysi-zsi-wsi
|
||||
if si.x >= si.x && si.x >= si.z && si.x >= si.w && si.x >= score {
|
||||
switch {
|
||||
case si.x >= si.x && si.x >= si.z && si.x >= si.w && si.x >= score:
|
||||
svp.x += PRIME_X
|
||||
si.x -= 1
|
||||
ssi -= UNSKEW_4D
|
||||
}
|
||||
else if si.y > si.x && si.y >= si.z && si.y >= si.w && si.y >= score {
|
||||
case si.y > si.x && si.y >= si.z && si.y >= si.w && si.y >= score:
|
||||
svp.y += PRIME_Y
|
||||
si.y -= 1
|
||||
ssi -= UNSKEW_4D
|
||||
}
|
||||
else if si.z > si.x && si.z > si.y && si.z >= si.w && si.z >= score {
|
||||
case si.z > si.x && si.z > si.y && si.z >= si.w && si.z >= score:
|
||||
svp.z += PRIME_Z
|
||||
si.z -= 1
|
||||
ssi -= UNSKEW_4D
|
||||
}
|
||||
else if si.w > si.x && si.w > si.y && si.w > si.z && si.w >= score {
|
||||
case si.w > si.x && si.w > si.y && si.w > si.z && si.w >= score:
|
||||
svp.w += PRIME_W
|
||||
si.w -= 1
|
||||
ssi -= UNSKEW_4D
|
||||
|
||||
@@ -655,7 +655,7 @@ choice_enum :: proc($T: typeid) -> T
|
||||
where
|
||||
intrinsics.type_is_enum(T),
|
||||
size_of(T) <= 8,
|
||||
len(T) == cap(T) /* Only allow contiguous enum types */
|
||||
len(T) == cap(T) /* Only allow contiguous enum types */ \
|
||||
{
|
||||
when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
|
||||
u64(max(T)) > u64(max(i64)) {
|
||||
|
||||
@@ -97,6 +97,10 @@ init :: proc{init_from_buffer, init_from_allocator}
|
||||
destroy :: proc(control: ^Allocator) {
|
||||
if control == nil { return }
|
||||
|
||||
if control.pool.allocator.procedure != nil {
|
||||
runtime.delete(control.pool.data, control.pool.allocator)
|
||||
}
|
||||
|
||||
// No need to call `pool_remove` or anything, as they're they're embedded in the backing memory.
|
||||
// We do however need to free the `Pool` tracking entities and the backing memory itself.
|
||||
// As `Allocator` is embedded in the first backing slice, the `control` pointer will be
|
||||
@@ -132,8 +136,9 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
return nil, nil
|
||||
|
||||
case .Free_All:
|
||||
clear(control)
|
||||
return nil, nil
|
||||
// NOTE: this doesn't work right at the moment, Jeroen has it on his to-do list :)
|
||||
// clear(control)
|
||||
return nil, .Mode_Not_Implemented
|
||||
|
||||
case .Resize:
|
||||
return resize(control, old_memory, uint(old_size), uint(size), uint(alignment))
|
||||
@@ -144,7 +149,7 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^runtime.Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, /* .Free_All, */ .Resize, .Resize_Non_Zeroed, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -153,4 +158,4 @@ allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ block_next :: proc(block: ^Block_Header) -> (next: ^Block_Header) {
|
||||
block_link_next :: proc(block: ^Block_Header) -> (next: ^Block_Header) {
|
||||
next = block_next(block)
|
||||
next.prev_phys_block = block
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
block_mark_as_free :: proc(block: ^Block_Header) {
|
||||
@@ -630,7 +630,7 @@ alloc_bytes_non_zeroed :: proc(control: ^Allocator, size: uint, align: uint) ->
|
||||
@(require_results)
|
||||
alloc_bytes :: proc(control: ^Allocator, size: uint, align: uint) -> (res: []byte, err: runtime.Allocator_Error) {
|
||||
res, err = alloc_bytes_non_zeroed(control, size, align)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
intrinsics.mem_zero(raw_data(res), len(res))
|
||||
}
|
||||
return
|
||||
@@ -735,4 +735,4 @@ resize_non_zeroed :: proc(control: ^Allocator, ptr: rawptr, old_size, new_size:
|
||||
block_trim_used(control, block, adjust)
|
||||
res = ([^]byte)(ptr)[:new_size]
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,9 @@ _release :: proc "contextless" (data: rawptr, size: uint) {
|
||||
_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
||||
pflags: linux.Mem_Protection
|
||||
pflags = {}
|
||||
if .Read in flags { pflags |= {.READ} }
|
||||
if .Write in flags { pflags |= {.WRITE} }
|
||||
if .Execute in flags { pflags |= {.EXEC} }
|
||||
if .Read in flags { pflags += {.READ} }
|
||||
if .Write in flags { pflags += {.WRITE} }
|
||||
if .Execute in flags { pflags += {.EXEC} }
|
||||
errno := linux.mprotect(data, size, pflags)
|
||||
return errno == .NONE
|
||||
}
|
||||
|
||||
+3
-4
@@ -370,7 +370,7 @@ parse_ip6_address :: proc(address_and_maybe_port: string) -> (addr: IP6_Address,
|
||||
val |= u16(ipv4[3])
|
||||
piece_values[7] = u16be(val)
|
||||
}
|
||||
return transmute(IP6_Address)piece_values, true
|
||||
return IP6_Address(piece_values), true
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -522,10 +522,9 @@ address_to_string :: proc(addr: Address, allocator := context.temp_allocator) ->
|
||||
run := Zero_Run{-1, -1}
|
||||
best := Zero_Run{-1, -1}
|
||||
|
||||
addr := transmute([8]u16be)v
|
||||
|
||||
last := u16be(1)
|
||||
for val, i in addr {
|
||||
for val, i in v {
|
||||
/*
|
||||
If we encounter adjacent zeroes, then start a new run if not already in one.
|
||||
Also remember the rightmost index regardless, because it'll be the new
|
||||
@@ -559,7 +558,7 @@ address_to_string :: proc(addr: Address, allocator := context.temp_allocator) ->
|
||||
last = val
|
||||
}
|
||||
|
||||
for val, i in addr {
|
||||
for val, i in v {
|
||||
if best.start == i || best.end == i {
|
||||
// For the left and right side of the best zero run, print a `:`.
|
||||
fmt.sbprint(&b, ":")
|
||||
|
||||
@@ -135,6 +135,7 @@ TCP_Send_Error :: enum c.int {
|
||||
No_Buffer_Space_Available = win.WSAENOBUFS,
|
||||
Network_Subsystem_Failure = win.WSAENETDOWN,
|
||||
Host_Unreachable = win.WSAEHOSTUNREACH,
|
||||
Would_Block = win.WSAEWOULDBLOCK,
|
||||
|
||||
// TODO: verify possible, as not mentioned in docs
|
||||
Offline = win.WSAENETUNREACH,
|
||||
|
||||
@@ -59,24 +59,21 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
|
||||
switch int(ifaddr.address.family) {
|
||||
case os.AF_INET, os.AF_INET6:
|
||||
address = _sockaddr_basic_to_endpoint(ifaddr.address).address
|
||||
case:
|
||||
}
|
||||
}
|
||||
|
||||
if ifaddr.netmask != nil {
|
||||
switch int(ifaddr.netmask.family) {
|
||||
case os.AF_INET, os.AF_INET6:
|
||||
netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address)
|
||||
case:
|
||||
netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address)
|
||||
}
|
||||
}
|
||||
|
||||
if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags {
|
||||
switch int(ifaddr.broadcast_or_dest.family) {
|
||||
case os.AF_INET, os.AF_INET6:
|
||||
broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address
|
||||
append(&iface.multicast, broadcast)
|
||||
case:
|
||||
broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address
|
||||
append(&iface.multicast, broadcast)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,19 +88,19 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
|
||||
/*
|
||||
TODO: Refine this based on the type of adapter.
|
||||
*/
|
||||
state := Link_State{}
|
||||
state := Link_State{}
|
||||
|
||||
if .UP in ifaddr.flags {
|
||||
state |= {.Up}
|
||||
}
|
||||
if .UP in ifaddr.flags {
|
||||
state += {.Up}
|
||||
}
|
||||
|
||||
/*if .DORMANT in ifaddr.flags {
|
||||
state |= {.Dormant}
|
||||
}*/
|
||||
/*if .DORMANT in ifaddr.flags {
|
||||
state |= {.Dormant}
|
||||
}*/
|
||||
|
||||
if .LOOPBACK in ifaddr.flags {
|
||||
state |= {.Loopback}
|
||||
}
|
||||
if .LOOPBACK in ifaddr.flags {
|
||||
state += {.Loopback}
|
||||
}
|
||||
iface.link.state = state
|
||||
}
|
||||
|
||||
|
||||
@@ -24,42 +24,42 @@ import strings "core:strings"
|
||||
_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
buf: []u8
|
||||
defer delete(buf)
|
||||
buf: []u8
|
||||
defer delete(buf)
|
||||
|
||||
buf_size: u32
|
||||
res: u32
|
||||
buf_size: u32
|
||||
res: u32
|
||||
|
||||
gaa: for _ in 1..=MAX_INTERFACE_ENUMERATION_TRIES {
|
||||
res = sys.get_adapters_addresses(
|
||||
.Unspecified, // Return both IPv4 and IPv6 adapters.
|
||||
gaa: for _ in 1..=MAX_INTERFACE_ENUMERATION_TRIES {
|
||||
res = sys.get_adapters_addresses(
|
||||
.Unspecified, // Return both IPv4 and IPv6 adapters.
|
||||
sys.GAA_Flags{
|
||||
.Include_Prefix, // (XP SP1+) Return a list of IP address prefixes on this adapter. When this flag is set, IP address prefixes are returned for both IPv6 and IPv4 addresses.
|
||||
.Include_Gateways, // (Vista+) Return the addresses of default gateways.
|
||||
.Include_Tunnel_Binding_Order, // (Vista+) Return the adapter addresses sorted in tunnel binding order.
|
||||
},
|
||||
nil, // Reserved
|
||||
(^sys.IP_Adapter_Addresses)(raw_data(buf)),
|
||||
&buf_size,
|
||||
)
|
||||
nil, // Reserved
|
||||
(^sys.IP_Adapter_Addresses)(raw_data(buf)),
|
||||
&buf_size,
|
||||
)
|
||||
|
||||
switch res {
|
||||
case 111: // ERROR_BUFFER_OVERFLOW:
|
||||
delete(buf)
|
||||
buf = make([]u8, buf_size)
|
||||
case 0:
|
||||
break gaa
|
||||
case:
|
||||
return {}, Platform_Error(res)
|
||||
}
|
||||
}
|
||||
switch res {
|
||||
case 111: // ERROR_BUFFER_OVERFLOW:
|
||||
delete(buf)
|
||||
buf = make([]u8, buf_size)
|
||||
case 0:
|
||||
break gaa
|
||||
case:
|
||||
return {}, Platform_Error(res)
|
||||
}
|
||||
}
|
||||
|
||||
if res != 0 {
|
||||
return {}, .Unable_To_Enumerate_Network_Interfaces
|
||||
}
|
||||
if res != 0 {
|
||||
return {}, .Unable_To_Enumerate_Network_Interfaces
|
||||
}
|
||||
|
||||
_interfaces := make([dynamic]Network_Interface, 0, allocator)
|
||||
for adapter := (^sys.IP_Adapter_Addresses)(raw_data(buf)); adapter != nil; adapter = adapter.Next {
|
||||
_interfaces := make([dynamic]Network_Interface, 0, allocator)
|
||||
for adapter := (^sys.IP_Adapter_Addresses)(raw_data(buf)); adapter != nil; adapter = adapter.Next {
|
||||
friendly_name, err1 := sys.wstring_to_utf8(sys.wstring(adapter.FriendlyName), 256, allocator)
|
||||
if err1 != nil { return {}, Platform_Error(err1) }
|
||||
|
||||
@@ -71,74 +71,74 @@ _enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []
|
||||
|
||||
interface := Network_Interface{
|
||||
adapter_name = strings.clone(string(adapter.AdapterName)),
|
||||
friendly_name = friendly_name,
|
||||
description = description,
|
||||
dns_suffix = dns_suffix,
|
||||
friendly_name = friendly_name,
|
||||
description = description,
|
||||
dns_suffix = dns_suffix,
|
||||
|
||||
mtu = adapter.MTU,
|
||||
mtu = adapter.MTU,
|
||||
|
||||
link = {
|
||||
link = {
|
||||
transmit_speed = adapter.TransmitLinkSpeed,
|
||||
receive_speed = adapter.ReceiveLinkSpeed,
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
if adapter.PhysicalAddressLength > 0 && adapter.PhysicalAddressLength <= len(adapter.PhysicalAddress) {
|
||||
interface.physical_address = physical_address_to_string(adapter.PhysicalAddress[:adapter.PhysicalAddressLength])
|
||||
}
|
||||
if adapter.PhysicalAddressLength > 0 && adapter.PhysicalAddressLength <= len(adapter.PhysicalAddress) {
|
||||
interface.physical_address = physical_address_to_string(adapter.PhysicalAddress[:adapter.PhysicalAddressLength])
|
||||
}
|
||||
|
||||
for u_addr := (^sys.IP_ADAPTER_UNICAST_ADDRESS_LH)(adapter.FirstUnicastAddress); u_addr != nil; u_addr = u_addr.Next {
|
||||
win_addr := parse_socket_address(u_addr.Address)
|
||||
for u_addr := (^sys.IP_ADAPTER_UNICAST_ADDRESS_LH)(adapter.FirstUnicastAddress); u_addr != nil; u_addr = u_addr.Next {
|
||||
win_addr := parse_socket_address(u_addr.Address)
|
||||
|
||||
lease := Lease{
|
||||
address = win_addr.address,
|
||||
origin = {
|
||||
prefix = Prefix_Origin(u_addr.PrefixOrigin),
|
||||
suffix = Suffix_Origin(u_addr.SuffixOrigin),
|
||||
},
|
||||
lifetime = {
|
||||
valid = u_addr.ValidLifetime,
|
||||
preferred = u_addr.PreferredLifetime,
|
||||
lease = u_addr.LeaseLifetime,
|
||||
},
|
||||
address_duplication = Address_Duplication(u_addr.DadState),
|
||||
}
|
||||
append(&interface.unicast, lease)
|
||||
}
|
||||
lease := Lease{
|
||||
address = win_addr.address,
|
||||
origin = {
|
||||
prefix = Prefix_Origin(u_addr.PrefixOrigin),
|
||||
suffix = Suffix_Origin(u_addr.SuffixOrigin),
|
||||
},
|
||||
lifetime = {
|
||||
valid = u_addr.ValidLifetime,
|
||||
preferred = u_addr.PreferredLifetime,
|
||||
lease = u_addr.LeaseLifetime,
|
||||
},
|
||||
address_duplication = Address_Duplication(u_addr.DadState),
|
||||
}
|
||||
append(&interface.unicast, lease)
|
||||
}
|
||||
|
||||
for a_addr := (^sys.IP_ADAPTER_ANYCAST_ADDRESS_XP)(adapter.FirstAnycastAddress); a_addr != nil; a_addr = a_addr.Next {
|
||||
addr := parse_socket_address(a_addr.Address)
|
||||
append(&interface.anycast, addr.address)
|
||||
}
|
||||
for a_addr := (^sys.IP_ADAPTER_ANYCAST_ADDRESS_XP)(adapter.FirstAnycastAddress); a_addr != nil; a_addr = a_addr.Next {
|
||||
addr := parse_socket_address(a_addr.Address)
|
||||
append(&interface.anycast, addr.address)
|
||||
}
|
||||
|
||||
for m_addr := (^sys.IP_ADAPTER_MULTICAST_ADDRESS_XP)(adapter.FirstMulticastAddress); m_addr != nil; m_addr = m_addr.Next {
|
||||
addr := parse_socket_address(m_addr.Address)
|
||||
append(&interface.multicast, addr.address)
|
||||
}
|
||||
for m_addr := (^sys.IP_ADAPTER_MULTICAST_ADDRESS_XP)(adapter.FirstMulticastAddress); m_addr != nil; m_addr = m_addr.Next {
|
||||
addr := parse_socket_address(m_addr.Address)
|
||||
append(&interface.multicast, addr.address)
|
||||
}
|
||||
|
||||
for g_addr := (^sys.IP_ADAPTER_GATEWAY_ADDRESS_LH)(adapter.FirstGatewayAddress); g_addr != nil; g_addr = g_addr.Next {
|
||||
addr := parse_socket_address(g_addr.Address)
|
||||
append(&interface.gateways, addr.address)
|
||||
}
|
||||
for g_addr := (^sys.IP_ADAPTER_GATEWAY_ADDRESS_LH)(adapter.FirstGatewayAddress); g_addr != nil; g_addr = g_addr.Next {
|
||||
addr := parse_socket_address(g_addr.Address)
|
||||
append(&interface.gateways, addr.address)
|
||||
}
|
||||
|
||||
interface.dhcp_v4 = parse_socket_address(adapter.Dhcpv4Server).address
|
||||
interface.dhcp_v6 = parse_socket_address(adapter.Dhcpv6Server).address
|
||||
|
||||
switch adapter.OperStatus {
|
||||
case .Up: interface.link.state = {.Up}
|
||||
case .Down: interface.link.state = {.Down}
|
||||
case .Testing: interface.link.state = {.Testing}
|
||||
case .Dormant: interface.link.state = {.Dormant}
|
||||
case .NotPresent: interface.link.state = {.Not_Present}
|
||||
case .LowerLayerDown: interface.link.state = {.Lower_Layer_Down}
|
||||
case .Unknown: fallthrough
|
||||
case: interface.link.state = {}
|
||||
}
|
||||
switch adapter.OperStatus {
|
||||
case .Up: interface.link.state = {.Up}
|
||||
case .Down: interface.link.state = {.Down}
|
||||
case .Testing: interface.link.state = {.Testing}
|
||||
case .Dormant: interface.link.state = {.Dormant}
|
||||
case .NotPresent: interface.link.state = {.Not_Present}
|
||||
case .LowerLayerDown: interface.link.state = {.Lower_Layer_Down}
|
||||
case .Unknown: fallthrough
|
||||
case: interface.link.state = {}
|
||||
}
|
||||
|
||||
interface.tunnel_type = Tunnel_Type(adapter.TunnelType)
|
||||
interface.tunnel_type = Tunnel_Type(adapter.TunnelType)
|
||||
|
||||
append(&_interfaces, interface)
|
||||
}
|
||||
append(&_interfaces, interface)
|
||||
}
|
||||
|
||||
return _interfaces[:], {}
|
||||
}
|
||||
|
||||
@@ -274,8 +274,7 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
|
||||
.Linger,
|
||||
.Send_Timeout,
|
||||
.Receive_Timeout:
|
||||
t, ok := value.(time.Duration)
|
||||
if !ok do panic("set_option() value must be a time.Duration here", loc)
|
||||
t := value.(time.Duration) or_else panic("set_option() value must be a time.Duration here", loc)
|
||||
|
||||
micros := i64(time.duration_microseconds(t))
|
||||
timeval_value.microseconds = int(micros % 1e6)
|
||||
@@ -320,7 +319,7 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
|
||||
}
|
||||
|
||||
if should_block {
|
||||
flags &= ~int(os.O_NONBLOCK)
|
||||
flags &~= int(os.O_NONBLOCK)
|
||||
} else {
|
||||
flags |= int(os.O_NONBLOCK)
|
||||
}
|
||||
|
||||
@@ -80,14 +80,14 @@ _unwrap_os_addr :: proc "contextless" (endpoint: Endpoint)->(linux.Sock_Addr_Any
|
||||
ipv4 = {
|
||||
sin_family = .INET,
|
||||
sin_port = u16be(endpoint.port),
|
||||
sin_addr = transmute([4]u8) endpoint.address.(IP4_Address),
|
||||
sin_addr = ([4]u8)(endpoint.address.(IP4_Address)),
|
||||
},
|
||||
}
|
||||
case IP6_Address:
|
||||
return {
|
||||
ipv6 = {
|
||||
sin6_port = u16be(endpoint.port),
|
||||
sin6_addr = transmute([16]u8) endpoint.address.(IP6_Address),
|
||||
sin6_addr = transmute([16]u8)endpoint.address.(IP6_Address),
|
||||
sin6_family = .INET6,
|
||||
},
|
||||
}
|
||||
@@ -377,9 +377,9 @@ _set_blocking :: proc(sock: Any_Socket, should_block: bool) -> (err: Network_Err
|
||||
return Set_Blocking_Error(errno)
|
||||
}
|
||||
if should_block {
|
||||
flags &= ~{.NONBLOCK}
|
||||
flags -= {.NONBLOCK}
|
||||
} else {
|
||||
flags |= {.NONBLOCK}
|
||||
flags += {.NONBLOCK}
|
||||
}
|
||||
errno = linux.fcntl(os_sock, linux.F_SETFL, flags)
|
||||
if errno != .NONE {
|
||||
|
||||
@@ -263,12 +263,15 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
|
||||
ptr = &bool_value
|
||||
len = size_of(bool_value)
|
||||
case .Linger:
|
||||
t, ok := value.(time.Duration)
|
||||
if !ok do panic("set_option() value must be a time.Duration here", loc)
|
||||
t := value.(time.Duration) or_else panic("set_option() value must be a time.Duration here", loc)
|
||||
|
||||
num_secs := i64(time.duration_seconds(t))
|
||||
if time.Duration(num_secs * 1e9) != t do return .Linger_Only_Supports_Whole_Seconds
|
||||
if num_secs > i64(max(u16)) do return .Value_Out_Of_Range
|
||||
if time.Duration(num_secs * 1e9) != t {
|
||||
return .Linger_Only_Supports_Whole_Seconds
|
||||
}
|
||||
if num_secs > i64(max(u16)) {
|
||||
return .Value_Out_Of_Range
|
||||
}
|
||||
linger_value.l_onoff = 1
|
||||
linger_value.l_linger = c.ushort(num_secs)
|
||||
|
||||
@@ -277,8 +280,7 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
|
||||
case
|
||||
.Receive_Timeout,
|
||||
.Send_Timeout:
|
||||
t, ok := value.(time.Duration)
|
||||
if !ok do panic("set_option() value must be a time.Duration here", loc)
|
||||
t := value.(time.Duration) or_else panic("set_option() value must be a time.Duration here", loc)
|
||||
|
||||
int_value = i32(time.duration_milliseconds(t))
|
||||
ptr = &int_value
|
||||
|
||||
@@ -513,6 +513,7 @@ Package_Decl :: struct {
|
||||
Import_Decl :: struct {
|
||||
using node: Decl,
|
||||
docs: ^Comment_Group,
|
||||
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
|
||||
is_using: bool,
|
||||
import_tok: tokenizer.Token,
|
||||
name: tokenizer.Token,
|
||||
|
||||
@@ -308,7 +308,7 @@ consume_comment_group :: proc(p: ^Parser, n: int) -> (comments: ^ast.Comment_Gro
|
||||
end_line = p.curr_tok.pos.line
|
||||
for p.curr_tok.kind == .Comment &&
|
||||
p.curr_tok.pos.line <= end_line+n {
|
||||
comment: tokenizer.Token
|
||||
comment: tokenizer.Token
|
||||
comment, end_line = consume_comment(p)
|
||||
append(&list, comment)
|
||||
}
|
||||
@@ -689,7 +689,12 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
|
||||
|
||||
prev_level := p.expr_level
|
||||
p.expr_level = -1
|
||||
prev_allow_in_expr := p.allow_in_expr
|
||||
p.allow_in_expr = true
|
||||
|
||||
cond = parse_expr(p, false)
|
||||
|
||||
p.allow_in_expr = prev_allow_in_expr
|
||||
p.expr_level = prev_level
|
||||
|
||||
if cond == nil {
|
||||
@@ -1103,6 +1108,9 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
|
||||
case ^ast.Foreign_Import_Decl:
|
||||
if d.docs == nil { d.docs = docs }
|
||||
append(&d.attributes, attribute)
|
||||
case ^ast.Import_Decl:
|
||||
if d.docs == nil { d.docs = docs }
|
||||
append(&d.attributes, attribute)
|
||||
case:
|
||||
error(p, decl.pos, "expected a value or foreign declaration after an attribute")
|
||||
free(attribute)
|
||||
@@ -1307,8 +1315,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
|
||||
// Unary Expressions
|
||||
.Add, .Sub, .Xor, .Not, .And:
|
||||
|
||||
s := parse_simple_stmt(p, {Stmt_Allow_Flag.Label})
|
||||
expect_semicolon(p, s)
|
||||
s := parse_simple_stmt(p, {Stmt_Allow_Flag.Label})
|
||||
expect_semicolon(p, s)
|
||||
return s
|
||||
|
||||
|
||||
@@ -1845,7 +1853,7 @@ parse_ident_list :: proc(p: ^Parser, allow_poly_names: bool) -> []^ast.Expr {
|
||||
}
|
||||
if p.curr_tok.kind != .Comma ||
|
||||
p.curr_tok.kind == .EOF {
|
||||
break
|
||||
break
|
||||
}
|
||||
advance_token(p)
|
||||
}
|
||||
@@ -2208,10 +2216,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
|
||||
case .Integer, .Float, .Imag,
|
||||
.Rune, .String:
|
||||
tok := advance_token(p)
|
||||
bl := ast.new(ast.Basic_Lit, tok.pos, end_pos(tok))
|
||||
bl.tok = tok
|
||||
return bl
|
||||
tok := advance_token(p)
|
||||
bl := ast.new(ast.Basic_Lit, tok.pos, end_pos(tok))
|
||||
bl.tok = tok
|
||||
return bl
|
||||
|
||||
case .Open_Brace:
|
||||
if !lhs {
|
||||
@@ -2999,7 +3007,7 @@ parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit {
|
||||
}
|
||||
p.expr_level -= 1
|
||||
|
||||
skip_possible_newline(p)
|
||||
skip_possible_newline(p)
|
||||
close := expect_closing_brace_of_field_list(p)
|
||||
|
||||
pos := type.pos if type != nil else open.pos
|
||||
|
||||
+2
-2
@@ -111,7 +111,7 @@ read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator,
|
||||
length: i64
|
||||
err: Errno
|
||||
if length, err = file_size(fd); err != 0 {
|
||||
return nil, false
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if length <= 0 {
|
||||
@@ -120,7 +120,7 @@ read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator,
|
||||
|
||||
data = make([]byte, int(length), allocator, loc)
|
||||
if data == nil {
|
||||
return nil, false
|
||||
return nil, false
|
||||
}
|
||||
|
||||
bytes_read, read_err := read_full(fd, data)
|
||||
|
||||
+210
-9
@@ -2,29 +2,230 @@
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
|
||||
import "core:sync"
|
||||
import "core:slice"
|
||||
import "core:strings"
|
||||
|
||||
// TODO: IF NO_CRT:
|
||||
// Override the libc environment functions' weak linkage to
|
||||
// allow us to interact with 3rd party code that DOES link
|
||||
// to libc. Otherwise, our environment can be out of sync.
|
||||
// ELSE:
|
||||
// Just use the libc.
|
||||
|
||||
NOT_FOUND :: -1
|
||||
|
||||
// the environment is a 0 delimited list of <key>=<value> strings
|
||||
_env: [dynamic]string
|
||||
|
||||
_env_mutex: sync.Mutex
|
||||
|
||||
// We need to be able to figure out if the environment variable
|
||||
// is contained in the original environment or not. This also
|
||||
// serves as a flag to determine if we have built _env.
|
||||
_org_env_begin: uintptr
|
||||
_org_env_end: uintptr
|
||||
|
||||
// Returns value + index location into _env
|
||||
// or -1 if not found
|
||||
_lookup :: proc(key: string) -> (value: string, idx: int) {
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
defer sync.mutex_unlock(&_env_mutex)
|
||||
|
||||
for entry, i in _env {
|
||||
if k, v := _kv_from_entry(entry); k == key {
|
||||
return v, i
|
||||
}
|
||||
}
|
||||
return "", -1
|
||||
}
|
||||
|
||||
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
//TODO
|
||||
if _org_env_begin == 0 {
|
||||
_build_env()
|
||||
}
|
||||
|
||||
if v, idx := _lookup(key); idx != -1 {
|
||||
found = true
|
||||
value, _ = clone_string(v, allocator)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_set_env :: proc(key, value: string) -> bool {
|
||||
//TODO
|
||||
return false
|
||||
_set_env :: proc(key, v_new: string) -> bool {
|
||||
if _org_env_begin == 0 {
|
||||
_build_env()
|
||||
}
|
||||
|
||||
// all key values are stored as "key=value\x00"
|
||||
kv_size := len(key) + len(v_new) + 2
|
||||
if v_curr, idx := _lookup(key); idx != NOT_FOUND {
|
||||
if v_curr == v_new {
|
||||
return true
|
||||
}
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
defer sync.mutex_unlock(&_env_mutex)
|
||||
|
||||
unordered_remove(&_env, idx)
|
||||
|
||||
if !_is_in_org_env(v_curr) {
|
||||
// We allocated this key-value. Possibly resize and
|
||||
// overwrite the value only. Otherwise, treat as if it
|
||||
// wasn't in the environment in the first place.
|
||||
k_addr, v_addr := _kv_addr_from_val(v_curr, key)
|
||||
if len(v_new) > len(v_curr) {
|
||||
k_addr = ([^]u8)(heap_resize(k_addr, kv_size))
|
||||
if k_addr == nil {
|
||||
return false
|
||||
}
|
||||
v_addr = &k_addr[len(key) + 1]
|
||||
}
|
||||
intrinsics.mem_copy_non_overlapping(v_addr, raw_data(v_new), len(v_new))
|
||||
v_addr[len(v_new)] = 0
|
||||
|
||||
append(&_env, string(k_addr[:kv_size]))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
k_addr := ([^]u8)(heap_alloc(kv_size))
|
||||
if k_addr == nil {
|
||||
return false
|
||||
}
|
||||
intrinsics.mem_copy_non_overlapping(k_addr, raw_data(key), len(key))
|
||||
k_addr[len(key)] = '='
|
||||
|
||||
val_slice := k_addr[len(key) + 1:]
|
||||
intrinsics.mem_copy_non_overlapping(&val_slice[0], raw_data(v_new), len(v_new))
|
||||
val_slice[len(v_new)] = 0
|
||||
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
append(&_env, string(k_addr[:kv_size - 1]))
|
||||
sync.mutex_unlock(&_env_mutex)
|
||||
return true
|
||||
}
|
||||
|
||||
_unset_env :: proc(key: string) -> bool {
|
||||
//TODO
|
||||
return false
|
||||
if _org_env_begin == 0 {
|
||||
_build_env()
|
||||
}
|
||||
|
||||
v: string
|
||||
i: int
|
||||
if v, i = _lookup(key); i == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
unordered_remove(&_env, i)
|
||||
sync.mutex_unlock(&_env_mutex)
|
||||
|
||||
if _is_in_org_env(v) {
|
||||
return true
|
||||
}
|
||||
|
||||
// if we got this far, the envrionment variable
|
||||
// existed AND was allocated by us.
|
||||
k_addr, _ := _kv_addr_from_val(v, key)
|
||||
heap_free(k_addr)
|
||||
return true
|
||||
}
|
||||
|
||||
_clear_env :: proc() {
|
||||
//TODO
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
defer sync.mutex_unlock(&_env_mutex)
|
||||
|
||||
for kv in _env {
|
||||
if !_is_in_org_env(kv) {
|
||||
heap_free(raw_data(kv))
|
||||
}
|
||||
}
|
||||
clear(&_env)
|
||||
|
||||
// nothing resides in the original environment either
|
||||
_org_env_begin = ~uintptr(0)
|
||||
_org_env_end = ~uintptr(0)
|
||||
}
|
||||
|
||||
_environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
//TODO
|
||||
return nil
|
||||
if _org_env_begin == 0 {
|
||||
_build_env()
|
||||
}
|
||||
env := make([]string, len(_env), allocator)
|
||||
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
defer sync.mutex_unlock(&_env_mutex)
|
||||
for entry, i in _env {
|
||||
env[i], _ = clone_string(entry, allocator)
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
// The entire environment is stored as 0 terminated strings,
|
||||
// so there is no need to clone/free individual variables
|
||||
export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
|
||||
if _org_env_begin == 0 {
|
||||
// The environment has not been modified, so we can just
|
||||
// send the original environment
|
||||
org_env := _get_original_env()
|
||||
n: int
|
||||
for ; org_env[n] != nil; n += 1 {}
|
||||
return slice.clone(org_env[:n + 1], allocator)
|
||||
}
|
||||
|
||||
// NOTE: already terminated by nil pointer via + 1
|
||||
env := make([]cstring, len(_env) + 1, allocator)
|
||||
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
defer sync.mutex_unlock(&_env_mutex)
|
||||
for entry, i in _env {
|
||||
env[i] = cstring(raw_data(entry))
|
||||
}
|
||||
return env
|
||||
}
|
||||
|
||||
_build_env :: proc() {
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
defer sync.mutex_unlock(&_env_mutex)
|
||||
if _org_env_begin != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
_env = make(type_of(_env), heap_allocator())
|
||||
cstring_env := _get_original_env()
|
||||
_org_env_begin = uintptr(rawptr(cstring_env[0]))
|
||||
for i := 0; cstring_env[i] != nil; i += 1 {
|
||||
bytes := ([^]u8)(cstring_env[i])
|
||||
n := len(cstring_env[i])
|
||||
_org_env_end = uintptr(&bytes[n])
|
||||
append(&_env, string(bytes[:n]))
|
||||
}
|
||||
}
|
||||
|
||||
_get_original_env :: #force_inline proc() -> [^]cstring {
|
||||
// essentially &argv[argc] which should be a nil pointer!
|
||||
#no_bounds_check env: [^]cstring = &runtime.args__[len(runtime.args__)]
|
||||
assert(env[0] == nil)
|
||||
return &env[1]
|
||||
}
|
||||
|
||||
_kv_from_entry :: #force_inline proc(entry: string) -> (k, v: string) {
|
||||
eq_idx := strings.index_byte(entry, '=')
|
||||
if eq_idx == -1 {
|
||||
return entry, ""
|
||||
}
|
||||
return entry[:eq_idx], entry[eq_idx + 1:]
|
||||
}
|
||||
|
||||
_kv_addr_from_val :: #force_inline proc(val: string, key: string) -> ([^]u8, [^]u8) {
|
||||
v_addr := raw_data(val)
|
||||
k_addr := ([^]u8)(&v_addr[-(len(key) + 1)])
|
||||
return k_addr, v_addr
|
||||
}
|
||||
|
||||
_is_in_org_env :: #force_inline proc(env_data: string) -> bool {
|
||||
addr := uintptr(raw_data(env_data))
|
||||
return addr >= _org_env_begin && addr < _org_env_end
|
||||
}
|
||||
|
||||
@@ -99,3 +99,19 @@ error_string :: proc(ferr: Error) -> string {
|
||||
|
||||
return "unknown error"
|
||||
}
|
||||
|
||||
print_error :: proc(f: ^File, ferr: Error, msg: string) {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
err_str := error_string(ferr)
|
||||
|
||||
// msg + ": " + err_str + '\n'
|
||||
length := len(msg) + 2 + len(err_str) + 1
|
||||
buf := make([]u8, length, temp_allocator())
|
||||
|
||||
copy(buf, msg)
|
||||
buf[len(msg)] = ':'
|
||||
buf[len(msg) + 1] = ' '
|
||||
copy(buf[len(msg) + 2:], err_str)
|
||||
buf[length - 1] = '\n'
|
||||
write(f, buf)
|
||||
}
|
||||
|
||||
+154
-134
@@ -1,145 +1,165 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:sys/unix"
|
||||
import "core:sys/linux"
|
||||
|
||||
EPERM :: 1
|
||||
ENOENT :: 2
|
||||
ESRCH :: 3
|
||||
EINTR :: 4
|
||||
EIO :: 5
|
||||
ENXIO :: 6
|
||||
EBADF :: 9
|
||||
EAGAIN :: 11
|
||||
ENOMEM :: 12
|
||||
EACCES :: 13
|
||||
EFAULT :: 14
|
||||
EEXIST :: 17
|
||||
ENODEV :: 19
|
||||
ENOTDIR :: 20
|
||||
EISDIR :: 21
|
||||
EINVAL :: 22
|
||||
ENFILE :: 23
|
||||
EMFILE :: 24
|
||||
ETXTBSY :: 26
|
||||
EFBIG :: 27
|
||||
ENOSPC :: 28
|
||||
ESPIPE :: 29
|
||||
EROFS :: 30
|
||||
EPIPE :: 32
|
||||
ERANGE :: 34 /* Result too large */
|
||||
EDEADLK :: 35 /* Resource deadlock would occur */
|
||||
ENAMETOOLONG :: 36 /* File name too long */
|
||||
ENOLCK :: 37 /* No record locks available */
|
||||
ENOSYS :: 38 /* Invalid system call number */
|
||||
ENOTEMPTY :: 39 /* Directory not empty */
|
||||
ELOOP :: 40 /* Too many symbolic links encountered */
|
||||
EWOULDBLOCK :: EAGAIN /* Operation would block */
|
||||
ENOMSG :: 42 /* No message of desired type */
|
||||
EIDRM :: 43 /* Identifier removed */
|
||||
ECHRNG :: 44 /* Channel number out of range */
|
||||
EL2NSYNC :: 45 /* Level 2 not synchronized */
|
||||
EL3HLT :: 46 /* Level 3 halted */
|
||||
EL3RST :: 47 /* Level 3 reset */
|
||||
ELNRNG :: 48 /* Link number out of range */
|
||||
EUNATCH :: 49 /* Protocol driver not attached */
|
||||
ENOCSI :: 50 /* No CSI structure available */
|
||||
EL2HLT :: 51 /* Level 2 halted */
|
||||
EBADE :: 52 /* Invalid exchange */
|
||||
EBADR :: 53 /* Invalid request descriptor */
|
||||
EXFULL :: 54 /* Exchange full */
|
||||
ENOANO :: 55 /* No anode */
|
||||
EBADRQC :: 56 /* Invalid request code */
|
||||
EBADSLT :: 57 /* Invalid slot */
|
||||
EDEADLOCK :: EDEADLK
|
||||
EBFONT :: 59 /* Bad font file format */
|
||||
ENOSTR :: 60 /* Device not a stream */
|
||||
ENODATA :: 61 /* No data available */
|
||||
ETIME :: 62 /* Timer expired */
|
||||
ENOSR :: 63 /* Out of streams resources */
|
||||
ENONET :: 64 /* Machine is not on the network */
|
||||
ENOPKG :: 65 /* Package not installed */
|
||||
EREMOTE :: 66 /* Object is remote */
|
||||
ENOLINK :: 67 /* Link has been severed */
|
||||
EADV :: 68 /* Advertise error */
|
||||
ESRMNT :: 69 /* Srmount error */
|
||||
ECOMM :: 70 /* Communication error on send */
|
||||
EPROTO :: 71 /* Protocol error */
|
||||
EMULTIHOP :: 72 /* Multihop attempted */
|
||||
EDOTDOT :: 73 /* RFS specific error */
|
||||
EBADMSG :: 74 /* Not a data message */
|
||||
EOVERFLOW :: 75 /* Value too large for defined data type */
|
||||
ENOTUNIQ :: 76 /* Name not unique on network */
|
||||
EBADFD :: 77 /* File descriptor in bad state */
|
||||
EREMCHG :: 78 /* Remote address changed */
|
||||
ELIBACC :: 79 /* Can not access a needed shared library */
|
||||
ELIBBAD :: 80 /* Accessing a corrupted shared library */
|
||||
ELIBSCN :: 81 /* .lib section in a.out corrupted */
|
||||
ELIBMAX :: 82 /* Attempting to link in too many shared libraries */
|
||||
ELIBEXEC :: 83 /* Cannot exec a shared library directly */
|
||||
EILSEQ :: 84 /* Illegal byte sequence */
|
||||
ERESTART :: 85 /* Interrupted system call should be restarted */
|
||||
ESTRPIPE :: 86 /* Streams pipe error */
|
||||
EUSERS :: 87 /* Too many users */
|
||||
ENOTSOCK :: 88 /* Socket operation on non-socket */
|
||||
EDESTADDRREQ :: 89 /* Destination address required */
|
||||
EMSGSIZE :: 90 /* Message too long */
|
||||
EPROTOTYPE :: 91 /* Protocol wrong type for socket */
|
||||
ENOPROTOOPT :: 92 /* Protocol not available */
|
||||
EPROTONOSUPPORT:: 93 /* Protocol not supported */
|
||||
ESOCKTNOSUPPORT:: 94 /* Socket type not supported */
|
||||
EOPNOTSUPP :: 95 /* Operation not supported on transport endpoint */
|
||||
EPFNOSUPPORT :: 96 /* Protocol family not supported */
|
||||
EAFNOSUPPORT :: 97 /* Address family not supported by protocol */
|
||||
EADDRINUSE :: 98 /* Address already in use */
|
||||
EADDRNOTAVAIL :: 99 /* Cannot assign requested address */
|
||||
ENETDOWN :: 100 /* Network is down */
|
||||
ENETUNREACH :: 101 /* Network is unreachable */
|
||||
ENETRESET :: 102 /* Network dropped connection because of reset */
|
||||
ECONNABORTED :: 103 /* Software caused connection abort */
|
||||
ECONNRESET :: 104 /* Connection reset by peer */
|
||||
ENOBUFS :: 105 /* No buffer space available */
|
||||
EISCONN :: 106 /* Transport endpoint is already connected */
|
||||
ENOTCONN :: 107 /* Transport endpoint is not connected */
|
||||
ESHUTDOWN :: 108 /* Cannot send after transport endpoint shutdown */
|
||||
ETOOMANYREFS :: 109 /* Too many references: cannot splice */
|
||||
ETIMEDOUT :: 110 /* Connection timed out */
|
||||
ECONNREFUSED :: 111 /* Connection refused */
|
||||
EHOSTDOWN :: 112 /* Host is down */
|
||||
EHOSTUNREACH :: 113 /* No route to host */
|
||||
EALREADY :: 114 /* Operation already in progress */
|
||||
EINPROGRESS :: 115 /* Operation now in progress */
|
||||
ESTALE :: 116 /* Stale file handle */
|
||||
EUCLEAN :: 117 /* Structure needs cleaning */
|
||||
ENOTNAM :: 118 /* Not a XENIX named type file */
|
||||
ENAVAIL :: 119 /* No XENIX semaphores available */
|
||||
EISNAM :: 120 /* Is a named type file */
|
||||
EREMOTEIO :: 121 /* Remote I/O error */
|
||||
EDQUOT :: 122 /* Quota exceeded */
|
||||
ENOMEDIUM :: 123 /* No medium found */
|
||||
EMEDIUMTYPE :: 124 /* Wrong medium type */
|
||||
ECANCELED :: 125 /* Operation Canceled */
|
||||
ENOKEY :: 126 /* Required key not available */
|
||||
EKEYEXPIRED :: 127 /* Key has expired */
|
||||
EKEYREVOKED :: 128 /* Key has been revoked */
|
||||
EKEYREJECTED :: 129 /* Key was rejected by service */
|
||||
EOWNERDEAD :: 130 /* Owner died */
|
||||
ENOTRECOVERABLE:: 131 /* State not recoverable */
|
||||
ERFKILL :: 132 /* Operation not possible due to RF-kill */
|
||||
EHWPOISON :: 133 /* Memory page has hardware error */
|
||||
@(rodata)
|
||||
_errno_strings : [linux.Errno]string = {
|
||||
.NONE = "Success",
|
||||
.EPERM = "Operation not permitted",
|
||||
.ENOENT = "No such file or directory",
|
||||
.ESRCH = "No such process",
|
||||
.EINTR = "Interrupted system call",
|
||||
.EIO = "Input/output error",
|
||||
.ENXIO = "No such device or address",
|
||||
.E2BIG = "Argument list too long",
|
||||
.ENOEXEC = "Exec format error",
|
||||
.EBADF = "Bad file descriptor",
|
||||
.ECHILD = "No child processes",
|
||||
.EAGAIN = "Resource temporarily unavailable",
|
||||
.ENOMEM = "Cannot allocate memory",
|
||||
.EACCES = "Permission denied",
|
||||
.EFAULT = "Bad address",
|
||||
.ENOTBLK = "Block device required",
|
||||
.EBUSY = "Device or resource busy",
|
||||
.EEXIST = "File exists",
|
||||
.EXDEV = "Invalid cross-device link",
|
||||
.ENODEV = "No such device",
|
||||
.ENOTDIR = "Not a directory",
|
||||
.EISDIR = "Is a directory",
|
||||
.EINVAL = "Invalid argument",
|
||||
.ENFILE = "Too many open files in system",
|
||||
.EMFILE = "Too many open files",
|
||||
.ENOTTY = "Inappropriate ioctl for device",
|
||||
.ETXTBSY = "Text file busy",
|
||||
.EFBIG = "File too large",
|
||||
.ENOSPC = "No space left on device",
|
||||
.ESPIPE = "Illegal seek",
|
||||
.EROFS = "Read-only file system",
|
||||
.EMLINK = "Too many links",
|
||||
.EPIPE = "Broken pipe",
|
||||
.EDOM = "Numerical argument out of domain",
|
||||
.ERANGE = "Numerical result out of range",
|
||||
.EDEADLK = "Resource deadlock avoided",
|
||||
.ENAMETOOLONG = "File name too long",
|
||||
.ENOLCK = "No locks available",
|
||||
.ENOSYS = "Function not implemented",
|
||||
.ENOTEMPTY = "Directory not empty",
|
||||
.ELOOP = "Too many levels of symbolic links",
|
||||
.EUNKNOWN_41 = "Unknown Error (41)",
|
||||
.ENOMSG = "No message of desired type",
|
||||
.EIDRM = "Identifier removed",
|
||||
.ECHRNG = "Channel number out of range",
|
||||
.EL2NSYNC = "Level 2 not synchronized",
|
||||
.EL3HLT = "Level 3 halted",
|
||||
.EL3RST = "Level 3 reset",
|
||||
.ELNRNG = "Link number out of range",
|
||||
.EUNATCH = "Protocol driver not attached",
|
||||
.ENOCSI = "No CSI structure available",
|
||||
.EL2HLT = "Level 2 halted",
|
||||
.EBADE = "Invalid exchange",
|
||||
.EBADR = "Invalid request descriptor",
|
||||
.EXFULL = "Exchange full",
|
||||
.ENOANO = "No anode",
|
||||
.EBADRQC = "Invalid request code",
|
||||
.EBADSLT = "Invalid slot",
|
||||
.EUNKNOWN_58 = "Unknown Error (58)",
|
||||
.EBFONT = "Bad font file format",
|
||||
.ENOSTR = "Device not a stream",
|
||||
.ENODATA = "No data available",
|
||||
.ETIME = "Timer expired",
|
||||
.ENOSR = "Out of streams resources",
|
||||
.ENONET = "Machine is not on the network",
|
||||
.ENOPKG = "Package not installed",
|
||||
.EREMOTE = "Object is remote",
|
||||
.ENOLINK = "Link has been severed",
|
||||
.EADV = "Advertise error",
|
||||
.ESRMNT = "Srmount error",
|
||||
.ECOMM = "Communication error on send",
|
||||
.EPROTO = "Protocol error",
|
||||
.EMULTIHOP = "Multihop attempted",
|
||||
.EDOTDOT = "RFS specific error",
|
||||
.EBADMSG = "Bad message",
|
||||
.EOVERFLOW = "Value too large for defined data type",
|
||||
.ENOTUNIQ = "Name not unique on network",
|
||||
.EBADFD = "File descriptor in bad state",
|
||||
.EREMCHG = "Remote address changed",
|
||||
.ELIBACC = "Can not access a needed shared library",
|
||||
.ELIBBAD = "Accessing a corrupted shared library",
|
||||
.ELIBSCN = ".lib section in a.out corrupted",
|
||||
.ELIBMAX = "Attempting to link in too many shared libraries",
|
||||
.ELIBEXEC = "Cannot exec a shared library directly",
|
||||
.EILSEQ = "Invalid or incomplete multibyte or wide character",
|
||||
.ERESTART = "Interrupted system call should be restarted",
|
||||
.ESTRPIPE = "Streams pipe error",
|
||||
.EUSERS = "Too many users",
|
||||
.ENOTSOCK = "Socket operation on non-socket",
|
||||
.EDESTADDRREQ = "Destination address required",
|
||||
.EMSGSIZE = "Message too long",
|
||||
.EPROTOTYPE = "Protocol wrong type for socket",
|
||||
.ENOPROTOOPT = "Protocol not available",
|
||||
.EPROTONOSUPPORT = "Protocol not supported",
|
||||
.ESOCKTNOSUPPORT = "Socket type not supported",
|
||||
.EOPNOTSUPP = "Operation not supported",
|
||||
.EPFNOSUPPORT = "Protocol family not supported",
|
||||
.EAFNOSUPPORT = "Address family not supported by protocol",
|
||||
.EADDRINUSE = "Address already in use",
|
||||
.EADDRNOTAVAIL = "Cannot assign requested address",
|
||||
.ENETDOWN = "Network is down",
|
||||
.ENETUNREACH = "Network is unreachable",
|
||||
.ENETRESET = "Network dropped connection on reset",
|
||||
.ECONNABORTED = "Software caused connection abort",
|
||||
.ECONNRESET = "Connection reset by peer",
|
||||
.ENOBUFS = "No buffer space available",
|
||||
.EISCONN = "Transport endpoint is already connected",
|
||||
.ENOTCONN = "Transport endpoint is not connected",
|
||||
.ESHUTDOWN = "Cannot send after transport endpoint shutdown",
|
||||
.ETOOMANYREFS = "Too many references: cannot splice",
|
||||
.ETIMEDOUT = "Connection timed out",
|
||||
.ECONNREFUSED = "Connection refused",
|
||||
.EHOSTDOWN = "Host is down",
|
||||
.EHOSTUNREACH = "No route to host",
|
||||
.EALREADY = "Operation already in progress",
|
||||
.EINPROGRESS = "Operation now in progress",
|
||||
.ESTALE = "Stale file handle",
|
||||
.EUCLEAN = "Structure needs cleaning",
|
||||
.ENOTNAM = "Not a XENIX named type file",
|
||||
.ENAVAIL = "No XENIX semaphores available",
|
||||
.EISNAM = "Is a named type file",
|
||||
.EREMOTEIO = "Remote I/O error",
|
||||
.EDQUOT = "Disk quota exceeded",
|
||||
.ENOMEDIUM = "No medium found",
|
||||
.EMEDIUMTYPE = "Wrong medium type",
|
||||
.ECANCELED = "Operation canceled",
|
||||
.ENOKEY = "Required key not available",
|
||||
.EKEYEXPIRED = "Key has expired",
|
||||
.EKEYREVOKED = "Key has been revoked",
|
||||
.EKEYREJECTED = "Key was rejected by service",
|
||||
.EOWNERDEAD = "Owner died",
|
||||
.ENOTRECOVERABLE = "State not recoverable",
|
||||
.ERFKILL = "Operation not possible due to RF-kill",
|
||||
.EHWPOISON = "Memory page has hardware error",
|
||||
}
|
||||
|
||||
|
||||
_get_platform_error :: proc(errno: linux.Errno) -> Error {
|
||||
#partial switch errno {
|
||||
case .NONE:
|
||||
return nil
|
||||
case .EPERM:
|
||||
return .Permission_Denied
|
||||
case .EEXIST:
|
||||
return .Exist
|
||||
case .ENOENT:
|
||||
return .Not_Exist
|
||||
}
|
||||
|
||||
_get_platform_error :: proc(res: int) -> Error {
|
||||
errno := unix.get_errno(res)
|
||||
return Platform_Error(i32(errno))
|
||||
}
|
||||
|
||||
_ok_or_error :: proc(res: int) -> Error {
|
||||
return res >= 0 ? nil : _get_platform_error(res)
|
||||
}
|
||||
|
||||
_error_string :: proc(errno: i32) -> string {
|
||||
if errno == 0 {
|
||||
return ""
|
||||
if errno >= 0 && errno <= i32(max(linux.Errno)) {
|
||||
return _errno_strings[linux.Errno(errno)]
|
||||
}
|
||||
return "Error"
|
||||
return "Unknown Error"
|
||||
}
|
||||
|
||||
@@ -45,13 +45,10 @@ O_TRUNC :: File_Flags{.Trunc}
|
||||
O_SPARSE :: File_Flags{.Sparse}
|
||||
O_CLOEXEC :: File_Flags{.Close_On_Exec}
|
||||
|
||||
|
||||
|
||||
stdin: ^File = nil // OS-Specific
|
||||
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))
|
||||
|
||||
+231
-161
@@ -1,39 +1,64 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
import "core:sys/unix"
|
||||
|
||||
INVALID_HANDLE :: -1
|
||||
|
||||
_O_RDONLY :: 0o00000000
|
||||
_O_WRONLY :: 0o00000001
|
||||
_O_RDWR :: 0o00000002
|
||||
_O_CREAT :: 0o00000100
|
||||
_O_EXCL :: 0o00000200
|
||||
_O_NOCTTY :: 0o00000400
|
||||
_O_TRUNC :: 0o00001000
|
||||
_O_APPEND :: 0o00002000
|
||||
_O_NONBLOCK :: 0o00004000
|
||||
_O_LARGEFILE :: 0o00100000
|
||||
_O_DIRECTORY :: 0o00200000
|
||||
_O_NOFOLLOW :: 0o00400000
|
||||
_O_SYNC :: 0o04010000
|
||||
_O_CLOEXEC :: 0o02000000
|
||||
_O_PATH :: 0o10000000
|
||||
|
||||
_AT_FDCWD :: -100
|
||||
|
||||
_CSTRING_NAME_HEAP_THRESHOLD :: 512
|
||||
import "base:runtime"
|
||||
import "core:sys/linux"
|
||||
|
||||
_File :: struct {
|
||||
name: string,
|
||||
fd: int,
|
||||
fd: linux.Fd,
|
||||
allocator: runtime.Allocator,
|
||||
}
|
||||
|
||||
_stdin : File = {
|
||||
impl = {
|
||||
name = "/proc/self/fd/0",
|
||||
fd = 0,
|
||||
allocator = _file_allocator(),
|
||||
},
|
||||
stream = {
|
||||
procedure = _file_stream_proc,
|
||||
},
|
||||
}
|
||||
_stdout : File = {
|
||||
impl = {
|
||||
name = "/proc/self/fd/1",
|
||||
fd = 1,
|
||||
allocator = _file_allocator(),
|
||||
},
|
||||
stream = {
|
||||
procedure = _file_stream_proc,
|
||||
},
|
||||
}
|
||||
_stderr : File = {
|
||||
impl = {
|
||||
name = "/proc/self/fd/2",
|
||||
fd = 2,
|
||||
allocator = _file_allocator(),
|
||||
},
|
||||
stream = {
|
||||
procedure = _file_stream_proc,
|
||||
},
|
||||
}
|
||||
|
||||
@init
|
||||
_standard_stream_init :: proc() {
|
||||
// cannot define these manually because cyclic reference
|
||||
_stdin.stream.data = &_stdin
|
||||
_stdout.stream.data = &_stdout
|
||||
_stderr.stream.data = &_stderr
|
||||
|
||||
stdin = &_stdin
|
||||
stdout = &_stdout
|
||||
stderr = &_stderr
|
||||
}
|
||||
|
||||
_file_allocator :: proc() -> runtime.Allocator {
|
||||
return heap_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
|
||||
@@ -41,40 +66,48 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, er
|
||||
// Just default to using O_NOCTTY because needing to open a controlling
|
||||
// terminal would be incredibly rare. This has no effect on files while
|
||||
// allowing us to open serial devices.
|
||||
flags_i: int = _O_NOCTTY
|
||||
sys_flags: linux.Open_Flags = {.NOCTTY}
|
||||
switch flags & O_RDONLY|O_WRONLY|O_RDWR {
|
||||
case O_RDONLY: flags_i = _O_RDONLY
|
||||
case O_WRONLY: flags_i = _O_WRONLY
|
||||
case O_RDWR: flags_i = _O_RDWR
|
||||
case O_RDONLY:
|
||||
case O_WRONLY: sys_flags += {.WRONLY}
|
||||
case O_RDWR: sys_flags += {.RDWR}
|
||||
}
|
||||
|
||||
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 }
|
||||
if .Append in flags { sys_flags += {.APPEND} }
|
||||
if .Create in flags { sys_flags += {.CREAT} }
|
||||
if .Excl in flags { sys_flags += {.EXCL} }
|
||||
if .Sync in flags { sys_flags += {.DSYNC} }
|
||||
if .Trunc in flags { sys_flags += {.TRUNC} }
|
||||
if .Close_On_Exec in flags { sys_flags += {.CLOEXEC} }
|
||||
|
||||
fd := unix.sys_open(name_cstr, flags_i, uint(perm))
|
||||
if fd < 0 {
|
||||
return nil, _get_platform_error(fd)
|
||||
fd, errno := linux.open(name_cstr, sys_flags, transmute(linux.Mode)(u32(perm)))
|
||||
if errno != .NONE {
|
||||
return nil, _get_platform_error(errno)
|
||||
}
|
||||
|
||||
return _new_file(uintptr(fd), name), nil
|
||||
}
|
||||
|
||||
_new_file :: proc(fd: uintptr, _: string) -> ^File {
|
||||
_new_file :: proc(fd: uintptr, _: string = "") -> ^File {
|
||||
file := new(File, file_allocator())
|
||||
file.impl.fd = int(fd)
|
||||
file.impl.allocator = file_allocator()
|
||||
file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator)
|
||||
file.stream = {
|
||||
data = file,
|
||||
procedure = _file_stream_proc,
|
||||
}
|
||||
_construct_file(file, fd, "")
|
||||
return file
|
||||
}
|
||||
|
||||
_construct_file :: proc(file: ^File, fd: uintptr, _: string = "") {
|
||||
file^ = {
|
||||
impl = {
|
||||
fd = linux.Fd(fd),
|
||||
allocator = file_allocator(),
|
||||
name = _get_full_path(file.impl.fd, file.impl.allocator),
|
||||
},
|
||||
stream = {
|
||||
data = file,
|
||||
procedure = _file_stream_proc,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
_destroy :: proc(f: ^File) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
@@ -86,12 +119,15 @@ _destroy :: proc(f: ^File) -> Error {
|
||||
|
||||
|
||||
_close :: proc(f: ^File) -> Error {
|
||||
if f != nil {
|
||||
res := unix.sys_close(f.impl.fd)
|
||||
_destroy(f)
|
||||
return _ok_or_error(res)
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
errno := linux.close(f.impl.fd)
|
||||
if errno == .EBADF { // avoid possible double free
|
||||
return _get_platform_error(errno)
|
||||
}
|
||||
_destroy(f)
|
||||
return _get_platform_error(errno)
|
||||
}
|
||||
|
||||
_fd :: proc(f: ^File) -> uintptr {
|
||||
@@ -106,20 +142,32 @@ _name :: proc(f: ^File) -> string {
|
||||
}
|
||||
|
||||
_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
|
||||
res := unix.sys_lseek(f.impl.fd, offset, int(whence))
|
||||
if res < 0 {
|
||||
return -1, _get_platform_error(int(res))
|
||||
n, errno := linux.lseek(f.impl.fd, offset, linux.Seek_Whence(whence))
|
||||
if errno != .NONE {
|
||||
return -1, _get_platform_error(errno)
|
||||
}
|
||||
return res, nil
|
||||
return n, nil
|
||||
}
|
||||
|
||||
_read :: proc(f: ^File, p: []byte) -> (i64, Error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
n := unix.sys_read(f.impl.fd, &p[0], len(p))
|
||||
if n < 0 {
|
||||
return -1, _get_platform_error(n)
|
||||
n, errno := linux.read(f.impl.fd, p[:])
|
||||
if errno != .NONE {
|
||||
return -1, _get_platform_error(errno)
|
||||
}
|
||||
return i64(n), n == 0 ? io.Error.EOF : nil
|
||||
}
|
||||
|
||||
_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
n, errno := linux.pread(f.impl.fd, p[:], offset)
|
||||
if errno != .NONE {
|
||||
return -1, _get_platform_error(errno)
|
||||
}
|
||||
if n == 0 {
|
||||
return 0, .EOF
|
||||
@@ -127,91 +175,67 @@ _read :: proc(f: ^File, p: []byte) -> (i64, Error) {
|
||||
return i64(n), nil
|
||||
}
|
||||
|
||||
_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := p, offset
|
||||
for len(b) > 0 {
|
||||
m := unix.sys_pread(f.impl.fd, &b[0], len(b), offset)
|
||||
if m < 0 {
|
||||
return -1, _get_platform_error(m)
|
||||
}
|
||||
if m == 0 {
|
||||
return 0, .EOF
|
||||
}
|
||||
n += i64(m)
|
||||
b = b[m:]
|
||||
offset += i64(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_write :: proc(f: ^File, p: []byte) -> (i64, Error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
n := unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
|
||||
if n < 0 {
|
||||
return -1, _get_platform_error(n)
|
||||
n, errno := linux.write(f.impl.fd, p[:])
|
||||
if errno != .NONE {
|
||||
return -1, _get_platform_error(errno)
|
||||
}
|
||||
return i64(n), nil
|
||||
}
|
||||
|
||||
_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
|
||||
_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := p, offset
|
||||
for len(b) > 0 {
|
||||
m := unix.sys_pwrite(f.impl.fd, &b[0], len(b), offset)
|
||||
if m < 0 {
|
||||
return -1, _get_platform_error(m)
|
||||
}
|
||||
n += i64(m)
|
||||
b = b[m:]
|
||||
offset += i64(m)
|
||||
n, errno := linux.pwrite(f.impl.fd, p[:], offset)
|
||||
if errno != .NONE {
|
||||
return -1, _get_platform_error(errno)
|
||||
}
|
||||
return
|
||||
return i64(n), nil
|
||||
}
|
||||
|
||||
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
s: _Stat = ---
|
||||
res := unix.sys_fstat(f.impl.fd, &s)
|
||||
if res < 0 {
|
||||
return -1, _get_platform_error(res)
|
||||
s: linux.Stat = ---
|
||||
errno := linux.fstat(f.impl.fd, &s)
|
||||
if errno != .NONE {
|
||||
return -1, _get_platform_error(errno)
|
||||
}
|
||||
return s.size, nil
|
||||
return i64(s.size), nil
|
||||
}
|
||||
|
||||
_sync :: proc(f: ^File) -> Error {
|
||||
return _ok_or_error(unix.sys_fsync(f.impl.fd))
|
||||
return _get_platform_error(linux.fsync(f.impl.fd))
|
||||
}
|
||||
|
||||
_flush :: proc(f: ^File) -> Error {
|
||||
return _ok_or_error(unix.sys_fsync(f.impl.fd))
|
||||
return _get_platform_error(linux.fsync(f.impl.fd))
|
||||
}
|
||||
|
||||
_truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
return _ok_or_error(unix.sys_ftruncate(f.impl.fd, size))
|
||||
return _get_platform_error(linux.ftruncate(f.impl.fd, size))
|
||||
}
|
||||
|
||||
_remove :: proc(name: string) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr := temp_cstring(name) or_return
|
||||
|
||||
fd := unix.sys_open(name_cstr, int(File_Flags.Read))
|
||||
if fd < 0 {
|
||||
return _get_platform_error(fd)
|
||||
fd, errno := linux.open(name_cstr, {.NOFOLLOW})
|
||||
#partial switch (errno) {
|
||||
case .ELOOP: /* symlink */
|
||||
case .NONE:
|
||||
defer linux.close(fd)
|
||||
if _is_dir_fd(fd) {
|
||||
return _get_platform_error(linux.rmdir(name_cstr))
|
||||
}
|
||||
case:
|
||||
return _get_platform_error(errno)
|
||||
}
|
||||
defer unix.sys_close(fd)
|
||||
|
||||
if _is_dir_fd(fd) {
|
||||
return _ok_or_error(unix.sys_rmdir(name_cstr))
|
||||
}
|
||||
return _ok_or_error(unix.sys_unlink(name_cstr))
|
||||
return _get_platform_error(linux.unlink(name_cstr))
|
||||
}
|
||||
|
||||
_rename :: proc(old_name, new_name: string) -> Error {
|
||||
@@ -219,7 +243,7 @@ _rename :: proc(old_name, new_name: string) -> Error {
|
||||
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))
|
||||
return _get_platform_error(linux.rename(old_name_cstr, new_name_cstr))
|
||||
}
|
||||
|
||||
_link :: proc(old_name, new_name: string) -> Error {
|
||||
@@ -227,148 +251,194 @@ _link :: proc(old_name, new_name: string) -> Error {
|
||||
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))
|
||||
return _get_platform_error(linux.link(old_name_cstr, new_name_cstr))
|
||||
}
|
||||
|
||||
_symlink :: proc(old_name, new_name: string) -> Error {
|
||||
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))
|
||||
return _get_platform_error(linux.symlink(old_name_cstr, new_name_cstr))
|
||||
}
|
||||
|
||||
_read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (string, Error) {
|
||||
bufsz : uint = 256
|
||||
buf := make([]byte, bufsz, allocator)
|
||||
for {
|
||||
rc := unix.sys_readlink(name_cstr, &buf[0], bufsz)
|
||||
if rc < 0 {
|
||||
delete(buf)
|
||||
return "", _get_platform_error(rc)
|
||||
} else if rc == int(bufsz) {
|
||||
sz, errno := linux.readlink(name_cstr, buf[:])
|
||||
if errno != .NONE {
|
||||
delete(buf, allocator)
|
||||
return "", _get_platform_error(errno)
|
||||
} else if sz == int(bufsz) {
|
||||
bufsz *= 2
|
||||
delete(buf)
|
||||
delete(buf, allocator)
|
||||
buf = make([]byte, bufsz, allocator)
|
||||
} else {
|
||||
return string(buf[:rc]), nil
|
||||
return string(buf[:sz]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) {
|
||||
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, e: Error) {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr := temp_cstring(name) or_return
|
||||
return _read_link_cstr(name_cstr, allocator)
|
||||
}
|
||||
|
||||
_unlink :: proc(name: string) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr := temp_cstring(name) or_return
|
||||
return _ok_or_error(unix.sys_unlink(name_cstr))
|
||||
}
|
||||
|
||||
_chdir :: proc(name: string) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr := temp_cstring(name) or_return
|
||||
return _ok_or_error(unix.sys_chdir(name_cstr))
|
||||
return _get_platform_error(linux.chdir(name_cstr))
|
||||
}
|
||||
|
||||
_fchdir :: proc(f: ^File) -> Error {
|
||||
return _ok_or_error(unix.sys_fchdir(f.impl.fd))
|
||||
return _get_platform_error(linux.fchdir(f.impl.fd))
|
||||
}
|
||||
|
||||
_chmod :: proc(name: string, mode: File_Mode) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr := temp_cstring(name) or_return
|
||||
return _ok_or_error(unix.sys_chmod(name_cstr, uint(mode)))
|
||||
return _get_platform_error(linux.chmod(name_cstr, transmute(linux.Mode)(u32(mode))))
|
||||
}
|
||||
|
||||
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||
return _ok_or_error(unix.sys_fchmod(f.impl.fd, uint(mode)))
|
||||
return _get_platform_error(linux.fchmod(f.impl.fd, transmute(linux.Mode)(u32(mode))))
|
||||
}
|
||||
|
||||
// NOTE: will throw error without super user priviledges
|
||||
_chown :: proc(name: string, uid, gid: int) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr := temp_cstring(name) or_return
|
||||
return _ok_or_error(unix.sys_chown(name_cstr, uid, gid))
|
||||
return _get_platform_error(linux.chown(name_cstr, linux.Uid(uid), linux.Gid(gid)))
|
||||
}
|
||||
|
||||
// NOTE: will throw error without super user priviledges
|
||||
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr := temp_cstring(name) or_return
|
||||
return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid))
|
||||
return _get_platform_error(linux.lchown(name_cstr, linux.Uid(uid), linux.Gid(gid)))
|
||||
}
|
||||
|
||||
// NOTE: will throw error without super user priviledges
|
||||
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
return _ok_or_error(unix.sys_fchown(f.impl.fd, uid, gid))
|
||||
return _get_platform_error(linux.fchown(f.impl.fd, linux.Uid(uid), linux.Gid(gid)))
|
||||
}
|
||||
|
||||
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr := temp_cstring(name) or_return
|
||||
times := [2]Unix_File_Time {
|
||||
{ atime._nsec, 0 },
|
||||
{ mtime._nsec, 0 },
|
||||
times := [2]linux.Time_Spec {
|
||||
{
|
||||
uint(atime._nsec) / uint(time.Second),
|
||||
uint(atime._nsec) % uint(time.Second),
|
||||
},
|
||||
{
|
||||
uint(mtime._nsec) / uint(time.Second),
|
||||
uint(mtime._nsec) % uint(time.Second),
|
||||
},
|
||||
}
|
||||
return _ok_or_error(unix.sys_utimensat(_AT_FDCWD, name_cstr, ×, 0))
|
||||
return _get_platform_error(linux.utimensat(linux.AT_FDCWD, name_cstr, ×[0], nil))
|
||||
}
|
||||
|
||||
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||
times := [2]Unix_File_Time {
|
||||
{ atime._nsec, 0 },
|
||||
{ mtime._nsec, 0 },
|
||||
times := [2]linux.Time_Spec {
|
||||
{
|
||||
uint(atime._nsec) / uint(time.Second),
|
||||
uint(atime._nsec) % uint(time.Second),
|
||||
},
|
||||
{
|
||||
uint(mtime._nsec) / uint(time.Second),
|
||||
uint(mtime._nsec) % uint(time.Second),
|
||||
},
|
||||
}
|
||||
return _ok_or_error(unix.sys_utimensat(f.impl.fd, nil, ×, 0))
|
||||
return _get_platform_error(linux.utimensat(f.impl.fd, nil, ×[0], nil))
|
||||
}
|
||||
|
||||
_exists :: proc(name: string) -> bool {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr, _ := temp_cstring(name)
|
||||
return unix.sys_access(name_cstr, F_OK) == 0
|
||||
res, errno := linux.access(name_cstr, linux.F_OK)
|
||||
return !res && errno == .NONE
|
||||
}
|
||||
|
||||
_is_file :: proc(name: string) -> bool {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr, _ := temp_cstring(name)
|
||||
s: _Stat
|
||||
res := unix.sys_stat(name_cstr, &s)
|
||||
if res < 0 {
|
||||
s: linux.Stat
|
||||
if linux.stat(name_cstr, &s) != .NONE {
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
return linux.S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
_is_file_fd :: proc(fd: int) -> bool {
|
||||
s: _Stat
|
||||
res := unix.sys_fstat(fd, &s)
|
||||
if res < 0 { // error
|
||||
_is_file_fd :: proc(fd: linux.Fd) -> bool {
|
||||
s: linux.Stat
|
||||
if linux.fstat(fd, &s) != .NONE {
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
return linux.S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
_is_dir :: proc(name: string) -> bool {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr, _ := temp_cstring(name)
|
||||
s: _Stat
|
||||
res := unix.sys_stat(name_cstr, &s)
|
||||
if res < 0 {
|
||||
s: linux.Stat
|
||||
if linux.stat(name_cstr, &s) != .NONE {
|
||||
return false
|
||||
}
|
||||
return S_ISDIR(s.mode)
|
||||
return linux.S_ISDIR(s.mode)
|
||||
}
|
||||
|
||||
_is_dir_fd :: proc(fd: int) -> bool {
|
||||
s: _Stat
|
||||
res := unix.sys_fstat(fd, &s)
|
||||
if res < 0 { // error
|
||||
_is_dir_fd :: proc(fd: linux.Fd) -> bool {
|
||||
s: linux.Stat
|
||||
if linux.fstat(fd, &s) != .NONE {
|
||||
return false
|
||||
}
|
||||
return S_ISDIR(s.mode)
|
||||
return linux.S_ISDIR(s.mode)
|
||||
}
|
||||
|
||||
/* Certain files in the Linux file system are not actual
|
||||
* files (e.g. everything in /proc/). Therefore, the
|
||||
* read_entire_file procs fail to actually read anything
|
||||
* since these "files" stat to a size of 0. Here, we just
|
||||
* read until there is nothing left.
|
||||
*/
|
||||
_read_entire_pseudo_file :: proc { _read_entire_pseudo_file_string, _read_entire_pseudo_file_cstring }
|
||||
|
||||
_read_entire_pseudo_file_string :: proc(name: string, allocator: runtime.Allocator) -> (b: []u8, e: Error) {
|
||||
name_cstr := clone_to_cstring(name, allocator) or_return
|
||||
defer delete(name, allocator)
|
||||
return _read_entire_pseudo_file_cstring(name_cstr, allocator)
|
||||
}
|
||||
|
||||
_read_entire_pseudo_file_cstring :: proc(name: cstring, allocator: runtime.Allocator) -> ([]u8, Error) {
|
||||
fd, errno := linux.open(name, {})
|
||||
if errno != .NONE {
|
||||
return nil, _get_platform_error(errno)
|
||||
}
|
||||
defer linux.close(fd)
|
||||
|
||||
BUF_SIZE_STEP :: 128
|
||||
contents := make([dynamic]u8, 0, BUF_SIZE_STEP, allocator)
|
||||
|
||||
n: int
|
||||
i: int
|
||||
for {
|
||||
resize(&contents, i + BUF_SIZE_STEP)
|
||||
n, errno = linux.read(fd, contents[i:i+BUF_SIZE_STEP])
|
||||
if errno != .NONE {
|
||||
delete(contents)
|
||||
return nil, _get_platform_error(errno)
|
||||
}
|
||||
if n < BUF_SIZE_STEP {
|
||||
break
|
||||
}
|
||||
i += BUF_SIZE_STEP
|
||||
}
|
||||
|
||||
resize(&contents, i + n)
|
||||
|
||||
return contents[:], nil
|
||||
}
|
||||
|
||||
@(private="package")
|
||||
|
||||
+11
-17
@@ -1,7 +1,7 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:sys/unix"
|
||||
import "core:sys/linux"
|
||||
import "core:sync"
|
||||
import "core:mem"
|
||||
|
||||
@@ -97,9 +97,8 @@ CURRENTLY_ACTIVE :: (^^Region)(~uintptr(0))
|
||||
|
||||
FREE_LIST_ENTRIES_PER_BLOCK :: BLOCK_SIZE / size_of(u16)
|
||||
|
||||
MMAP_FLAGS :: unix.MAP_ANONYMOUS | unix.MAP_PRIVATE
|
||||
MMAP_PROT :: unix.PROT_READ | unix.PROT_WRITE
|
||||
|
||||
MMAP_FLAGS : linux.Map_Flags : {.ANONYMOUS, .PRIVATE}
|
||||
MMAP_PROT : linux.Mem_Protection : {.READ, .WRITE}
|
||||
|
||||
@thread_local _local_region: ^Region
|
||||
global_regions: ^Region
|
||||
@@ -324,11 +323,11 @@ heap_free :: proc(memory: rawptr) {
|
||||
// Regions
|
||||
//
|
||||
_new_region :: proc() -> ^Region #no_bounds_check {
|
||||
res := unix.sys_mmap(nil, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
||||
if res < 0 {
|
||||
ptr, errno := linux.mmap(0, uint(SIZE_OF_REGION), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
||||
if errno != .NONE {
|
||||
return nil
|
||||
}
|
||||
new_region := (^Region)(uintptr(res))
|
||||
new_region := (^Region)(ptr)
|
||||
|
||||
new_region.hdr.local_addr = CURRENTLY_ACTIVE
|
||||
new_region.hdr.reset_addr = &_local_region
|
||||
@@ -634,8 +633,8 @@ _region_free_list_remove :: proc(region: ^Region, free_idx: u16) #no_bounds_chec
|
||||
//
|
||||
_direct_mmap_alloc :: proc(size: int) -> rawptr {
|
||||
mmap_size := _round_up_to_nearest(size + BLOCK_SIZE, PAGE_SIZE)
|
||||
new_allocation := unix.sys_mmap(nil, uint(mmap_size), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
||||
if new_allocation < 0 && new_allocation > -4096 {
|
||||
new_allocation, errno := linux.mmap(0, uint(mmap_size), MMAP_PROT, MMAP_FLAGS, -1, 0)
|
||||
if errno != .NONE {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -655,13 +654,8 @@ _direct_mmap_resize :: proc(alloc: ^Allocation_Header, new_size: int) -> rawptr
|
||||
return mem.ptr_offset(alloc, 1)
|
||||
}
|
||||
|
||||
new_allocation := unix.sys_mremap(
|
||||
alloc,
|
||||
uint(old_mmap_size),
|
||||
uint(new_mmap_size),
|
||||
unix.MREMAP_MAYMOVE,
|
||||
)
|
||||
if new_allocation < 0 && new_allocation > -4096 {
|
||||
new_allocation, errno := linux.mremap(alloc, uint(old_mmap_size), uint(new_mmap_size), {.MAYMOVE})
|
||||
if errno != .NONE {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -702,7 +696,7 @@ _direct_mmap_to_region :: proc(alloc: ^Allocation_Header, new_size: int) -> rawp
|
||||
_direct_mmap_free :: proc(alloc: ^Allocation_Header) {
|
||||
requested := int(alloc.requested & REQUESTED_MASK)
|
||||
mmap_size := _round_up_to_nearest(requested + BLOCK_SIZE, PAGE_SIZE)
|
||||
unix.sys_munmap(alloc, uint(mmap_size))
|
||||
linux.munmap(alloc, uint(mmap_size))
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
+70
-86
@@ -3,104 +3,89 @@ package os2
|
||||
|
||||
import "core:strconv"
|
||||
import "base:runtime"
|
||||
import "core:sys/unix"
|
||||
import "core:sys/linux"
|
||||
|
||||
_Path_Separator :: '/'
|
||||
_Path_Separator_String :: "/"
|
||||
_Path_List_Separator :: ':'
|
||||
|
||||
_S_IFMT :: 0o170000 // Type of file mask
|
||||
_S_IFIFO :: 0o010000 // Named pipe (fifo)
|
||||
_S_IFCHR :: 0o020000 // Character special
|
||||
_S_IFDIR :: 0o040000 // Directory
|
||||
_S_IFBLK :: 0o060000 // Block special
|
||||
_S_IFREG :: 0o100000 // Regular
|
||||
_S_IFLNK :: 0o120000 // Symbolic link
|
||||
_S_IFSOCK :: 0o140000 // Socket
|
||||
|
||||
_OPENDIR_FLAGS :: _O_RDONLY|_O_NONBLOCK|_O_DIRECTORY|_O_LARGEFILE|_O_CLOEXEC
|
||||
_OPENDIR_FLAGS : linux.Open_Flags : {.NONBLOCK, .DIRECTORY, .LARGEFILE, .CLOEXEC}
|
||||
|
||||
_is_path_separator :: proc(c: byte) -> bool {
|
||||
return c == '/'
|
||||
}
|
||||
|
||||
_mkdir :: proc(path: string, perm: File_Mode) -> Error {
|
||||
// NOTE: These modes would require sys_mknod, however, that would require
|
||||
// additional arguments to this function.
|
||||
// TODO: These modes would require mknod, however, that would also
|
||||
// require additional arguments to this function..
|
||||
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
|
||||
return .Invalid_Argument
|
||||
}
|
||||
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
path_cstr := temp_cstring(path) or_return
|
||||
return _ok_or_error(unix.sys_mkdir(path_cstr, uint(perm & 0o777)))
|
||||
return _get_platform_error(linux.mkdir(path_cstr, transmute(linux.Mode)(u32(perm) & 0o777)))
|
||||
}
|
||||
|
||||
_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
|
||||
_mkdirat :: proc(dfd: int, path: []u8, perm: int, has_created: ^bool) -> Error {
|
||||
if len(path) == 0 {
|
||||
return _ok_or_error(unix.sys_close(dfd))
|
||||
}
|
||||
mkdirat :: proc(dfd: linux.Fd, path: []u8, perm: int, has_created: ^bool) -> Error {
|
||||
i: int
|
||||
for /**/; i < len(path) - 1 && path[i] != '/'; i += 1 {}
|
||||
for ; i < len(path) - 1 && path[i] != '/'; i += 1 {}
|
||||
if i == 0 {
|
||||
return _get_platform_error(linux.close(dfd))
|
||||
}
|
||||
path[i] = 0
|
||||
new_dfd := unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
|
||||
switch new_dfd {
|
||||
case -ENOENT:
|
||||
if res := unix.sys_mkdirat(dfd, cstring(&path[0]), uint(perm)); res < 0 {
|
||||
return _get_platform_error(res)
|
||||
new_dfd, errno := linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
|
||||
#partial switch errno {
|
||||
case .ENOENT:
|
||||
if errno = linux.mkdirat(dfd, cstring(&path[0]), transmute(linux.Mode)(u32(perm))); errno != .NONE {
|
||||
return _get_platform_error(errno)
|
||||
}
|
||||
has_created^ = true
|
||||
if new_dfd = unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); new_dfd < 0 {
|
||||
return _get_platform_error(new_dfd)
|
||||
if new_dfd, errno = linux.openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); errno != .NONE {
|
||||
return _get_platform_error(errno)
|
||||
}
|
||||
fallthrough
|
||||
case 0:
|
||||
if res := unix.sys_close(dfd); res < 0 {
|
||||
return _get_platform_error(res)
|
||||
case .NONE:
|
||||
if errno = linux.close(dfd); errno != .NONE {
|
||||
return _get_platform_error(errno)
|
||||
}
|
||||
// skip consecutive '/'
|
||||
for i += 1; i < len(path) && path[i] == '/'; i += 1 {}
|
||||
return _mkdirat(new_dfd, path[i:], perm, has_created)
|
||||
return mkdirat(new_dfd, path[i:], perm, has_created)
|
||||
case:
|
||||
return _get_platform_error(new_dfd)
|
||||
return _get_platform_error(errno)
|
||||
}
|
||||
unreachable()
|
||||
}
|
||||
|
||||
// TODO
|
||||
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
|
||||
return .Invalid_Argument
|
||||
}
|
||||
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
// need something we can edit, and use to generate cstrings
|
||||
allocated: bool
|
||||
path_bytes: []u8
|
||||
if len(path) > _CSTRING_NAME_HEAP_THRESHOLD {
|
||||
allocated = true
|
||||
path_bytes = make([]u8, len(path) + 1)
|
||||
} else {
|
||||
path_bytes = make([]u8, len(path) + 1, temp_allocator())
|
||||
}
|
||||
path_bytes := make([]u8, len(path) + 1, temp_allocator())
|
||||
|
||||
// NULL terminate the byte slice to make it a valid cstring
|
||||
// zero terminate the byte slice to make it a valid cstring
|
||||
copy(path_bytes, path)
|
||||
path_bytes[len(path)] = 0
|
||||
|
||||
dfd: int
|
||||
dfd: linux.Fd
|
||||
errno: linux.Errno
|
||||
if path_bytes[0] == '/' {
|
||||
dfd = unix.sys_open("/", _OPENDIR_FLAGS)
|
||||
dfd, errno = linux.open("/", _OPENDIR_FLAGS)
|
||||
path_bytes = path_bytes[1:]
|
||||
} else {
|
||||
dfd = unix.sys_open(".", _OPENDIR_FLAGS)
|
||||
dfd, errno = linux.open(".", _OPENDIR_FLAGS)
|
||||
}
|
||||
if dfd < 0 {
|
||||
return _get_platform_error(dfd)
|
||||
if errno != .NONE {
|
||||
return _get_platform_error(errno)
|
||||
}
|
||||
|
||||
has_created: bool
|
||||
_mkdirat(dfd, path_bytes, int(perm & 0o777), &has_created) or_return
|
||||
mkdirat(dfd, path_bytes, int(perm & 0o777), &has_created) or_return
|
||||
if has_created {
|
||||
return nil
|
||||
}
|
||||
@@ -119,28 +104,28 @@ dirent64 :: struct {
|
||||
_remove_all :: proc(path: string) -> Error {
|
||||
DT_DIR :: 4
|
||||
|
||||
_remove_all_dir :: proc(dfd: int) -> Error {
|
||||
remove_all_dir :: proc(dfd: linux.Fd) -> Error {
|
||||
n := 64
|
||||
buf := make([]u8, n)
|
||||
defer delete(buf)
|
||||
|
||||
loop: for {
|
||||
getdents_res := unix.sys_getdents64(dfd, &buf[0], n)
|
||||
switch getdents_res {
|
||||
case -EINVAL:
|
||||
buflen, errno := linux.getdents(dfd, buf[:])
|
||||
#partial switch errno {
|
||||
case .EINVAL:
|
||||
delete(buf)
|
||||
n *= 2
|
||||
buf = make([]u8, n)
|
||||
continue loop
|
||||
case -4096..<0:
|
||||
return _get_platform_error(getdents_res)
|
||||
case 0:
|
||||
break loop
|
||||
case .NONE:
|
||||
if buflen == 0 { break loop }
|
||||
case:
|
||||
return _get_platform_error(errno)
|
||||
}
|
||||
|
||||
d: ^dirent64
|
||||
|
||||
for i := 0; i < getdents_res; i += int(d.d_reclen) {
|
||||
for i := 0; i < buflen; i += int(d.d_reclen) {
|
||||
d = (^dirent64)(rawptr(&buf[i]))
|
||||
d_name_cstr := cstring(&d.d_name[0])
|
||||
|
||||
@@ -156,23 +141,22 @@ _remove_all :: proc(path: string) -> Error {
|
||||
continue
|
||||
}
|
||||
|
||||
unlink_res: int
|
||||
|
||||
switch d.d_type {
|
||||
case DT_DIR:
|
||||
new_dfd := unix.sys_openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
|
||||
if new_dfd < 0 {
|
||||
return _get_platform_error(new_dfd)
|
||||
new_dfd: linux.Fd
|
||||
new_dfd, errno = linux.openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
|
||||
if errno != .NONE {
|
||||
return _get_platform_error(errno)
|
||||
}
|
||||
defer unix.sys_close(new_dfd)
|
||||
_remove_all_dir(new_dfd) or_return
|
||||
unlink_res = unix.sys_unlinkat(dfd, d_name_cstr, int(unix.AT_REMOVEDIR))
|
||||
defer linux.close(new_dfd)
|
||||
remove_all_dir(new_dfd) or_return
|
||||
errno = linux.unlinkat(dfd, d_name_cstr, {.REMOVEDIR})
|
||||
case:
|
||||
unlink_res = unix.sys_unlinkat(dfd, d_name_cstr)
|
||||
errno = linux.unlinkat(dfd, d_name_cstr, nil)
|
||||
}
|
||||
|
||||
if unlink_res < 0 {
|
||||
return _get_platform_error(unlink_res)
|
||||
if errno != .NONE {
|
||||
return _get_platform_error(errno)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,17 +166,19 @@ _remove_all :: proc(path: string) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
path_cstr := temp_cstring(path) or_return
|
||||
|
||||
fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS)
|
||||
switch fd {
|
||||
case -ENOTDIR:
|
||||
return _ok_or_error(unix.sys_unlink(path_cstr))
|
||||
case -4096..<0:
|
||||
return _get_platform_error(fd)
|
||||
fd, errno := linux.open(path_cstr, _OPENDIR_FLAGS)
|
||||
#partial switch errno {
|
||||
case .NONE:
|
||||
break
|
||||
case .ENOTDIR:
|
||||
return _get_platform_error(linux.unlink(path_cstr))
|
||||
case:
|
||||
return _get_platform_error(errno)
|
||||
}
|
||||
|
||||
defer unix.sys_close(fd)
|
||||
_remove_all_dir(fd) or_return
|
||||
return _ok_or_error(unix.sys_rmdir(path_cstr))
|
||||
defer linux.close(fd)
|
||||
remove_all_dir(fd) or_return
|
||||
return _get_platform_error(linux.rmdir(path_cstr))
|
||||
}
|
||||
|
||||
_getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
||||
@@ -203,13 +189,12 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
||||
PATH_MAX :: 4096
|
||||
buf := make([dynamic]u8, PATH_MAX, allocator)
|
||||
for {
|
||||
#no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
|
||||
|
||||
if res >= 0 {
|
||||
return string_from_null_terminated_bytes(buf[:]), nil
|
||||
#no_bounds_check n, errno := linux.getcwd(buf[:])
|
||||
if errno == .NONE {
|
||||
return string(buf[:n-1]), nil
|
||||
}
|
||||
if res != -ERANGE {
|
||||
return "", _get_platform_error(res)
|
||||
if errno != .ERANGE {
|
||||
return "", _get_platform_error(errno)
|
||||
}
|
||||
resize(&buf, len(buf)+PATH_MAX)
|
||||
}
|
||||
@@ -218,16 +203,16 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
||||
|
||||
_setwd :: proc(dir: string) -> Error {
|
||||
dir_cstr := temp_cstring(dir) or_return
|
||||
return _ok_or_error(unix.sys_chdir(dir_cstr))
|
||||
return _get_platform_error(linux.chdir(dir_cstr))
|
||||
}
|
||||
|
||||
_get_full_path :: proc(fd: int, allocator: runtime.Allocator) -> string {
|
||||
_get_full_path :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> string {
|
||||
PROC_FD_PATH :: "/proc/self/fd/"
|
||||
|
||||
buf: [32]u8
|
||||
copy(buf[:], PROC_FD_PATH)
|
||||
|
||||
strconv.itoa(buf[len(PROC_FD_PATH):], fd)
|
||||
strconv.itoa(buf[len(PROC_FD_PATH):], int(fd))
|
||||
|
||||
fullpath: string
|
||||
err: Error
|
||||
@@ -236,4 +221,3 @@ _get_full_path :: proc(fd: int, allocator: runtime.Allocator) -> string {
|
||||
}
|
||||
return fullpath
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:sys/linux"
|
||||
|
||||
_pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
return nil, nil, nil
|
||||
fds: [2]linux.Fd
|
||||
errno := linux.pipe2(&fds, {.CLOEXEC})
|
||||
if errno != .NONE {
|
||||
return nil, nil,_get_platform_error(errno)
|
||||
}
|
||||
|
||||
r = _new_file(uintptr(fds[0]))
|
||||
w = _new_file(uintptr(fds[1]))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+20
-96
@@ -3,108 +3,32 @@ package os2
|
||||
|
||||
import "core:time"
|
||||
import "base:runtime"
|
||||
import "core:sys/unix"
|
||||
import "core:sys/linux"
|
||||
import "core:path/filepath"
|
||||
|
||||
// File type
|
||||
S_IFMT :: 0o170000 // Type of file mask
|
||||
S_IFIFO :: 0o010000 // Named pipe (fifo)
|
||||
S_IFCHR :: 0o020000 // Character special
|
||||
S_IFDIR :: 0o040000 // Directory
|
||||
S_IFBLK :: 0o060000 // Block special
|
||||
S_IFREG :: 0o100000 // Regular
|
||||
S_IFLNK :: 0o120000 // Symbolic link
|
||||
S_IFSOCK :: 0o140000 // Socket
|
||||
|
||||
// File mode
|
||||
// Read, write, execute/search by owner
|
||||
S_IRWXU :: 0o0700 // RWX mask for owner
|
||||
S_IRUSR :: 0o0400 // R for owner
|
||||
S_IWUSR :: 0o0200 // W for owner
|
||||
S_IXUSR :: 0o0100 // X for owner
|
||||
|
||||
// Read, write, execute/search by group
|
||||
S_IRWXG :: 0o0070 // RWX mask for group
|
||||
S_IRGRP :: 0o0040 // R for group
|
||||
S_IWGRP :: 0o0020 // W for group
|
||||
S_IXGRP :: 0o0010 // X for group
|
||||
|
||||
// Read, write, execute/search by others
|
||||
S_IRWXO :: 0o0007 // RWX mask for other
|
||||
S_IROTH :: 0o0004 // R for other
|
||||
S_IWOTH :: 0o0002 // W for other
|
||||
S_IXOTH :: 0o0001 // X for other
|
||||
|
||||
S_ISUID :: 0o4000 // Set user id on execution
|
||||
S_ISGID :: 0o2000 // Set group id on execution
|
||||
S_ISVTX :: 0o1000 // Directory restrcted delete
|
||||
|
||||
|
||||
S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
|
||||
S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
|
||||
S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
|
||||
S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
|
||||
S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
|
||||
S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
|
||||
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
|
||||
|
||||
F_OK :: 0 // Test for file existance
|
||||
X_OK :: 1 // Test for execute permission
|
||||
W_OK :: 2 // Test for write permission
|
||||
R_OK :: 4 // Test for read permission
|
||||
|
||||
@private
|
||||
Unix_File_Time :: struct {
|
||||
seconds: i64,
|
||||
nanoseconds: i64,
|
||||
}
|
||||
|
||||
@private
|
||||
_Stat :: struct {
|
||||
device_id: u64, // ID of device containing file
|
||||
serial: u64, // File serial number
|
||||
nlink: u64, // Number of hard links
|
||||
mode: u32, // Mode of the file
|
||||
uid: u32, // User ID of the file's owner
|
||||
gid: u32, // Group ID of the file's group
|
||||
_padding: i32, // 32 bits of padding
|
||||
rdev: u64, // Device ID, if device
|
||||
size: i64, // Size of the file, in bytes
|
||||
block_size: i64, // Optimal bllocksize for I/O
|
||||
blocks: i64, // Number of 512-byte blocks allocated
|
||||
|
||||
last_access: Unix_File_Time, // Time of last access
|
||||
modified: Unix_File_Time, // Time of last modification
|
||||
status_change: Unix_File_Time, // Time of last status change
|
||||
|
||||
_reserve1,
|
||||
_reserve2,
|
||||
_reserve3: i64,
|
||||
}
|
||||
|
||||
|
||||
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
return _fstat_internal(f.impl.fd, allocator)
|
||||
}
|
||||
|
||||
_fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
s: _Stat
|
||||
result := unix.sys_fstat(fd, &s)
|
||||
if result < 0 {
|
||||
return {}, _get_platform_error(result)
|
||||
_fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
s: linux.Stat
|
||||
errno := linux.fstat(fd, &s)
|
||||
if errno != .NONE {
|
||||
return {}, _get_platform_error(errno)
|
||||
}
|
||||
|
||||
// TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time
|
||||
fi := File_Info {
|
||||
fullpath = _get_full_path(fd, allocator),
|
||||
name = "",
|
||||
size = s.size,
|
||||
size = i64(s.size),
|
||||
mode = 0,
|
||||
is_directory = S_ISDIR(s.mode),
|
||||
modification_time = time.Time {s.modified.seconds},
|
||||
access_time = time.Time {s.last_access.seconds},
|
||||
creation_time = time.Time{0}, // regular stat does not provide this
|
||||
is_directory = linux.S_ISDIR(s.mode),
|
||||
modification_time = time.Time {i64(s.mtime.time_sec) * i64(time.Second) + i64(s.mtime.time_nsec)},
|
||||
access_time = time.Time {i64(s.atime.time_sec) * i64(time.Second) + i64(s.atime.time_nsec)},
|
||||
creation_time = time.Time{i64(s.ctime.time_sec) * i64(time.Second) + i64(s.ctime.time_nsec)}, // regular stat does not provide this
|
||||
}
|
||||
fi.creation_time = fi.modification_time
|
||||
|
||||
fi.name = filepath.base(fi.fullpath)
|
||||
return fi, nil
|
||||
@@ -115,11 +39,11 @@ _stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
name_cstr := temp_cstring(name) or_return
|
||||
|
||||
fd := unix.sys_open(name_cstr, _O_RDONLY)
|
||||
if fd < 0 {
|
||||
return {}, _get_platform_error(fd)
|
||||
fd, errno := linux.open(name_cstr, {})
|
||||
if errno != .NONE {
|
||||
return {}, _get_platform_error(errno)
|
||||
}
|
||||
defer unix.sys_close(fd)
|
||||
defer linux.close(fd)
|
||||
return _fstat_internal(fd, allocator)
|
||||
}
|
||||
|
||||
@@ -127,11 +51,11 @@ _lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, er
|
||||
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 {
|
||||
return {}, _get_platform_error(fd)
|
||||
fd, errno := linux.open(name_cstr, {.PATH, .NOFOLLOW})
|
||||
if errno != .NONE {
|
||||
return {}, _get_platform_error(errno)
|
||||
}
|
||||
defer unix.sys_close(fd)
|
||||
defer linux.close(fd)
|
||||
return _fstat_internal(fd, allocator)
|
||||
}
|
||||
|
||||
|
||||
@@ -555,7 +555,7 @@ open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (Handle, Errno
|
||||
err := fchmod(handle, cast(u16)mode)
|
||||
if err != 0 {
|
||||
_unix_close(handle)
|
||||
return INVALID_HANDLE, cast(Errno)err
|
||||
return INVALID_HANDLE, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -883,8 +883,8 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
||||
}
|
||||
defer _unix_free(path_ptr)
|
||||
|
||||
path_cstr := transmute(cstring)path_ptr
|
||||
path = strings.clone( string(path_cstr) )
|
||||
path_cstr := cast(cstring)path_ptr
|
||||
path = strings.clone(string(path_cstr))
|
||||
|
||||
return path, ERROR_NONE
|
||||
}
|
||||
@@ -896,7 +896,7 @@ access :: proc(path: string, mask: int) -> bool {
|
||||
}
|
||||
|
||||
flush :: proc(fd: Handle) -> Errno {
|
||||
return cast(Errno)_unix_fsync(fd)
|
||||
return cast(Errno)_unix_fsync(fd)
|
||||
}
|
||||
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
|
||||
@@ -648,8 +648,8 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
||||
}
|
||||
defer _unix_free(path_ptr)
|
||||
|
||||
path_cstr := transmute(cstring)path_ptr
|
||||
path = strings.clone( string(path_cstr) )
|
||||
|
||||
path = strings.clone(string(cstring(path_ptr)))
|
||||
|
||||
return path, ERROR_NONE
|
||||
}
|
||||
@@ -705,7 +705,9 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_chdir(cstr)
|
||||
if res == -1 do return Errno(get_last_error())
|
||||
if res == -1 {
|
||||
return Errno(get_last_error())
|
||||
}
|
||||
return ERROR_NONE
|
||||
}
|
||||
|
||||
@@ -743,7 +745,9 @@ get_page_size :: proc "contextless" () -> int {
|
||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||
// if we don't have to.
|
||||
@static page_size := -1
|
||||
if page_size != -1 do return page_size
|
||||
if page_size != -1 {
|
||||
return page_size
|
||||
}
|
||||
|
||||
page_size = int(_unix_getpagesize())
|
||||
return page_size
|
||||
|
||||
@@ -890,8 +890,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
||||
}
|
||||
defer _unix_free(path_ptr)
|
||||
|
||||
path_cstr := transmute(cstring)path_ptr
|
||||
path = strings.clone( string(path_cstr) )
|
||||
path = strings.clone(string(cstring(path_ptr)))
|
||||
|
||||
return path, ERROR_NONE
|
||||
}
|
||||
|
||||
@@ -658,8 +658,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
||||
}
|
||||
defer _unix_free(path_ptr)
|
||||
|
||||
path_cstr := transmute(cstring)path_ptr
|
||||
path = strings.clone( string(path_cstr) )
|
||||
path = strings.clone(string(cstring(path_ptr)))
|
||||
|
||||
return path, ERROR_NONE
|
||||
}
|
||||
@@ -715,7 +714,9 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_chdir(cstr)
|
||||
if res == -1 do return Errno(get_last_error())
|
||||
if res == -1 {
|
||||
return Errno(get_last_error())
|
||||
}
|
||||
return ERROR_NONE
|
||||
}
|
||||
|
||||
@@ -756,7 +757,9 @@ get_page_size :: proc() -> int {
|
||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||
// if we don't have to.
|
||||
@static page_size := -1
|
||||
if page_size != -1 do return page_size
|
||||
if page_size != -1 {
|
||||
return page_size
|
||||
}
|
||||
|
||||
page_size = int(_unix_getpagesize())
|
||||
return page_size
|
||||
|
||||
@@ -610,8 +610,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
||||
}
|
||||
defer _unix_free(path_ptr)
|
||||
|
||||
path_cstr := transmute(cstring)path_ptr
|
||||
path = strings.clone( string(path_cstr) )
|
||||
path = strings.clone(string(cstring(path_ptr)))
|
||||
|
||||
return path, ERROR_NONE
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import "base:runtime"
|
||||
Handle :: distinct i32
|
||||
Errno :: distinct i32
|
||||
|
||||
INVALID_HANDLE :: -1
|
||||
|
||||
ERROR_NONE :: Errno(wasi.errno_t.SUCCESS)
|
||||
|
||||
O_RDONLY :: 0x00000
|
||||
@@ -26,6 +28,16 @@ stdout: Handle = 1
|
||||
stderr: Handle = 2
|
||||
current_dir: Handle = 3
|
||||
|
||||
args := _alloc_command_line_arguments()
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> (args: []string) {
|
||||
args = make([]string, len(runtime.args__))
|
||||
for &arg, i in args {
|
||||
arg = string(runtime.args__[i])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
iovs := wasi.ciovec_t(data)
|
||||
n, err := wasi.fd_write(wasi.fd_t(fd), {iovs})
|
||||
|
||||
@@ -178,8 +178,8 @@ WINDOWS_11_BUILD_CUTOFF :: 22_000
|
||||
get_windows_version_w :: proc() -> win32.OSVERSIONINFOEXW {
|
||||
osvi : win32.OSVERSIONINFOEXW
|
||||
osvi.dwOSVersionInfoSize = size_of(win32.OSVERSIONINFOEXW)
|
||||
win32.RtlGetVersion(&osvi)
|
||||
return osvi
|
||||
win32.RtlGetVersion(&osvi)
|
||||
return osvi
|
||||
}
|
||||
|
||||
is_windows_xp :: proc() -> bool {
|
||||
|
||||
@@ -432,7 +432,7 @@ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (
|
||||
then `"."` is returned.
|
||||
*/
|
||||
dir :: proc(path: string, allocator := context.allocator) -> string {
|
||||
context.allocator = allocator
|
||||
context.allocator = allocator
|
||||
vol := volume_name(path)
|
||||
i := len(path) - 1
|
||||
for i >= len(vol) && !is_separator(path[i]) {
|
||||
|
||||
@@ -114,7 +114,7 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
||||
|
||||
case Type_Info_Struct:
|
||||
y := b.variant.(Type_Info_Struct) or_return
|
||||
switch {
|
||||
switch {
|
||||
case len(x.types) != len(y.types),
|
||||
x.is_packed != y.is_packed,
|
||||
x.is_raw_union != y.is_raw_union,
|
||||
@@ -122,7 +122,7 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
||||
x.soa_kind != y.soa_kind,
|
||||
x.soa_base_type != y.soa_base_type,
|
||||
x.soa_len != y.soa_len:
|
||||
return false
|
||||
return false
|
||||
}
|
||||
for _, i in x.types {
|
||||
xn, yn := x.names[i], y.names[i]
|
||||
|
||||
@@ -156,8 +156,7 @@ linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, f
|
||||
*/
|
||||
@(require_results)
|
||||
binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
|
||||
where intrinsics.type_is_ordered(T) #no_bounds_check
|
||||
{
|
||||
where intrinsics.type_is_ordered(T) #no_bounds_check {
|
||||
return binary_search_by(array, key, cmp_proc(T))
|
||||
}
|
||||
|
||||
@@ -712,7 +711,7 @@ enumerated_array :: proc(ptr: ^$T) -> []intrinsics.type_elem_type(T)
|
||||
@(require_results)
|
||||
enum_slice_to_bitset :: proc(enums: []$E, $T: typeid/bit_set[E]) -> (bits: T) where intrinsics.type_is_enum(E), intrinsics.type_bit_set_elem_type(T) == E {
|
||||
for v in enums {
|
||||
bits |= {v}
|
||||
bits += {v}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ Builder :: struct {
|
||||
buf: [dynamic]byte,
|
||||
}
|
||||
/*
|
||||
Produces a Builder with a default length of 0 and cap of 16
|
||||
Produces an empty Builder
|
||||
|
||||
*Allocates Using Provided Allocator*
|
||||
|
||||
@@ -39,7 +39,7 @@ builder_make_none :: proc(allocator := context.allocator, loc := #caller_locatio
|
||||
return Builder{buf=make([dynamic]byte, allocator, loc) or_return }, nil
|
||||
}
|
||||
/*
|
||||
Produces a Builder with a specified length and cap of max(16,len) byte buffer
|
||||
Produces a Builder with specified length and capacity `len`.
|
||||
|
||||
*Allocates Using Provided Allocator*
|
||||
|
||||
@@ -55,7 +55,7 @@ builder_make_len :: proc(len: int, allocator := context.allocator, loc := #calle
|
||||
return Builder{buf=make([dynamic]byte, len, allocator, loc) or_return }, nil
|
||||
}
|
||||
/*
|
||||
Produces a Builder with a specified length and cap
|
||||
Produces a Builder with specified length `len` and capacity `cap`.
|
||||
|
||||
*Allocates Using Provided Allocator*
|
||||
|
||||
@@ -103,7 +103,7 @@ builder_make :: proc{
|
||||
builder_make_len_cap,
|
||||
}
|
||||
/*
|
||||
Initializes a Builder with a length of 0 and cap of 16
|
||||
Initializes an empty Builder
|
||||
It replaces the existing `buf`
|
||||
|
||||
*Allocates Using Provided Allocator*
|
||||
@@ -121,7 +121,7 @@ builder_init_none :: proc(b: ^Builder, allocator := context.allocator, loc := #c
|
||||
return b, nil
|
||||
}
|
||||
/*
|
||||
Initializes a Builder with a specified length and cap, which is max(len,16)
|
||||
Initializes a Builder with specified length and capacity `len`.
|
||||
It replaces the existing `buf`
|
||||
|
||||
*Allocates Using Provided Allocator*
|
||||
@@ -140,7 +140,7 @@ builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator,
|
||||
return b, nil
|
||||
}
|
||||
/*
|
||||
Initializes a Builder with a specified length and cap
|
||||
Initializes a Builder with specified length `len` and capacity `cap`.
|
||||
It replaces the existing `buf`
|
||||
|
||||
Inputs:
|
||||
@@ -723,11 +723,11 @@ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n:
|
||||
return write_string(b, s)
|
||||
}
|
||||
/*
|
||||
Writes a f32 value to the Builder and returns the number of characters written
|
||||
Writes a f64 value to the Builder and returns the number of characters written
|
||||
|
||||
Inputs:
|
||||
- b: A pointer to the Builder
|
||||
- f: The f32 value to be appended
|
||||
- f: The f64 value to be appended
|
||||
- fmt: The format byte
|
||||
- always_signed: Optional boolean flag to always include the sign
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ Returns:
|
||||
intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator, loc := #caller_location) -> (err: mem.Allocator_Error) {
|
||||
m.allocator = allocator
|
||||
m.entries = make(map[string]^Intern_Entry, 16, map_allocator, loc) or_return
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
/*
|
||||
Frees the map and all its content allocated using the `.allocator`.
|
||||
|
||||
@@ -304,7 +304,7 @@ try_recv_raw :: proc "contextless" (c: ^Raw_Chan, msg_out: rawptr) -> bool {
|
||||
|
||||
if sync.atomic_load(&c.closed) ||
|
||||
sync.atomic_load(&c.w_waiting) == 0 {
|
||||
return false
|
||||
return false
|
||||
}
|
||||
|
||||
mem.copy(msg_out, c.unbuffered_data, int(c.msg_size))
|
||||
|
||||
@@ -169,7 +169,7 @@ atomic_rw_mutex_shared_unlock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
|
||||
|
||||
if (state & Atomic_RW_Mutex_State_Reader_Mask == Atomic_RW_Mutex_State_Reader) &&
|
||||
(state & Atomic_RW_Mutex_State_Is_Writing != 0) {
|
||||
atomic_sema_post(&rw.sema)
|
||||
atomic_sema_post(&rw.sema)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ StringCopyToOdinString :: proc(
|
||||
max := StringGetMaximumSizeForEncoding(length, StringEncoding(StringBuiltInEncodings.UTF8))
|
||||
|
||||
buf, err := make([]byte, max, allocator)
|
||||
if err != nil do return
|
||||
if err != nil { return }
|
||||
|
||||
raw_str := runtime.Raw_String {
|
||||
data = raw_data(buf),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -117,7 +117,7 @@ init_cpu_name :: proc "c" () {
|
||||
return
|
||||
}
|
||||
|
||||
_buf := transmute(^[0x12]u32)&_cpu_name_buf
|
||||
_buf := (^[0x12]u32)(&_cpu_name_buf)
|
||||
_buf[ 0], _buf[ 1], _buf[ 2], _buf[ 3] = cpuid(0x8000_0002, 0)
|
||||
_buf[ 4], _buf[ 5], _buf[ 6], _buf[ 7] = cpuid(0x8000_0003, 0)
|
||||
_buf[ 8], _buf[ 9], _buf[10], _buf[11] = cpuid(0x8000_0004, 0)
|
||||
|
||||
+86
-49
@@ -48,6 +48,7 @@ Errno :: enum i32 {
|
||||
ENOSYS = 38,
|
||||
ENOTEMPTY = 39,
|
||||
ELOOP = 40,
|
||||
EUNKNOWN_41 = 41,
|
||||
ENOMSG = 42,
|
||||
EIDRM = 43,
|
||||
ECHRNG = 44,
|
||||
@@ -64,6 +65,7 @@ Errno :: enum i32 {
|
||||
ENOANO = 55,
|
||||
EBADRQC = 56,
|
||||
EBADSLT = 57,
|
||||
EUNKNOWN_58 = 58,
|
||||
EBFONT = 59,
|
||||
ENOSTR = 60,
|
||||
ENODATA = 61,
|
||||
@@ -150,44 +152,66 @@ Errno :: enum i32 {
|
||||
RDONLY flag is not present, because it has the value of 0, i.e. it is the
|
||||
default, unless WRONLY or RDWR is specified.
|
||||
*/
|
||||
Open_Flags_Bits :: enum {
|
||||
WRONLY = 0,
|
||||
RDWR = 1,
|
||||
CREAT = 6,
|
||||
EXCL = 7,
|
||||
NOCTTY = 8,
|
||||
TRUNC = 9,
|
||||
APPEND = 10,
|
||||
NONBLOCK = 11,
|
||||
DSYNC = 12,
|
||||
ASYNC = 13,
|
||||
DIRECT = 14,
|
||||
LARGEFILE = 15,
|
||||
DIRECTORY = 16,
|
||||
NOFOLLOW = 17,
|
||||
NOATIME = 18,
|
||||
CLOEXEC = 19,
|
||||
PATH = 21,
|
||||
}
|
||||
when ODIN_ARCH != .arm64 && ODIN_ARCH != .arm32 {
|
||||
Open_Flags_Bits :: enum {
|
||||
WRONLY = 0,
|
||||
RDWR = 1,
|
||||
CREAT = 6,
|
||||
EXCL = 7,
|
||||
NOCTTY = 8,
|
||||
TRUNC = 9,
|
||||
APPEND = 10,
|
||||
NONBLOCK = 11,
|
||||
DSYNC = 12,
|
||||
ASYNC = 13,
|
||||
DIRECT = 14,
|
||||
LARGEFILE = 15,
|
||||
DIRECTORY = 16,
|
||||
NOFOLLOW = 17,
|
||||
NOATIME = 18,
|
||||
CLOEXEC = 19,
|
||||
PATH = 21,
|
||||
}
|
||||
// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
|
||||
#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
|
||||
#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
|
||||
#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
|
||||
#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
|
||||
#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
|
||||
#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
|
||||
#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
|
||||
#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
|
||||
#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
|
||||
#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
|
||||
|
||||
// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
|
||||
#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
|
||||
#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
|
||||
#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
|
||||
#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
|
||||
#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
|
||||
#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
|
||||
#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
|
||||
#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
|
||||
#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
|
||||
#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
|
||||
#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
|
||||
#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
|
||||
} else {
|
||||
Open_Flags_Bits :: enum {
|
||||
WRONLY = 0,
|
||||
RDWR = 1,
|
||||
CREAT = 6,
|
||||
EXCL = 7,
|
||||
NOCTTY = 8,
|
||||
TRUNC = 9,
|
||||
APPEND = 10,
|
||||
NONBLOCK = 11,
|
||||
DSYNC = 12,
|
||||
ASYNC = 13,
|
||||
DIRECTORY = 14,
|
||||
NOFOLLOW = 15,
|
||||
DIRECT = 16,
|
||||
LARGEFILE = 17,
|
||||
NOATIME = 18,
|
||||
CLOEXEC = 19,
|
||||
PATH = 21,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Bits for FD_Flags bitset
|
||||
@@ -867,7 +891,7 @@ Wait_Option :: enum {
|
||||
WSTOPPED = 1,
|
||||
WEXITED = 2,
|
||||
WCONTINUED = 3,
|
||||
WNOWAIT = 24,
|
||||
WNOWAIT = 24,
|
||||
// // For processes created using clone
|
||||
__WNOTHREAD = 29,
|
||||
__WALL = 30,
|
||||
@@ -946,9 +970,22 @@ Sig_Stack_Flag :: enum i32 {
|
||||
AUTODISARM = 31,
|
||||
}
|
||||
|
||||
Sig_Action_Flag :: enum u32 {
|
||||
NOCLDSTOP = 0,
|
||||
NOCLDWAIT = 1,
|
||||
SIGINFO = 2,
|
||||
UNSUPPORTED = 10,
|
||||
EXPOSE_TAGBITS = 11,
|
||||
RESTORER = 26,
|
||||
ONSTACK = 27,
|
||||
RESTART = 28,
|
||||
NODEFER = 30,
|
||||
RESETHAND = 31,
|
||||
}
|
||||
|
||||
/*
|
||||
Type of socket to create
|
||||
- For TCP you want to use SOCK_STREAM
|
||||
- For TCP you want to use SOCK_STREAM
|
||||
- For UDP you want to use SOCK_DGRAM
|
||||
Also see `Protocol`
|
||||
*/
|
||||
@@ -1427,16 +1464,16 @@ Futex_Flags_Bits :: enum {
|
||||
Kind of operation on futex, see FUTEX_WAKE_OP
|
||||
*/
|
||||
Futex_Arg_Op :: enum {
|
||||
SET = 0, /* uaddr2 = oparg; */
|
||||
ADD = 1, /* uaddr2 += oparg; */
|
||||
OR = 2, /* uaddr2 |= oparg; */
|
||||
ANDN = 3, /* uaddr2 &= ~oparg; */
|
||||
XOR = 4, /* uaddr2 ^= oparg; */
|
||||
PO2_SET = 0, /* uaddr2 = 1<<oparg; */
|
||||
PO2_ADD = 1, /* uaddr2 += 1<<oparg; */
|
||||
PO2_OR = 2, /* uaddr2 |= 1<<oparg; */
|
||||
PO2_ANDN = 3, /* uaddr2 &= ~(1<<oparg); */
|
||||
PO2_XOR = 4, /* uaddr2 ^= 1<<oparg; */
|
||||
SET = 0, /* uaddr2 = oparg */
|
||||
ADD = 1, /* uaddr2 += oparg */
|
||||
OR = 2, /* uaddr2 |= oparg */
|
||||
ANDN = 3, /* uaddr2 &= ~oparg */
|
||||
XOR = 4, /* uaddr2 ^= oparg */
|
||||
PO2_SET = 0, /* uaddr2 = 1<<oparg */
|
||||
PO2_ADD = 1, /* uaddr2 += 1<<oparg */
|
||||
PO2_OR = 2, /* uaddr2 |= 1<<oparg */
|
||||
PO2_ANDN = 3, /* uaddr2 &~= 1<<oparg */
|
||||
PO2_XOR = 4, /* uaddr2 ^= 1<<oparg */
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+37
-27
@@ -69,7 +69,7 @@ close :: proc "contextless" (fd: Fd) -> (Errno) {
|
||||
stat :: proc "contextless" (filename: cstring, stat: ^Stat) -> (Errno) {
|
||||
when size_of(int) == 8 {
|
||||
when ODIN_ARCH == .arm64 {
|
||||
ret := syscall(SYS_fstatat, AT_FDCWD, cast(rawptr) filename, stat)
|
||||
ret := syscall(SYS_fstatat, AT_FDCWD, cast(rawptr) filename, stat, 0)
|
||||
return Errno(-ret)
|
||||
} else {
|
||||
ret := syscall(SYS_stat, cast(rawptr) filename, stat)
|
||||
@@ -200,10 +200,25 @@ brk :: proc "contextless" (addr: uintptr) -> (Errno) {
|
||||
return Errno(-ret)
|
||||
}
|
||||
|
||||
/*
|
||||
Returns from signal handlers on some archs.
|
||||
*/
|
||||
rt_sigreturn :: proc "c" () -> ! {
|
||||
intrinsics.syscall(uintptr(SYS_rt_sigreturn))
|
||||
unreachable()
|
||||
}
|
||||
|
||||
/*
|
||||
Alter an action taken by a process.
|
||||
*/
|
||||
rt_sigaction :: proc "contextless" (sig: Signal, sigaction: ^Sig_Action, old_sigaction: ^Sig_Action) -> Errno {
|
||||
rt_sigaction :: proc "contextless" (sig: Signal, sigaction: ^Sig_Action($T), old_sigaction: ^Sig_Action) -> Errno {
|
||||
// NOTE(jason): It appears that the restorer is required for i386 and amd64
|
||||
when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
|
||||
sigaction.flags += {.RESTORER}
|
||||
}
|
||||
if sigaction != nil && sigaction.restorer == nil && .RESTORER in sigaction.flags {
|
||||
sigaction.restorer = rt_sigreturn
|
||||
}
|
||||
ret := syscall(SYS_rt_sigaction, sig, sigaction, old_sigaction, size_of(Sig_Set))
|
||||
return Errno(-ret)
|
||||
}
|
||||
@@ -1123,7 +1138,7 @@ ftruncate :: proc "contextless" (fd: Fd, length: i64) -> (Errno) {
|
||||
ret := syscall(SYS_ftruncate64, fd, compat64_arg_pair(length))
|
||||
return Errno(-ret)
|
||||
} else {
|
||||
ret := syscall(SYS_truncate, fd, compat64_arg_pair(length))
|
||||
ret := syscall(SYS_ftruncate, fd, compat64_arg_pair(length))
|
||||
return Errno(-ret)
|
||||
}
|
||||
}
|
||||
@@ -1231,7 +1246,7 @@ creat :: proc "contextless" (name: cstring, mode: Mode) -> (Fd, Errno) {
|
||||
*/
|
||||
link :: proc "contextless" (target: cstring, linkpath: cstring) -> (Errno) {
|
||||
when ODIN_ARCH == .arm64 {
|
||||
ret := syscall(SYS_linkat, AT_FDCWD, cast(rawptr) target, AT_FDCWD, cast(rawptr) linkpath)
|
||||
ret := syscall(SYS_linkat, AT_FDCWD, cast(rawptr) target, AT_FDCWD, cast(rawptr) linkpath, 0)
|
||||
return Errno(-ret)
|
||||
} else {
|
||||
ret := syscall(SYS_link, cast(rawptr) target, cast(rawptr) linkpath)
|
||||
@@ -1261,7 +1276,7 @@ unlink :: proc "contextless" (name: cstring) -> (Errno) {
|
||||
*/
|
||||
symlink :: proc "contextless" (target: cstring, linkpath: cstring) -> (Errno) {
|
||||
when ODIN_ARCH == .arm64 {
|
||||
ret := syscall(SYS_symlinkat, AT_FDCWD, cast(rawptr) target, cast(rawptr) linkpath)
|
||||
ret := syscall(SYS_symlinkat, cast(rawptr) target, AT_FDCWD, cast(rawptr) linkpath)
|
||||
return Errno(-ret)
|
||||
} else {
|
||||
ret := syscall(SYS_symlink, cast(rawptr) target, cast(rawptr) linkpath)
|
||||
@@ -1291,7 +1306,7 @@ readlink :: proc "contextless" (name: cstring, buf: []u8) -> (int, Errno) {
|
||||
*/
|
||||
chmod :: proc "contextless" (name: cstring, mode: Mode) -> (Errno) {
|
||||
when ODIN_ARCH == .arm64 {
|
||||
ret := syscall(SYS_fchmodat, cast(rawptr) name, transmute(u32) mode, 0)
|
||||
ret := syscall(SYS_fchmodat, AT_FDCWD, cast(rawptr) name, transmute(u32) mode)
|
||||
return Errno(-ret)
|
||||
} else {
|
||||
ret := syscall(SYS_chmod, cast(rawptr) name, transmute(u32) mode)
|
||||
@@ -2226,8 +2241,7 @@ futex_wake :: proc "contextless" (futex: ^Futex, op: Futex_Wake_Type, flags: Fut
|
||||
Returns the total number of waiters that have been woken up plus the number of waiters requeued.
|
||||
*/
|
||||
futex_cmp_requeue :: proc "contextless" (futex: ^Futex, op: Futex_Cmp_Requeue_Type, flags: Futex_Flags, requeue_threshold: u32,
|
||||
requeue_max: i32, requeue_futex: ^Futex, val: i32) -> (int, Errno)
|
||||
{
|
||||
requeue_max: i32, requeue_futex: ^Futex, val: i32) -> (int, Errno) {
|
||||
futex_flags := cast(u32) op + transmute(u32) flags
|
||||
ret := syscall(SYS_futex, futex, futex_flags, requeue_threshold, requeue_max, requeue_futex, val)
|
||||
return errno_unwrap(ret, int)
|
||||
@@ -2238,8 +2252,7 @@ futex_cmp_requeue :: proc "contextless" (futex: ^Futex, op: Futex_Cmp_Requeue_Ty
|
||||
Returns the total number of waiters that have been woken up.
|
||||
*/
|
||||
futex_requeue :: proc "contextless" (futex: ^Futex, op: Futex_Requeue_Type, flags: Futex_Flags, requeue_threshold: u32,
|
||||
requeue_max: i32, requeue_futex: ^Futex) -> (int, Errno)
|
||||
{
|
||||
requeue_max: i32, requeue_futex: ^Futex) -> (int, Errno) {
|
||||
futex_flags := cast(u32) op + transmute(u32) flags
|
||||
ret := syscall(SYS_futex, futex, futex_flags, requeue_threshold, requeue_max, requeue_futex)
|
||||
return errno_unwrap(ret, int)
|
||||
@@ -2250,8 +2263,7 @@ futex_requeue :: proc "contextless" (futex: ^Futex, op: Futex_Requeue_Type, flag
|
||||
purpose is to allow implementing conditional values sync primitive, it seems like.
|
||||
*/
|
||||
futex_wake_op :: proc "contextless" (futex: ^Futex, op: Futex_Wake_Op_Type, flags: Futex_Flags, wakeup: i32,
|
||||
dst_wakeup, dst: ^Futex, futex_op: u32) -> (int, Errno)
|
||||
{
|
||||
dst_wakeup, dst: ^Futex, futex_op: u32) -> (int, Errno) {
|
||||
futex_flags := cast(u32) op + transmute(u32) flags
|
||||
ret := syscall(SYS_futex, futex, futex_flags, wakeup, dst_wakeup, dst, futex_op)
|
||||
return errno_unwrap(ret, int)
|
||||
@@ -2261,8 +2273,7 @@ futex_wake_op :: proc "contextless" (futex: ^Futex, op: Futex_Wake_Op_Type, flag
|
||||
Same as wait, but mask specifies bits that must be equal for the mutex to wake up.
|
||||
*/
|
||||
futex_wait_bitset :: proc "contextless" (futex: ^Futex, op: Futex_Wait_Bitset_Type, flags: Futex_Flags, val: u32,
|
||||
timeout: ^Time_Spec, mask: u32) -> (int, Errno)
|
||||
{
|
||||
timeout: ^Time_Spec, mask: u32) -> (int, Errno) {
|
||||
futex_flags := cast(u32) op + transmute(u32) flags
|
||||
ret := syscall(SYS_futex, futex, futex_flags, val, timeout, 0, mask)
|
||||
return errno_unwrap(ret, int)
|
||||
@@ -2271,8 +2282,7 @@ futex_wait_bitset :: proc "contextless" (futex: ^Futex, op: Futex_Wait_Bitset_Ty
|
||||
/*
|
||||
Wake up on bitset.
|
||||
*/
|
||||
futex_wake_bitset :: proc "contextless" (futex: ^Futex, op: Futex_Wake_Bitset_Type, flags: Futex_Flags, n_wakeup: u32, mask: u32) -> (int, Errno)
|
||||
{
|
||||
futex_wake_bitset :: proc "contextless" (futex: ^Futex, op: Futex_Wake_Bitset_Type, flags: Futex_Flags, n_wakeup: u32, mask: u32) -> (int, Errno) {
|
||||
futex_flags := cast(u32) op + transmute(u32) flags
|
||||
ret := syscall(SYS_futex, futex, futex_flags, n_wakeup, 0, 0, mask)
|
||||
return errno_unwrap(ret, int)
|
||||
@@ -2280,7 +2290,7 @@ futex_wake_bitset :: proc "contextless" (futex: ^Futex, op: Futex_Wake_Bitset_Ty
|
||||
|
||||
// TODO(flysand): Priority inheritance (PI) futicees
|
||||
|
||||
futex :: proc {
|
||||
futex :: proc{
|
||||
futex_wait,
|
||||
futex_wake,
|
||||
futex_cmp_requeue,
|
||||
@@ -2476,8 +2486,8 @@ tgkill :: proc "contextless" (tgid, tid: Pid, sig: Signal) -> (Errno) {
|
||||
Wait on process, process group or pid file descriptor.
|
||||
Available since Linux 2.6.10.
|
||||
*/
|
||||
waitid :: proc "contextless" (id_type: Id_Type, id: Id, sig_info: ^Sig_Info, options: Wait_Options) -> (Errno) {
|
||||
ret := syscall(SYS_waitid, id_type, id, sig_info, transmute(i32) options)
|
||||
waitid :: proc "contextless" (id_type: Id_Type, id: Id, sig_info: ^Sig_Info, options: Wait_Options, rusage: ^RUsage) -> (Errno) {
|
||||
ret := syscall(SYS_waitid, id_type, id, sig_info, transmute(i32) options, rusage)
|
||||
return Errno(-ret)
|
||||
}
|
||||
|
||||
@@ -2504,7 +2514,7 @@ waitid :: proc "contextless" (id_type: Id_Type, id: Id, sig_info: ^Sig_Info, opt
|
||||
Available since Linux 2.6.16.
|
||||
*/
|
||||
openat :: proc "contextless" (fd: Fd, name: cstring, flags: Open_Flags, mode: Mode = {}) -> (Fd, Errno) {
|
||||
ret := syscall(SYS_openat, fd, AT_FDCWD, transmute(uintptr) name, transmute(u32) mode)
|
||||
ret := syscall(SYS_openat, fd, transmute(uintptr) name, transmute(u32) flags, transmute(u32) mode)
|
||||
return errno_unwrap(ret, Fd)
|
||||
}
|
||||
|
||||
@@ -2583,8 +2593,8 @@ linkat :: proc "contextless" (target_dirfd: Fd, oldpath: cstring, link_dirfd: Fd
|
||||
Create a symbolic link at specified dirfd.
|
||||
Available since Linux 2.6.16.
|
||||
*/
|
||||
symlinkat :: proc "contextless" (dirfd: Fd, target: cstring, linkpath: cstring) -> (Errno) {
|
||||
ret := syscall(SYS_symlinkat, dirfd, cast(rawptr) target, cast(rawptr) linkpath)
|
||||
symlinkat :: proc "contextless" (target: cstring, dirfd: Fd, linkpath: cstring) -> (Errno) {
|
||||
ret := syscall(SYS_symlinkat, cast(rawptr) target, dirfd, cast(rawptr) linkpath)
|
||||
return Errno(-ret)
|
||||
}
|
||||
|
||||
@@ -2619,13 +2629,13 @@ faccessat :: proc "contextless" (dirfd: Fd, name: cstring, mode: Mode = F_OK) ->
|
||||
Wait for events on a file descriptor.
|
||||
Available since Linux 2.6.16.
|
||||
*/
|
||||
ppoll :: proc "contextless" (fds: []Poll_Fd, timeout: ^Time_Spec, sigmask: ^Sig_Set) -> (Errno) {
|
||||
ppoll :: proc "contextless" (fds: []Poll_Fd, timeout: ^Time_Spec, sigmask: ^Sig_Set) -> (i32, Errno) {
|
||||
when size_of(int) == 8 {
|
||||
ret := syscall(SYS_ppoll, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
|
||||
return Errno(-ret)
|
||||
return errno_unwrap(ret, i32)
|
||||
} else {
|
||||
ret := syscall(SYS_ppoll_time64, raw_data(fds), len(fds), timeout, sigmask, size_of(Sig_Set))
|
||||
return Errno(-ret)
|
||||
return errno_unwrap(ret, i32)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2808,8 +2818,8 @@ getrandom :: proc "contextless" (buf: []u8, flags: Get_Random_Flags) -> (int, Er
|
||||
Execute program relative to a directory file descriptor.
|
||||
Available since Linux 3.19.
|
||||
*/
|
||||
execveat :: proc "contextless" (dirfd: Fd, name: cstring, argv: [^]cstring, envp: [^]cstring) -> (Errno) {
|
||||
ret := syscall(SYS_execveat, dirfd, cast(rawptr) name, cast(rawptr) argv, cast(rawptr) envp)
|
||||
execveat :: proc "contextless" (dirfd: Fd, name: cstring, argv: [^]cstring, envp: [^]cstring, flags: FD_Flags = {}) -> (Errno) {
|
||||
ret := syscall(SYS_execveat, dirfd, cast(rawptr) name, cast(rawptr) argv, cast(rawptr) envp, transmute(i32) flags)
|
||||
return Errno(-ret)
|
||||
}
|
||||
|
||||
|
||||
+76
-20
@@ -18,7 +18,7 @@ Gid :: distinct u32
|
||||
/*
|
||||
Type for Process IDs, Thread IDs, Thread group ID.
|
||||
*/
|
||||
Pid :: distinct int
|
||||
Pid :: distinct i32
|
||||
|
||||
/*
|
||||
Type for any of: pid, pidfd, pgid.
|
||||
@@ -89,11 +89,11 @@ FD_Flags :: bit_set[FD_Flags_Bits; i32]
|
||||
Represents file's permission and status bits
|
||||
**Example:**
|
||||
When you're passing a value of this type the recommended usage is:
|
||||
|
||||
|
||||
```
|
||||
linux.Mode{.S_IXOTH, .S_IROTH} | linux.S_IRWXU | linux.S_IRWXG
|
||||
```
|
||||
|
||||
|
||||
This would generate a mode that has full permissions for the
|
||||
file's owner and group, and only "read" and "execute" bits
|
||||
for others.
|
||||
@@ -151,9 +151,9 @@ when ODIN_ARCH == .amd64 {
|
||||
size: i64,
|
||||
blksize: uint,
|
||||
blocks: u64,
|
||||
atim: Time_Spec,
|
||||
mtim: Time_Spec,
|
||||
ctim: Time_Spec,
|
||||
atime: Time_Spec,
|
||||
mtime: Time_Spec,
|
||||
ctime: Time_Spec,
|
||||
ino: Inode,
|
||||
}
|
||||
}
|
||||
@@ -495,16 +495,15 @@ Pid_FD_Flags :: bit_set[Pid_FD_Flags_Bits; i32]
|
||||
// 1. Odin's bitfields start from 0, whereas signals start from 1
|
||||
// 2. It's unclear how bitfields act in terms of ABI (are they an array of ints or an array of longs?).
|
||||
// it makes a difference because ARM is big endian.
|
||||
@private _SIGSET_NWORDS :: (1024 / (8 * size_of(uint)))
|
||||
@private _SIGSET_NWORDS :: (8 / size_of(uint))
|
||||
Sig_Set :: [_SIGSET_NWORDS]uint
|
||||
|
||||
@private SI_MAX_SIZE :: 128
|
||||
@private SI_ARCH_PREAMBLE :: 3 * size_of(i32)
|
||||
@private SI_PAD_SIZE :: (SI_MAX_SIZE - SI_ARCH_PREAMBLE) / size_of(i32)
|
||||
@private SI_TIMER_PAD_SIZE :: size_of(Uid) - size_of(i32)
|
||||
@private SI_ARCH_PREAMBLE :: 4 * size_of(i32)
|
||||
@private SI_PAD_SIZE :: SI_MAX_SIZE - SI_ARCH_PREAMBLE
|
||||
|
||||
Sig_Handler_Fn :: #type proc "c" (sig: Signal)
|
||||
Sig_Restore_Fn :: #type proc "c" ()
|
||||
Sig_Restore_Fn :: #type proc "c" () -> !
|
||||
|
||||
Sig_Info :: struct #packed {
|
||||
signo: Signal,
|
||||
@@ -518,8 +517,9 @@ Sig_Info :: struct #packed {
|
||||
uid: Uid, /* sender's uid */
|
||||
},
|
||||
using _timer: struct {
|
||||
timerid: i32, /* timer id */
|
||||
timerid: i32, /* timer id */
|
||||
overrun: i32, /* overrun count */
|
||||
value: Sig_Val, /* timer value */
|
||||
},
|
||||
/* POSIX.1b signals */
|
||||
using _rt: struct {
|
||||
@@ -528,8 +528,8 @@ Sig_Info :: struct #packed {
|
||||
},
|
||||
/* SIGCHLD */
|
||||
using _sigchld: struct {
|
||||
_pid1: Pid, /* which child */
|
||||
_uid1: Uid, /* sender's uid */
|
||||
_pid1: Pid, /* which child */
|
||||
_uid1: Uid, /* sender's uid */
|
||||
status: i32, /* exit code */
|
||||
utime: uint,
|
||||
stime: uint, //clock_t
|
||||
@@ -537,7 +537,24 @@ Sig_Info :: struct #packed {
|
||||
/* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
|
||||
using _sigfault: struct {
|
||||
addr: rawptr, /* faulting insn/memory ref. */
|
||||
addr_lsb: i16, /* LSB of the reported address */
|
||||
using _: struct #raw_union {
|
||||
trapno: i32, /* Trap number that caused signal */
|
||||
addr_lsb: i16, /* LSB of the reported address */
|
||||
using _addr_bnd: struct {
|
||||
_pad2: u64,
|
||||
lower: rawptr, /* lower bound during fault */
|
||||
upper: rawptr, /* upper bound during fault */
|
||||
},
|
||||
using _addr_pkey: struct {
|
||||
_pad3: u64,
|
||||
pkey: u32, /* protection key on PTE that faulted */
|
||||
},
|
||||
using _perf: struct {
|
||||
perf_data: u64,
|
||||
perf_type: u32,
|
||||
perf_flags: u32,
|
||||
},
|
||||
},
|
||||
},
|
||||
/* SIGPOLL */
|
||||
using _sigpoll: struct {
|
||||
@@ -547,12 +564,43 @@ Sig_Info :: struct #packed {
|
||||
/* SIGSYS */
|
||||
using _sigsys: struct {
|
||||
call_addr: rawptr, /* calling user insn */
|
||||
syscall: i32, /* triggering system call number */
|
||||
arch: u32, /* AUDIT_ARCH_* of syscall */
|
||||
syscall: i32, /* triggering system call number */
|
||||
arch: u32, /* AUDIT_ARCH_* of syscall */
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
#assert(size_of(Sig_Info) == 128)
|
||||
when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
|
||||
#assert(offset_of(Sig_Info, signo) == 0x00)
|
||||
#assert(offset_of(Sig_Info, errno) == 0x04)
|
||||
#assert(offset_of(Sig_Info, code) == 0x08)
|
||||
#assert(offset_of(Sig_Info, pid) == 0x10)
|
||||
#assert(offset_of(Sig_Info, uid) == 0x14)
|
||||
#assert(offset_of(Sig_Info, timerid) == 0x10)
|
||||
#assert(offset_of(Sig_Info, overrun) == 0x14)
|
||||
#assert(offset_of(Sig_Info, value) == 0x18)
|
||||
#assert(offset_of(Sig_Info, status) == 0x18)
|
||||
#assert(offset_of(Sig_Info, utime) == 0x20)
|
||||
#assert(offset_of(Sig_Info, stime) == 0x28)
|
||||
#assert(offset_of(Sig_Info, addr) == 0x10)
|
||||
#assert(offset_of(Sig_Info, addr_lsb) == 0x18)
|
||||
#assert(offset_of(Sig_Info, trapno) == 0x18)
|
||||
#assert(offset_of(Sig_Info, lower) == 0x20)
|
||||
#assert(offset_of(Sig_Info, upper) == 0x28)
|
||||
#assert(offset_of(Sig_Info, pkey) == 0x20)
|
||||
#assert(offset_of(Sig_Info, perf_data) == 0x18)
|
||||
#assert(offset_of(Sig_Info, perf_type) == 0x20)
|
||||
#assert(offset_of(Sig_Info, perf_flags) == 0x24)
|
||||
#assert(offset_of(Sig_Info, band) == 0x10)
|
||||
#assert(offset_of(Sig_Info, fd) == 0x18)
|
||||
#assert(offset_of(Sig_Info, call_addr) == 0x10)
|
||||
#assert(offset_of(Sig_Info, syscall) == 0x18)
|
||||
#assert(offset_of(Sig_Info, arch) == 0x1C)
|
||||
} else {
|
||||
// TODO
|
||||
}
|
||||
|
||||
SIGEV_MAX_SIZE :: 64
|
||||
SIGEV_PAD_SIZE :: ((SIGEV_MAX_SIZE-size_of(i32)*2+size_of(Sig_Val))/size_of(i32))
|
||||
|
||||
@@ -583,12 +631,20 @@ Sig_Stack :: struct {
|
||||
size: uintptr,
|
||||
}
|
||||
|
||||
Sig_Action_Special :: enum uint {
|
||||
SIG_DFL = 0,
|
||||
SIG_IGN = 1,
|
||||
SIG_ERR = ~uint(0),
|
||||
}
|
||||
|
||||
Sig_Action_Flags :: bit_set[Sig_Action_Flag; uint]
|
||||
Sig_Action :: struct($T: typeid) {
|
||||
using _u: struct #raw_union {
|
||||
handler: Sig_Handler_Fn,
|
||||
sigaction: #type proc "c" (sig: Signal, si: ^Sig_Info, ctx: ^T),
|
||||
special: Sig_Action_Special,
|
||||
},
|
||||
flags: uint,
|
||||
flags: Sig_Action_Flags,
|
||||
restorer: Sig_Restore_Fn,
|
||||
mask: Sig_Set,
|
||||
}
|
||||
@@ -733,7 +789,7 @@ RLimit :: struct {
|
||||
|
||||
/*
|
||||
Structure representing how much of each resource got used.
|
||||
*/
|
||||
*/
|
||||
RUsage :: struct {
|
||||
utime: Time_Val,
|
||||
stime: Time_Val,
|
||||
@@ -813,7 +869,7 @@ when size_of(int) == 8 || ODIN_ARCH == .i386 {
|
||||
cpid: Pid,
|
||||
lpid: Pid,
|
||||
nattach: uint,
|
||||
_: [2]uint,
|
||||
_: [2]uint,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ WCOREDUMP :: #force_inline proc "contextless" (s: u32) -> bool {
|
||||
return 1 << ((cast(uint)(sig) - 1) % (8*size_of(uint)))
|
||||
}
|
||||
@private _sigword :: proc "contextless" (sig: Signal) -> (uint) {
|
||||
return (cast(uint)sig - 1) / (8*size_of(uint))
|
||||
return (cast(uint)sig - 1) / (8*size_of(uint))
|
||||
}
|
||||
|
||||
// TODO: sigaddset etc
|
||||
@@ -85,13 +85,13 @@ dirent_iterate_buf :: proc "contextless" (buf: []u8, offs: ^int) -> (d: ^Dirent,
|
||||
/// Obtain the name of dirent as a string
|
||||
/// The lifetime of the string is bound to the lifetime of the provided dirent structure
|
||||
dirent_name :: proc "contextless" (dirent: ^Dirent) -> string #no_bounds_check {
|
||||
str := transmute([^]u8) &dirent.name
|
||||
str := ([^]u8)(&dirent.name)
|
||||
// Note(flysand): The string size calculated above applies only to the ideal case
|
||||
// we subtract 1 byte from the string size, because a null terminator is guaranteed
|
||||
// to be present. But! That said, the dirents are aligned to 8 bytes and the padding
|
||||
// between the null terminator and the start of the next struct may be not initialized
|
||||
// which means we also have to scan these garbage bytes.
|
||||
str_size := (cast(int) dirent.reclen) - 1 - cast(int) offset_of(Dirent, name)
|
||||
str_size := int(dirent.reclen) - 1 - cast(int)offset_of(Dirent, name)
|
||||
// This skips *only* over the garbage, since if we're not garbage we're at nul terminator,
|
||||
// which skips this loop
|
||||
for str[str_size] != 0 {
|
||||
@@ -115,7 +115,6 @@ futex_op :: proc "contextless" (arg_op: Futex_Arg_Op, cmp_op: Futex_Cmp_Op, op_a
|
||||
/// Helper function for constructing the config for caches
|
||||
perf_cache_config :: #force_inline proc "contextless" (id: Perf_Hardware_Cache_Id,
|
||||
op: Perf_Hardware_Cache_Op_Id,
|
||||
res: Perf_Hardware_Cache_Result_Id) -> u64
|
||||
{
|
||||
res: Perf_Hardware_Cache_Result_Id) -> u64 {
|
||||
return u64(id) | (u64(op) << 8) | (u64(res) << 16)
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//+build windows
|
||||
package sys_windows
|
||||
|
||||
foreign import kernel32 "system:Kernel32.lib"
|
||||
|
||||
@(default_calling_convention="system")
|
||||
foreign kernel32 {
|
||||
CreateToolhelp32Snapshot :: proc (dwFlags: DWORD, th32ProcessID: DWORD) -> HANDLE ---
|
||||
Process32FirstW :: proc (hSnapshot: HANDLE, lppe: LPPROCESSENTRY32W) -> BOOL ---
|
||||
Process32NextW :: proc (hSnapshot: HANDLE, lppe: LPPROCESSENTRY32W) -> BOOL ---
|
||||
Thread32First :: proc (hSnapshot: HANDLE, lpte: LPTHREADENTRY32) -> BOOL ---
|
||||
Thread32Next :: proc (hSnapshot: HANDLE, lpte: LPTHREADENTRY32) -> BOOL ---
|
||||
Module32FirstW :: proc (hSnapshot: HANDLE, lpme: LPMODULEENTRY32W) -> BOOL ---
|
||||
Module32NextW :: proc (hSnapshot: HANDLE, lpme: LPMODULEENTRY32W) -> BOOL ---
|
||||
Heap32ListFirst :: proc (hSnapshot: HANDLE, lphl: LPHEAPLIST32) -> BOOL ---
|
||||
Heap32ListNext :: proc (hSnapshot: HANDLE, lphl: LPHEAPLIST32) -> BOOL ---
|
||||
Heap32First :: proc (lphe: LPHEAPENTRY32, th32ProcessID: DWORD, th32HeapID: ULONG_PTR) -> BOOL ---
|
||||
Heap32Next :: proc (lphe: LPHEAPENTRY32) -> BOOL ---
|
||||
Toolhelp32ReadProcessMemory :: proc (
|
||||
th32ProcessID: DWORD,
|
||||
lpBaseAddress: LPCVOID,
|
||||
lpBuffer: LPVOID,
|
||||
cbRead: SIZE_T,
|
||||
lpNumberOfBytesRead: ^SIZE_T,
|
||||
) -> BOOL ---
|
||||
}
|
||||
|
||||
MAX_MODULE_NAME32 :: 255
|
||||
|
||||
TH32CS_INHERIT :: 0x80000000
|
||||
TH32CS_SNAPHEAPLIST :: 0x00000001
|
||||
TH32CS_SNAPPROCESS :: 0x00000002
|
||||
TH32CS_SNAPTHREAD :: 0x00000004
|
||||
TH32CS_SNAPMODULE :: 0x00000008
|
||||
TH32CS_SNAPMODULE32 :: 0x00000010
|
||||
TH32CS_SNAPALL :: TH32CS_SNAPHEAPLIST | TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD | TH32CS_SNAPMODULE
|
||||
|
||||
PROCESSENTRY32W :: struct {
|
||||
dwSize: DWORD,
|
||||
cntUsage: DWORD,
|
||||
th32ProcessID: DWORD,
|
||||
th32DefaultHeapID: ULONG_PTR,
|
||||
th32ModuleID: DWORD,
|
||||
cntThreads: DWORD,
|
||||
th32ParentProcessID: DWORD,
|
||||
pcPriClassBase: LONG,
|
||||
dwFlags: DWORD,
|
||||
szExeFile: [MAX_PATH]WCHAR,
|
||||
}
|
||||
|
||||
LPPROCESSENTRY32W :: ^PROCESSENTRY32W
|
||||
|
||||
THREADENTRY32 :: struct {
|
||||
dwSize: DWORD,
|
||||
cntUsage: DWORD,
|
||||
th32ThreadID: DWORD,
|
||||
th32OwnerProcessID: DWORD,
|
||||
tpBasePri: LONG,
|
||||
tpDeltaPri: LONG,
|
||||
dwFlags: DWORD,
|
||||
}
|
||||
|
||||
LPTHREADENTRY32 :: ^THREADENTRY32
|
||||
|
||||
MODULEENTRY32W :: struct {
|
||||
dwSize: DWORD,
|
||||
th32ModuleID: DWORD,
|
||||
th32ProcessID: DWORD,
|
||||
GlblcntUsage: DWORD,
|
||||
ProccntUsage: DWORD,
|
||||
modBaseAddr: BYTE,
|
||||
modBaseSize: DWORD,
|
||||
hModule: HMODULE,
|
||||
szModule: [MAX_MODULE_NAME32 + 1]WCHAR,
|
||||
szExePath: [MAX_PATH]WCHAR,
|
||||
}
|
||||
|
||||
LPMODULEENTRY32W :: ^MODULEENTRY32W
|
||||
|
||||
HEAPLIST32 :: struct {
|
||||
dwSize: SIZE_T,
|
||||
th32ProcessID: DWORD,
|
||||
th32HeapID: ULONG_PTR,
|
||||
dwFlags: DWORD,
|
||||
}
|
||||
|
||||
LPHEAPLIST32 :: ^HEAPLIST32
|
||||
|
||||
HEAPENTRY32 :: struct {
|
||||
dwSize: SIZE_T,
|
||||
hHandle: HANDLE,
|
||||
dwAddress: ULONG_PTR,
|
||||
dwBlockSize: SIZE_T,
|
||||
dwFlags: DWORD,
|
||||
dwLockCount: DWORD,
|
||||
dwResvd: DWORD,
|
||||
th32ProcessID: DWORD,
|
||||
th32HeapID: ULONG_PTR,
|
||||
}
|
||||
|
||||
LPHEAPENTRY32 :: ^HEAPENTRY32
|
||||
@@ -51,12 +51,11 @@ get_log_level :: #force_inline proc() -> runtime.Logger_Level {
|
||||
// Always use .Debug in `-debug` mode.
|
||||
return .Debug
|
||||
} else {
|
||||
when LOG_LEVEL == "debug" { return .Debug }
|
||||
else when LOG_LEVEL == "info" { return .Info }
|
||||
else when LOG_LEVEL == "warning" { return .Warning }
|
||||
else when LOG_LEVEL == "error" { return .Error }
|
||||
else when LOG_LEVEL == "fatal" { return .Fatal }
|
||||
else {
|
||||
when LOG_LEVEL == "debug" { return .Debug } else
|
||||
when LOG_LEVEL == "info" { return .Info } else
|
||||
when LOG_LEVEL == "warning" { return .Warning } else
|
||||
when LOG_LEVEL == "error" { return .Error } else
|
||||
when LOG_LEVEL == "fatal" { return .Fatal } else {
|
||||
#panic("Unknown `ODIN_TEST_LOG_LEVEL`: \"" + LOG_LEVEL + "\", possible levels are: \"debug\", \"info\", \"warning\", \"error\", or \"fatal\".")
|
||||
}
|
||||
}
|
||||
@@ -198,8 +197,11 @@ runner :: proc(internal_tests: []Internal_Test) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
// `-vet` needs parameters to be shadowed by themselves first as an
|
||||
// explicit declaration, to allow the next line to work.
|
||||
internal_tests := internal_tests
|
||||
// Intentional shadow with user-specified tests.
|
||||
internal_tests := select_internal_tests[:]
|
||||
internal_tests = select_internal_tests[:]
|
||||
}
|
||||
|
||||
total_failure_count := 0
|
||||
|
||||
@@ -9,6 +9,7 @@ Stop_Reason :: enum {
|
||||
Illegal_Instruction,
|
||||
Arithmetic_Error,
|
||||
Segmentation_Fault,
|
||||
Unhandled_Trap,
|
||||
}
|
||||
|
||||
test_assertion_failure_proc :: proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! {
|
||||
|
||||
@@ -19,6 +19,11 @@ import "core:os"
|
||||
@(private="file", thread_local)
|
||||
local_test_index: libc.sig_atomic_t
|
||||
|
||||
// Windows does not appear to have a SIGTRAP, so this is defined here, instead
|
||||
// of in the libc package, just so there's no confusion about it being
|
||||
// available there.
|
||||
SIGTRAP :: 5
|
||||
|
||||
@(private="file")
|
||||
stop_runner_callback :: proc "c" (sig: libc.int) {
|
||||
prev := intrinsics.atomic_add(&stop_runner_flag, 1)
|
||||
@@ -110,6 +115,10 @@ _setup_signal_handler :: proc() {
|
||||
// For tests:
|
||||
// Catch asserts and panics.
|
||||
libc.signal(libc.SIGILL, stop_test_callback)
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Darwin {
|
||||
// Catch panics on Darwin and unhandled calls to `debug_trap`.
|
||||
libc.signal(SIGTRAP, stop_test_callback)
|
||||
}
|
||||
// Catch arithmetic errors.
|
||||
libc.signal(libc.SIGFPE, stop_test_callback)
|
||||
// Catch segmentation faults (illegal memory access).
|
||||
@@ -141,6 +150,7 @@ _should_stop_test :: proc() -> (test_index: int, reason: Stop_Reason, ok: bool)
|
||||
case libc.SIGFPE: reason = .Arithmetic_Error
|
||||
case libc.SIGILL: reason = .Illegal_Instruction
|
||||
case libc.SIGSEGV: reason = .Segmentation_Fault
|
||||
case SIGTRAP: reason = .Unhandled_Trap
|
||||
}
|
||||
ok = true
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import "base:intrinsics"
|
||||
|
||||
_ :: intrinsics
|
||||
|
||||
IS_SUPPORTED :: _IS_SUPPORTED
|
||||
|
||||
Thread_Proc :: #type proc(^Thread)
|
||||
|
||||
MAX_USER_ARGUMENTS :: 8
|
||||
@@ -58,7 +60,9 @@ Thread :: struct {
|
||||
creation_allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
#assert(size_of(Thread{}.user_index) == size_of(uintptr))
|
||||
when IS_SUPPORTED {
|
||||
#assert(size_of(Thread{}.user_index) == size_of(uintptr))
|
||||
}
|
||||
|
||||
Thread_Priority :: enum {
|
||||
Normal,
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
//+build js
|
||||
package thread
|
||||
|
||||
import "base:intrinsics"
|
||||
import "core:sync"
|
||||
import "core:mem"
|
||||
|
||||
Thread_Os_Specific :: struct {}
|
||||
|
||||
_thread_priority_map := [Thread_Priority]i32{
|
||||
.Normal = 0,
|
||||
.Low = -2,
|
||||
.High = +2,
|
||||
}
|
||||
|
||||
_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
|
||||
unimplemented("core:thread procedure not supported on js target")
|
||||
}
|
||||
|
||||
_start :: proc(t: ^Thread) {
|
||||
unimplemented("core:thread procedure not supported on js target")
|
||||
}
|
||||
|
||||
_is_done :: proc(t: ^Thread) -> bool {
|
||||
unimplemented("core:thread procedure not supported on js target")
|
||||
}
|
||||
|
||||
_join :: proc(t: ^Thread) {
|
||||
unimplemented("core:thread procedure not supported on js target")
|
||||
}
|
||||
|
||||
_join_multiple :: proc(threads: ..^Thread) {
|
||||
unimplemented("core:thread procedure not supported on js target")
|
||||
}
|
||||
|
||||
_destroy :: proc(thread: ^Thread) {
|
||||
unimplemented("core:thread procedure not supported on js target")
|
||||
}
|
||||
|
||||
_terminate :: proc(using thread : ^Thread, exit_code: int) {
|
||||
unimplemented("core:thread procedure not supported on js target")
|
||||
}
|
||||
|
||||
_yield :: proc() {
|
||||
unimplemented("core:thread procedure not supported on js target")
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user