This commit is contained in:
gingerBill
2025-06-09 17:19:58 +01:00
56 changed files with 1075 additions and 504 deletions

View File

@@ -169,6 +169,7 @@ type_is_union :: proc($T: typeid) -> bool ---
type_is_enum :: proc($T: typeid) -> bool ---
type_is_proc :: 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_matrix :: proc($T: typeid) -> bool ---

View File

@@ -648,6 +648,9 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i
@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 {
when !ODIN_NO_BOUNDS_CHECK {
ensure(index >= 0, "Index must be positive.", loc)
}
if array == nil {
return
}
@@ -666,6 +669,9 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcas
@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 {
when !ODIN_NO_BOUNDS_CHECK {
ensure(index >= 0, "Index must be positive.", loc)
}
if array == nil {
return
}
@@ -689,6 +695,9 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca
@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 {
when !ODIN_NO_BOUNDS_CHECK {
ensure(index >= 0, "Index must be positive.", loc)
}
if array == nil {
return
}

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,
}

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.
case f16:
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:] }
io.write_string(w, str) or_return
case f32:
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:] }
io.write_string(w, str) or_return
case f64:
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:] }
io.write_string(w, str) or_return

View File

@@ -612,6 +612,42 @@ _marshal_into_encoder :: proc(e: Encoder, v: any, ti: ^runtime.Type_Info) -> (er
case:
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)

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) }
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)
}
}

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) {
switch i in a {
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:
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 {
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
@@ -286,7 +286,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_Integer:
buf: [40]byte
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
case: return .Unsupported_Type

View File

@@ -175,7 +175,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
data = bytes.clone(data)
}
t := &Tokenizer{}
t := new(Tokenizer)
init(t, string(data), path, error_handler)
doc = new(Document)
@@ -403,6 +403,7 @@ destroy :: proc(doc: ^Document) {
}
delete(doc.strings_to_free)
free(doc.tokenizer)
free(doc)
}

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
if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
if fi.plus { flags += {.Plus} }
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
s := strconv.write_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
prev_zero := fi.zero
defer fi.zero = prev_zero
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
if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
if fi.plus { flags += {.Plus} }
s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
s := strconv.write_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
if fi.hash && fi.zero && fi.indent == 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
str := strconv.append_float(buf[:], amt, 'f', prec, 64)
str := strconv.write_float(buf[:], amt, 'f', prec, 64)
// Add the unit at the end.
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
// 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 {
// Strip sign from "+<value>" but not "+Inf".

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) {
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)
}
write_i64 :: proc(w: Writer, i: i64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
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)
}
@@ -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) {
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)
}
write_i128 :: proc(w: Writer, i: i128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
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)
}
write_f16 :: proc(w: Writer, val: f16, n_written: ^int = nil) -> (n: int, err: Error) {
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]
if s[1] == '+' || 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) {
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]
if s[1] == '+' || 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) {
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]
if s[1] == '+' || 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
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) {
case 0:
write_string(w, "00", &n) or_return

View File

@@ -1370,8 +1370,8 @@ _private_int_div_recursive :: proc(quotient, remainder, a, b: ^Int, allocator :=
/*
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) {
ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{}

View File

@@ -103,7 +103,7 @@ round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
}
@(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
x := x
@@ -124,16 +124,16 @@ append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
when size_of(Backing) < 16 {
T :: u64
append_uint :: strconv.append_uint
write_uint :: strconv.write_uint
} else {
T :: u128
append_uint :: strconv.append_u128
write_uint :: strconv.write_u128
}
integer := T(x.i) >> Fraction_Width
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)
if fraction != 0 {
buf[i] = '.'
@@ -155,7 +155,7 @@ append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
@(require_results)
to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.allocator) -> string {
buf: [48]byte
s := append(buf[:], x)
s := write(buf[:], x)
str := make([]byte, len(s), allocator)
copy(str, s)
return string(str)
@@ -294,3 +294,8 @@ _power_of_two_table := [129]string{
"85070591730234615865843651857942052864",
"170141183460469231731687303715884105728",
}
@(deprecated="Use write instead")
append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
return write(dst, x)
}

View File

@@ -125,7 +125,7 @@ percent_encode :: proc(s: string, allocator := context.allocator) -> string {
bytes, n := utf8.encode_rune(ch)
for byte in bytes[:n] {
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_string(&b, t)
}

View File

@@ -57,7 +57,7 @@ write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) {
if r < 32 {
if wrap(write_string(f, "\\x"), &n, &err) { return }
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) {
case 0: if wrap(write_string(f, "00"), &n, &err) { return }
case 1: if wrap(write_rune(f, '0'), &n, &err) { return }

View File

@@ -27,6 +27,9 @@ General_Error :: enum u32 {
Pattern_Has_Separator,
No_HOME_Variable,
Wordexp_Failed,
Unsupported,
}
@@ -59,20 +62,22 @@ error_string :: proc(ferr: Error) -> string {
case General_Error:
switch e {
case .None: return ""
case .Permission_Denied: return "permission denied"
case .Exist: return "file already exists"
case .Not_Exist: return "file does not exist"
case .Closed: return "file already closed"
case .Timeout: return "i/o timeout"
case .Broken_Pipe: return "Broken pipe"
case .No_Size: return "file has no definite size"
case .Invalid_File: return "invalid file"
case .Invalid_Dir: return "invalid directory"
case .Invalid_Path: return "invalid path"
case .Invalid_Callback: return "invalid callback"
case .Invalid_Command: return "invalid command"
case .Unsupported: return "unsupported"
case .Pattern_Has_Separator: return "pattern has separator"
case .Permission_Denied: return "permission denied"
case .Exist: return "file already exists"
case .Not_Exist: return "file does not exist"
case .Closed: return "file already closed"
case .Timeout: return "i/o timeout"
case .Broken_Pipe: return "Broken pipe"
case .No_Size: return "file has no definite size"
case .Invalid_File: return "invalid file"
case .Invalid_Dir: return "invalid directory"
case .Invalid_Path: return "invalid path"
case .Invalid_Callback: return "invalid callback"
case .Invalid_Command: return "invalid command"
case .Unsupported: return "unsupported"
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:
switch e {

View File

@@ -269,6 +269,7 @@ _write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (nt: i64, err: Error
return
}
@(no_sanitize_memory)
_file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
// TODO: Identify 0-sized "pseudo" files and return No_Size. This would
// eliminate the need for the _read_entire_pseudo_file procs.

View File

@@ -59,7 +59,7 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
if r < 32 {
if wrap(write_string(f, "\\x"), &n, &err) { return }
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) {
case 0: if wrap(write_string(f, "00"), &n, &err) { return }
case 1: if wrap(write_rune(f, '0'), &n, &err) { return }

View File

@@ -2,78 +2,148 @@ package os2
import "base:runtime"
@(require_results)
user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
#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
}
// ```
// Windows: C:\Users\Alice
// macOS: /Users/Alice
// Linux: /home/alice
// ```
@(require_results)
user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
env := "HOME"
#partial switch ODIN_OS {
case .Windows:
env = "USERPROFILE"
}
if v := get_env(env, allocator); v != "" {
return v, nil
}
return "", .Invalid_Path
return _user_home_dir(allocator)
}
// 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
core/os/os2/user_posix.odin Normal file
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
}

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
}

View File

@@ -662,7 +662,7 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Error) {
return File_Time(modified), nil
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -674,7 +674,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -688,7 +688,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
s: OS_Stat = ---
result := _unix_fstat(fd, &s)

View File

@@ -325,7 +325,7 @@ _alloc_command_line_arguments :: proc() -> []string {
return res
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -339,7 +339,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -353,7 +353,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized
s: OS_Stat = ---

View File

@@ -674,7 +674,7 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
return i64(res), nil
}
@(require_results)
@(require_results, no_sanitize_memory)
file_size :: proc(fd: Handle) -> (i64, Error) {
// deliberately uninitialized; the syscall fills this buffer for us
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
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -808,7 +808,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -822,7 +822,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---

View File

@@ -724,7 +724,7 @@ last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
return File_Time(modified), nil
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -736,7 +736,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -750,7 +750,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
s: OS_Stat = ---
result := _unix_fstat(fd, &s)

View File

@@ -639,7 +639,7 @@ last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
return File_Time(modified), nil
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -653,7 +653,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -667,7 +667,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) {
return s, nil
}
@(private, require_results)
@(private, require_results, no_sanitize_memory)
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized
s: OS_Stat = ---

View File

@@ -30,14 +30,6 @@ sort :: proc(it: Interface) {
_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) {
return Interface{
collection = rawptr(s),
@@ -80,31 +72,6 @@ reverse_sort :: proc(it: Interface) {
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 {
n := it->len()
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) {
assert(f != nil)
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) {
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) {
assert(f != nil)
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)
}
// @(deprecated="use sort.sort_slice or slice.sort")
quick_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
a := array
n := len(a)
@@ -420,7 +379,6 @@ _log2 :: proc(x: int) -> int {
return res
}
// @(deprecated="use sort.sort or slice.sort_by")
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) {
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)
}
// @(deprecated="use sort.sort_slice or slice.sort")
merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
merge :: proc(a: A, start, mid, end: int) {
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)
}
// @(deprecated="use sort.sort or slice.sort_by")
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 {
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) {
sift :: proc(a: A, pi: int, n: int) #no_bounds_check {
p := pi

View File

@@ -12,11 +12,11 @@ Decimal :: struct {
Sets a Decimal from a given string `s`. The string is expected to represent a float. Stores parsed number in the given Decimal structure.
If parsing fails, the Decimal will be left in an undefined state.
**Inputs**
**Inputs**
- d: Pointer to a Decimal struct where the parsed result will be stored
- s: The input string representing the floating-point number
**Returns**
**Returns**
- ok: A boolean indicating whether the parsing was successful
*/
set :: proc(d: ^Decimal, s: string) -> (ok: bool) {
@@ -104,11 +104,11 @@ set :: proc(d: ^Decimal, s: string) -> (ok: bool) {
/*
Converts a Decimal to a string representation, using the provided buffer as storage.
**Inputs**
**Inputs**
- buf: A byte slice buffer to hold the resulting string
- a: The struct to be converted to a string
**Returns**
**Returns**
- A string representation of the Decimal
*/
decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
@@ -150,7 +150,7 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
/*
Trims trailing zeros in the given Decimal, updating the count and decimal_point values as needed.
**Inputs**
**Inputs**
- a: Pointer to the Decimal struct to be trimmed
*/
trim :: proc(a: ^Decimal) {
@@ -166,7 +166,7 @@ Converts a given u64 integer `idx` to its Decimal representation in the provided
**Used for internal Decimal Operations.**
**Inputs**
**Inputs**
- a: Where the result will be stored
- idx: The value to be assigned to the Decimal
*/
@@ -190,11 +190,11 @@ assign :: proc(a: ^Decimal, idx: u64) {
trim(a)
}
/*
Shifts the Decimal value to the right by k positions.
Shifts the Decimal value to the right by k positions.
**Used for internal Decimal Operations.**
**Inputs**
**Inputs**
- a: The Decimal struct to be shifted
- k: The number of positions to shift right
*/
@@ -344,7 +344,7 @@ Shifts the decimal of the input value to the left by `k` places
WARNING: asserts `k < 61`
**Inputs**
**Inputs**
- a: The Decimal to be modified
- k: The number of places to shift the decimal to the left
*/
@@ -405,7 +405,7 @@ shift_left :: proc(a: ^Decimal, k: uint) #no_bounds_check {
/*
Shifts the decimal of the input value by the specified number of places
**Inputs**
**Inputs**
- a: The Decimal to be modified
- i: The number of places to shift the decimal (positive for left shift, negative for right shift)
*/
@@ -435,7 +435,7 @@ shift :: proc(a: ^Decimal, i: int) {
/*
Determines if the Decimal can be rounded up at the given digit index
**Inputs**
**Inputs**
- a: The Decimal to check
- nd: The digit index to consider for rounding up
@@ -455,7 +455,7 @@ can_round_up :: proc(a: ^Decimal, nd: int) -> bool {
/*
Rounds the Decimal at the given digit index
**Inputs**
**Inputs**
- a: The Decimal to be modified
- nd: The digit index to round
*/
@@ -470,7 +470,7 @@ round :: proc(a: ^Decimal, nd: int) {
/*
Rounds the Decimal up at the given digit index
**Inputs**
**Inputs**
- a: The Decimal to be modified
- nd: The digit index to round up
*/
@@ -493,7 +493,7 @@ round_up :: proc(a: ^Decimal, nd: int) {
/*
Rounds down the decimal value to the specified number of decimal places
**Inputs**
**Inputs**
- a: The Decimal value to be rounded down
- nd: The number of decimal places to round down to
@@ -522,7 +522,7 @@ round_down :: proc(a: ^Decimal, nd: int) {
/*
Extracts the rounded integer part of a decimal value
**Inputs**
**Inputs**
- a: A pointer to the Decimal value to extract the rounded integer part from
WARNING: There are no guarantees about overflow.

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)
}

View File

@@ -23,7 +23,7 @@ _f64_info := Float_Info{52, 11, -1023}
/*
Converts a floating-point number to a string with the specified format and precision.
**Inputs**
**Inputs**
buf: A byte slice to store the resulting string
val: The floating-point value to be converted
@@ -40,7 +40,7 @@ Example:
bit_size := 64
result := strconv.generic_ftoa(buf[:], val, fmt, precision, bit_size) -> "3.14"
**Returns**
**Returns**
- A byte slice containing the formatted string
*/
generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int) -> []byte {
@@ -122,7 +122,7 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int)
/*
Converts a decimal floating-point number into a byte buffer with the given format
**Inputs**
**Inputs**
- buf: The byte buffer to store the formatted number
- shortest: If true, generates the shortest representation of the number
- neg: If true, the number is negative
@@ -130,7 +130,7 @@ Converts a decimal floating-point number into a byte buffer with the given forma
- precision: The number of digits after the decimal point
- fmt: The format specifier (accepted values: 'f', 'F', 'e', 'E', 'g', 'G')
**Returns**
**Returns**
- A byte slice containing the formatted decimal floating-point number
*/
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, precision: int, fmt: byte) -> []byte {
@@ -256,7 +256,7 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slic
/*
Rounds the given decimal number to its shortest representation, considering the provided floating-point format
**Inputs**
**Inputs**
- d: The decimal number to round
- mant: The mantissa of the floating-point number
- exp: The exponent of the floating-point number
@@ -331,11 +331,11 @@ round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Inf
/*
Converts a decimal number to its floating-point representation with the given format and returns the resulting bits
**Inputs**
**Inputs**
- d: Pointer to the decimal number to convert
- info: Pointer to the Float_Info structure containing information about the floating-point format
**Returns**
**Returns**
- b: The bits representing the floating-point number
- overflow: A boolean indicating whether an overflow occurred during conversion
*/

View File

@@ -12,12 +12,12 @@ digits := "0123456789abcdefghijklmnopqrstuvwxyz"
/*
Determines whether the given unsigned 64-bit integer is a negative value by interpreting it as a signed integer with the specified bit size.
**Inputs**
**Inputs**
- x: The unsigned 64-bit integer to check for negativity
- is_signed: A boolean indicating if the input should be treated as a signed integer
- bit_size: The bit size of the signed integer representation (8, 16, 32, or 64)
**Returns**
**Returns**
- u: The absolute value of the input integer
- neg: A boolean indicating whether the input integer is negative
*/
@@ -48,9 +48,9 @@ is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64,
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
- x: The integer value to convert
- base: The base for the integer representation (2 <= base <= MAX_BASE)
@@ -59,12 +59,12 @@ Appends the string representation of an integer to a buffer with specified base,
- digits: The digit set used for the integer representation
- flags: The Int_Flags bit set to control integer formatting
**Returns**
**Returns**
- 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 {
panic("strconv: illegal base passed to append_bits")
panic("strconv: illegal base passed to write_bits")
}
a: [129]byte
@@ -106,12 +106,12 @@ append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: i
/*
Determines whether the given unsigned 128-bit integer is a negative value by interpreting it as a signed integer with the specified bit size.
**Inputs**
**Inputs**
- x: The unsigned 128-bit integer to check for negativity
- is_signed: A boolean indicating if the input should be treated as a signed integer
- bit_size: The bit size of the signed integer representation (8, 16, 32, 64, or 128)
**Returns**
**Returns**
- u: The absolute value of the input integer
- neg: A boolean indicating whether the input integer is negative
*/
@@ -146,9 +146,9 @@ is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u:
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
- x: The 128-bit integer value to convert
- base: The base for the integer representation (2 <= base <= MAX_BASE)
@@ -157,12 +157,12 @@ Appends the string representation of a 128-bit integer to a buffer with specifie
- digits: The digit set used for the integer representation
- flags: The Int_Flags bit set to control integer formatting
**Returns**
- The string containing the integer representation appended to the buffer
**Returns**
- 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 {
panic("strconv: illegal base passed to append_bits")
panic("strconv: illegal base passed to write_bits")
}
a: [140]byte

View File

@@ -5,8 +5,8 @@ import "decimal"
/*
Parses a boolean value from the input string
**Inputs**
- s: The input string
**Inputs**
- s: The input string
- true: "1", "t", "T", "true", "TRUE", "True"
- false: "0", "f", "F", "false", "FALSE", "False"
- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
@@ -386,7 +386,7 @@ Parses an unsigned integer value from the input string, using the specified base
- If base is not 0, it will be used for parsing regardless of any prefix in the input string
Example:
import "core:fmt"
import "core:strconv"
parse_uint_example :: proc() {
@@ -399,14 +399,14 @@ Example:
n, ok = strconv.parse_uint("0xffff") // with prefix and inferred base
fmt.println(n,ok)
}
Output:
1234 true
65535 true
65535 true
**Returns**
**Returns**
value: The parsed uint value
ok: `false` if no appropriate value could be found; the value was negative; he input string contained more than just the number
@@ -423,7 +423,7 @@ parse_uint :: proc(s: string, base := 0, n: ^int = nil) -> (value: uint, ok: boo
/*
Parses an integer value from a string in the given base, without any prefix
**Inputs**
**Inputs**
- str: The input string containing the integer value
- base: The base (radix) to use for parsing the integer (1-16)
- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
@@ -436,12 +436,12 @@ Example:
n, ok := strconv.parse_i128_of_base("-1234eeee", 10)
fmt.println(n,ok)
}
Output:
-1234 false
**Returns**
**Returns**
- value: The parsed i128 value
- ok: false if no numeric value of the appropriate base could be found, or if the input string contained more than just the number.
*/
@@ -491,7 +491,7 @@ parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i12
/*
Parses an integer value from a string in base 10, unless there's a prefix
**Inputs**
**Inputs**
- str: The input string containing the integer value
- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
@@ -506,13 +506,13 @@ Example:
n, ok = strconv.parse_i128_maybe_prefixed("0xeeee")
fmt.println(n, ok)
}
Output:
1234 true
61166 true
**Returns**
**Returns**
- value: The parsed i128 value
- ok: `false` if a valid integer could not be found, or if the input string contained more than just the number.
*/
@@ -574,7 +574,7 @@ parse_i128 :: proc{parse_i128_maybe_prefixed, parse_i128_of_base}
/*
Parses an unsigned integer value from a string in the given base, without any prefix
**Inputs**
**Inputs**
- str: The input string containing the integer value
- base: The base (radix) to use for parsing the integer (1-16)
- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
@@ -590,13 +590,13 @@ Example:
n, ok = strconv.parse_u128_of_base("5678eeee", 16)
fmt.println(n, ok)
}
Output:
1234 false
1450766062 true
**Returns**
**Returns**
- value: The parsed u128 value
- ok: `false` if no numeric value of the appropriate base could be found, or if the input string contained more than just the number.
*/
@@ -634,7 +634,7 @@ parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u12
/*
Parses an unsigned integer value from a string in base 10, unless there's a prefix
**Inputs**
**Inputs**
- str: The input string containing the integer value
- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
@@ -649,13 +649,13 @@ Example:
n, ok = strconv.parse_u128_maybe_prefixed("5678eeee")
fmt.println(n, ok)
}
Output:
1234 true
5678 false
**Returns**
**Returns**
- value: The parsed u128 value
- ok: false if a valid integer could not be found, if the value was negative, or if the input string contained more than just the number.
*/
@@ -706,10 +706,10 @@ parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base}
/*
Converts a byte to lowercase
**Inputs**
**Inputs**
- ch: A byte character to be converted to lowercase.
**Returns**
**Returns**
- A lowercase byte character.
*/
@(private)
@@ -717,7 +717,7 @@ lower :: #force_inline proc "contextless" (ch: byte) -> byte { return ('a' - 'A'
/*
Parses a 32-bit floating point number from a string
**Inputs**
**Inputs**
- s: The input string containing a 32-bit floating point number.
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
@@ -732,13 +732,13 @@ Example:
n, ok = strconv.parse_f32("5678e2")
fmt.printfln("%.3f %v", n, ok)
}
Output:
0.000 false
567800.000 true
**Returns**
**Returns**
- value: The parsed 32-bit floating point number.
- ok: `false` if a base 10 float could not be found, or if the input string contained more than just the number.
*/
@@ -750,7 +750,7 @@ parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) {
/*
Parses a 64-bit floating point number from a string
**Inputs**
**Inputs**
- str: The input string containing a 64-bit floating point number.
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
@@ -765,13 +765,13 @@ Example:
n, ok = strconv.parse_f64("5678e2")
fmt.printfln("%.3f %v", n, ok)
}
Output:
0.000 false
567800.000 true
**Returns**
**Returns**
- value: The parsed 64-bit floating point number.
- ok: `false` if a base 10 float could not be found, or if the input string contained more than just the number.
*/
@@ -787,7 +787,7 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
/*
Parses a 32-bit floating point number from a string and returns the parsed number, the length of the parsed substring, and a boolean indicating whether the parsing was successful
**Inputs**
**Inputs**
- str: The input string containing a 32-bit floating point number.
Example:
@@ -801,14 +801,14 @@ Example:
n, _, ok = strconv.parse_f32_prefix("5678e2")
fmt.printfln("%.3f %v", n, ok)
}
Output:
0.000 false
567800.000 true
**Returns**
**Returns**
- value: The parsed 32-bit floating point number.
- nr: The length of the parsed substring.
- ok: A boolean indicating whether the parsing was successful.
@@ -822,7 +822,7 @@ parse_f32_prefix :: proc(str: string) -> (value: f32, nr: int, ok: bool) {
/*
Parses a 64-bit floating point number from a string and returns the parsed number, the length of the parsed substring, and a boolean indicating whether the parsing was successful
**Inputs**
**Inputs**
- str: The input string containing a 64-bit floating point number.
Example:
@@ -846,7 +846,7 @@ Output:
1234.000 true
13.370 true
**Returns**
**Returns**
- value: The parsed 64-bit floating point number.
- nr: The length of the parsed substring.
- ok: `false` if a base 10 float could not be found
@@ -1184,7 +1184,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
/*
Parses a 128-bit complex number from a string
**Inputs**
**Inputs**
- str: The input string containing a 128-bit complex number.
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
@@ -1200,13 +1200,13 @@ Example:
c, ok = strconv.parse_complex128("5+7i hellope", &n)
fmt.printfln("%v %i %t", c, n, ok)
}
Output:
3+1i 4 true
5+7i 4 false
**Returns**
**Returns**
- value: The parsed 128-bit complex number.
- ok: `false` if a complex number could not be found, or if the input string contained more than just the number.
*/
@@ -1232,12 +1232,12 @@ parse_complex128 :: proc(str: string, n: ^int = nil) -> (value: complex128, ok:
}
value = complex(real_value, imag_value)
return
return
}
/*
Parses a 64-bit complex number from a string
**Inputs**
**Inputs**
- str: The input string containing a 64-bit complex number.
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
@@ -1253,13 +1253,13 @@ Example:
c, ok = strconv.parse_complex64("5+7i hellope", &n)
fmt.printfln("%v %i %t", c, n, ok)
}
Output:
3+1i 4 true
5+7i 4 false
**Returns**
**Returns**
- value: The parsed 64-bit complex number.
- ok: `false` if a complex number could not be found, or if the input string contained more than just the number.
*/
@@ -1271,7 +1271,7 @@ parse_complex64 :: proc(str: string, n: ^int = nil) -> (value: complex64, ok: bo
/*
Parses a 32-bit complex number from a string
**Inputs**
**Inputs**
- str: The input string containing a 32-bit complex number.
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
@@ -1287,13 +1287,13 @@ Example:
c, ok = strconv.parse_complex32("5+7i hellope", &n)
fmt.printfln("%v %i %t", c, n, ok)
}
Output:
3+1i 4 true
5+7i 4 false
**Returns**
**Returns**
- value: The parsed 32-bit complex number.
- ok: `false` if a complex number could not be found, or if the input string contained more than just the number.
*/
@@ -1305,7 +1305,7 @@ parse_complex32 :: proc(str: string, n: ^int = nil) -> (value: complex32, ok: bo
/*
Parses a 256-bit quaternion from a string
**Inputs**
**Inputs**
- str: The input string containing a 256-bit quaternion.
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
@@ -1321,13 +1321,13 @@ Example:
q, ok = strconv.parse_quaternion256("1+2i+3j+4k hellope", &n)
fmt.printfln("%v %i %t", q, n, ok)
}
Output:
1+2i+3j+4k 10 true
1+2i+3j+4k 10 false
**Returns**
**Returns**
- value: The parsed 256-bit quaternion.
- ok: `false` if a quaternion could not be found, or if the input string contained more than just the quaternion.
*/
@@ -1385,7 +1385,7 @@ parse_quaternion256 :: proc(str: string, n: ^int = nil) -> (value: quaternion256
/*
Parses a 128-bit quaternion from a string
**Inputs**
**Inputs**
- str: The input string containing a 128-bit quaternion.
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
@@ -1401,13 +1401,13 @@ Example:
q, ok = strconv.parse_quaternion128("1+2i+3j+4k hellope", &n)
fmt.printfln("%v %i %t", q, n, ok)
}
Output:
1+2i+3j+4k 10 true
1+2i+3j+4k 10 false
**Returns**
**Returns**
- value: The parsed 128-bit quaternion.
- ok: `false` if a quaternion could not be found, or if the input string contained more than just the quaternion.
*/
@@ -1419,7 +1419,7 @@ parse_quaternion128 :: proc(str: string, n: ^int = nil) -> (value: quaternion128
/*
Parses a 64-bit quaternion from a string
**Inputs**
**Inputs**
- str: The input string containing a 64-bit quaternion.
- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
@@ -1435,13 +1435,13 @@ Example:
q, ok = strconv.parse_quaternion64("1+2i+3j+4k hellope", &n)
fmt.printfln("%v %i %t", q, n, ok)
}
Output:
1+2i+3j+4k 10 true
1+2i+3j+4k 10 false
**Returns**
**Returns**
- value: The parsed 64-bit quaternion.
- ok: `false` if a quaternion could not be found, or if the input string contained more than just the quaternion.
*/
@@ -1450,20 +1450,20 @@ parse_quaternion64 :: proc(str: string, n: ^int = nil) -> (value: quaternion64,
v, ok = parse_quaternion256(str, n)
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**
- buf: The buffer to append the boolean value to
- b: The boolean value to be appended
**Inputs**
- buf: The buffer to write the boolean value to
- b: The boolean value to be written
Example:
import "core:fmt"
import "core:strconv"
append_bool_example :: proc() {
write_bool_example :: proc() {
buf: [6]byte
result := strconv.append_bool(buf[:], true)
result := strconv.write_bool(buf[:], true)
fmt.println(result, buf)
}
@@ -1471,10 +1471,10 @@ Output:
true [116, 114, 117, 101, 0, 0]
**Returns**
- The resulting string after appending the boolean value
**Returns**
- 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
if b {
n = copy(buf, "true")
@@ -1483,21 +1483,21 @@ append_bool :: proc(buf: []byte, b: bool) -> string {
}
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**
- buf: The buffer to append the unsigned integer value to
- u: The unsigned integer value to be appended
**Inputs**
- buf: The buffer to write the unsigned integer value to
- u: The unsigned integer value to be written
- base: The base to use for converting the integer value
Example:
import "core:fmt"
import "core:strconv"
append_uint_example :: proc() {
write_uint_example :: proc() {
buf: [4]byte
result := strconv.append_uint(buf[:], 42, 16)
result := strconv.write_uint(buf[:], 42, 16)
fmt.println(result, buf)
}
@@ -1505,27 +1505,27 @@ Output:
2a [50, 97, 0, 0]
**Returns**
- The resulting string after appending the unsigned integer value
**Returns**
- The resulting string after writing the unsigned integer value
*/
append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
return append_bits(buf, u, base, false, 8*size_of(uint), digits, nil)
write_uint :: proc(buf: []byte, u: u64, base: int) -> string {
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**
- buf: The buffer to append the signed integer value to
- i: The signed integer value to be appended
**Inputs**
- buf: The buffer to write the signed integer value to
- i: The signed integer value to be written
- base: The base to use for converting the integer value
Example:
import "core:fmt"
import "core:strconv"
append_int_example :: proc() {
write_int_example :: proc() {
buf: [4]byte
result := strconv.append_int(buf[:], -42, 10)
result := strconv.write_int(buf[:], -42, 10)
fmt.println(result, buf)
}
@@ -1533,23 +1533,23 @@ Output:
-42 [45, 52, 50, 0]
**Returns**
- The resulting string after appending the signed integer value
**Returns**
- The resulting string after writing the signed integer value
*/
append_int :: proc(buf: []byte, i: i64, base: int) -> string {
return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil)
write_int :: proc(buf: []byte, i: i64, base: int) -> string {
return write_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil)
}
append_u128 :: proc(buf: []byte, u: u128, base: int) -> string {
return append_bits_128(buf, u, base, false, 8*size_of(uint), digits, nil)
write_u128 :: proc(buf: []byte, u: u128, base: int) -> string {
return write_bits_128(buf, u, base, false, 8*size_of(uint), digits, nil)
}
/*
/*
Converts an integer value to a string and stores it in the given buffer
**Inputs**
**Inputs**
- buf: The buffer to store the resulting string
- i: The integer value to be converted
@@ -1567,16 +1567,16 @@ Output:
42 [52, 50, 0, 0]
**Returns**
**Returns**
- The resulting string after converting the integer value
*/
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
**Inputs**
**Inputs**
- s: The string to be converted
Example:
@@ -1591,17 +1591,17 @@ Output:
42
**Returns**
**Returns**
- The resulting integer value
*/
atoi :: proc(s: string) -> int {
v, _ := parse_int(s)
return v
}
/*
/*
Converts a string to a float64 value
**Inputs**
**Inputs**
- s: The string to be converted
Example:
@@ -1616,21 +1616,21 @@ Output:
3.140
**Returns**
**Returns**
- The resulting float64 value after converting the string
*/
atof :: proc(s: string) -> f64 {
v, _ := parse_f64(s)
return v
}
// Alias to `append_float`
ftoa :: append_float
/*
Appends a float64 value as a string to the given buffer with the specified format and precision
// Alias to `write_float`
ftoa :: write_float
/*
Writes a float64 value as a string to the given buffer with the specified format and precision
**Inputs**
- buf: The buffer to append the float64 value to
- f: The float64 value to be appended
**Inputs**
- buf: The buffer to write the float64 value to
- f: The float64 value to be written
- fmt: The byte specifying the format 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)
@@ -1639,9 +1639,9 @@ Example:
import "core:fmt"
import "core:strconv"
append_float_example :: proc() {
write_float_example :: proc() {
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)
}
@@ -1649,20 +1649,20 @@ Output:
+3.14 [43, 51, 46, 49, 52, 0, 0, 0]
**Returns**
- The resulting string after appending the float
**Returns**
- 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))
}
/*
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**
- buf: The byte slice to which the quoted string will be appended
**Inputs**
- buf: The byte slice to which the quoted string will be written
- str: The input string to be quoted
!! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected
!! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected
Example:
@@ -1678,8 +1678,8 @@ 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]
**Returns**
- The resulting string after appending the quoted string representation
**Returns**
- The resulting string after writing the quoted string representation
*/
quote :: proc(buf: []byte, str: string) -> string {
write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) {
@@ -1719,10 +1719,10 @@ quote :: proc(buf: []byte, str: string) -> string {
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**
- buf: The byte slice to which the quoted rune will be appended
**Inputs**
- buf: The byte slice to which the quoted rune will be written
- r: The input rune to be quoted
Example:
@@ -1739,8 +1739,8 @@ Output:
'A' [39, 65, 39, 0]
**Returns**
- The resulting string after appending the quoted rune representation
**Returns**
- The resulting string after writing the quoted rune representation
*/
quote_rune :: proc(buf: []byte, r: rune) -> string {
write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) {
@@ -1783,7 +1783,7 @@ quote_rune :: proc(buf: []byte, r: rune) -> string {
if r < 32 {
write_string(buf, &i, "\\x")
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) {
case 0: write_string(buf, &i, "00")
case 1: write_rune(buf, &i, '0')
@@ -1800,11 +1800,11 @@ quote_rune :: proc(buf: []byte, r: rune) -> string {
/*
Unquotes a single character from the input string, considering the given quote character
**Inputs**
**Inputs**
- str: The input string containing the character to unquote
- quote: The quote character to consider (e.g., '"')
Example:
Example:
import "core:fmt"
import "core:strconv"
@@ -1815,12 +1815,12 @@ Example:
fmt.printf("r: <%v>, multiple_bytes:%v, tail_string:<%s>, success:%v\n",r, multiple_bytes, tail_string, success)
}
Output:
Output:
Source: 'The' raven
r: <'>, multiple_bytes:false, tail_string:<The' raven>, success:true
**Returns**
**Returns**
- r: The unquoted rune
- multiple_bytes: A boolean indicating if the rune has multiple bytes
- tail_string: The remaining portion of the input string after unquoting the character
@@ -1923,13 +1923,13 @@ unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool
/*
Unquotes the input string considering any type of quote character and returns the unquoted string
**Inputs**
**Inputs**
- lit: The input string to unquote
- allocator: (default: context.allocator)
WARNING: This procedure gives unexpected results if the quotes are not the first and last characters.
Example:
Example:
import "core:fmt"
import "core:strconv"
@@ -1947,10 +1947,10 @@ Example:
src="The raven \'Huginn\' is black."
s, allocated, ok = strconv.unquote_string(src) // Will produce undesireable results
fmt.println(src)
fmt.printf("Unquoted: <%s>, alloc:%v, ok:%v\n", s, allocated, ok)
fmt.printf("Unquoted: <%s>, alloc:%v, ok:%v\n", s, allocated, ok)
}
Output:
Output:
"The raven Huginn is black."
Unquoted: <The raven Huginn is black.>, alloc:false, ok:true
@@ -1961,7 +1961,7 @@ Output:
The raven 'Huginn' is black.
Unquoted: <he raven 'Huginn' is black>, alloc:false, ok:true
**Returns**
**Returns**
- res: The resulting unquoted string
- allocated: A boolean indicating if the resulting string was allocated using the provided allocator
- success: A boolean indicating whether the unquoting was successful
@@ -2002,7 +2002,7 @@ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: str
return s, false, true
}
}
context.allocator = allocator
buf_len := 3*len(s) / 2

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) {
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,
// we skip it unless it's followed by an `I` (because of +Inf).
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) {
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') {
s = s[1:]
}
@@ -739,7 +739,7 @@ Output:
*/
write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) {
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') {
s = s[1:]
}
@@ -761,7 +761,7 @@ Returns:
*/
write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) {
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') {
s = s[1:]
}
@@ -782,7 +782,7 @@ Returns:
*/
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
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)
}
/*
@@ -800,7 +800,7 @@ Returns:
*/
write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) {
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)
}
/*

View File

@@ -28,24 +28,7 @@ clone :: proc(s: string, allocator := context.allocator, loc := #caller_location
copy(c, s)
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
@@ -66,6 +49,7 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #call
c[len(s)] = 0
return cstring(&c[0]), nil
}
/*
Transmutes a raw pointer into a string. Non-allocating.
@@ -81,6 +65,7 @@ Returns:
string_from_ptr :: proc(ptr: ^byte, len: int) -> (res: string) {
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
@@ -99,20 +84,7 @@ string_from_null_terminated_ptr :: proc "contextless" (ptr: [^]byte, len: int) -
s = truncate_to_byte(s, 0)
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
@@ -128,6 +100,7 @@ unsafe_string_to_cstring :: proc(str: string) -> (res: cstring) {
d := transmute(mem.Raw_String)str
return cstring(d.data)
}
/*
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]
}
/*
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]
}
/*
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
return string(c[:len(s)]), nil
}
/*
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 {
return clone(string(s), allocator, loc)
}
/*
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)
return clone(s, allocator, loc)
}
// Overloaded procedure to clone from a string, `[]byte`, `cstring` or a `^byte` + length
clone_from :: proc{
clone,
@@ -229,6 +207,7 @@ clone_from :: proc{
clone_from_cstring,
clone_from_ptr,
}
/*
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)
return clone(s, allocator, loc)
}
/*
Compares two strings, returning a value representing which one comes first lexicographically.
-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) {
return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs)
}
/*
Checks if rune `r` in the string `s`
@@ -283,6 +264,7 @@ contains_rune :: proc(s: string, r: rune) -> (result: bool) {
}
return false
}
/*
Returns true when the string `substr` is contained inside the string `s`
@@ -314,6 +296,7 @@ Output:
contains :: proc(s, substr: string) -> (res: bool) {
return index(s, substr) >= 0
}
/*
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) {
return utf8.rune_count_in_string(s)
}
/*
Returns whether the strings `u` and `v` are the same alpha characters, ignoring different casings
Works with UTF-8 string content
@@ -508,6 +492,7 @@ prefix_length :: proc "contextless" (a, b: string) -> (n: int) {
}
return
}
/*
Returns the common prefix between strings `a` and `b`
@@ -540,6 +525,7 @@ Output:
common_prefix :: proc(a, b: string) -> string {
return a[:prefix_length(a, b)]
}
/*
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
}
/*
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
@@ -723,22 +692,6 @@ concatenate :: proc(a: []string, allocator := context.allocator, loc := #caller_
}
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
@@ -901,6 +854,7 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato
return res[:i+1], nil
}
/*
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 {
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.
@@ -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 {
return _split(s, sep, 0, n, allocator)
}
/*
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 {
return _split(s, sep, len(sep), -1, allocator)
}
/*
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 {
return _split(s, sep, len(sep), n, allocator)
}
/*
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.
@@ -1083,6 +1041,7 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string,
}
return
}
/*
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
}
/*
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) {
return _split_iterator(s, sep, 0)
}
/*
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) {
return _split_iterator(s, sep, len(sep))
}
/*
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
}
/*
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
}
/*
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
}
/*
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
}
/*
Splits the input string at every line break `\n` leaving the `\n` in the resulting strings.
Only runs for n parts.
@@ -1377,6 +1343,7 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -
}
return lines, nil
}
/*
Splits the input string at every line break `\n`.
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
return _trim_cr(line), true
}
/*
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.
@@ -1448,6 +1416,7 @@ split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
line = _split_iterator(s, sep, len(sep)) or_return
return _trim_cr(line), true
}
/*
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.
@@ -1482,6 +1451,7 @@ Output:
index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) {
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.
@@ -1517,6 +1487,7 @@ Output:
last_index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) {
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.
Invalid runes return -1
@@ -1657,6 +1628,7 @@ index :: proc "contextless" (s, substr: string) -> (res: int) {
}
return -1
}
/*
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
}
/*
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
}
/*
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
}
/*
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
}
/*
Counts the number of non-overlapping occurrences of `substr` in `s`
@@ -1985,6 +1961,7 @@ count :: proc(s, substr: string) -> (res: int) {
}
return n
}
/*
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
}
/*
Replaces all occurrences of `old` in `s` with `new`
@@ -2063,9 +2041,11 @@ Output:
zzzz true
*/
replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return replace(s, old, new, -1, allocator)
}
/*
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])
return
}
/*
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) {
return replace(s, key, "", n, allocator)
}
/*
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) {
return remove(s, key, -1, allocator)
}
// 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}
@@ -2320,6 +2303,7 @@ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int
}
return -1
}
// 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) {
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
}
// 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) {
// 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
}
// 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) {
// 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
}
/*
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:]
}
/*
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:]
}
/*
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]
}
/*
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]
}
// Procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison
is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) {
cutset := (^string)(state)^
@@ -2473,6 +2464,7 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) {
}
return false
}
/*
Trims the cutset string from the `s` string
@@ -2490,6 +2482,7 @@ trim_left :: proc(s: string, cutset: string) -> (res: string) {
state := cutset
return trim_left_proc_with_state(s, is_in_cutset, &state)
}
/*
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
return trim_right_proc_with_state(s, is_in_cutset, &state)
}
/*
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) {
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"
@@ -2532,6 +2527,7 @@ Returns:
trim_left_space :: proc(s: string) -> (res: string) {
return trim_left_proc(s, is_space)
}
/*
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) {
return trim_right_proc(s, is_space)
}
/*
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) {
return trim_right_space(trim_left_space(s))
}
/*
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) {
return trim_left_proc(s, is_null)
}
/*
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) {
return trim_right_proc(s, is_null)
}
/*
Trims null runes from both sides, "\x00\x00testing\x00\x00" -> "testing"
@@ -2591,6 +2591,7 @@ Returns:
trim_null :: proc(s: string) -> (res: string) {
return trim_right_null(trim_left_null(s))
}
/*
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
}
/*
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
}
/*
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)
return results[:], nil
}
/*
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
return
}
/*
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
}
/*
Reverses the input string `s`
@@ -2889,6 +2895,7 @@ reverse :: proc(s: string, allocator := context.allocator, loc := #caller_locati
}
return string(buf), nil
}
/*
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
*/
expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
if tab_size <= 0 {
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
}
/*
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):]
return
}
// Alias for centre_justify
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.
@@ -3048,6 +3059,7 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
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.
@@ -3082,6 +3094,7 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
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.
@@ -3116,6 +3129,7 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex
return to_string(b), nil
}
/*
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:]
}
}
/*
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
}
/*
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
}
/*
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):]
return
}
/*
Computes the Levenshtein edit distance between two strings
@@ -3460,4 +3478,4 @@ substring_to :: proc(s: string, rune_end: int) -> (sub: string, ok: bool) {
}
return internal_substring(s, -1, rune_end)
}
}

View File

@@ -82,7 +82,6 @@ Application_setActivationPolicy :: proc "c" (self: ^Application, activationPolic
// 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,
// that does not work the same way.
// @(deprecated="Use NSApplication method activate instead.")
@(objc_type=Application, objc_name="activateIgnoringOtherApps")
Application_activateIgnoringOtherApps :: proc "c" (self: ^Application, ignoreOtherApps: BOOL) {
msgSend(nil, self, "activateIgnoringOtherApps:", ignoreOtherApps)

View File

@@ -1413,7 +1413,7 @@ umask :: proc "contextless" (mask: Mode) -> Mode {
Available since Linux 1.0.
*/
gettimeofday :: proc "contextless" (tv: ^Time_Val) -> (Errno) {
ret := syscall(SYS_gettimeofday, tv)
ret := syscall(SYS_gettimeofday, tv, rawptr(nil))
return Errno(-ret)
}

View File

@@ -75,14 +75,6 @@ LANGIDFROMLCID :: #force_inline proc "contextless" (lcid: LCID) -> LANGID {
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 {
if len(s) < 1 {
return nil

View File

@@ -243,9 +243,9 @@ foreign ws2_32 {
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohs)
ntohs :: proc(netshort: c_ushort) -> c_ushort ---
// [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 ---
// [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 ---
}
}

View File

@@ -577,12 +577,7 @@ parse_tzif :: proc(_buffer: []u8, region_name: string, allocator := context.allo
footer_str := string(buffer[:end_idx])
// UTC is a special case, we don't need to alloc
if len(local_time_types) == 1 {
name := cstring(raw_data(timezone_string_table[local_time_types[0].idx:]))
if name != "UTC" {
return
}
if len(local_time_types) == 1 && local_time_types[0].utoff == 0 {
return nil, true
}

View File

@@ -41,6 +41,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool
is_type_enum,
is_type_proc,
is_type_bit_set,
is_type_bit_field,
is_type_simd_vector,
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_proc:
case BuiltinProc_type_is_bit_set:
case BuiltinProc_type_is_bit_field:
case BuiltinProc_type_is_simd_vector:
case BuiltinProc_type_is_matrix:
case BuiltinProc_type_is_specialized_polymorphic_record:

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.no_sanitize_address = ac.no_sanitize_address;
e->Procedure.no_sanitize_memory = ac.no_sanitize_memory;
e->deprecated_message = ac.deprecated_message;
e->warning_message = ac.warning_message;

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");
}
}
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) {

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) {
if (t == nullptr) {
if (t == nullptr || c == nullptr) {
return;
}
@@ -3776,6 +3776,12 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
}
ac->no_sanitize_address = 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;
}
@@ -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) {
for_array(i, c->info.definitions) {
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);
if (align > 0 && ptr_set_exists(&c->info.minimum_dependency_set, e)) {
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
for_array(i, c->info.definitions) {
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);
} else if (e->kind == Entity_Procedure) {
DeclInfo *decl = e->decl_info;

View File

@@ -140,6 +140,7 @@ struct AttributeContext {
bool instrumentation_enter : 1;
bool instrumentation_exit : 1;
bool no_sanitize_address : 1;
bool no_sanitize_memory : 1;
bool rodata : 1;
bool ignore_duplicates : 1;
u32 optimization_mode; // ProcedureOptimizationMode

View File

@@ -240,6 +240,7 @@ BuiltinProc__type_begin,
BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_boolean,
BuiltinProc_type_is_bit_field,
BuiltinProc_type_is_integer,
BuiltinProc_type_is_rune,
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_proc"), 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_matrix"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},

View File

@@ -263,6 +263,7 @@ struct Entity {
bool uses_branch_location : 1;
bool is_anonymous : 1;
bool no_sanitize_address : 1;
bool no_sanitize_memory : 1;
} Procedure;
struct {
Array<Entity *> entities;

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);
return res;
} else {
// Assume that compound value is an array literal
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);

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) {
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");
}
if (build_context.sanitizer_flags & SanitizerFlag_Thread) {

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) {
{
gb_printf("\n");
gb_printf("Total Lines - %td\n", lines);
gb_printf("Total Tokens - %td\n", tokens);
gb_printf("Total Files - %td\n", files);
gb_printf("Total Packages - %td\n", packages);
gb_printf("Total File Size - %td\n", total_file_size);
gb_printf("\n");
gb_printf_err("\n");
gb_printf_err("Total Lines - %td\n", lines);
gb_printf_err("Total Tokens - %td\n", tokens);
gb_printf_err("Total Files - %td\n", files);
gb_printf_err("Total Packages - %td\n", packages);
gb_printf_err("Total File Size - %td\n", total_file_size);
gb_printf_err("\n");
}
{
f64 time = total_tokenizing_time;
gb_printf("Tokenization Only\n");
gb_printf("LOC/s - %.3f\n", cast(f64)lines/time);
gb_printf("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines);
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/time);
gb_printf("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens);
gb_printf("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("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size);
gb_printf_err("Tokenization Only\n");
gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/time);
gb_printf_err("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines);
gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/time);
gb_printf_err("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens);
gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/time);
gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024));
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;
gb_printf("Parsing Only\n");
gb_printf("LOC/s - %.3f\n", cast(f64)lines/time);
gb_printf("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines);
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/time);
gb_printf("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens);
gb_printf("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("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size);
gb_printf_err("Parsing Only\n");
gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/time);
gb_printf_err("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines);
gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/time);
gb_printf_err("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens);
gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/time);
gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024));
gb_printf_err("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size);
gb_printf("\n");
gb_printf_err("\n");
}
{
TimeStamp ts = {};
@@ -2041,16 +2041,16 @@ gb_internal void show_timings(Checker *c, Timings *t) {
GB_ASSERT(ts.label == "parse files");
f64 parse_time = time_stamp_as_s(ts, t->freq);
gb_printf("Parse pass\n");
gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
gb_printf("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("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("Parse pass\n");
gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/parse_time);
gb_printf_err("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
gb_printf_err("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time);
gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/parse_time)/(1024*1024));
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 = {};
@@ -2071,27 +2071,27 @@ gb_internal void show_timings(Checker *c, Timings *t) {
ts.finish = ts_end.finish;
f64 parse_time = time_stamp_as_s(ts, t->freq);
gb_printf("Checker pass\n");
gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
gb_printf("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("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("\n");
gb_printf_err("Checker pass\n");
gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/parse_time);
gb_printf_err("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
gb_printf_err("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time);
gb_printf_err("MiB/s - %.3f\n", (cast(f64)total_file_size/parse_time)/(1024*1024));
gb_printf_err("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size);
gb_printf_err("\n");
}
{
f64 total_time = t->total_time_seconds;
gb_printf("Total pass\n");
gb_printf("LOC/s - %.3f\n", cast(f64)lines/total_time);
gb_printf("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines);
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/total_time);
gb_printf("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("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("\n");
gb_printf_err("Total pass\n");
gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/total_time);
gb_printf_err("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines);
gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/total_time);
gb_printf_err("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens);
gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/total_time);
gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/total_time)/(1024*1024));
gb_printf_err("us/bytes - %.3f\n", 1.0e6*total_time/cast(f64)total_file_size);
gb_printf_err("\n");
}
}
}

View File

@@ -5794,7 +5794,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const
for (FileInfo fi : list) {
String name = fi.name;
String ext = path_extension(name);
if (ext == FILE_EXT) {
if (ext == FILE_EXT && !path_is_directory(name)) {
files_with_ext += 1;
}
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) {
String name = fi.name;
String ext = path_extension(name);
if (ext == FILE_EXT) {
if (ext == FILE_EXT && !path_is_directory(name)) {
if (is_excluded_target_filename(name)) {
continue;
}

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);
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),
cast(int)(max_len-t->total.label.len), SPACES,
total_time,
@@ -207,7 +207,7 @@ gb_internal void timings_print_all(Timings *t, TimingUnit unit = TimingUnit_Mill
for_array(i, t->sections) {
TimeStamp ts = t->sections[i];
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),
cast(int)(max_len-ts.label.len), SPACES,
section_time,

View File

@@ -43,6 +43,8 @@ Foo :: struct {
biggest: big.Int,
smallest: big.Int,
ignore_this: ^Foo `cbor:"-"`,
mat: matrix[4, 4]f32,
vec: #simd [4]f64,
}
FooBar :: enum {
@@ -95,6 +97,8 @@ test_marshalling :: proc(t: ^testing.T) {
onetwenty = i128(12345),
small_onetwenty = -i128(max(u64)),
ignore_this = &Foo{},
mat = 1,
vec = 2,
}
big.atoi(&f.biggest, "1234567891011121314151617181920")
@@ -120,11 +124,35 @@ test_marshalling :: proc(t: ^testing.T) {
defer delete(diagnosis)
testing.expect_value(t, diagnosis, `{
"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,
"nos": undefined,
"now": 1(1701117968),
"pos": 1212,
"str": "Hellope",
"vec": [
2.0000,
2.0000,
2.0000,
2.0000
],
"yes": true,
"comp": [
32.0000,

View File

@@ -1238,7 +1238,7 @@ test_count_digits :: proc(t: ^testing.T) {
buf: [64]u8
for n in 0..<i64(base*base*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,
len(str) == count,
"decimal %i in base-%i digit count is %i, does not match length %i of %q",

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_2666.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 build ..\test_issue_5043.odin %COMMON% || exit /b
..\..\..\odin build ..\test_issue_5097.odin %COMMON% || exit /b

View File

@@ -18,6 +18,7 @@ $ODIN test ../test_issue_2615.odin $COMMON
$ODIN test ../test_issue_2637.odin $COMMON
$ODIN test ../test_issue_2666.odin $COMMON
$ODIN test ../test_issue_4210.odin $COMMON
$ODIN test ../test_issue_4364.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
echo "SUCCESSFUL 1/1"

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})
}

View File

@@ -194,7 +194,7 @@ ICompiler_VTable :: struct {
using iunknown_vtable: IUnknown_VTable,
Compile: proc "system" (
this: ^ICompiler,
pSource: ^Buffer,
pSource: ^IBlob,
pSourceName: wstring,
pEntryPoint: wstring,
pTargetProfile: wstring,
@@ -206,7 +206,7 @@ ICompiler_VTable :: struct {
ppResult: ^^IOperationResult) -> HRESULT,
Preprocess: proc "system" (
this: ^ICompiler,
pSource: ^Buffer,
pSource: ^IBlob,
pSourceName: wstring,
pArguments: [^]wstring,
argCount: u32,
@@ -227,7 +227,7 @@ ICompiler2_VTable :: struct {
using idxccompiler_vtable: ^ICompiler_VTable,
CompileWithDebug: proc "system" (
this: ^ICompiler2,
pSource: ^Buffer,
pSource: ^IBlob,
pSourceName: wstring,
pEntryPoint: wstring,
pTargetProfile: wstring,