This commit is contained in:
gingerBill
2025-06-09 17:19:58 +01:00
56 changed files with 1075 additions and 504 deletions
+1
View File
@@ -169,6 +169,7 @@ type_is_union :: proc($T: typeid) -> bool ---
type_is_enum :: proc($T: typeid) -> bool --- type_is_enum :: proc($T: typeid) -> bool ---
type_is_proc :: proc($T: typeid) -> bool --- type_is_proc :: proc($T: typeid) -> bool ---
type_is_bit_set :: proc($T: typeid) -> bool --- type_is_bit_set :: proc($T: typeid) -> bool ---
type_is_bit_field :: proc($T: typeid) -> bool ---
type_is_simd_vector :: proc($T: typeid) -> bool --- type_is_simd_vector :: proc($T: typeid) -> bool ---
type_is_matrix :: proc($T: typeid) -> bool --- type_is_matrix :: proc($T: typeid) -> bool ---
+9
View File
@@ -648,6 +648,9 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i
@builtin @builtin
inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
when !ODIN_NO_BOUNDS_CHECK {
ensure(index >= 0, "Index must be positive.", loc)
}
if array == nil { if array == nil {
return return
} }
@@ -666,6 +669,9 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcas
@builtin @builtin
inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
when !ODIN_NO_BOUNDS_CHECK {
ensure(index >= 0, "Index must be positive.", loc)
}
if array == nil { if array == nil {
return return
} }
@@ -689,6 +695,9 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca
@builtin @builtin
inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
when !ODIN_NO_BOUNDS_CHECK {
ensure(index >= 0, "Index must be positive.", loc)
}
if array == nil { if array == nil {
return return
} }
+74
View File
@@ -0,0 +1,74 @@
#+no-instrumentation
package sanitizer
@(private="file")
MSAN_ENABLED :: .Memory in ODIN_SANITIZER_FLAGS
@(private="file")
@(default_calling_convention="system")
foreign {
__msan_unpoison :: proc(addr: rawptr, size: uint) ---
}
/*
Marks a slice as fully initialized.
Code instrumented with `-sanitize:memory` will be permitted to access any
address within the slice as if it had already been initialized.
When msan is not enabled this procedure does nothing.
*/
memory_unpoison_slice :: proc "contextless" (region: $T/[]$E) {
when MSAN_ENABLED {
__msan_unpoison(raw_data(region), size_of(E) * len(region))
}
}
/*
Marks a pointer as fully initialized.
Code instrumented with `-sanitize:memory` will be permitted to access memory
within the region the pointer points to as if it had already been initialized.
When msan is not enabled this procedure does nothing.
*/
memory_unpoison_ptr :: proc "contextless" (ptr: ^$T) {
when MSAN_ENABLED {
__msan_unpoison(ptr, size_of(T))
}
}
/*
Marks the region covering `[ptr, ptr+len)` as fully initialized.
Code instrumented with `-sanitize:memory` will be permitted to access memory
within this range as if it had already been initialized.
When msan is not enabled this procedure does nothing.
*/
memory_unpoison_rawptr :: proc "contextless" (ptr: rawptr, len: int) {
when MSAN_ENABLED {
__msan_unpoison(ptr, uint(len))
}
}
/*
Marks the region covering `[ptr, ptr+len)` as fully initialized.
Code instrumented with `-sanitize:memory` will be permitted to access memory
within this range as if it had already been initialized.
When msan is not enabled this procedure does nothing.
*/
memory_unpoison_rawptr_uint :: proc "contextless" (ptr: rawptr, len: uint) {
when MSAN_ENABLED {
__msan_unpoison(ptr, len)
}
}
memory_unpoison :: proc {
memory_unpoison_slice,
memory_unpoison_ptr,
memory_unpoison_rawptr,
memory_unpoison_rawptr_uint,
}
+3 -3
View File
@@ -385,17 +385,17 @@ to_diagnostic_format_writer :: proc(w: io.Writer, val: Value, padding := 0) -> i
// which we want for the diagnostic format. // which we want for the diagnostic format.
case f16: case f16:
buf: [64]byte buf: [64]byte
str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f16), 8*size_of(f16)) str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f16), 8*size_of(f16))
if str[0] == '+' && str != "+Inf" { str = str[1:] } if str[0] == '+' && str != "+Inf" { str = str[1:] }
io.write_string(w, str) or_return io.write_string(w, str) or_return
case f32: case f32:
buf: [128]byte buf: [128]byte
str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f32), 8*size_of(f32)) str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f32), 8*size_of(f32))
if str[0] == '+' && str != "+Inf" { str = str[1:] } if str[0] == '+' && str != "+Inf" { str = str[1:] }
io.write_string(w, str) or_return io.write_string(w, str) or_return
case f64: case f64:
buf: [256]byte buf: [256]byte
str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f64), 8*size_of(f64)) str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f64), 8*size_of(f64))
if str[0] == '+' && str != "+Inf" { str = str[1:] } if str[0] == '+' && str != "+Inf" { str = str[1:] }
io.write_string(w, str) or_return io.write_string(w, str) or_return
+36
View File
@@ -612,6 +612,42 @@ _marshal_into_encoder :: proc(e: Encoder, v: any, ti: ^runtime.Type_Info) -> (er
case: case:
panic("unknown bit_size size") panic("unknown bit_size size")
} }
case runtime.Type_Info_Matrix:
count := info.column_count * info.elem_stride
err_conv(_encode_u64(e, u64(count), .Array)) or_return
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
for i in 0..<count {
data := uintptr(v.data) + uintptr(i*info.elem_size)
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
}
return
}
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
for i in 0..<count {
data := uintptr(v.data) + uintptr(i*info.elem_size)
_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
}
return
case runtime.Type_Info_Simd_Vector:
err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
if impl, ok := _tag_implementations_type[info.elem.id]; ok {
for i in 0..<info.count {
data := uintptr(v.data) + uintptr(i*info.elem_size)
impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
}
return
}
elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
for i in 0..<info.count {
data := uintptr(v.data) + uintptr(i*info.elem_size)
_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
}
return
} }
return _unsupported(v.id, nil) return _unsupported(v.id, nil)
+25
View File
@@ -591,6 +591,31 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if out_of_space { return _unsupported(v, hdr) } if out_of_space { return _unsupported(v, hdr) }
return return
case reflect.Type_Info_Matrix:
count := t.column_count * t.elem_stride
length, _ := err_conv(_decode_len_container(d, add)) or_return
if length > count {
return _unsupported(v, hdr)
}
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator }
out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
if out_of_space { return _unsupported(v, hdr) }
return
case reflect.Type_Info_Simd_Vector:
length, _ := err_conv(_decode_len_container(d, add)) or_return
if length > t.count {
return _unsupported(v, hdr)
}
da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator }
out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
if out_of_space { return _unsupported(v, hdr) }
return
case: return _unsupported(v, hdr) case: return _unsupported(v, hdr)
} }
} }
+4 -4
View File
@@ -108,13 +108,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) { if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
switch i in a { 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 }) s = strconv.write_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
case: case:
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) s = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
} }
} else { } else {
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) s = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
} }
io.write_string(w, s) or_return io.write_string(w, s) or_return
@@ -286,7 +286,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_Integer: case runtime.Type_Info_Integer:
buf: [40]byte buf: [40]byte
u := cast_any_int_to_u128(ka) u := cast_any_int_to_u128(ka)
name = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*kti.size, "0123456789", nil) name = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*kti.size, "0123456789", nil)
opt_write_key(w, opt, name) or_return opt_write_key(w, opt, name) or_return
case: return .Unsupported_Type case: return .Unsupported_Type
+2 -1
View File
@@ -175,7 +175,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
data = bytes.clone(data) data = bytes.clone(data)
} }
t := &Tokenizer{} t := new(Tokenizer)
init(t, string(data), path, error_handler) init(t, string(data), path, error_handler)
doc = new(Document) doc = new(Document)
@@ -403,6 +403,7 @@ destroy :: proc(doc: ^Document) {
} }
delete(doc.strings_to_free) delete(doc.strings_to_free)
free(doc.tokenizer)
free(doc) free(doc)
} }
+4 -4
View File
@@ -1122,7 +1122,7 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
flags: strconv.Int_Flags flags: strconv.Int_Flags
if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} } if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
if fi.plus { flags += {.Plus} } if fi.plus { flags += {.Plus} }
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags) s := strconv.write_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
prev_zero := fi.zero prev_zero := fi.zero
defer fi.zero = prev_zero defer fi.zero = prev_zero
fi.zero = false fi.zero = false
@@ -1207,7 +1207,7 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
flags: strconv.Int_Flags flags: strconv.Int_Flags
if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} } if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
if fi.plus { flags += {.Plus} } if fi.plus { flags += {.Plus} }
s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags) s := strconv.write_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
if fi.hash && fi.zero && fi.indent == 0 { if fi.hash && fi.zero && fi.indent == 0 {
c: byte = 0 c: byte = 0
@@ -1272,7 +1272,7 @@ _fmt_memory :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, units: st
} }
buf: [256]byte buf: [256]byte
str := strconv.append_float(buf[:], amt, 'f', prec, 64) str := strconv.write_float(buf[:], amt, 'f', prec, 64)
// Add the unit at the end. // Add the unit at the end.
copy(buf[len(str):], units[off:off+unit_len]) copy(buf[len(str):], units[off:off+unit_len])
@@ -1424,7 +1424,7 @@ _fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: b
buf: [386]byte buf: [386]byte
// Can return "NaN", "+Inf", "-Inf", "+<value>", "-<value>". // Can return "NaN", "+Inf", "-Inf", "+<value>", "-<value>".
str := strconv.append_float(buf[:], v, float_fmt, prec, bit_size) str := strconv.write_float(buf[:], v, float_fmt, prec, bit_size)
if !fi.plus { if !fi.plus {
// Strip sign from "+<value>" but not "+Inf". // Strip sign from "+<value>" but not "+Inf".
+8 -8
View File
@@ -22,12 +22,12 @@ write_ptr_at :: proc(w: Writer_At, p: rawptr, byte_size: int, offset: i64, n_wri
write_u64 :: proc(w: Writer, i: u64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { write_u64 :: proc(w: Writer, i: u64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
buf: [32]byte buf: [32]byte
s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil) s := strconv.write_bits(buf[:], i, base, false, 64, strconv.digits, nil)
return write_string(w, s, n_written) return write_string(w, s, n_written)
} }
write_i64 :: proc(w: Writer, i: i64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { write_i64 :: proc(w: Writer, i: i64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
buf: [32]byte buf: [32]byte
s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) s := strconv.write_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
return write_string(w, s, n_written) return write_string(w, s, n_written)
} }
@@ -40,18 +40,18 @@ write_int :: proc(w: Writer, i: int, base: int = 10, n_written: ^int = nil) -> (
write_u128 :: proc(w: Writer, i: u128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { write_u128 :: proc(w: Writer, i: u128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
buf: [39]byte buf: [39]byte
s := strconv.append_bits_128(buf[:], i, base, false, 128, strconv.digits, nil) s := strconv.write_bits_128(buf[:], i, base, false, 128, strconv.digits, nil)
return write_string(w, s, n_written) return write_string(w, s, n_written)
} }
write_i128 :: proc(w: Writer, i: i128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { write_i128 :: proc(w: Writer, i: i128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
buf: [40]byte buf: [40]byte
s := strconv.append_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil) s := strconv.write_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil)
return write_string(w, s, n_written) return write_string(w, s, n_written)
} }
write_f16 :: proc(w: Writer, val: f16, n_written: ^int = nil) -> (n: int, err: Error) { write_f16 :: proc(w: Writer, val: f16, n_written: ^int = nil) -> (n: int, err: Error) {
buf: [386]byte buf: [386]byte
str := strconv.append_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val)) str := strconv.write_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val))
s := buf[:len(str)+1] s := buf[:len(str)+1]
if s[1] == '+' || s[1] == '-' { if s[1] == '+' || s[1] == '-' {
s = s[1:] s = s[1:]
@@ -67,7 +67,7 @@ write_f16 :: proc(w: Writer, val: f16, n_written: ^int = nil) -> (n: int, err: E
write_f32 :: proc(w: Writer, val: f32, n_written: ^int = nil) -> (n: int, err: Error) { write_f32 :: proc(w: Writer, val: f32, n_written: ^int = nil) -> (n: int, err: Error) {
buf: [386]byte buf: [386]byte
str := strconv.append_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val)) str := strconv.write_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val))
s := buf[:len(str)+1] s := buf[:len(str)+1]
if s[1] == '+' || s[1] == '-' { if s[1] == '+' || s[1] == '-' {
s = s[1:] s = s[1:]
@@ -83,7 +83,7 @@ write_f32 :: proc(w: Writer, val: f32, n_written: ^int = nil) -> (n: int, err: E
write_f64 :: proc(w: Writer, val: f64, n_written: ^int = nil) -> (n: int, err: Error) { write_f64 :: proc(w: Writer, val: f64, n_written: ^int = nil) -> (n: int, err: Error) {
buf: [386]byte buf: [386]byte
str := strconv.append_float(buf[1:], val, 'f', 2*size_of(val), 8*size_of(val)) str := strconv.write_float(buf[1:], val, 'f', 2*size_of(val), 8*size_of(val))
s := buf[:len(str)+1] s := buf[:len(str)+1]
if s[1] == '+' || s[1] == '-' { if s[1] == '+' || s[1] == '-' {
s = s[1:] s = s[1:]
@@ -130,7 +130,7 @@ write_encoded_rune :: proc(w: Writer, r: rune, write_quote := true, n_written: ^
write_string(w, `\x`, &n) or_return write_string(w, `\x`, &n) or_return
buf: [2]byte buf: [2]byte
s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil) s := strconv.write_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil)
switch len(s) { switch len(s) {
case 0: case 0:
write_string(w, "00", &n) or_return write_string(w, "00", &n) or_return
+1 -1
View File
@@ -1370,8 +1370,8 @@ _private_int_div_recursive :: proc(quotient, remainder, a, b: ^Int, allocator :=
/* /*
Slower bit-bang division... also smaller. Slower bit-bang division... also smaller.
Prefer `_int_div_school` for speed.
*/ */
@(deprecated="Use `_int_div_school`, it's 3.5x faster.")
_private_int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { _private_int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) {
ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{} ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{}
+10 -5
View File
@@ -103,7 +103,7 @@ round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
} }
@(require_results) @(require_results)
append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string { write :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
Integer_Width :: 8*size_of(Backing) - Fraction_Width Integer_Width :: 8*size_of(Backing) - Fraction_Width
x := x x := x
@@ -124,16 +124,16 @@ append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
when size_of(Backing) < 16 { when size_of(Backing) < 16 {
T :: u64 T :: u64
append_uint :: strconv.append_uint write_uint :: strconv.write_uint
} else { } else {
T :: u128 T :: u128
append_uint :: strconv.append_u128 write_uint :: strconv.write_u128
} }
integer := T(x.i) >> Fraction_Width integer := T(x.i) >> Fraction_Width
fraction := T(x.i) & (1<<Fraction_Width - 1) fraction := T(x.i) & (1<<Fraction_Width - 1)
s := append_uint(buf[i:], integer, 10) s := write_uint(buf[i:], integer, 10)
i += len(s) i += len(s)
if fraction != 0 { if fraction != 0 {
buf[i] = '.' buf[i] = '.'
@@ -155,7 +155,7 @@ append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
@(require_results) @(require_results)
to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.allocator) -> string { to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.allocator) -> string {
buf: [48]byte buf: [48]byte
s := append(buf[:], x) s := write(buf[:], x)
str := make([]byte, len(s), allocator) str := make([]byte, len(s), allocator)
copy(str, s) copy(str, s)
return string(str) return string(str)
@@ -294,3 +294,8 @@ _power_of_two_table := [129]string{
"85070591730234615865843651857942052864", "85070591730234615865843651857942052864",
"170141183460469231731687303715884105728", "170141183460469231731687303715884105728",
} }
@(deprecated="Use write instead")
append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
return write(dst, x)
}
+1 -1
View File
@@ -125,7 +125,7 @@ percent_encode :: proc(s: string, allocator := context.allocator) -> string {
bytes, n := utf8.encode_rune(ch) bytes, n := utf8.encode_rune(ch)
for byte in bytes[:n] { for byte in bytes[:n] {
buf: [2]u8 = --- buf: [2]u8 = ---
t := strconv.append_int(buf[:], i64(byte), 16) t := strconv.write_int(buf[:], i64(byte), 16)
strings.write_rune(&b, '%') strings.write_rune(&b, '%')
strings.write_string(&b, t) strings.write_string(&b, t)
} }
+1 -1
View File
@@ -57,7 +57,7 @@ write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) {
if r < 32 { if r < 32 {
if wrap(write_string(f, "\\x"), &n, &err) { return } if wrap(write_string(f, "\\x"), &n, &err) { return }
b: [2]byte b: [2]byte
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) s := strconv.write_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
switch len(s) { switch len(s) {
case 0: if wrap(write_string(f, "00"), &n, &err) { return } case 0: if wrap(write_string(f, "00"), &n, &err) { return }
case 1: if wrap(write_rune(f, '0'), &n, &err) { return } case 1: if wrap(write_rune(f, '0'), &n, &err) { return }
+5
View File
@@ -27,6 +27,9 @@ General_Error :: enum u32 {
Pattern_Has_Separator, Pattern_Has_Separator,
No_HOME_Variable,
Wordexp_Failed,
Unsupported, Unsupported,
} }
@@ -73,6 +76,8 @@ error_string :: proc(ferr: Error) -> string {
case .Invalid_Command: return "invalid command" case .Invalid_Command: return "invalid command"
case .Unsupported: return "unsupported" case .Unsupported: return "unsupported"
case .Pattern_Has_Separator: return "pattern has separator" case .Pattern_Has_Separator: return "pattern has separator"
case .No_HOME_Variable: return "no $HOME variable"
case .Wordexp_Failed: return "posix.wordexp was unable to expand"
} }
case io.Error: case io.Error:
switch e { switch e {
+1
View File
@@ -269,6 +269,7 @@ _write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (nt: i64, err: Error
return return
} }
@(no_sanitize_memory)
_file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) { _file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
// TODO: Identify 0-sized "pseudo" files and return No_Size. This would // TODO: Identify 0-sized "pseudo" files and return No_Size. This would
// eliminate the need for the _read_entire_pseudo_file procs. // eliminate the need for the _read_entire_pseudo_file procs.
+1 -1
View File
@@ -59,7 +59,7 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
if r < 32 { if r < 32 {
if wrap(write_string(f, "\\x"), &n, &err) { return } if wrap(write_string(f, "\\x"), &n, &err) { return }
b: [2]byte b: [2]byte
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) s := strconv.write_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
switch len(s) { switch len(s) {
case 0: if wrap(write_string(f, "00"), &n, &err) { return } case 0: if wrap(write_string(f, "00"), &n, &err) { return }
case 1: if wrap(write_rune(f, '0'), &n, &err) { return } case 1: if wrap(write_rune(f, '0'), &n, &err) { return }
+141 -71
View File
@@ -2,78 +2,148 @@ package os2
import "base:runtime" import "base:runtime"
@(require_results) // ```
user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { // Windows: C:\Users\Alice
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) // macOS: /Users/Alice
// Linux: /home/alice
#partial switch ODIN_OS { // ```
case .Windows:
dir = get_env("LocalAppData", temp_allocator)
if dir != "" {
dir = clone_string(dir, temp_allocator) or_return
}
case .Darwin:
dir = get_env("HOME", temp_allocator)
if dir != "" {
dir = concatenate({dir, "/Library/Caches"}, temp_allocator) or_return
}
case: // All other UNIX systems
dir = get_env("XDG_CACHE_HOME", allocator)
if dir == "" {
dir = get_env("HOME", temp_allocator)
if dir == "" {
return
}
dir = concatenate({dir, "/.cache"}, temp_allocator) or_return
}
}
if dir == "" {
err = .Invalid_Path
}
return
}
@(require_results)
user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
#partial switch ODIN_OS {
case .Windows:
dir = get_env("AppData", temp_allocator)
if dir != "" {
dir = clone_string(dir, allocator) or_return
}
case .Darwin:
dir = get_env("HOME", temp_allocator)
if dir != "" {
dir = concatenate({dir, "/.config"}, allocator) or_return
}
case: // All other UNIX systems
dir = get_env("XDG_CONFIG_HOME", allocator)
if dir == "" {
dir = get_env("HOME", temp_allocator)
if dir == "" {
return
}
dir = concatenate({dir, "/.config"}, allocator) or_return
}
}
if dir == "" {
err = .Invalid_Path
}
return
}
@(require_results) @(require_results)
user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
env := "HOME" return _user_home_dir(allocator)
#partial switch ODIN_OS {
case .Windows:
env = "USERPROFILE"
}
if v := get_env(env, allocator); v != "" {
return v, nil
}
return "", .Invalid_Path
} }
// Files that applications can regenerate/refetch at a loss of speed, e.g. shader caches
//
// Sometimes deleted for system maintenance
//
// ```
// Windows: C:\Users\Alice\AppData\Local
// macOS: /Users/Alice/Library/Caches
// Linux: /home/alice/.cache
// ```
@(require_results)
user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _user_cache_dir(allocator)
}
// User-hidden application data
//
// ```
// Windows: C:\Users\Alice\AppData\Local ("C:\Users\Alice\AppData\Roaming" if `roaming`)
// macOS: /Users/Alice/Library/Application Support
// Linux: /home/alice/.local/share
// ```
//
// NOTE: (Windows only) `roaming` is for syncing across multiple devices within a *domain network*
@(require_results)
user_data_dir :: proc(allocator: runtime.Allocator, roaming := false) -> (dir: string, err: Error) {
return _user_data_dir(allocator, roaming)
}
// Non-essential application data, e.g. history, ui layout state
//
// ```
// Windows: C:\Users\Alice\AppData\Local
// macOS: /Users/Alice/Library/Application Support
// Linux: /home/alice/.local/state
// ```
@(require_results)
user_state_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _user_state_dir(allocator)
}
// Application log files
//
// ```
// Windows: C:\Users\Alice\AppData\Local
// macOS: /Users/Alice/Library/Logs
// Linux: /home/alice/.local/state
// ```
@(require_results)
user_log_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _user_log_dir(allocator)
}
// Application settings/preferences
//
// ```
// Windows: C:\Users\Alice\AppData\Local ("C:\Users\Alice\AppData\Roaming" if `roaming`)
// macOS: /Users/Alice/Library/Application Support
// Linux: /home/alice/.config
// ```
//
// NOTE: (Windows only) `roaming` is for syncing across multiple devices within a *domain network*
@(require_results)
user_config_dir :: proc(allocator: runtime.Allocator, roaming := false) -> (dir: string, err: Error) {
return _user_config_dir(allocator, roaming)
}
// ```
// Windows: C:\Users\Alice\Music
// macOS: /Users/Alice/Music
// Linux: /home/alice/Music
// ```
@(require_results)
user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _user_music_dir(allocator)
}
// ```
// Windows: C:\Users\Alice\Desktop
// macOS: /Users/Alice/Desktop
// Linux: /home/alice/Desktop
// ```
@(require_results)
user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _user_desktop_dir(allocator)
}
// ```
// Windows: C:\Users\Alice\Documents
// macOS: /Users/Alice/Documents
// Linux: /home/alice/Documents
// ```
@(require_results)
user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _user_documents_dir(allocator)
}
// ```
// Windows: C:\Users\Alice\Downloads
// macOS: /Users/Alice/Downloads
// Linux: /home/alice/Downloads
// ```
@(require_results)
user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _user_downloads_dir(allocator)
}
// ```
// Windows: C:\Users\Alice\Pictures
// macOS: /Users/Alice/Pictures
// Linux: /home/alice/Pictures
// ```
@(require_results)
user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _user_pictures_dir(allocator)
}
// ```
// Windows: C:\Users\Alice\Public
// macOS: /Users/Alice/Public
// Linux: /home/alice/Public
// ```
@(require_results)
user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _user_public_dir(allocator)
}
// ```
// Windows: C:\Users\Alice\Videos
// macOS: /Users/Alice/Movies
// Linux: /home/alice/Videos
// ```
@(require_results)
user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
return _user_videos_dir(allocator)
}
+183
View File
@@ -0,0 +1,183 @@
#+build !windows
package os2
import "base:runtime"
import "core:encoding/ini"
import "core:strings"
import "core:sys/posix"
_user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Darwin:
return _xdg_lookup("", "/Library/Caches", allocator)
case: // Unix
return _xdg_lookup("XDG_CACHE_HOME", "/.cache", allocator)
}
}
_user_config_dir :: proc(allocator: runtime.Allocator, _roaming: bool) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Darwin:
return _xdg_lookup("", "/Library/Application Support", allocator)
case: // Unix
return _xdg_lookup("XDG_CONFIG_HOME", "/.config", allocator)
}
}
_user_state_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Darwin:
return _xdg_lookup("", "/Library/Application Support", allocator)
case: // Unix
return _xdg_lookup("XDG_STATE_HOME", "/.local/state", allocator)
}
}
_user_log_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Darwin:
return _xdg_lookup("", "/Library/Logs", allocator)
case: // Unix
return _xdg_lookup("XDG_STATE_HOME", "/.local/state", allocator)
}
}
_user_data_dir :: proc(allocator: runtime.Allocator, _roaming: bool) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Darwin:
return _xdg_lookup("", "/Library/Application Support", allocator)
case: // Unix
return _xdg_lookup("XDG_DATA_HOME", "/.local/share", allocator)
}
}
_user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Darwin:
return _xdg_lookup("", "/Music", allocator)
case: // Unix
return _xdg_lookup("XDG_MUSIC_DIR", "/Music", allocator)
}
}
_user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Darwin:
return _xdg_lookup("", "/Desktop", allocator)
case: // Unix
return _xdg_lookup("XDG_DESKTOP_DIR", "/Desktop", allocator)
}
}
_user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Darwin:
return _xdg_lookup("", "/Documents", allocator)
case: // Unix
return _xdg_lookup("XDG_DOCUMENTS_DIR", "/Documents", allocator)
}
}
_user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Darwin:
return _xdg_lookup("", "/Downloads", allocator)
case: // Unix
return _xdg_lookup("XDG_DOWNLOAD_DIR", "/Downloads", allocator)
}
}
_user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Darwin:
return _xdg_lookup("", "/Pictures", allocator)
case: // Unix
return _xdg_lookup("XDG_PICTURES_DIR", "/Pictures", allocator)
}
}
_user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Darwin:
return _xdg_lookup("", "/Public", allocator)
case: // Unix
return _xdg_lookup("XDG_PUBLICSHARE_DIR", "/Public", allocator)
}
}
_user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
#partial switch ODIN_OS {
case .Darwin:
return _xdg_lookup("", "/Movies", allocator)
case: // Unix
return _xdg_lookup("XDG_VIDEOS_DIR", "/Videos", allocator)
}
}
_user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
if v := get_env("HOME", allocator); v != "" {
return v, nil
}
err = .No_HOME_Variable
return
}
_xdg_lookup :: proc(xdg_key: string, fallback_suffix: string, allocator: runtime.Allocator) -> (dir: string, err: Error) {
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
if xdg_key == "" { // Darwin doesn't have XDG paths.
dir = get_env("HOME", temp_allocator)
if dir == "" {
err = .No_HOME_Variable
return
}
return concatenate({dir, fallback_suffix}, allocator)
} else {
if strings.ends_with(xdg_key, "_DIR") {
dir = _xdg_user_dirs_lookup(xdg_key, allocator) or_return
} else {
dir = get_env(xdg_key, allocator)
}
if dir == "" {
dir = get_env("HOME", temp_allocator)
if dir == "" {
err = .No_HOME_Variable
return
}
dir = concatenate({dir, fallback_suffix}, allocator) or_return
}
return
}
}
// If `<config-dir>/user-dirs.dirs` doesn't exist, or `xdg_key` can't be found there: returns `""`
_xdg_user_dirs_lookup :: proc(xdg_key: string, allocator: runtime.Allocator) -> (dir: string, err: Error) {
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
config_dir := user_config_dir(temp_allocator) or_return
user_dirs_path := concatenate({config_dir, "/user-dirs.dirs"}, temp_allocator) or_return
content := read_entire_file(user_dirs_path, temp_allocator) or_return
it := ini.Iterator{
section = "",
_src = string(content),
options = ini.Options{
comment = "#",
key_lower_case = false,
},
}
for k, v in ini.iterate(&it) {
if k == xdg_key {
we: posix.wordexp_t
defer posix.wordfree(&we)
if _err := posix.wordexp(strings.clone_to_cstring(v, temp_allocator), &we, nil); _err != nil || we.we_wordc != 1 {
return "", .Wordexp_Failed
}
return strings.clone_from_cstring(we.we_wordv[0], allocator)
}
}
return
}
+79
View File
@@ -0,0 +1,79 @@
package os2
import "base:runtime"
@(require) import win32 "core:sys/windows"
_local_appdata :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
guid := win32.FOLDERID_LocalAppData
return _get_known_folder_path(&guid, allocator)
}
_local_appdata_or_roaming :: proc(allocator: runtime.Allocator, roaming: bool) -> (dir: string, err: Error) {
guid := win32.FOLDERID_LocalAppData
if roaming {
guid = win32.FOLDERID_RoamingAppData
}
return _get_known_folder_path(&guid, allocator)
}
_user_config_dir :: _local_appdata_or_roaming
_user_data_dir :: _local_appdata_or_roaming
_user_state_dir :: _local_appdata
_user_log_dir :: _local_appdata
_user_cache_dir :: _local_appdata
_user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
guid := win32.FOLDERID_Profile
return _get_known_folder_path(&guid, allocator)
}
_user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
guid := win32.FOLDERID_Music
return _get_known_folder_path(&guid, allocator)
}
_user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
guid := win32.FOLDERID_Desktop
return _get_known_folder_path(&guid, allocator)
}
_user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
guid := win32.FOLDERID_Documents
return _get_known_folder_path(&guid, allocator)
}
_user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
guid := win32.FOLDERID_Downloads
return _get_known_folder_path(&guid, allocator)
}
_user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
guid := win32.FOLDERID_Pictures
return _get_known_folder_path(&guid, allocator)
}
_user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
guid := win32.FOLDERID_Public
return _get_known_folder_path(&guid, allocator)
}
_user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
guid := win32.FOLDERID_Videos
return _get_known_folder_path(&guid, allocator)
}
_get_known_folder_path :: proc(rfid: win32.REFKNOWNFOLDERID, allocator: runtime.Allocator) -> (dir: string, err: Error) {
// https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath
// See also `known_folders.odin` in `core:sys/windows` for the GUIDs.
path_w: win32.LPWSTR
res := win32.SHGetKnownFolderPath(rfid, 0, nil, &path_w)
defer win32.CoTaskMemFree(path_w)
if res != 0 {
return "", .Invalid_Path
}
dir, _ = win32.wstring_to_utf8(path_w, -1, allocator)
return
}
+3 -3
View File
@@ -662,7 +662,7 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Error) {
return File_Time(modified), nil return File_Time(modified), nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_stat :: proc(path: string) -> (OS_Stat, Error) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -674,7 +674,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil return s, nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_lstat :: proc(path: string) -> (OS_Stat, Error) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -688,7 +688,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil return s, nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
s: OS_Stat = --- s: OS_Stat = ---
result := _unix_fstat(fd, &s) result := _unix_fstat(fd, &s)
+3 -3
View File
@@ -325,7 +325,7 @@ _alloc_command_line_arguments :: proc() -> []string {
return res return res
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_stat :: proc(path: string) -> (OS_Stat, Error) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -339,7 +339,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil return s, nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_lstat :: proc(path: string) -> (OS_Stat, Error) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -353,7 +353,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil return s, nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized // deliberately uninitialized
s: OS_Stat = --- s: OS_Stat = ---
+4 -4
View File
@@ -674,7 +674,7 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
return i64(res), nil return i64(res), nil
} }
@(require_results) @(require_results, no_sanitize_memory)
file_size :: proc(fd: Handle) -> (i64, Error) { file_size :: proc(fd: Handle) -> (i64, Error) {
// deliberately uninitialized; the syscall fills this buffer for us // deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = --- s: OS_Stat = ---
@@ -794,7 +794,7 @@ last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
return File_Time(modified), nil return File_Time(modified), nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_stat :: proc(path: string) -> (OS_Stat, Error) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -808,7 +808,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil return s, nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_lstat :: proc(path: string) -> (OS_Stat, Error) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -822,7 +822,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil return s, nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized; the syscall fills this buffer for us // deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = --- s: OS_Stat = ---
+3 -3
View File
@@ -724,7 +724,7 @@ last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
return File_Time(modified), nil return File_Time(modified), nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_stat :: proc(path: string) -> (OS_Stat, Error) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -736,7 +736,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil return s, nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_lstat :: proc(path: string) -> (OS_Stat, Error) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -750,7 +750,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil return s, nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
s: OS_Stat = --- s: OS_Stat = ---
result := _unix_fstat(fd, &s) result := _unix_fstat(fd, &s)
+3 -3
View File
@@ -639,7 +639,7 @@ last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
return File_Time(modified), nil return File_Time(modified), nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_stat :: proc(path: string) -> (OS_Stat, Error) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -653,7 +653,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil return s, nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_lstat :: proc(path: string) -> (OS_Stat, Error) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -667,7 +667,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil return s, nil
} }
@(private, require_results) @(private, require_results, no_sanitize_memory)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized // deliberately uninitialized
s: OS_Stat = --- s: OS_Stat = ---
-46
View File
@@ -30,14 +30,6 @@ sort :: proc(it: Interface) {
_quick_sort(it, 0, n, max_depth(n)) _quick_sort(it, 0, n, max_depth(n))
} }
@(deprecated="use slice.sort")
slice :: proc(array: $T/[]$E) where ORD(E) {
_slice.sort(array)
// s := array;
// sort(slice_interface(&s));
}
slice_interface :: proc(s: ^$T/[]$E) -> Interface where ORD(E) { slice_interface :: proc(s: ^$T/[]$E) -> Interface where ORD(E) {
return Interface{ return Interface{
collection = rawptr(s), collection = rawptr(s),
@@ -80,31 +72,6 @@ reverse_sort :: proc(it: Interface) {
sort(reverse_interface(&it)) sort(reverse_interface(&it))
} }
@(deprecated="use slice.reverse")
reverse_slice :: proc(array: $T/[]$E) where ORD(E) {
_slice.reverse(array)
/*
s := array;
sort(Interface{
collection = rawptr(&s),
len = proc(it: Interface) -> int {
s := (^T)(it.collection);
return len(s^);
},
less = proc(it: Interface, i, j: int) -> bool {
s := (^T)(it.collection);
return s[j] < s[i]; // manual set up
},
swap = proc(it: Interface, i, j: int) {
s := (^T)(it.collection);
s[i], s[j] = s[j], s[i];
},
});
*/
}
is_sorted :: proc(it: Interface) -> bool { is_sorted :: proc(it: Interface) -> bool {
n := it->len() n := it->len()
for i := n-1; i > 0; i -= 1 { for i := n-1; i > 0; i -= 1 {
@@ -294,11 +261,6 @@ _insertion_sort :: proc(it: Interface, a, b: int) {
} }
} }
// @(deprecated="use sort.sort or slice.sort_by")
bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
assert(f != nil) assert(f != nil)
count := len(array) count := len(array)
@@ -327,7 +289,6 @@ bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
} }
} }
// @(deprecated="use sort.sort_slice or slice.sort")
bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
count := len(array) count := len(array)
@@ -355,7 +316,6 @@ bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
} }
} }
// @(deprecated="use sort.sort or slice.sort_by")
quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
assert(f != nil) assert(f != nil)
a := array a := array
@@ -384,7 +344,6 @@ quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
quick_sort_proc(a[i:n], f) quick_sort_proc(a[i:n], f)
} }
// @(deprecated="use sort.sort_slice or slice.sort")
quick_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { quick_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
a := array a := array
n := len(a) n := len(a)
@@ -420,7 +379,6 @@ _log2 :: proc(x: int) -> int {
return res return res
} }
// @(deprecated="use sort.sort or slice.sort_by")
merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
merge :: proc(a: A, start, mid, end: int, f: proc(T, T) -> int) { merge :: proc(a: A, start, mid, end: int, f: proc(T, T) -> int) {
s, m := start, mid s, m := start, mid
@@ -462,7 +420,6 @@ merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
internal_sort(array, 0, len(array)-1, f) internal_sort(array, 0, len(array)-1, f)
} }
// @(deprecated="use sort.sort_slice or slice.sort")
merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
merge :: proc(a: A, start, mid, end: int) { merge :: proc(a: A, start, mid, end: int) {
s, m := start, mid s, m := start, mid
@@ -504,8 +461,6 @@ merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
internal_sort(array, 0, len(array)-1) internal_sort(array, 0, len(array)-1)
} }
// @(deprecated="use sort.sort or slice.sort_by")
heap_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { heap_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
sift_proc :: proc(a: A, pi: int, n: int, f: proc(T, T) -> int) #no_bounds_check { sift_proc :: proc(a: A, pi: int, n: int, f: proc(T, T) -> int) #no_bounds_check {
p := pi p := pi
@@ -540,7 +495,6 @@ heap_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
} }
} }
// @(deprecated="use sort.sort_slice or slice.sort")
heap_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { heap_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
sift :: proc(a: A, pi: int, n: int) #no_bounds_check { sift :: proc(a: A, pi: int, n: int) #no_bounds_check {
p := pi p := pi
+38
View File
@@ -0,0 +1,38 @@
package strconv
// (2025-06-05) These procedures are to be removed at a later release.
@(deprecated="Use write_bits instead")
append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
return write_bits(buf, x, base, is_signed, bit_size, digits, flags)
}
@(deprecated="Use write_bits_128 instead")
append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
return write_bits_128(buf, x, base, is_signed, bit_size, digits, flags)
}
@(deprecated="Use write_bool instead")
append_bool :: proc(buf: []byte, b: bool) -> string {
return write_bool(buf, b)
}
@(deprecated="Use write_uint instead")
append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
return write_uint(buf, u, base)
}
@(deprecated="Use write_int instead")
append_int :: proc(buf: []byte, i: i64, base: int) -> string {
return write_int(buf, i, base)
}
@(deprecated="Use write_u128 instead")
append_u128 :: proc(buf: []byte, u: u128, base: int) -> string {
return write_u128(buf, u, base)
}
@(deprecated="Use write_float instead")
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
return write_float(buf, f, fmt, prec, bit_size)
}
+7 -7
View File
@@ -48,7 +48,7 @@ is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64,
return return
} }
/* /*
Appends the string representation of an integer to a buffer with specified base, flags, and digit set. Writes the string representation of an integer to a buffer with specified base, flags, and digit set.
**Inputs** **Inputs**
- buf: The buffer to append the integer representation to - buf: The buffer to append the integer representation to
@@ -62,9 +62,9 @@ Appends the string representation of an integer to a buffer with specified base,
**Returns** **Returns**
- The string containing the integer representation appended to the buffer - The string containing the integer representation appended to the buffer
*/ */
append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { write_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE { if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits") panic("strconv: illegal base passed to write_bits")
} }
a: [129]byte a: [129]byte
@@ -146,7 +146,7 @@ is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u:
return return
} }
/* /*
Appends the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set. Writes the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set.
**Inputs** **Inputs**
- buf: The buffer to append the integer representation to - buf: The buffer to append the integer representation to
@@ -158,11 +158,11 @@ Appends the string representation of a 128-bit integer to a buffer with specifie
- flags: The Int_Flags bit set to control integer formatting - flags: The Int_Flags bit set to control integer formatting
**Returns** **Returns**
- The string containing the integer representation appended to the buffer - The string containing the integer representation written to the buffer
*/ */
append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { write_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE { if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits") panic("strconv: illegal base passed to write_bits")
} }
a: [140]byte a: [140]byte
+42 -42
View File
@@ -1451,19 +1451,19 @@ parse_quaternion64 :: proc(str: string, n: ^int = nil) -> (value: quaternion64,
return cast(quaternion64)v, ok return cast(quaternion64)v, ok
} }
/* /*
Appends a boolean value as a string to the given buffer Writes a boolean value as a string to the given buffer
**Inputs** **Inputs**
- buf: The buffer to append the boolean value to - buf: The buffer to write the boolean value to
- b: The boolean value to be appended - b: The boolean value to be written
Example: Example:
import "core:fmt" import "core:fmt"
import "core:strconv" import "core:strconv"
append_bool_example :: proc() { write_bool_example :: proc() {
buf: [6]byte buf: [6]byte
result := strconv.append_bool(buf[:], true) result := strconv.write_bool(buf[:], true)
fmt.println(result, buf) fmt.println(result, buf)
} }
@@ -1472,9 +1472,9 @@ Output:
true [116, 114, 117, 101, 0, 0] true [116, 114, 117, 101, 0, 0]
**Returns** **Returns**
- The resulting string after appending the boolean value - The resulting string after writing the boolean value
*/ */
append_bool :: proc(buf: []byte, b: bool) -> string { write_bool :: proc(buf: []byte, b: bool) -> string {
n := 0 n := 0
if b { if b {
n = copy(buf, "true") n = copy(buf, "true")
@@ -1484,20 +1484,20 @@ append_bool :: proc(buf: []byte, b: bool) -> string {
return string(buf[:n]) return string(buf[:n])
} }
/* /*
Appends an unsigned integer value as a string to the given buffer with the specified base Writes an unsigned integer value as a string to the given buffer with the specified base
**Inputs** **Inputs**
- buf: The buffer to append the unsigned integer value to - buf: The buffer to write the unsigned integer value to
- u: The unsigned integer value to be appended - u: The unsigned integer value to be written
- base: The base to use for converting the integer value - base: The base to use for converting the integer value
Example: Example:
import "core:fmt" import "core:fmt"
import "core:strconv" import "core:strconv"
append_uint_example :: proc() { write_uint_example :: proc() {
buf: [4]byte buf: [4]byte
result := strconv.append_uint(buf[:], 42, 16) result := strconv.write_uint(buf[:], 42, 16)
fmt.println(result, buf) fmt.println(result, buf)
} }
@@ -1506,26 +1506,26 @@ Output:
2a [50, 97, 0, 0] 2a [50, 97, 0, 0]
**Returns** **Returns**
- The resulting string after appending the unsigned integer value - The resulting string after writing the unsigned integer value
*/ */
append_uint :: proc(buf: []byte, u: u64, base: int) -> string { write_uint :: proc(buf: []byte, u: u64, base: int) -> string {
return append_bits(buf, u, base, false, 8*size_of(uint), digits, nil) return write_bits(buf, u, base, false, 8*size_of(uint), digits, nil)
} }
/* /*
Appends a signed integer value as a string to the given buffer with the specified base Writes a signed integer value as a string to the given buffer with the specified base
**Inputs** **Inputs**
- buf: The buffer to append the signed integer value to - buf: The buffer to write the signed integer value to
- i: The signed integer value to be appended - i: The signed integer value to be written
- base: The base to use for converting the integer value - base: The base to use for converting the integer value
Example: Example:
import "core:fmt" import "core:fmt"
import "core:strconv" import "core:strconv"
append_int_example :: proc() { write_int_example :: proc() {
buf: [4]byte buf: [4]byte
result := strconv.append_int(buf[:], -42, 10) result := strconv.write_int(buf[:], -42, 10)
fmt.println(result, buf) fmt.println(result, buf)
} }
@@ -1534,16 +1534,16 @@ Output:
-42 [45, 52, 50, 0] -42 [45, 52, 50, 0]
**Returns** **Returns**
- The resulting string after appending the signed integer value - The resulting string after writing the signed integer value
*/ */
append_int :: proc(buf: []byte, i: i64, base: int) -> string { write_int :: proc(buf: []byte, i: i64, base: int) -> string {
return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil) return write_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil)
} }
append_u128 :: proc(buf: []byte, u: u128, base: int) -> string { write_u128 :: proc(buf: []byte, u: u128, base: int) -> string {
return append_bits_128(buf, u, base, false, 8*size_of(uint), digits, nil) return write_bits_128(buf, u, base, false, 8*size_of(uint), digits, nil)
} }
/* /*
@@ -1571,7 +1571,7 @@ Output:
- The resulting string after converting the integer value - The resulting string after converting the integer value
*/ */
itoa :: proc(buf: []byte, i: int) -> string { itoa :: proc(buf: []byte, i: int) -> string {
return append_int(buf, i64(i), 10) return write_int(buf, i64(i), 10)
} }
/* /*
Converts a string to an integer value Converts a string to an integer value
@@ -1623,14 +1623,14 @@ atof :: proc(s: string) -> f64 {
v, _ := parse_f64(s) v, _ := parse_f64(s)
return v return v
} }
// Alias to `append_float` // Alias to `write_float`
ftoa :: append_float ftoa :: write_float
/* /*
Appends a float64 value as a string to the given buffer with the specified format and precision Writes a float64 value as a string to the given buffer with the specified format and precision
**Inputs** **Inputs**
- buf: The buffer to append the float64 value to - buf: The buffer to write the float64 value to
- f: The float64 value to be appended - f: The float64 value to be written
- fmt: The byte specifying the format to use for the conversion - fmt: The byte specifying the format to use for the conversion
- prec: The precision to use for the conversion - prec: The precision to use for the conversion
- bit_size: The size of the float in bits (32 or 64) - bit_size: The size of the float in bits (32 or 64)
@@ -1639,9 +1639,9 @@ Example:
import "core:fmt" import "core:fmt"
import "core:strconv" import "core:strconv"
append_float_example :: proc() { write_float_example :: proc() {
buf: [8]byte buf: [8]byte
result := strconv.append_float(buf[:], 3.14159, 'f', 2, 64) result := strconv.write_float(buf[:], 3.14159, 'f', 2, 64)
fmt.println(result, buf) fmt.println(result, buf)
} }
@@ -1650,16 +1650,16 @@ Output:
+3.14 [43, 51, 46, 49, 52, 0, 0, 0] +3.14 [43, 51, 46, 49, 52, 0, 0, 0]
**Returns** **Returns**
- The resulting string after appending the float - The resulting string after writing the float
*/ */
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { write_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
return string(generic_ftoa(buf, f, fmt, prec, bit_size)) return string(generic_ftoa(buf, f, fmt, prec, bit_size))
} }
/* /*
Appends a quoted string representation of the input string to a given byte slice and returns the result as a string Writes a quoted string representation of the input string to a given byte slice and returns the result as a string
**Inputs** **Inputs**
- buf: The byte slice to which the quoted string will be appended - buf: The byte slice to which the quoted string will be written
- str: The input string to be quoted - str: The input string to be quoted
!! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected !! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected
@@ -1679,7 +1679,7 @@ Output:
"'h''e''l''l''o'" [34, 39, 104, 39, 39, 101, 39, 39, 108, 39, 39, 108, 39, 39, 111, 39, 34, 0, 0, 0] "'h''e''l''l''o'" [34, 39, 104, 39, 39, 101, 39, 39, 108, 39, 39, 108, 39, 39, 111, 39, 34, 0, 0, 0]
**Returns** **Returns**
- The resulting string after appending the quoted string representation - The resulting string after writing the quoted string representation
*/ */
quote :: proc(buf: []byte, str: string) -> string { quote :: proc(buf: []byte, str: string) -> string {
write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) { write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) {
@@ -1719,10 +1719,10 @@ quote :: proc(buf: []byte, str: string) -> string {
return string(buf[:i]) return string(buf[:i])
} }
/* /*
Appends a quoted rune representation of the input rune to a given byte slice and returns the result as a string Writes a quoted rune representation of the input rune to a given byte slice and returns the result as a string
**Inputs** **Inputs**
- buf: The byte slice to which the quoted rune will be appended - buf: The byte slice to which the quoted rune will be written
- r: The input rune to be quoted - r: The input rune to be quoted
Example: Example:
@@ -1740,7 +1740,7 @@ Output:
'A' [39, 65, 39, 0] 'A' [39, 65, 39, 0]
**Returns** **Returns**
- The resulting string after appending the quoted rune representation - The resulting string after writing the quoted rune representation
*/ */
quote_rune :: proc(buf: []byte, r: rune) -> string { quote_rune :: proc(buf: []byte, r: rune) -> string {
write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) { write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) {
@@ -1783,7 +1783,7 @@ quote_rune :: proc(buf: []byte, r: rune) -> string {
if r < 32 { if r < 32 {
write_string(buf, &i, "\\x") write_string(buf, &i, "\\x")
b: [2]byte b: [2]byte
s := append_bits(b[:], u64(r), 16, true, 64, digits, nil) s := write_bits(b[:], u64(r), 16, true, 64, digits, nil)
switch len(s) { switch len(s) {
case 0: write_string(buf, &i, "00") case 0: write_string(buf, &i, "00")
case 1: write_rune(buf, &i, '0') case 1: write_rune(buf, &i, '0')
+6 -6
View File
@@ -675,7 +675,7 @@ Returns:
*/ */
write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) { write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) {
buf: [384]byte buf: [384]byte
s := strconv.append_float(buf[:], f, fmt, prec, bit_size) s := strconv.write_float(buf[:], f, fmt, prec, bit_size)
// If the result starts with a `+` then unless we always want signed results, // If the result starts with a `+` then unless we always want signed results,
// we skip it unless it's followed by an `I` (because of +Inf). // we skip it unless it's followed by an `I` (because of +Inf).
if !always_signed && (buf[0] == '+' && buf[1] != 'I') { if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
@@ -699,7 +699,7 @@ Returns:
*/ */
write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) { write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) {
buf: [384]byte buf: [384]byte
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
if !always_signed && (buf[0] == '+' && buf[1] != 'I') { if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
s = s[1:] s = s[1:]
} }
@@ -739,7 +739,7 @@ Output:
*/ */
write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) { write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) {
buf: [384]byte buf: [384]byte
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
if !always_signed && (buf[0] == '+' && buf[1] != 'I') { if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
s = s[1:] s = s[1:]
} }
@@ -761,7 +761,7 @@ Returns:
*/ */
write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) { write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) {
buf: [384]byte buf: [384]byte
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
if !always_signed && (buf[0] == '+' && buf[1] != 'I') { if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
s = s[1:] s = s[1:]
} }
@@ -782,7 +782,7 @@ Returns:
*/ */
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) { write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
buf: [32]byte buf: [32]byte
s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil) s := strconv.write_bits(buf[:], i, base, false, 64, strconv.digits, nil)
return write_string(b, s) return write_string(b, s)
} }
/* /*
@@ -800,7 +800,7 @@ Returns:
*/ */
write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) { write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) {
buf: [32]byte buf: [32]byte
s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) s := strconv.write_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
return write_string(b, s) return write_string(b, s)
} }
/* /*
+81 -63
View File
@@ -28,24 +28,7 @@ clone :: proc(s: string, allocator := context.allocator, loc := #caller_location
copy(c, s) copy(c, s)
return string(c), nil return string(c), nil
} }
/*
Clones a string safely (returns early with an allocation error on failure)
*Allocates Using Provided Allocator*
Inputs:
- s: The string to be cloned
- allocator: (default: context.allocator)
- loc: The caller location for debugging purposes (default: #caller_location)
Returns:
- res: The cloned string
- err: An allocator error if one occured, `nil` otherwise
*/
@(deprecated="Prefer clone. It now returns an optional allocator error")
clone_safe :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) {
return clone(s, allocator, loc)
}
/* /*
Clones a string and appends a null-byte to make it a cstring Clones a string and appends a null-byte to make it a cstring
@@ -66,6 +49,7 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #call
c[len(s)] = 0 c[len(s)] = 0
return cstring(&c[0]), nil return cstring(&c[0]), nil
} }
/* /*
Transmutes a raw pointer into a string. Non-allocating. Transmutes a raw pointer into a string. Non-allocating.
@@ -81,6 +65,7 @@ Returns:
string_from_ptr :: proc(ptr: ^byte, len: int) -> (res: string) { string_from_ptr :: proc(ptr: ^byte, len: int) -> (res: string) {
return transmute(string)mem.Raw_String{ptr, len} return transmute(string)mem.Raw_String{ptr, len}
} }
/* /*
Transmutes a raw pointer (null-terminated) into a string. Non-allocating. Searches for a null-byte from `0..<len`, otherwise `len` will be the end size Transmutes a raw pointer (null-terminated) into a string. Non-allocating. Searches for a null-byte from `0..<len`, otherwise `len` will be the end size
@@ -99,20 +84,7 @@ string_from_null_terminated_ptr :: proc "contextless" (ptr: [^]byte, len: int) -
s = truncate_to_byte(s, 0) s = truncate_to_byte(s, 0)
return s return s
} }
/*
Gets the raw byte pointer for the start of a string `str`
Inputs:
- str: The input string
Returns:
- res: A pointer to the start of the string's bytes
*/
@(deprecated="Prefer the builtin raw_data.")
ptr_from_string :: proc(str: string) -> (res: ^byte) {
d := transmute(mem.Raw_String)str
return d.data
}
/* /*
Converts a string `str` to a cstring Converts a string `str` to a cstring
@@ -128,6 +100,7 @@ unsafe_string_to_cstring :: proc(str: string) -> (res: cstring) {
d := transmute(mem.Raw_String)str d := transmute(mem.Raw_String)str
return cstring(d.data) return cstring(d.data)
} }
/* /*
Truncates a string `str` at the first occurrence of char/byte `b` Truncates a string `str` at the first occurrence of char/byte `b`
@@ -147,6 +120,7 @@ truncate_to_byte :: proc "contextless" (str: string, b: byte) -> (res: string) {
} }
return str[:n] return str[:n]
} }
/* /*
Truncates a string `str` at the first occurrence of rune `r` as a slice of the original, entire string if not found Truncates a string `str` at the first occurrence of rune `r` as a slice of the original, entire string if not found
@@ -164,6 +138,7 @@ truncate_to_rune :: proc(str: string, r: rune) -> (res: string) {
} }
return str[:n] return str[:n]
} }
/* /*
Clones a byte array `s` and appends a null-byte Clones a byte array `s` and appends a null-byte
@@ -184,6 +159,7 @@ clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #call
c[len(s)] = 0 c[len(s)] = 0
return string(c[:len(s)]), nil return string(c[:len(s)]), nil
} }
/* /*
Clones a cstring `s` as a string Clones a cstring `s` as a string
@@ -201,6 +177,7 @@ Returns:
clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error { clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
return clone(string(s), allocator, loc) return clone(string(s), allocator, loc)
} }
/* /*
Clones a string from a byte pointer `ptr` and a byte length `len` Clones a string from a byte pointer `ptr` and a byte length `len`
@@ -222,6 +199,7 @@ clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc
s := string_from_ptr(ptr, len) s := string_from_ptr(ptr, len)
return clone(s, allocator, loc) return clone(s, allocator, loc)
} }
// Overloaded procedure to clone from a string, `[]byte`, `cstring` or a `^byte` + length // Overloaded procedure to clone from a string, `[]byte`, `cstring` or a `^byte` + length
clone_from :: proc{ clone_from :: proc{
clone, clone,
@@ -229,6 +207,7 @@ clone_from :: proc{
clone_from_cstring, clone_from_cstring,
clone_from_ptr, clone_from_ptr,
} }
/* /*
Clones a string from a null-terminated cstring `ptr` and a byte length `len` Clones a string from a null-terminated cstring `ptr` and a byte length `len`
@@ -251,6 +230,7 @@ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.
s = truncate_to_byte(s, 0) s = truncate_to_byte(s, 0)
return clone(s, allocator, loc) return clone(s, allocator, loc)
} }
/* /*
Compares two strings, returning a value representing which one comes first lexicographically. Compares two strings, returning a value representing which one comes first lexicographically.
-1 for `lhs`; 1 for `rhs`, or 0 if they are equal. -1 for `lhs`; 1 for `rhs`, or 0 if they are equal.
@@ -265,6 +245,7 @@ Returns:
compare :: proc "contextless" (lhs, rhs: string) -> (result: int) { compare :: proc "contextless" (lhs, rhs: string) -> (result: int) {
return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs) return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs)
} }
/* /*
Checks if rune `r` in the string `s` Checks if rune `r` in the string `s`
@@ -283,6 +264,7 @@ contains_rune :: proc(s: string, r: rune) -> (result: bool) {
} }
return false return false
} }
/* /*
Returns true when the string `substr` is contained inside the string `s` Returns true when the string `substr` is contained inside the string `s`
@@ -314,6 +296,7 @@ Output:
contains :: proc(s, substr: string) -> (res: bool) { contains :: proc(s, substr: string) -> (res: bool) {
return index(s, substr) >= 0 return index(s, substr) >= 0
} }
/* /*
Returns `true` when the string `s` contains any of the characters inside the string `chars` Returns `true` when the string `s` contains any of the characters inside the string `chars`
@@ -386,6 +369,7 @@ Output:
rune_count :: proc(s: string) -> (res: int) { rune_count :: proc(s: string) -> (res: int) {
return utf8.rune_count_in_string(s) return utf8.rune_count_in_string(s)
} }
/* /*
Returns whether the strings `u` and `v` are the same alpha characters, ignoring different casings Returns whether the strings `u` and `v` are the same alpha characters, ignoring different casings
Works with UTF-8 string content Works with UTF-8 string content
@@ -508,6 +492,7 @@ prefix_length :: proc "contextless" (a, b: string) -> (n: int) {
} }
return return
} }
/* /*
Returns the common prefix between strings `a` and `b` Returns the common prefix between strings `a` and `b`
@@ -540,6 +525,7 @@ Output:
common_prefix :: proc(a, b: string) -> string { common_prefix :: proc(a, b: string) -> string {
return a[:prefix_length(a, b)] return a[:prefix_length(a, b)]
} }
/* /*
Determines if a string `s` starts with a given `prefix` Determines if a string `s` starts with a given `prefix`
@@ -661,24 +647,7 @@ join :: proc(a: []string, sep: string, allocator := context.allocator, loc := #c
} }
return string(b), nil return string(b), nil
} }
/*
Joins a slice of strings `a` with a `sep` string, returns an error on allocation failure
*Allocates Using Provided Allocator*
Inputs:
- a: A slice of strings to join
- sep: The separator string
- allocator: (default is context.allocator)
Returns:
- str: A combined string from the slice of strings `a` separated with the `sep` string
- err: An allocator error if one occured, `nil` otherwise
*/
@(deprecated="Prefer join. It now returns an optional allocator error")
join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) {
return join(a, sep, allocator)
}
/* /*
Returns a combined string from the slice of strings `a` without a separator Returns a combined string from the slice of strings `a` without a separator
@@ -723,22 +692,6 @@ concatenate :: proc(a: []string, allocator := context.allocator, loc := #caller_
} }
return string(b), nil return string(b), nil
} }
/*
Returns a combined string from the slice of strings `a` without a separator, or an error if allocation fails
*Allocates Using Provided Allocator*
Inputs:
- a: A slice of strings to concatenate
- allocator: (default is context.allocator)
Returns:
The concatenated string, and an error if allocation fails
*/
@(deprecated="Prefer concatenate. It now returns an optional allocator error")
concatenate_safe :: proc(a: []string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) {
return concatenate(a, allocator)
}
/* /*
Returns a substring of the input string `s` with the specified rune offset and length Returns a substring of the input string `s` with the specified rune offset and length
@@ -901,6 +854,7 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato
return res[:i+1], nil return res[:i+1], nil
} }
/* /*
Splits a string into parts based on a separator. Splits a string into parts based on a separator.
@@ -936,6 +890,7 @@ Output:
split :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error { split :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
return _split(s, sep, 0, -1, allocator) return _split(s, sep, 0, -1, allocator)
} }
/* /*
Splits a string into parts based on a separator. If n < count of seperators, the remainder of the string is returned in the last entry. Splits a string into parts based on a separator. If n < count of seperators, the remainder of the string is returned in the last entry.
@@ -972,6 +927,7 @@ Output:
split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error { split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
return _split(s, sep, 0, n, allocator) return _split(s, sep, 0, n, allocator)
} }
/* /*
Splits a string into parts after the separator, retaining it in the substrings. Splits a string into parts after the separator, retaining it in the substrings.
@@ -1007,6 +963,7 @@ Output:
split_after :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error { split_after :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
return _split(s, sep, len(sep), -1, allocator) return _split(s, sep, len(sep), -1, allocator)
} }
/* /*
Splits a string into a total of `n` parts after the separator. Splits a string into a total of `n` parts after the separator.
@@ -1043,6 +1000,7 @@ Output:
split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error { split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
return _split(s, sep, len(sep), n, allocator) return _split(s, sep, len(sep), n, allocator)
} }
/* /*
Searches for the first occurrence of `sep` in the given string and returns the substring Searches for the first occurrence of `sep` in the given string and returns the substring
up to (but not including) the separator, as well as a boolean indicating success. up to (but not including) the separator, as well as a boolean indicating success.
@@ -1083,6 +1041,7 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string,
} }
return return
} }
/* /*
Splits the input string by the byte separator in an iterator fashion. Splits the input string by the byte separator in an iterator fashion.
@@ -1129,6 +1088,7 @@ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) {
} }
return return
} }
/* /*
Splits the input string by the separator string in an iterator fashion. Splits the input string by the separator string in an iterator fashion.
@@ -1164,6 +1124,7 @@ Output:
split_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) { split_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) {
return _split_iterator(s, sep, 0) return _split_iterator(s, sep, 0)
} }
/* /*
Splits the input string after every separator string in an iterator fashion. Splits the input string after every separator string in an iterator fashion.
@@ -1199,6 +1160,7 @@ Output:
split_after_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) { split_after_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) {
return _split_iterator(s, sep, len(sep)) return _split_iterator(s, sep, len(sep))
} }
/* /*
Trims the carriage return character from the end of the input string. Trims the carriage return character from the end of the input string.
@@ -1220,6 +1182,7 @@ _trim_cr :: proc(s: string) -> (res: string) {
} }
return s return s
} }
/* /*
Splits the input string at every line break `\n`. Splits the input string at every line break `\n`.
@@ -1257,6 +1220,7 @@ split_lines :: proc(s: string, allocator := context.allocator) -> (res: []string
} }
return lines, nil return lines, nil
} }
/* /*
Splits the input string at every line break `\n` for `n` parts. Splits the input string at every line break `\n` for `n` parts.
@@ -1297,6 +1261,7 @@ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> (res
} }
return lines, nil return lines, nil
} }
/* /*
Splits the input string at every line break `\n` leaving the `\n` in the resulting strings. Splits the input string at every line break `\n` leaving the `\n` in the resulting strings.
@@ -1336,6 +1301,7 @@ split_lines_after :: proc(s: string, allocator := context.allocator) -> (res: []
} }
return lines, nil return lines, nil
} }
/* /*
Splits the input string at every line break `\n` leaving the `\n` in the resulting strings. Splits the input string at every line break `\n` leaving the `\n` in the resulting strings.
Only runs for n parts. Only runs for n parts.
@@ -1377,6 +1343,7 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -
} }
return lines, nil return lines, nil
} }
/* /*
Splits the input string at every line break `\n`. Splits the input string at every line break `\n`.
Returns the current split string every iteration until the string is consumed. Returns the current split string every iteration until the string is consumed.
@@ -1411,6 +1378,7 @@ split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
line = _split_iterator(s, sep, 0) or_return line = _split_iterator(s, sep, 0) or_return
return _trim_cr(line), true return _trim_cr(line), true
} }
/* /*
Splits the input string at every line break `\n`. Splits the input string at every line break `\n`.
Returns the current split string with line breaks included every iteration until the string is consumed. Returns the current split string with line breaks included every iteration until the string is consumed.
@@ -1448,6 +1416,7 @@ split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
line = _split_iterator(s, sep, len(sep)) or_return line = _split_iterator(s, sep, len(sep)) or_return
return _trim_cr(line), true return _trim_cr(line), true
} }
/* /*
Returns the byte offset of the first byte `c` in the string s it finds, -1 when not found. Returns the byte offset of the first byte `c` in the string s it finds, -1 when not found.
NOTE: Can't find UTF-8 based runes. NOTE: Can't find UTF-8 based runes.
@@ -1482,6 +1451,7 @@ Output:
index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) { index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) {
return #force_inline bytes.index_byte(transmute([]u8)s, c) return #force_inline bytes.index_byte(transmute([]u8)s, c)
} }
/* /*
Returns the byte offset of the last byte `c` in the string `s`, -1 when not found. Returns the byte offset of the last byte `c` in the string `s`, -1 when not found.
@@ -1517,6 +1487,7 @@ Output:
last_index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) { last_index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) {
return #force_inline bytes.last_index_byte(transmute([]u8)s, c) return #force_inline bytes.last_index_byte(transmute([]u8)s, c)
} }
/* /*
Returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found. Returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found.
Invalid runes return -1 Invalid runes return -1
@@ -1657,6 +1628,7 @@ index :: proc "contextless" (s, substr: string) -> (res: int) {
} }
return -1 return -1
} }
/* /*
Returns the last byte offset of the string `substr` in the string `s`, -1 when not found. Returns the last byte offset of the string `substr` in the string `s`, -1 when not found.
@@ -1734,6 +1706,7 @@ last_index :: proc(s, substr: string) -> (res: int) {
} }
return -1 return -1
} }
/* /*
Returns the index of any first char of `chars` found in `s`, -1 if not found. Returns the index of any first char of `chars` found in `s`, -1 if not found.
@@ -1797,6 +1770,7 @@ index_any :: proc(s, chars: string) -> (res: int) {
} }
return -1 return -1
} }
/* /*
Finds the last occurrence of any character in `chars` within `s`. Iterates in reverse. Finds the last occurrence of any character in `chars` within `s`. Iterates in reverse.
@@ -1878,6 +1852,7 @@ last_index_any :: proc(s, chars: string) -> (res: int) {
} }
return -1 return -1
} }
/* /*
Finds the first occurrence of any substring in `substrs` within `s` Finds the first occurrence of any substring in `substrs` within `s`
@@ -1919,6 +1894,7 @@ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) {
} }
return return
} }
/* /*
Counts the number of non-overlapping occurrences of `substr` in `s` Counts the number of non-overlapping occurrences of `substr` in `s`
@@ -1985,6 +1961,7 @@ count :: proc(s, substr: string) -> (res: int) {
} }
return n return n
} }
/* /*
Repeats the string `s` `count` times, concatenating the result Repeats the string `s` `count` times, concatenating the result
@@ -2030,6 +2007,7 @@ repeat :: proc(s: string, count: int, allocator := context.allocator, loc := #ca
} }
return string(b), nil return string(b), nil
} }
/* /*
Replaces all occurrences of `old` in `s` with `new` Replaces all occurrences of `old` in `s` with `new`
@@ -2063,9 +2041,11 @@ Output:
zzzz true zzzz true
*/ */
replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return replace(s, old, new, -1, allocator) return replace(s, old, new, -1, allocator)
} }
/* /*
Replaces n instances of old in the string s with the new string Replaces n instances of old in the string s with the new string
@@ -2144,6 +2124,7 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator, loc
output = string(t[0:w]) output = string(t[0:w])
return return
} }
/* /*
Removes the key string `n` times from the `s` string Removes the key string `n` times from the `s` string
@@ -2182,6 +2163,7 @@ Output:
remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) { remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return replace(s, key, "", n, allocator) return replace(s, key, "", n, allocator)
} }
/* /*
Removes all the `key` string instances from the `s` string Removes all the `key` string instances from the `s` string
@@ -2217,6 +2199,7 @@ Output:
remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return remove(s, key, -1, allocator) return remove(s, key, -1, allocator)
} }
// Returns true if is an ASCII space character ('\t', '\n', '\v', '\f', '\r', ' ') // Returns true if is an ASCII space character ('\t', '\n', '\v', '\f', '\r', ' ')
@(private) _ascii_space := [256]bool{'\t' = true, '\n' = true, '\v' = true, '\f' = true, '\r' = true, ' ' = true} @(private) _ascii_space := [256]bool{'\t' = true, '\n' = true, '\v' = true, '\f' = true, '\r' = true, ' ' = true}
@@ -2320,6 +2303,7 @@ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int
} }
return -1 return -1
} }
// Same as `index_proc`, but the procedure p takes a raw pointer for state // Same as `index_proc`, but the procedure p takes a raw pointer for state
index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) { index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) {
for r, i in s { for r, i in s {
@@ -2329,6 +2313,7 @@ index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: r
} }
return -1 return -1
} }
// Finds the index of the *last* rune in the string s for which the procedure p returns the same value as truth // Finds the index of the *last* rune in the string s for which the procedure p returns the same value as truth
last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int) { last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int) {
// TODO(bill): Probably use Rabin-Karp Search // TODO(bill): Probably use Rabin-Karp Search
@@ -2341,6 +2326,7 @@ last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res
} }
return -1 return -1
} }
// Same as `index_proc_with_state`, runs through the string in reverse // Same as `index_proc_with_state`, runs through the string in reverse
last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) { last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) {
// TODO(bill): Probably use Rabin-Karp Search // TODO(bill): Probably use Rabin-Karp Search
@@ -2353,6 +2339,7 @@ last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta
} }
return -1 return -1
} }
/* /*
Trims the input string `s` from the left until the procedure `p` returns false Trims the input string `s` from the left until the procedure `p` returns false
@@ -2387,6 +2374,7 @@ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) {
} }
return s[i:] return s[i:]
} }
/* /*
Trims the input string `s` from the left until the procedure `p` with state returns false Trims the input string `s` from the left until the procedure `p` with state returns false
@@ -2405,6 +2393,7 @@ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, stat
} }
return s[i:] return s[i:]
} }
/* /*
Trims the input string `s` from the right until the procedure `p` returns `false` Trims the input string `s` from the right until the procedure `p` returns `false`
@@ -2442,6 +2431,7 @@ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) {
} }
return s[0:i] return s[0:i]
} }
/* /*
Trims the input string `s` from the right until the procedure `p` with state returns `false` Trims the input string `s` from the right until the procedure `p` with state returns `false`
@@ -2463,6 +2453,7 @@ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta
} }
return s[0:i] return s[0:i]
} }
// Procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison // Procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison
is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) { is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) {
cutset := (^string)(state)^ cutset := (^string)(state)^
@@ -2473,6 +2464,7 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) {
} }
return false return false
} }
/* /*
Trims the cutset string from the `s` string Trims the cutset string from the `s` string
@@ -2490,6 +2482,7 @@ trim_left :: proc(s: string, cutset: string) -> (res: string) {
state := cutset state := cutset
return trim_left_proc_with_state(s, is_in_cutset, &state) return trim_left_proc_with_state(s, is_in_cutset, &state)
} }
/* /*
Trims the cutset string from the `s` string from the right Trims the cutset string from the `s` string from the right
@@ -2507,6 +2500,7 @@ trim_right :: proc(s: string, cutset: string) -> (res: string) {
state := cutset state := cutset
return trim_right_proc_with_state(s, is_in_cutset, &state) return trim_right_proc_with_state(s, is_in_cutset, &state)
} }
/* /*
Trims the cutset string from the `s` string, both from left and right Trims the cutset string from the `s` string, both from left and right
@@ -2520,6 +2514,7 @@ Returns:
trim :: proc(s: string, cutset: string) -> (res: string) { trim :: proc(s: string, cutset: string) -> (res: string) {
return trim_right(trim_left(s, cutset), cutset) return trim_right(trim_left(s, cutset), cutset)
} }
/* /*
Trims until a valid non-space rune from the left, "\t\txyz\t\t" -> "xyz\t\t" Trims until a valid non-space rune from the left, "\t\txyz\t\t" -> "xyz\t\t"
@@ -2532,6 +2527,7 @@ Returns:
trim_left_space :: proc(s: string) -> (res: string) { trim_left_space :: proc(s: string) -> (res: string) {
return trim_left_proc(s, is_space) return trim_left_proc(s, is_space)
} }
/* /*
Trims from the right until a valid non-space rune, "\t\txyz\t\t" -> "\t\txyz" Trims from the right until a valid non-space rune, "\t\txyz\t\t" -> "\t\txyz"
@@ -2544,6 +2540,7 @@ Returns:
trim_right_space :: proc(s: string) -> (res: string) { trim_right_space :: proc(s: string) -> (res: string) {
return trim_right_proc(s, is_space) return trim_right_proc(s, is_space)
} }
/* /*
Trims from both sides until a valid non-space rune, "\t\txyz\t\t" -> "xyz" Trims from both sides until a valid non-space rune, "\t\txyz\t\t" -> "xyz"
@@ -2556,6 +2553,7 @@ Returns:
trim_space :: proc(s: string) -> (res: string) { trim_space :: proc(s: string) -> (res: string) {
return trim_right_space(trim_left_space(s)) return trim_right_space(trim_left_space(s))
} }
/* /*
Trims null runes from the left, "\x00\x00testing\x00\x00" -> "testing\x00\x00" Trims null runes from the left, "\x00\x00testing\x00\x00" -> "testing\x00\x00"
@@ -2568,6 +2566,7 @@ Returns:
trim_left_null :: proc(s: string) -> (res: string) { trim_left_null :: proc(s: string) -> (res: string) {
return trim_left_proc(s, is_null) return trim_left_proc(s, is_null)
} }
/* /*
Trims null runes from the right, "\x00\x00testing\x00\x00" -> "\x00\x00testing" Trims null runes from the right, "\x00\x00testing\x00\x00" -> "\x00\x00testing"
@@ -2580,6 +2579,7 @@ Returns:
trim_right_null :: proc(s: string) -> (res: string) { trim_right_null :: proc(s: string) -> (res: string) {
return trim_right_proc(s, is_null) return trim_right_proc(s, is_null)
} }
/* /*
Trims null runes from both sides, "\x00\x00testing\x00\x00" -> "testing" Trims null runes from both sides, "\x00\x00testing\x00\x00" -> "testing"
@@ -2591,6 +2591,7 @@ Returns:
trim_null :: proc(s: string) -> (res: string) { trim_null :: proc(s: string) -> (res: string) {
return trim_right_null(trim_left_null(s)) return trim_right_null(trim_left_null(s))
} }
/* /*
Trims a `prefix` string from the start of the `s` string and returns the trimmed string Trims a `prefix` string from the start of the `s` string and returns the trimmed string
@@ -2623,6 +2624,7 @@ trim_prefix :: proc(s, prefix: string) -> (res: string) {
} }
return s return s
} }
/* /*
Trims a `suffix` string from the end of the `s` string and returns the trimmed string Trims a `suffix` string from the end of the `s` string and returns the trimmed string
@@ -2655,6 +2657,7 @@ trim_suffix :: proc(s, suffix: string) -> (res: string) {
} }
return s return s
} }
/* /*
Splits the input string `s` by all possible `substrs` and returns an allocated array of strings Splits the input string `s` by all possible `substrs` and returns an allocated array of strings
@@ -2727,6 +2730,7 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator
assert(len(results) == n) assert(len(results) == n)
return results[:], nil return results[:], nil
} }
/* /*
Splits the input string `s` by all possible `substrs` in an iterator fashion. The full string is returned if no match. Splits the input string `s` by all possible `substrs` in an iterator fashion. The full string is returned if no match.
@@ -2786,6 +2790,7 @@ split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok:
ok = true ok = true
return return
} }
/* /*
Replaces invalid UTF-8 characters in the input string with a specified replacement string. Adjacent invalid bytes are only replaced once. Replaces invalid UTF-8 characters in the input string with a specified replacement string. Adjacent invalid bytes are only replaced once.
@@ -2846,6 +2851,7 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) ->
return to_string(b), nil return to_string(b), nil
} }
/* /*
Reverses the input string `s` Reverses the input string `s`
@@ -2889,6 +2895,7 @@ reverse :: proc(s: string, allocator := context.allocator, loc := #caller_locati
} }
return string(buf), nil return string(buf), nil
} }
/* /*
Expands the input string by replacing tab characters with spaces to align to a specified tab size Expands the input string by replacing tab characters with spaces to align to a specified tab size
@@ -2920,6 +2927,7 @@ Output:
abc1 abc2 abc3 abc1 abc2 abc3
*/ */
expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error { expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
if tab_size <= 0 { if tab_size <= 0 {
panic("tab size must be positive") panic("tab size must be positive")
@@ -2961,6 +2969,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
return to_string(b), nil return to_string(b), nil
} }
/* /*
Splits the input string `str` by the separator `sep` string and returns 3 parts. The values are slices of the original string. Splits the input string `str` by the separator `sep` string and returns 3 parts. The values are slices of the original string.
@@ -3011,8 +3020,10 @@ partition :: proc(str, sep: string) -> (head, match, tail: string) {
tail = str[i+len(sep):] tail = str[i+len(sep):]
return return
} }
// Alias for centre_justify // Alias for centre_justify
center_justify :: centre_justify // NOTE(bill): Because Americans exist center_justify :: centre_justify // NOTE(bill): Because Americans exist
/* /*
Centers the input string within a field of specified length by adding pad string on both sides, if its length is less than the target length. Centers the input string within a field of specified length by adding pad string on both sides, if its length is less than the target length.
@@ -3048,6 +3059,7 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
return to_string(b), nil return to_string(b), nil
} }
/* /*
Left-justifies the input string within a field of specified length by adding pad string on the right side, if its length is less than the target length. Left-justifies the input string within a field of specified length by adding pad string on the right side, if its length is less than the target length.
@@ -3082,6 +3094,7 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
return to_string(b), nil return to_string(b), nil
} }
/* /*
Right-justifies the input string within a field of specified length by adding pad string on the left side, if its length is less than the target length. Right-justifies the input string within a field of specified length by adding pad string on the left side, if its length is less than the target length.
@@ -3116,6 +3129,7 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex
return to_string(b), nil return to_string(b), nil
} }
/* /*
Writes a given pad string a specified number of times to an `io.Writer` Writes a given pad string a specified number of times to an `io.Writer`
@@ -3142,6 +3156,7 @@ write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) {
p = p[width:] p = p[width:]
} }
} }
/* /*
Splits a string into a slice of substrings at each instance of one or more consecutive white space characters, as defined by `unicode.is_space` Splits a string into a slice of substrings at each instance of one or more consecutive white space characters, as defined by `unicode.is_space`
@@ -3203,6 +3218,7 @@ fields :: proc(s: string, allocator := context.allocator, loc := #caller_locatio
} }
return a, nil return a, nil
} }
/* /*
Splits a string into a slice of substrings at each run of unicode code points `r` satisfying the predicate `f(r)` Splits a string into a slice of substrings at each run of unicode code points `r` satisfying the predicate `f(r)`
@@ -3245,6 +3261,7 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc
return substrings[:], nil return substrings[:], nil
} }
/* /*
Retrieves the first non-space substring from a mutable string reference and advances the reference. `s` is advanced from any space after the substring, or be an empty string if the substring was the remaining characters Retrieves the first non-space substring from a mutable string reference and advances the reference. `s` is advanced from any space after the substring, or be an empty string if the substring was the remaining characters
@@ -3283,6 +3300,7 @@ fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) {
s^ = s[len(s):] s^ = s[len(s):]
return return
} }
/* /*
Computes the Levenshtein edit distance between two strings Computes the Levenshtein edit distance between two strings
@@ -82,7 +82,6 @@ Application_setActivationPolicy :: proc "c" (self: ^Application, activationPolic
// NOTE: this is technically deprecated but still actively used (Sokol, glfw, SDL, etc.) // NOTE: this is technically deprecated but still actively used (Sokol, glfw, SDL, etc.)
// and has no clear alternative although `activate` is what Apple tells you to use, // and has no clear alternative although `activate` is what Apple tells you to use,
// that does not work the same way. // that does not work the same way.
// @(deprecated="Use NSApplication method activate instead.")
@(objc_type=Application, objc_name="activateIgnoringOtherApps") @(objc_type=Application, objc_name="activateIgnoringOtherApps")
Application_activateIgnoringOtherApps :: proc "c" (self: ^Application, ignoreOtherApps: BOOL) { Application_activateIgnoringOtherApps :: proc "c" (self: ^Application, ignoreOtherApps: BOOL) {
msgSend(nil, self, "activateIgnoringOtherApps:", ignoreOtherApps) msgSend(nil, self, "activateIgnoringOtherApps:", ignoreOtherApps)
+1 -1
View File
@@ -1413,7 +1413,7 @@ umask :: proc "contextless" (mask: Mode) -> Mode {
Available since Linux 1.0. Available since Linux 1.0.
*/ */
gettimeofday :: proc "contextless" (tv: ^Time_Val) -> (Errno) { gettimeofday :: proc "contextless" (tv: ^Time_Val) -> (Errno) {
ret := syscall(SYS_gettimeofday, tv) ret := syscall(SYS_gettimeofday, tv, rawptr(nil))
return Errno(-ret) return Errno(-ret)
} }
-8
View File
@@ -75,14 +75,6 @@ LANGIDFROMLCID :: #force_inline proc "contextless" (lcid: LCID) -> LANGID {
return LANGID(lcid) return LANGID(lcid)
} }
// this one gave me trouble as it do not mask the values.
// the _ in the name is also off comparing to the c code
// i can't find any usage in the odin repo
@(deprecated = "use MAKEWORD")
MAKE_WORD :: #force_inline proc "contextless" (x, y: WORD) -> WORD {
return x << 8 | y
}
utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 { utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
if len(s) < 1 { if len(s) < 1 {
return nil return nil
+2 -2
View File
@@ -243,9 +243,9 @@ foreign ws2_32 {
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohs) // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohs)
ntohs :: proc(netshort: c_ushort) -> c_ushort --- ntohs :: proc(netshort: c_ushort) -> c_ushort ---
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htonl) // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htonl)
@(deprecated="Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types") // Prefer using endian-specific integers instead, https://odin-lang.org/docs/overview/#basic-types
htonl :: proc(hostlong: c_ulong) -> c_ulong --- htonl :: proc(hostlong: c_ulong) -> c_ulong ---
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htons) // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htons)
@(deprecated="Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types") // Prefer using endian-specific integers instead, https://odin-lang.org/docs/overview/#basic-types
htons :: proc(hostshort: c_ushort) -> c_ushort --- htons :: proc(hostshort: c_ushort) -> c_ushort ---
} }
+1 -6
View File
@@ -577,12 +577,7 @@ parse_tzif :: proc(_buffer: []u8, region_name: string, allocator := context.allo
footer_str := string(buffer[:end_idx]) footer_str := string(buffer[:end_idx])
// UTC is a special case, we don't need to alloc // UTC is a special case, we don't need to alloc
if len(local_time_types) == 1 { if len(local_time_types) == 1 && local_time_types[0].utoff == 0 {
name := cstring(raw_data(timezone_string_table[local_time_types[0].idx:]))
if name != "UTC" {
return
}
return nil, true return nil, true
} }
+2
View File
@@ -41,6 +41,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool
is_type_enum, is_type_enum,
is_type_proc, is_type_proc,
is_type_bit_set, is_type_bit_set,
is_type_bit_field,
is_type_simd_vector, is_type_simd_vector,
is_type_matrix, is_type_matrix,
@@ -6081,6 +6082,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_type_is_enum: case BuiltinProc_type_is_enum:
case BuiltinProc_type_is_proc: case BuiltinProc_type_is_proc:
case BuiltinProc_type_is_bit_set: case BuiltinProc_type_is_bit_set:
case BuiltinProc_type_is_bit_field:
case BuiltinProc_type_is_simd_vector: case BuiltinProc_type_is_simd_vector:
case BuiltinProc_type_is_matrix: case BuiltinProc_type_is_matrix:
case BuiltinProc_type_is_specialized_polymorphic_record: case BuiltinProc_type_is_specialized_polymorphic_record:
+1
View File
@@ -1370,6 +1370,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
e->Procedure.has_instrumentation = has_instrumentation; e->Procedure.has_instrumentation = has_instrumentation;
e->Procedure.no_sanitize_address = ac.no_sanitize_address; e->Procedure.no_sanitize_address = ac.no_sanitize_address;
e->Procedure.no_sanitize_memory = ac.no_sanitize_memory;
e->deprecated_message = ac.deprecated_message; e->deprecated_message = ac.deprecated_message;
e->warning_message = ac.warning_message; e->warning_message = ac.warning_message;
+14
View File
@@ -1593,6 +1593,20 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
error_line("\tSuggestion: Was '#partial switch' wanted?\n"); error_line("\tSuggestion: Was '#partial switch' wanted?\n");
} }
} }
if (build_context.strict_style) {
Token stok = ss->token;
for_array(i, bs->stmts) {
Ast *stmt = bs->stmts[i];
if (stmt->kind != Ast_CaseClause) {
continue;
}
Token ctok = stmt->CaseClause.token;
if (ctok.pos.column > stok.pos.column) {
error(ctok, "With '-strict-style', 'case' statements must share the same column as the 'switch' token");
}
}
}
} }
gb_internal void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body) { gb_internal void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body) {
+9 -3
View File
@@ -2054,7 +2054,7 @@ gb_internal void add_type_info_type(CheckerContext *c, Type *t) {
} }
gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
if (t == nullptr) { if (t == nullptr || c == nullptr) {
return; return;
} }
@@ -3776,6 +3776,12 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
} }
ac->no_sanitize_address = true; ac->no_sanitize_address = true;
return true; return true;
} else if (name == "no_sanitize_memory") {
if (value != nullptr) {
error(value, "'%.*s' expects no parameter", LIT(name));
}
ac->no_sanitize_memory = true;
return true;
} }
return false; return false;
} }
@@ -6672,7 +6678,7 @@ gb_internal void check_sort_init_and_fini_procedures(Checker *c) {
gb_internal void add_type_info_for_type_definitions(Checker *c) { gb_internal void add_type_info_for_type_definitions(Checker *c) {
for_array(i, c->info.definitions) { for_array(i, c->info.definitions) {
Entity *e = c->info.definitions[i]; Entity *e = c->info.definitions[i];
if (e->kind == Entity_TypeName && e->type != nullptr) { if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) {
i64 align = type_align_of(e->type); i64 align = type_align_of(e->type);
if (align > 0 && ptr_set_exists(&c->info.minimum_dependency_set, e)) { if (align > 0 && ptr_set_exists(&c->info.minimum_dependency_set, e)) {
add_type_info_type(&c->builtin_ctx, e->type); add_type_info_type(&c->builtin_ctx, e->type);
@@ -6794,7 +6800,7 @@ gb_internal void check_parsed_files(Checker *c) {
// NOTE(bill): Check for illegal cyclic type declarations // NOTE(bill): Check for illegal cyclic type declarations
for_array(i, c->info.definitions) { for_array(i, c->info.definitions) {
Entity *e = c->info.definitions[i]; Entity *e = c->info.definitions[i];
if (e->kind == Entity_TypeName && e->type != nullptr) { if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) {
(void)type_align_of(e->type); (void)type_align_of(e->type);
} else if (e->kind == Entity_Procedure) { } else if (e->kind == Entity_Procedure) {
DeclInfo *decl = e->decl_info; DeclInfo *decl = e->decl_info;
+1
View File
@@ -140,6 +140,7 @@ struct AttributeContext {
bool instrumentation_enter : 1; bool instrumentation_enter : 1;
bool instrumentation_exit : 1; bool instrumentation_exit : 1;
bool no_sanitize_address : 1; bool no_sanitize_address : 1;
bool no_sanitize_memory : 1;
bool rodata : 1; bool rodata : 1;
bool ignore_duplicates : 1; bool ignore_duplicates : 1;
u32 optimization_mode; // ProcedureOptimizationMode u32 optimization_mode; // ProcedureOptimizationMode
+2
View File
@@ -240,6 +240,7 @@ BuiltinProc__type_begin,
BuiltinProc__type_simple_boolean_begin, BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_boolean, BuiltinProc_type_is_boolean,
BuiltinProc_type_is_bit_field,
BuiltinProc_type_is_integer, BuiltinProc_type_is_integer,
BuiltinProc_type_is_rune, BuiltinProc_type_is_rune,
BuiltinProc_type_is_float, BuiltinProc_type_is_float,
@@ -630,6 +631,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_matrix"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_matrix"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+1
View File
@@ -263,6 +263,7 @@ struct Entity {
bool uses_branch_location : 1; bool uses_branch_location : 1;
bool is_anonymous : 1; bool is_anonymous : 1;
bool no_sanitize_address : 1; bool no_sanitize_address : 1;
bool no_sanitize_memory : 1;
} Procedure; } Procedure;
struct { struct {
Array<Entity *> entities; Array<Entity *> entities;
+11
View File
@@ -1054,9 +1054,20 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
} }
} }
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
return res;
} else if (value.value_compound->tav.type == elem_type) {
// Compound is of array item type; expand its value to all items in array.
LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count);
for (isize i = 0; i < type->Array.count; i++) {
values[i] = lb_const_value(m, elem_type, value, cc).value;
}
res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
return res; return res;
} else { } else {
// Assume that compound value is an array literal
GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count);
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count); LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count);
+1 -1
View File
@@ -345,7 +345,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
if (build_context.sanitizer_flags & SanitizerFlag_Address && !entity->Procedure.no_sanitize_address) { if (build_context.sanitizer_flags & SanitizerFlag_Address && !entity->Procedure.no_sanitize_address) {
lb_add_attribute_to_proc(m, p->value, "sanitize_address"); lb_add_attribute_to_proc(m, p->value, "sanitize_address");
} }
if (build_context.sanitizer_flags & SanitizerFlag_Memory) { if (build_context.sanitizer_flags & SanitizerFlag_Memory && !entity->Procedure.no_sanitize_memory) {
lb_add_attribute_to_proc(m, p->value, "sanitize_memory"); lb_add_attribute_to_proc(m, p->value, "sanitize_memory");
} }
if (build_context.sanitizer_flags & SanitizerFlag_Thread) { if (build_context.sanitizer_flags & SanitizerFlag_Thread) {
+52 -52
View File
@@ -1996,39 +1996,39 @@ gb_internal void show_timings(Checker *c, Timings *t) {
if (build_context.show_debug_messages && build_context.show_more_timings) { if (build_context.show_debug_messages && build_context.show_more_timings) {
{ {
gb_printf("\n"); gb_printf_err("\n");
gb_printf("Total Lines - %td\n", lines); gb_printf_err("Total Lines - %td\n", lines);
gb_printf("Total Tokens - %td\n", tokens); gb_printf_err("Total Tokens - %td\n", tokens);
gb_printf("Total Files - %td\n", files); gb_printf_err("Total Files - %td\n", files);
gb_printf("Total Packages - %td\n", packages); gb_printf_err("Total Packages - %td\n", packages);
gb_printf("Total File Size - %td\n", total_file_size); gb_printf_err("Total File Size - %td\n", total_file_size);
gb_printf("\n"); gb_printf_err("\n");
} }
{ {
f64 time = total_tokenizing_time; f64 time = total_tokenizing_time;
gb_printf("Tokenization Only\n"); gb_printf_err("Tokenization Only\n");
gb_printf("LOC/s - %.3f\n", cast(f64)lines/time); gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/time);
gb_printf("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines); gb_printf_err("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines);
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/time); gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/time);
gb_printf("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens); gb_printf_err("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens);
gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/time); gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/time);
gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024)); gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024));
gb_printf("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size); gb_printf_err("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size);
gb_printf("\n"); gb_printf_err("\n");
} }
{ {
f64 time = total_parsing_time; f64 time = total_parsing_time;
gb_printf("Parsing Only\n"); gb_printf_err("Parsing Only\n");
gb_printf("LOC/s - %.3f\n", cast(f64)lines/time); gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/time);
gb_printf("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines); gb_printf_err("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines);
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/time); gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/time);
gb_printf("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens); gb_printf_err("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens);
gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/time); gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/time);
gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024)); gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024));
gb_printf("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size); gb_printf_err("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size);
gb_printf("\n"); gb_printf_err("\n");
} }
{ {
TimeStamp ts = {}; TimeStamp ts = {};
@@ -2041,16 +2041,16 @@ gb_internal void show_timings(Checker *c, Timings *t) {
GB_ASSERT(ts.label == "parse files"); GB_ASSERT(ts.label == "parse files");
f64 parse_time = time_stamp_as_s(ts, t->freq); f64 parse_time = time_stamp_as_s(ts, t->freq);
gb_printf("Parse pass\n"); gb_printf_err("Parse pass\n");
gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time); gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/parse_time);
gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines); gb_printf_err("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time); gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens); gb_printf_err("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time); gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time);
gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/parse_time)/(1024*1024)); gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/parse_time)/(1024*1024));
gb_printf("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size); gb_printf_err("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size);
gb_printf("\n"); gb_printf_err("\n");
} }
{ {
TimeStamp ts = {}; TimeStamp ts = {};
@@ -2071,27 +2071,27 @@ gb_internal void show_timings(Checker *c, Timings *t) {
ts.finish = ts_end.finish; ts.finish = ts_end.finish;
f64 parse_time = time_stamp_as_s(ts, t->freq); f64 parse_time = time_stamp_as_s(ts, t->freq);
gb_printf("Checker pass\n"); gb_printf_err("Checker pass\n");
gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time); gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/parse_time);
gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines); gb_printf_err("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time); gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens); gb_printf_err("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time); gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time);
gb_printf("MiB/s - %.3f\n", (cast(f64)total_file_size/parse_time)/(1024*1024)); gb_printf_err("MiB/s - %.3f\n", (cast(f64)total_file_size/parse_time)/(1024*1024));
gb_printf("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size); gb_printf_err("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size);
gb_printf("\n"); gb_printf_err("\n");
} }
{ {
f64 total_time = t->total_time_seconds; f64 total_time = t->total_time_seconds;
gb_printf("Total pass\n"); gb_printf_err("Total pass\n");
gb_printf("LOC/s - %.3f\n", cast(f64)lines/total_time); gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/total_time);
gb_printf("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines); gb_printf_err("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines);
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/total_time); gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/total_time);
gb_printf("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens); gb_printf_err("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens);
gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/total_time); gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/total_time);
gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/total_time)/(1024*1024)); gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/total_time)/(1024*1024));
gb_printf("us/bytes - %.3f\n", 1.0e6*total_time/cast(f64)total_file_size); gb_printf_err("us/bytes - %.3f\n", 1.0e6*total_time/cast(f64)total_file_size);
gb_printf("\n"); gb_printf_err("\n");
} }
} }
} }
+2 -2
View File
@@ -5794,7 +5794,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
for (FileInfo fi : list) { for (FileInfo fi : list) {
String name = fi.name; String name = fi.name;
String ext = path_extension(name); String ext = path_extension(name);
if (ext == FILE_EXT) { if (ext == FILE_EXT && !path_is_directory(name)) {
files_with_ext += 1; files_with_ext += 1;
} }
if (ext == FILE_EXT && !is_excluded_target_filename(name)) { if (ext == FILE_EXT && !is_excluded_target_filename(name)) {
@@ -5819,7 +5819,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
for (FileInfo fi : list) { for (FileInfo fi : list) {
String name = fi.name; String name = fi.name;
String ext = path_extension(name); String ext = path_extension(name);
if (ext == FILE_EXT) { if (ext == FILE_EXT && !path_is_directory(name)) {
if (is_excluded_target_filename(name)) { if (is_excluded_target_filename(name)) {
continue; continue;
} }
+2 -2
View File
@@ -197,7 +197,7 @@ gb_internal void timings_print_all(Timings *t, TimingUnit unit = TimingUnit_Mill
f64 total_time = time_stamp(t->total, t->freq, unit); f64 total_time = time_stamp(t->total, t->freq, unit);
gb_printf("%.*s%.*s - % 9.3f %s - %6.2f%%\n", gb_printf_err("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
LIT(t->total.label), LIT(t->total.label),
cast(int)(max_len-t->total.label.len), SPACES, cast(int)(max_len-t->total.label.len), SPACES,
total_time, total_time,
@@ -207,7 +207,7 @@ gb_internal void timings_print_all(Timings *t, TimingUnit unit = TimingUnit_Mill
for_array(i, t->sections) { for_array(i, t->sections) {
TimeStamp ts = t->sections[i]; TimeStamp ts = t->sections[i];
f64 section_time = time_stamp(ts, t->freq, unit); f64 section_time = time_stamp(ts, t->freq, unit);
gb_printf("%.*s%.*s - % 9.3f %s - %6.2f%%\n", gb_printf_err("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
LIT(ts.label), LIT(ts.label),
cast(int)(max_len-ts.label.len), SPACES, cast(int)(max_len-ts.label.len), SPACES,
section_time, section_time,
@@ -43,6 +43,8 @@ Foo :: struct {
biggest: big.Int, biggest: big.Int,
smallest: big.Int, smallest: big.Int,
ignore_this: ^Foo `cbor:"-"`, ignore_this: ^Foo `cbor:"-"`,
mat: matrix[4, 4]f32,
vec: #simd [4]f64,
} }
FooBar :: enum { FooBar :: enum {
@@ -95,6 +97,8 @@ test_marshalling :: proc(t: ^testing.T) {
onetwenty = i128(12345), onetwenty = i128(12345),
small_onetwenty = -i128(max(u64)), small_onetwenty = -i128(max(u64)),
ignore_this = &Foo{}, ignore_this = &Foo{},
mat = 1,
vec = 2,
} }
big.atoi(&f.biggest, "1234567891011121314151617181920") big.atoi(&f.biggest, "1234567891011121314151617181920")
@@ -120,11 +124,35 @@ test_marshalling :: proc(t: ^testing.T) {
defer delete(diagnosis) defer delete(diagnosis)
testing.expect_value(t, diagnosis, `{ testing.expect_value(t, diagnosis, `{
"no": null, "no": null,
"mat": [
1.0000,
0.0000,
0.0000,
0.0000,
0.0000,
1.0000,
0.0000,
0.0000,
0.0000,
0.0000,
1.0000,
0.0000,
0.0000,
0.0000,
0.0000,
1.0000
],
"neg": -69, "neg": -69,
"nos": undefined, "nos": undefined,
"now": 1(1701117968), "now": 1(1701117968),
"pos": 1212, "pos": 1212,
"str": "Hellope", "str": "Hellope",
"vec": [
2.0000,
2.0000,
2.0000,
2.0000
],
"yes": true, "yes": true,
"comp": [ "comp": [
32.0000, 32.0000,
+1 -1
View File
@@ -1238,7 +1238,7 @@ test_count_digits :: proc(t: ^testing.T) {
buf: [64]u8 buf: [64]u8
for n in 0..<i64(base*base*base) { for n in 0..<i64(base*base*base) {
count := math.count_digits_of_base(n, base) count := math.count_digits_of_base(n, base)
str := strconv.append_int(buf[:], n, base) str := strconv.write_int(buf[:], n, base)
if !testing.expectf(t, if !testing.expectf(t,
len(str) == count, len(str) == count,
"decimal %i in base-%i digit count is %i, does not match length %i of %q", "decimal %i in base-%i digit count is %i, does not match length %i of %q",
+1
View File
@@ -17,6 +17,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style
..\..\..\odin test ..\test_issue_2637.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_2637.odin %COMMON% || exit /b
..\..\..\odin test ..\test_issue_2666.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_2666.odin %COMMON% || exit /b
..\..\..\odin test ..\test_issue_4210.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_4210.odin %COMMON% || exit /b
..\..\..\odin test ..\test_issue_4364.odin %COMMON% || exit /b
..\..\..\odin test ..\test_issue_4584.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_4584.odin %COMMON% || exit /b
..\..\..\odin build ..\test_issue_5043.odin %COMMON% || exit /b ..\..\..\odin build ..\test_issue_5043.odin %COMMON% || exit /b
..\..\..\odin build ..\test_issue_5097.odin %COMMON% || exit /b ..\..\..\odin build ..\test_issue_5097.odin %COMMON% || exit /b
+1
View File
@@ -18,6 +18,7 @@ $ODIN test ../test_issue_2615.odin $COMMON
$ODIN test ../test_issue_2637.odin $COMMON $ODIN test ../test_issue_2637.odin $COMMON
$ODIN test ../test_issue_2666.odin $COMMON $ODIN test ../test_issue_2666.odin $COMMON
$ODIN test ../test_issue_4210.odin $COMMON $ODIN test ../test_issue_4210.odin $COMMON
$ODIN test ../test_issue_4364.odin $COMMON
$ODIN test ../test_issue_4584.odin $COMMON $ODIN test ../test_issue_4584.odin $COMMON
if [[ $($ODIN build ../test_issue_2395.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]] ; then if [[ $($ODIN build ../test_issue_2395.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]] ; then
echo "SUCCESSFUL 1/1" echo "SUCCESSFUL 1/1"
+18
View File
@@ -0,0 +1,18 @@
// Tests issue #4364 https://github.com/odin-lang/Odin/issues/4364
package test_issues
import "core:testing"
@test
test_const_array_fill_assignment :: proc(t: ^testing.T) {
MAGIC :: 12345
Struct :: struct {x: int}
CONST_ARR : [4]Struct : Struct{MAGIC}
arr := CONST_ARR
testing.expect_value(t, len(arr), 4)
testing.expect_value(t, arr[0], Struct{MAGIC})
testing.expect_value(t, arr[1], Struct{MAGIC})
testing.expect_value(t, arr[2], Struct{MAGIC})
testing.expect_value(t, arr[3], Struct{MAGIC})
}
+3 -3
View File
@@ -194,7 +194,7 @@ ICompiler_VTable :: struct {
using iunknown_vtable: IUnknown_VTable, using iunknown_vtable: IUnknown_VTable,
Compile: proc "system" ( Compile: proc "system" (
this: ^ICompiler, this: ^ICompiler,
pSource: ^Buffer, pSource: ^IBlob,
pSourceName: wstring, pSourceName: wstring,
pEntryPoint: wstring, pEntryPoint: wstring,
pTargetProfile: wstring, pTargetProfile: wstring,
@@ -206,7 +206,7 @@ ICompiler_VTable :: struct {
ppResult: ^^IOperationResult) -> HRESULT, ppResult: ^^IOperationResult) -> HRESULT,
Preprocess: proc "system" ( Preprocess: proc "system" (
this: ^ICompiler, this: ^ICompiler,
pSource: ^Buffer, pSource: ^IBlob,
pSourceName: wstring, pSourceName: wstring,
pArguments: [^]wstring, pArguments: [^]wstring,
argCount: u32, argCount: u32,
@@ -227,7 +227,7 @@ ICompiler2_VTable :: struct {
using idxccompiler_vtable: ^ICompiler_VTable, using idxccompiler_vtable: ^ICompiler_VTable,
CompileWithDebug: proc "system" ( CompileWithDebug: proc "system" (
this: ^ICompiler2, this: ^ICompiler2,
pSource: ^Buffer, pSource: ^IBlob,
pSourceName: wstring, pSourceName: wstring,
pEntryPoint: wstring, pEntryPoint: wstring,
pTargetProfile: wstring, pTargetProfile: wstring,