Browse Source

Merge branch 'master' of https://github.com/odin-lang/Odin

gingerBill 2 months ago
parent
commit
002b50360c
56 changed files with 1068 additions and 497 deletions
  1. 1 0
      base/intrinsics/intrinsics.odin
  2. 9 0
      base/runtime/core_builtin.odin
  3. 74 0
      base/sanitizer/memory.odin
  4. 3 3
      core/encoding/cbor/cbor.odin
  5. 36 0
      core/encoding/cbor/marshal.odin
  6. 25 0
      core/encoding/cbor/unmarshal.odin
  7. 4 4
      core/encoding/json/marshal.odin
  8. 2 1
      core/encoding/xml/xml_reader.odin
  9. 4 4
      core/fmt/fmt.odin
  10. 8 8
      core/io/util.odin
  11. 1 1
      core/math/big/private.odin
  12. 10 5
      core/math/fixed/fixed.odin
  13. 1 1
      core/net/url.odin
  14. 1 1
      core/os/os.odin
  15. 19 14
      core/os/os2/errors.odin
  16. 1 0
      core/os/os2/file_linux.odin
  17. 1 1
      core/os/os2/file_util.odin
  18. 133 63
      core/os/os2/user.odin
  19. 183 0
      core/os/os2/user_posix.odin
  20. 79 0
      core/os/os2/user_windows.odin
  21. 3 3
      core/os/os_freebsd.odin
  22. 3 3
      core/os/os_haiku.odin
  23. 4 4
      core/os/os_linux.odin
  24. 3 3
      core/os/os_netbsd.odin
  25. 3 3
      core/os/os_openbsd.odin
  26. 0 46
      core/sort/sort.odin
  27. 15 15
      core/strconv/decimal/decimal.odin
  28. 38 0
      core/strconv/deprecated.odin
  29. 7 7
      core/strconv/generic_float.odin
  30. 15 15
      core/strconv/integers.odin
  31. 139 139
      core/strconv/strconv.odin
  32. 6 6
      core/strings/builder.odin
  33. 82 64
      core/strings/strings.odin
  34. 0 1
      core/sys/darwin/Foundation/NSApplication.odin
  35. 1 1
      core/sys/linux/sys.odin
  36. 0 8
      core/sys/windows/util.odin
  37. 3 3
      core/sys/windows/ws2_32.odin
  38. 1 6
      core/time/timezone/tzif.odin
  39. 2 0
      src/check_builtin.cpp
  40. 1 0
      src/check_decl.cpp
  41. 14 0
      src/check_stmt.cpp
  42. 9 3
      src/checker.cpp
  43. 1 0
      src/checker.hpp
  44. 2 0
      src/checker_builtin_procs.hpp
  45. 1 0
      src/entity.cpp
  46. 11 0
      src/llvm_backend_const.cpp
  47. 1 1
      src/llvm_backend_proc.cpp
  48. 52 52
      src/main.cpp
  49. 2 2
      src/parser.cpp
  50. 2 2
      src/timings.cpp
  51. 28 0
      tests/core/encoding/cbor/test_core_cbor.odin
  52. 1 1
      tests/core/math/test_core_math.odin
  53. 1 0
      tests/issues/run.bat
  54. 1 0
      tests/issues/run.sh
  55. 18 0
      tests/issues/test_issue_4364.odin
  56. 3 3
      vendor/directx/dxc/dxcapi.odin

+ 1 - 0
base/intrinsics/intrinsics.odin

@@ -169,6 +169,7 @@ type_is_union            :: proc($T: typeid) -> bool ---
 type_is_enum             :: proc($T: typeid) -> bool ---
 type_is_enum             :: proc($T: typeid) -> bool ---
 type_is_proc             :: proc($T: typeid) -> bool ---
 type_is_proc             :: proc($T: typeid) -> bool ---
 type_is_bit_set          :: proc($T: typeid) -> bool ---
 type_is_bit_set          :: proc($T: typeid) -> bool ---
+type_is_bit_field        :: proc($T: typeid) -> bool ---
 type_is_simd_vector      :: proc($T: typeid) -> bool ---
 type_is_simd_vector      :: proc($T: typeid) -> bool ---
 type_is_matrix           :: proc($T: typeid) -> bool ---
 type_is_matrix           :: proc($T: typeid) -> bool ---
 
 

+ 9 - 0
base/runtime/core_builtin.odin

@@ -648,6 +648,9 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i
 
 
 @builtin
 @builtin
 inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
 inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
+	when !ODIN_NO_BOUNDS_CHECK {
+		ensure(index >= 0, "Index must be positive.", loc)
+	}
 	if array == nil {
 	if array == nil {
 		return
 		return
 	}
 	}
@@ -666,6 +669,9 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcas
 
 
 @builtin
 @builtin
 inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
 inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
+	when !ODIN_NO_BOUNDS_CHECK {
+		ensure(index >= 0, "Index must be positive.", loc)
+	}
 	if array == nil {
 	if array == nil {
 		return
 		return
 	}
 	}
@@ -689,6 +695,9 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca
 
 
 @builtin
 @builtin
 inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
 inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
+	when !ODIN_NO_BOUNDS_CHECK {
+		ensure(index >= 0, "Index must be positive.", loc)
+	}
 	if array == nil {
 	if array == nil {
 		return
 		return
 	}
 	}

+ 74 - 0
base/sanitizer/memory.odin

@@ -0,0 +1,74 @@
+#+no-instrumentation
+package sanitizer
+
+@(private="file")
+MSAN_ENABLED :: .Memory in ODIN_SANITIZER_FLAGS
+
+@(private="file")
+@(default_calling_convention="system")
+foreign {
+	__msan_unpoison :: proc(addr: rawptr, size: uint) ---
+}
+
+/*
+Marks a slice as fully initialized.
+
+Code instrumented with `-sanitize:memory` will be permitted to access any
+address within the slice as if it had already been initialized.
+
+When msan is not enabled this procedure does nothing.
+*/
+memory_unpoison_slice :: proc "contextless" (region: $T/[]$E) {
+	when MSAN_ENABLED {
+		__msan_unpoison(raw_data(region),  size_of(E) * len(region))
+	}
+}
+
+/*
+Marks a pointer as fully initialized.
+
+Code instrumented with `-sanitize:memory` will be permitted to access memory
+within the region the pointer points to as if it had already been initialized.
+
+When msan is not enabled this procedure does nothing.
+*/
+memory_unpoison_ptr :: proc "contextless" (ptr: ^$T) {
+	when MSAN_ENABLED {
+		__msan_unpoison(ptr, size_of(T))
+	}
+}
+
+/*
+Marks the region covering `[ptr, ptr+len)` as fully initialized.
+
+Code instrumented with `-sanitize:memory` will be permitted to access memory
+within this range as if it had already been initialized.
+
+When msan is not enabled this procedure does nothing.
+*/
+memory_unpoison_rawptr :: proc "contextless" (ptr: rawptr, len: int) {
+	when MSAN_ENABLED {
+		__msan_unpoison(ptr, uint(len))
+	}
+}
+
+/*
+Marks the region covering `[ptr, ptr+len)` as fully initialized.
+
+Code instrumented with `-sanitize:memory` will be permitted to access memory
+within this range as if it had already been initialized.
+
+When msan is not enabled this procedure does nothing.
+*/
+memory_unpoison_rawptr_uint :: proc "contextless" (ptr: rawptr, len: uint) {
+	when MSAN_ENABLED {
+		__msan_unpoison(ptr, len)
+	}
+}
+
+memory_unpoison :: proc {
+	memory_unpoison_slice,
+	memory_unpoison_ptr,
+	memory_unpoison_rawptr,
+	memory_unpoison_rawptr_uint,
+}

+ 3 - 3
core/encoding/cbor/cbor.odin

@@ -385,17 +385,17 @@ to_diagnostic_format_writer :: proc(w: io.Writer, val: Value, padding := 0) -> i
 	// which we want for the diagnostic format.
 	// which we want for the diagnostic format.
 	case f16:
 	case f16:
 		buf: [64]byte
 		buf: [64]byte
-		str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f16), 8*size_of(f16))
+		str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f16), 8*size_of(f16))
 		if str[0] == '+' && str != "+Inf" { str = str[1:] }
 		if str[0] == '+' && str != "+Inf" { str = str[1:] }
 		io.write_string(w, str) or_return
 		io.write_string(w, str) or_return
 	case f32:
 	case f32:
 		buf: [128]byte
 		buf: [128]byte
-		str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f32), 8*size_of(f32))
+		str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f32), 8*size_of(f32))
 		if str[0] == '+' && str != "+Inf" { str = str[1:] }
 		if str[0] == '+' && str != "+Inf" { str = str[1:] }
 		io.write_string(w, str) or_return
 		io.write_string(w, str) or_return
 	case f64:
 	case f64:
 		buf: [256]byte
 		buf: [256]byte
-		str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f64), 8*size_of(f64))
+		str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f64), 8*size_of(f64))
 		if str[0] == '+' && str != "+Inf" { str = str[1:] }
 		if str[0] == '+' && str != "+Inf" { str = str[1:] }
 		io.write_string(w, str) or_return
 		io.write_string(w, str) or_return
 
 

+ 36 - 0
core/encoding/cbor/marshal.odin

@@ -612,6 +612,42 @@ _marshal_into_encoder :: proc(e: Encoder, v: any, ti: ^runtime.Type_Info) -> (er
 		case:
 		case:
 			panic("unknown bit_size size")
 			panic("unknown bit_size size")
 		}
 		}
+	case runtime.Type_Info_Matrix:
+		count := info.column_count * info.elem_stride
+		err_conv(_encode_u64(e, u64(count), .Array)) or_return
+
+		if impl, ok := _tag_implementations_type[info.elem.id]; ok {
+			for i in 0..<count {
+				data := uintptr(v.data) + uintptr(i*info.elem_size)
+				impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
+			}
+			return
+		}
+
+		elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
+		for i in 0..<count {
+			data := uintptr(v.data) + uintptr(i*info.elem_size)
+			_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
+		}
+		return
+
+	case runtime.Type_Info_Simd_Vector:
+		err_conv(_encode_u64(e, u64(info.count), .Array)) or_return
+
+		if impl, ok := _tag_implementations_type[info.elem.id]; ok {
+			for i in 0..<info.count {
+				data := uintptr(v.data) + uintptr(i*info.elem_size)
+				impl->marshal(e, any{rawptr(data), info.elem.id}) or_return
+			}
+			return
+		}
+
+		elem_ti := runtime.type_info_core(type_info_of(info.elem.id))
+		for i in 0..<info.count {
+			data := uintptr(v.data) + uintptr(i*info.elem_size)
+			_marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return
+		}
+		return
 	}
 	}
 
 
 	return _unsupported(v.id, nil)
 	return _unsupported(v.id, nil)

+ 25 - 0
core/encoding/cbor/unmarshal.odin

@@ -591,6 +591,31 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
 		if out_of_space { return _unsupported(v, hdr) }
 		if out_of_space { return _unsupported(v, hdr) }
 		return
 		return
 
 
+	case reflect.Type_Info_Matrix:
+		count := t.column_count * t.elem_stride
+		length, _ := err_conv(_decode_len_container(d, add)) or_return
+		if length > count {
+			return _unsupported(v, hdr)
+		}
+
+		da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator }
+
+		out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
+		if out_of_space { return _unsupported(v, hdr) }
+		return
+
+	case reflect.Type_Info_Simd_Vector:
+		length, _ := err_conv(_decode_len_container(d, add)) or_return
+		if length > t.count {
+			return _unsupported(v, hdr)
+		}
+
+		da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator }
+
+		out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return
+		if out_of_space { return _unsupported(v, hdr) }
+		return
+
 	case: return _unsupported(v, hdr)
 	case: return _unsupported(v, hdr)
 	}
 	}
 }
 }

+ 4 - 4
core/encoding/json/marshal.odin

@@ -108,13 +108,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
 		if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
 		if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
 			switch i in a {
 			switch i in a {
 			case u8, u16, u32, u64, u128:
 			case u8, u16, u32, u64, u128:
-				s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
+				s = strconv.write_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
 
 
 			case:
 			case:
-				s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
+				s = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
 			}
 			}
 		} else {
 		} else {
-			s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
+			s = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
 		}
 		}
 
 
 		io.write_string(w, s) or_return
 		io.write_string(w, s) or_return
@@ -286,7 +286,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
 						case runtime.Type_Info_Integer:
 						case runtime.Type_Info_Integer:
 							buf: [40]byte
 							buf: [40]byte
 							u := cast_any_int_to_u128(ka)
 							u := cast_any_int_to_u128(ka)
-							name = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*kti.size, "0123456789", nil)
+							name = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*kti.size, "0123456789", nil)
 							
 							
 							opt_write_key(w, opt, name) or_return
 							opt_write_key(w, opt, name) or_return
 						case: return .Unsupported_Type
 						case: return .Unsupported_Type

+ 2 - 1
core/encoding/xml/xml_reader.odin

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

+ 4 - 4
core/fmt/fmt.odin

@@ -1122,7 +1122,7 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
 	flags: strconv.Int_Flags
 	flags: strconv.Int_Flags
 	if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
 	if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
 	if fi.plus                           { flags += {.Plus}   }
 	if fi.plus                           { flags += {.Plus}   }
-	s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
+	s := strconv.write_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
 	prev_zero := fi.zero
 	prev_zero := fi.zero
 	defer fi.zero = prev_zero
 	defer fi.zero = prev_zero
 	fi.zero = false
 	fi.zero = false
@@ -1207,7 +1207,7 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
 	flags: strconv.Int_Flags
 	flags: strconv.Int_Flags
 	if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
 	if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} }
 	if fi.plus                           { flags += {.Plus}   }
 	if fi.plus                           { flags += {.Plus}   }
-	s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
+	s := strconv.write_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
 
 
 	if fi.hash && fi.zero && fi.indent == 0 {
 	if fi.hash && fi.zero && fi.indent == 0 {
 		c: byte = 0
 		c: byte = 0
@@ -1272,7 +1272,7 @@ _fmt_memory :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, units: st
 	}
 	}
 
 
 	buf: [256]byte
 	buf: [256]byte
-	str := strconv.append_float(buf[:], amt, 'f', prec, 64)
+	str := strconv.write_float(buf[:], amt, 'f', prec, 64)
 
 
 	// Add the unit at the end.
 	// Add the unit at the end.
 	copy(buf[len(str):], units[off:off+unit_len])
 	copy(buf[len(str):], units[off:off+unit_len])
@@ -1424,7 +1424,7 @@ _fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: b
 	buf: [386]byte
 	buf: [386]byte
 
 
 	// Can return "NaN", "+Inf", "-Inf", "+<value>", "-<value>".
 	// Can return "NaN", "+Inf", "-Inf", "+<value>", "-<value>".
-	str := strconv.append_float(buf[:], v, float_fmt, prec, bit_size)
+	str := strconv.write_float(buf[:], v, float_fmt, prec, bit_size)
 
 
 	if !fi.plus {
 	if !fi.plus {
 		// Strip sign from "+<value>" but not "+Inf".
 		// Strip sign from "+<value>" but not "+Inf".

+ 8 - 8
core/io/util.odin

@@ -22,12 +22,12 @@ write_ptr_at :: proc(w: Writer_At, p: rawptr, byte_size: int, offset: i64, n_wri
 
 
 write_u64 :: proc(w: Writer, i: u64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
 write_u64 :: proc(w: Writer, i: u64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
 	buf: [32]byte
 	buf: [32]byte
-	s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil)
+	s := strconv.write_bits(buf[:], i, base, false, 64, strconv.digits, nil)
 	return write_string(w, s, n_written)
 	return write_string(w, s, n_written)
 }
 }
 write_i64 :: proc(w: Writer, i: i64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
 write_i64 :: proc(w: Writer, i: i64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
 	buf: [32]byte
 	buf: [32]byte
-	s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
+	s := strconv.write_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
 	return write_string(w, s, n_written)
 	return write_string(w, s, n_written)
 }
 }
 
 
@@ -40,18 +40,18 @@ write_int :: proc(w: Writer, i: int, base: int = 10, n_written: ^int = nil) -> (
 
 
 write_u128 :: proc(w: Writer, i: u128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
 write_u128 :: proc(w: Writer, i: u128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
 	buf: [39]byte
 	buf: [39]byte
-	s := strconv.append_bits_128(buf[:], i, base, false, 128, strconv.digits, nil)
+	s := strconv.write_bits_128(buf[:], i, base, false, 128, strconv.digits, nil)
 	return write_string(w, s, n_written)
 	return write_string(w, s, n_written)
 }
 }
 write_i128 :: proc(w: Writer, i: i128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
 write_i128 :: proc(w: Writer, i: i128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) {
 	buf: [40]byte
 	buf: [40]byte
-	s := strconv.append_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil)
+	s := strconv.write_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil)
 	return write_string(w, s, n_written)
 	return write_string(w, s, n_written)
 }
 }
 write_f16 :: proc(w: Writer, val: f16, n_written: ^int = nil) -> (n: int, err: Error) {
 write_f16 :: proc(w: Writer, val: f16, n_written: ^int = nil) -> (n: int, err: Error) {
 	buf: [386]byte
 	buf: [386]byte
 
 
-	str := strconv.append_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val))
+	str := strconv.write_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val))
 	s := buf[:len(str)+1]
 	s := buf[:len(str)+1]
 	if s[1] == '+' || s[1] == '-' {
 	if s[1] == '+' || s[1] == '-' {
 		s = s[1:]
 		s = s[1:]
@@ -67,7 +67,7 @@ write_f16 :: proc(w: Writer, val: f16, n_written: ^int = nil) -> (n: int, err: E
 write_f32 :: proc(w: Writer, val: f32, n_written: ^int = nil) -> (n: int, err: Error) {
 write_f32 :: proc(w: Writer, val: f32, n_written: ^int = nil) -> (n: int, err: Error) {
 	buf: [386]byte
 	buf: [386]byte
 
 
-	str := strconv.append_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val))
+	str := strconv.write_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val))
 	s := buf[:len(str)+1]
 	s := buf[:len(str)+1]
 	if s[1] == '+' || s[1] == '-' {
 	if s[1] == '+' || s[1] == '-' {
 		s = s[1:]
 		s = s[1:]
@@ -83,7 +83,7 @@ write_f32 :: proc(w: Writer, val: f32, n_written: ^int = nil) -> (n: int, err: E
 write_f64 :: proc(w: Writer, val: f64, n_written: ^int = nil) -> (n: int, err: Error) {
 write_f64 :: proc(w: Writer, val: f64, n_written: ^int = nil) -> (n: int, err: Error) {
 	buf: [386]byte
 	buf: [386]byte
 
 
-	str := strconv.append_float(buf[1:], val, 'f', 2*size_of(val), 8*size_of(val))
+	str := strconv.write_float(buf[1:], val, 'f', 2*size_of(val), 8*size_of(val))
 	s := buf[:len(str)+1]
 	s := buf[:len(str)+1]
 	if s[1] == '+' || s[1] == '-' {
 	if s[1] == '+' || s[1] == '-' {
 		s = s[1:]
 		s = s[1:]
@@ -130,7 +130,7 @@ write_encoded_rune :: proc(w: Writer, r: rune, write_quote := true, n_written: ^
 			write_string(w, `\x`, &n) or_return
 			write_string(w, `\x`, &n) or_return
 			
 			
 			buf: [2]byte
 			buf: [2]byte
-			s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil)
+			s := strconv.write_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil)
 			switch len(s) {
 			switch len(s) {
 			case 0: 
 			case 0: 
 				write_string(w, "00", &n) or_return
 				write_string(w, "00", &n) or_return

+ 1 - 1
core/math/big/private.odin

@@ -1370,8 +1370,8 @@ _private_int_div_recursive :: proc(quotient, remainder, a, b: ^Int, allocator :=
 
 
 /*
 /*
 	Slower bit-bang division... also smaller.
 	Slower bit-bang division... also smaller.
+	Prefer `_int_div_school` for speed.
 */
 */
-@(deprecated="Use `_int_div_school`, it's 3.5x faster.")
 _private_int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) {
 _private_int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) {
 
 
 	ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{}
 	ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{}

+ 10 - 5
core/math/fixed/fixed.odin

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

+ 1 - 1
core/net/url.odin

@@ -125,7 +125,7 @@ percent_encode :: proc(s: string, allocator := context.allocator) -> string {
 			bytes, n := utf8.encode_rune(ch)
 			bytes, n := utf8.encode_rune(ch)
 			for byte in bytes[:n] {
 			for byte in bytes[:n] {
 				buf: [2]u8 = ---
 				buf: [2]u8 = ---
-				t := strconv.append_int(buf[:], i64(byte), 16)
+				t := strconv.write_int(buf[:], i64(byte), 16)
 				strings.write_rune(&b, '%')
 				strings.write_rune(&b, '%')
 				strings.write_string(&b, t)
 				strings.write_string(&b, t)
 			}
 			}

+ 1 - 1
core/os/os.odin

@@ -57,7 +57,7 @@ write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) {
 		if r < 32 {
 		if r < 32 {
 			if wrap(write_string(f, "\\x"), &n, &err) { return }
 			if wrap(write_string(f, "\\x"), &n, &err) { return }
 			b: [2]byte
 			b: [2]byte
-			s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
+			s := strconv.write_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
 			switch len(s) {
 			switch len(s) {
 			case 0: if wrap(write_string(f, "00"), &n, &err) { return }
 			case 0: if wrap(write_string(f, "00"), &n, &err) { return }
 			case 1: if wrap(write_rune(f, '0'), &n, &err)    { return }
 			case 1: if wrap(write_rune(f, '0'), &n, &err)    { return }

+ 19 - 14
core/os/os2/errors.odin

@@ -27,6 +27,9 @@ General_Error :: enum u32 {
 
 
 	Pattern_Has_Separator,
 	Pattern_Has_Separator,
 
 
+	No_HOME_Variable,
+	Wordexp_Failed,
+
 	Unsupported,
 	Unsupported,
 }
 }
 
 
@@ -59,20 +62,22 @@ error_string :: proc(ferr: Error) -> string {
 	case General_Error:
 	case General_Error:
 		switch e {
 		switch e {
 		case .None: return ""
 		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:
 	case io.Error:
 		switch e {
 		switch e {

+ 1 - 0
core/os/os2/file_linux.odin

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

+ 1 - 1
core/os/os2/file_util.odin

@@ -59,7 +59,7 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
 		if r < 32 {
 		if r < 32 {
 			if wrap(write_string(f, "\\x"), &n, &err) { return }
 			if wrap(write_string(f, "\\x"), &n, &err) { return }
 			b: [2]byte
 			b: [2]byte
-			s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
+			s := strconv.write_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
 			switch len(s) {
 			switch len(s) {
 			case 0: if wrap(write_string(f, "00"), &n, &err) { return }
 			case 0: if wrap(write_string(f, "00"), &n, &err) { return }
 			case 1: if wrap(write_rune(f, '0'), &n, &err)    { return }
 			case 1: if wrap(write_rune(f, '0'), &n, &err)    { return }

+ 133 - 63
core/os/os2/user.odin

@@ -2,78 +2,148 @@ package os2
 
 
 import "base:runtime"
 import "base:runtime"
 
 
+// ```
+// Windows:  C:\Users\Alice
+// macOS:    /Users/Alice
+// Linux:    /home/alice
+// ```
+@(require_results)
+user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	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)
 @(require_results)
 user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
 user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
-	temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
+	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)
+}
 
 
-	#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
+// 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)
 @(require_results)
-user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
-	temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator })
+user_log_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	return _user_log_dir(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
+// 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)
 @(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
+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 - 0
core/os/os2/user_posix.odin

@@ -0,0 +1,183 @@
+#+build !windows
+package os2
+
+import "base:runtime"
+import "core:encoding/ini"
+import "core:strings"
+import "core:sys/posix"
+
+_user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	#partial switch ODIN_OS {
+	case .Darwin:
+		return _xdg_lookup("", "/Library/Caches", allocator)
+	case: // Unix
+		return _xdg_lookup("XDG_CACHE_HOME", "/.cache", allocator)
+	}
+}
+
+_user_config_dir :: proc(allocator: runtime.Allocator, _roaming: bool) -> (dir: string, err: Error) {
+	#partial switch ODIN_OS {
+	case .Darwin:
+		return _xdg_lookup("", "/Library/Application Support", allocator)
+	case: // Unix
+		return _xdg_lookup("XDG_CONFIG_HOME", "/.config", allocator)
+	}
+}
+
+_user_state_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	#partial switch ODIN_OS {
+	case .Darwin:
+		return _xdg_lookup("", "/Library/Application Support", allocator)
+	case: // Unix
+		return _xdg_lookup("XDG_STATE_HOME", "/.local/state", allocator)
+	}
+}
+
+_user_log_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	#partial switch ODIN_OS {
+	case .Darwin:
+		return _xdg_lookup("", "/Library/Logs", allocator)
+	case: // Unix
+		return _xdg_lookup("XDG_STATE_HOME", "/.local/state", allocator)
+	}
+}
+
+_user_data_dir :: proc(allocator: runtime.Allocator, _roaming: bool) -> (dir: string, err: Error) {
+	#partial switch ODIN_OS {
+	case .Darwin:
+		return _xdg_lookup("", "/Library/Application Support", allocator)
+	case: // Unix
+		return _xdg_lookup("XDG_DATA_HOME", "/.local/share", allocator)
+	}
+}
+
+_user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	#partial switch ODIN_OS {
+	case .Darwin:
+		return _xdg_lookup("", "/Music", allocator)
+	case: // Unix
+		return _xdg_lookup("XDG_MUSIC_DIR", "/Music", allocator)
+	}
+}
+
+_user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	#partial switch ODIN_OS {
+	case .Darwin:
+		return _xdg_lookup("", "/Desktop", allocator)
+	case: // Unix
+		return _xdg_lookup("XDG_DESKTOP_DIR", "/Desktop", allocator)
+	}
+}
+
+_user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	#partial switch ODIN_OS {
+	case .Darwin:
+		return _xdg_lookup("", "/Documents", allocator)
+	case: // Unix
+		return _xdg_lookup("XDG_DOCUMENTS_DIR", "/Documents", allocator)
+	}
+}
+
+_user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	#partial switch ODIN_OS {
+	case .Darwin:
+		return _xdg_lookup("", "/Downloads", allocator)
+	case: // Unix
+		return _xdg_lookup("XDG_DOWNLOAD_DIR", "/Downloads", allocator)
+	}
+}
+
+_user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	#partial switch ODIN_OS {
+	case .Darwin:
+		return _xdg_lookup("", "/Pictures", allocator)
+	case: // Unix
+		return _xdg_lookup("XDG_PICTURES_DIR", "/Pictures", allocator)
+	}
+}
+
+_user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	#partial switch ODIN_OS {
+	case .Darwin:
+		return _xdg_lookup("", "/Public", allocator)
+	case: // Unix
+		return _xdg_lookup("XDG_PUBLICSHARE_DIR", "/Public", allocator)
+	}
+}
+
+_user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	#partial switch ODIN_OS {
+	case .Darwin:
+		return _xdg_lookup("", "/Movies", allocator)
+	case: // Unix
+		return _xdg_lookup("XDG_VIDEOS_DIR", "/Videos", allocator)
+	}
+}
+
+_user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	if v := get_env("HOME", allocator); v != "" {
+		return v, nil
+	}
+	err = .No_HOME_Variable
+	return
+}
+
+_xdg_lookup :: proc(xdg_key: string, fallback_suffix: string, allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	temp_allocator  := TEMP_ALLOCATOR_GUARD({ allocator })
+
+	if xdg_key == "" { // Darwin doesn't have XDG paths.
+		dir = get_env("HOME", temp_allocator)
+		if dir == "" {
+			err = .No_HOME_Variable
+			return
+		}
+		return concatenate({dir, fallback_suffix}, allocator)
+	} else {
+		if strings.ends_with(xdg_key, "_DIR") {
+			dir = _xdg_user_dirs_lookup(xdg_key, allocator) or_return
+		} else {
+			dir = get_env(xdg_key, allocator)
+		}
+
+		if dir == "" {
+			dir = get_env("HOME", temp_allocator)
+			if dir == "" {
+				err = .No_HOME_Variable
+				return
+			}
+			dir = concatenate({dir, fallback_suffix}, allocator) or_return
+		}
+		return
+	}
+}
+
+// If `<config-dir>/user-dirs.dirs` doesn't exist, or `xdg_key` can't be found there: returns `""`
+_xdg_user_dirs_lookup :: proc(xdg_key: string, allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	temp_allocator  := TEMP_ALLOCATOR_GUARD({ allocator })
+	config_dir      := user_config_dir(temp_allocator) or_return
+	user_dirs_path  := concatenate({config_dir, "/user-dirs.dirs"}, temp_allocator) or_return
+	content         := read_entire_file(user_dirs_path, temp_allocator) or_return
+
+	it := ini.Iterator{
+		section = "",
+		_src    = string(content),
+		options = ini.Options{
+			comment        = "#",
+			key_lower_case = false,
+		},
+	}
+
+	for k, v in ini.iterate(&it) {
+		if k == xdg_key {
+			we: posix.wordexp_t
+			defer posix.wordfree(&we)
+
+			if _err := posix.wordexp(strings.clone_to_cstring(v, temp_allocator), &we, nil); _err != nil || we.we_wordc != 1 {
+				return "", .Wordexp_Failed
+			}
+
+			return strings.clone_from_cstring(we.we_wordv[0], allocator)
+		}
+	}
+	return
+}

+ 79 - 0
core/os/os2/user_windows.odin

@@ -0,0 +1,79 @@
+package os2
+
+import "base:runtime"
+@(require) import win32 "core:sys/windows"
+
+_local_appdata :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	guid := win32.FOLDERID_LocalAppData
+	return _get_known_folder_path(&guid, allocator)
+}
+
+_local_appdata_or_roaming :: proc(allocator: runtime.Allocator, roaming: bool) -> (dir: string, err: Error) {
+	guid := win32.FOLDERID_LocalAppData
+	if roaming {
+		guid = win32.FOLDERID_RoamingAppData
+	}
+	return _get_known_folder_path(&guid, allocator)
+}
+
+_user_config_dir :: _local_appdata_or_roaming
+_user_data_dir :: _local_appdata_or_roaming
+
+_user_state_dir :: _local_appdata
+_user_log_dir :: _local_appdata
+_user_cache_dir :: _local_appdata
+
+_user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	guid := win32.FOLDERID_Profile
+	return _get_known_folder_path(&guid, allocator)
+}
+
+_user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	guid := win32.FOLDERID_Music
+	return _get_known_folder_path(&guid, allocator)
+}
+
+_user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	guid := win32.FOLDERID_Desktop
+	return _get_known_folder_path(&guid, allocator)
+}
+
+_user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	guid := win32.FOLDERID_Documents
+	return _get_known_folder_path(&guid, allocator)
+}
+
+_user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	guid := win32.FOLDERID_Downloads
+	return _get_known_folder_path(&guid, allocator)
+}
+
+_user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	guid := win32.FOLDERID_Pictures
+	return _get_known_folder_path(&guid, allocator)
+}
+
+_user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	guid := win32.FOLDERID_Public
+	return _get_known_folder_path(&guid, allocator)
+}
+
+_user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	guid := win32.FOLDERID_Videos
+	return _get_known_folder_path(&guid, allocator)
+}
+
+_get_known_folder_path :: proc(rfid: win32.REFKNOWNFOLDERID, allocator: runtime.Allocator) -> (dir: string, err: Error) {
+	// https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath
+	// See also `known_folders.odin` in `core:sys/windows` for the GUIDs.
+	path_w: win32.LPWSTR
+	res  := win32.SHGetKnownFolderPath(rfid, 0, nil, &path_w)
+	defer win32.CoTaskMemFree(path_w)
+
+	if res != 0 {
+		return "", .Invalid_Path
+	}
+
+	dir, _ = win32.wstring_to_utf8(path_w, -1, allocator)
+	return
+}

+ 3 - 3
core/os/os_freebsd.odin

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

+ 3 - 3
core/os/os_haiku.odin

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

+ 4 - 4
core/os/os_linux.odin

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

+ 3 - 3
core/os/os_netbsd.odin

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

+ 3 - 3
core/os/os_openbsd.odin

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

+ 0 - 46
core/sort/sort.odin

@@ -30,14 +30,6 @@ sort :: proc(it: Interface) {
 	_quick_sort(it, 0, n, max_depth(n))
 	_quick_sort(it, 0, n, max_depth(n))
 }
 }
 
 
-
-@(deprecated="use slice.sort")
-slice :: proc(array: $T/[]$E) where ORD(E) {
-	_slice.sort(array)
-	// s := array;
-	// sort(slice_interface(&s));
-}
-
 slice_interface :: proc(s: ^$T/[]$E) -> Interface where ORD(E) {
 slice_interface :: proc(s: ^$T/[]$E) -> Interface where ORD(E) {
 	return Interface{
 	return Interface{
 		collection = rawptr(s),
 		collection = rawptr(s),
@@ -80,31 +72,6 @@ reverse_sort :: proc(it: Interface) {
 	sort(reverse_interface(&it))
 	sort(reverse_interface(&it))
 }
 }
 
 
-@(deprecated="use slice.reverse")
-reverse_slice :: proc(array: $T/[]$E) where ORD(E) {
-	_slice.reverse(array)
-	/*
-	s := array;
-	sort(Interface{
-		collection = rawptr(&s),
-		len = proc(it: Interface) -> int {
-			s := (^T)(it.collection);
-			return len(s^);
-		},
-		less = proc(it: Interface, i, j: int) -> bool {
-			s := (^T)(it.collection);
-			return s[j] < s[i]; // manual set up
-		},
-		swap = proc(it: Interface, i, j: int) {
-			s := (^T)(it.collection);
-			s[i], s[j] = s[j], s[i];
-		},
-	});
-	*/
-}
-
-
-
 is_sorted :: proc(it: Interface) -> bool {
 is_sorted :: proc(it: Interface) -> bool {
 	n := it->len()
 	n := it->len()
 	for i := n-1; i > 0; i -= 1 {
 	for i := n-1; i > 0; i -= 1 {
@@ -294,11 +261,6 @@ _insertion_sort :: proc(it: Interface, a, b: int) {
 	}
 	}
 }
 }
 
 
-
-
-
-
-// @(deprecated="use sort.sort or slice.sort_by")
 bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
 bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
 	assert(f != nil)
 	assert(f != nil)
 	count := len(array)
 	count := len(array)
@@ -327,7 +289,6 @@ bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
 	}
 	}
 }
 }
 
 
-// @(deprecated="use sort.sort_slice or slice.sort")
 bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
 bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
 	count := len(array)
 	count := len(array)
 
 
@@ -355,7 +316,6 @@ bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
 	}
 	}
 }
 }
 
 
-// @(deprecated="use sort.sort or slice.sort_by")
 quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
 quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
 	assert(f != nil)
 	assert(f != nil)
 	a := array
 	a := array
@@ -384,7 +344,6 @@ quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
 	quick_sort_proc(a[i:n], f)
 	quick_sort_proc(a[i:n], f)
 }
 }
 
 
-// @(deprecated="use sort.sort_slice or slice.sort")
 quick_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
 quick_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
 	a := array
 	a := array
 	n := len(a)
 	n := len(a)
@@ -420,7 +379,6 @@ _log2 :: proc(x: int) -> int {
 	return res
 	return res
 }
 }
 
 
-// @(deprecated="use sort.sort or slice.sort_by")
 merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
 merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
 	merge :: proc(a: A, start, mid, end: int, f: proc(T, T) -> int) {
 	merge :: proc(a: A, start, mid, end: int, f: proc(T, T) -> int) {
 		s, m := start, mid
 		s, m := start, mid
@@ -462,7 +420,6 @@ merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
 	internal_sort(array, 0, len(array)-1, f)
 	internal_sort(array, 0, len(array)-1, f)
 }
 }
 
 
-// @(deprecated="use sort.sort_slice or slice.sort")
 merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
 merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
 	merge :: proc(a: A, start, mid, end: int) {
 	merge :: proc(a: A, start, mid, end: int) {
 		s, m := start, mid
 		s, m := start, mid
@@ -504,8 +461,6 @@ merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
 	internal_sort(array, 0, len(array)-1)
 	internal_sort(array, 0, len(array)-1)
 }
 }
 
 
-
-// @(deprecated="use sort.sort or slice.sort_by")
 heap_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
 heap_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
 	sift_proc :: proc(a: A, pi: int, n: int, f: proc(T, T) -> int) #no_bounds_check {
 	sift_proc :: proc(a: A, pi: int, n: int, f: proc(T, T) -> int) #no_bounds_check {
 		p := pi
 		p := pi
@@ -540,7 +495,6 @@ heap_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
 	}
 	}
 }
 }
 
 
-// @(deprecated="use sort.sort_slice or slice.sort")
 heap_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
 heap_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
 	sift :: proc(a: A, pi: int, n: int) #no_bounds_check {
 	sift :: proc(a: A, pi: int, n: int) #no_bounds_check {
 		p := pi
 		p := pi

+ 15 - 15
core/strconv/decimal/decimal.odin

@@ -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.
 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.
 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
 - d: Pointer to a Decimal struct where the parsed result will be stored
 - s: The input string representing the floating-point number
 - s: The input string representing the floating-point number
 
 
-**Returns**  
+**Returns**
 - ok: A boolean indicating whether the parsing was successful
 - ok: A boolean indicating whether the parsing was successful
 */
 */
 set :: proc(d: ^Decimal, s: string) -> (ok: bool) {
 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.
 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
 - buf: A byte slice buffer to hold the resulting string
 - a: The struct to be converted to a string
 - a: The struct to be converted to a string
 
 
-**Returns**  
+**Returns**
 - A string representation of the Decimal
 - A string representation of the Decimal
 */
 */
 decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
 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.
 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
 - a: Pointer to the Decimal struct to be trimmed
 */
 */
 trim :: proc(a: ^Decimal) {
 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.**
 **Used for internal Decimal Operations.**
 
 
-**Inputs**  
+**Inputs**
 - a: Where the result will be stored
 - a: Where the result will be stored
 - idx: The value to be assigned to the Decimal
 - idx: The value to be assigned to the Decimal
 */
 */
@@ -190,11 +190,11 @@ assign :: proc(a: ^Decimal, idx: u64) {
 	trim(a)
 	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.**
 **Used for internal Decimal Operations.**
 
 
-**Inputs**  
+**Inputs**
 - a: The Decimal struct to be shifted
 - a: The Decimal struct to be shifted
 - k: The number of positions to shift right
 - 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`
 WARNING: asserts `k < 61`
 
 
-**Inputs**  
+**Inputs**
 - a: The Decimal to be modified
 - a: The Decimal to be modified
 - k: The number of places to shift the decimal to the left
 - 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
 Shifts the decimal of the input value by the specified number of places
 
 
-**Inputs**  
+**Inputs**
 - a: The Decimal to be modified
 - a: The Decimal to be modified
 - i: The number of places to shift the decimal (positive for left shift, negative for right shift)
 - 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
 Determines if the Decimal can be rounded up at the given digit index
 
 
-**Inputs**  
+**Inputs**
 - a: The Decimal to check
 - a: The Decimal to check
 - nd: The digit index to consider for rounding up
 - 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
 Rounds the Decimal at the given digit index
 
 
-**Inputs**  
+**Inputs**
 - a: The Decimal to be modified
 - a: The Decimal to be modified
 - nd: The digit index to round
 - 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
 Rounds the Decimal up at the given digit index
 
 
-**Inputs**  
+**Inputs**
 - a: The Decimal to be modified
 - a: The Decimal to be modified
 - nd: The digit index to round up
 - 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
 Rounds down the decimal value to the specified number of decimal places
 
 
-**Inputs**  
+**Inputs**
 - a: The Decimal value to be rounded down
 - a: The Decimal value to be rounded down
 - nd: The number of decimal places to round down to
 - 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
 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
 - a: A pointer to the Decimal value to extract the rounded integer part from
 
 
 WARNING: There are no guarantees about overflow.
 WARNING: There are no guarantees about overflow.

+ 38 - 0
core/strconv/deprecated.odin

@@ -0,0 +1,38 @@
+package strconv
+
+// (2025-06-05) These procedures are to be removed at a later release.
+
+@(deprecated="Use write_bits instead")
+append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
+	return write_bits(buf, x, base, is_signed, bit_size, digits, flags)
+}
+
+@(deprecated="Use write_bits_128 instead")
+append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
+	return write_bits_128(buf, x, base, is_signed, bit_size, digits, flags)
+}
+
+@(deprecated="Use write_bool instead")
+append_bool :: proc(buf: []byte, b: bool) -> string {
+	return write_bool(buf, b)
+}
+
+@(deprecated="Use write_uint instead")
+append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
+	return write_uint(buf, u, base)
+}
+
+@(deprecated="Use write_int instead")
+append_int :: proc(buf: []byte, i: i64, base: int) -> string {
+	return write_int(buf, i, base)
+}
+
+@(deprecated="Use write_u128 instead")
+append_u128 :: proc(buf: []byte, u: u128, base: int) -> string {
+	return write_u128(buf, u, base)
+}
+
+@(deprecated="Use write_float instead")
+append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
+	return write_float(buf, f, fmt, prec, bit_size)
+}

+ 7 - 7
core/strconv/generic_float.odin

@@ -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.
 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
 buf: A byte slice to store the resulting string
 val: The floating-point value to be converted
 val: The floating-point value to be converted
@@ -40,7 +40,7 @@ Example:
 	bit_size := 64
 	bit_size := 64
 	result := strconv.generic_ftoa(buf[:], val, fmt, precision, bit_size) -> "3.14"
 	result := strconv.generic_ftoa(buf[:], val, fmt, precision, bit_size) -> "3.14"
 
 
-**Returns**  
+**Returns**
 - A byte slice containing the formatted string
 - A byte slice containing the formatted string
 */
 */
 generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int) -> []byte {
 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
 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
 - buf: The byte buffer to store the formatted number
 - shortest: If true, generates the shortest representation of the number
 - shortest: If true, generates the shortest representation of the number
 - neg: If true, the number is negative
 - 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
 - precision: The number of digits after the decimal point
 - fmt: The format specifier (accepted values: 'f', 'F', 'e', 'E', 'g', 'G')
 - fmt: The format specifier (accepted values: 'f', 'F', 'e', 'E', 'g', 'G')
 
 
-**Returns**  
+**Returns**
 - A byte slice containing the formatted decimal floating-point number
 - 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 {
 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
 Rounds the given decimal number to its shortest representation, considering the provided floating-point format
 
 
-**Inputs**  
+**Inputs**
 - d: The decimal number to round
 - d: The decimal number to round
 - mant: The mantissa of the floating-point number
 - mant: The mantissa of the floating-point number
 - exp: The exponent 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
 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
 - d: Pointer to the decimal number to convert
 - info: Pointer to the Float_Info structure containing information about the floating-point format
 - info: Pointer to the Float_Info structure containing information about the floating-point format
 
 
-**Returns**  
+**Returns**
 - b: The bits representing the floating-point number
 - b: The bits representing the floating-point number
 - overflow: A boolean indicating whether an overflow occurred during conversion
 - overflow: A boolean indicating whether an overflow occurred during conversion
 */
 */

+ 15 - 15
core/strconv/integers.odin

@@ -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.
 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
 - 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
 - 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)
 - 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
 - u: The absolute value of the input integer
 - neg: A boolean indicating whether the input integer is negative
 - 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
 	return
 }
 }
 /*
 /*
-Appends the string representation of an integer to a buffer with specified base, flags, and digit set.
+Writes the string representation of an integer to a buffer with specified base, flags, and digit set.
 
 
-**Inputs**  
+**Inputs**
 - buf: The buffer to append the integer representation to
 - buf: The buffer to append the integer representation to
 - x: The integer value to convert
 - x: The integer value to convert
 - base: The base for the integer representation (2 <= base <= MAX_BASE)
 - 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
 - digits: The digit set used for the integer representation
 - flags: The Int_Flags bit set to control integer formatting
 - flags: The Int_Flags bit set to control integer formatting
 
 
-**Returns**  
+**Returns**
 - The string containing the integer representation appended to the buffer
 - The string containing the integer representation appended to the buffer
 */
 */
-append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
+write_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
 	if base < 2 || base > MAX_BASE {
 	if base < 2 || base > MAX_BASE {
-		panic("strconv: illegal base passed to append_bits")
+		panic("strconv: illegal base passed to write_bits")
 	}
 	}
 
 
 	a: [129]byte
 	a: [129]byte
@@ -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.
 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
 - 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
 - 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)
 - 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
 - u: The absolute value of the input integer
 - neg: A boolean indicating whether the input integer is negative
 - 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
 	return
 }
 }
 /*
 /*
-Appends the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set.
+Writes the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set.
 
 
-**Inputs**  
+**Inputs**
 - buf: The buffer to append the integer representation to
 - buf: The buffer to append the integer representation to
 - x: The 128-bit integer value to convert
 - x: The 128-bit integer value to convert
 - base: The base for the integer representation (2 <= base <= MAX_BASE)
 - 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
 - digits: The digit set used for the integer representation
 - flags: The Int_Flags bit set to control integer formatting
 - 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 {
 	if base < 2 || base > MAX_BASE {
-		panic("strconv: illegal base passed to append_bits")
+		panic("strconv: illegal base passed to write_bits")
 	}
 	}
 
 
 	a: [140]byte
 	a: [140]byte

+ 139 - 139
core/strconv/strconv.odin

@@ -5,8 +5,8 @@ import "decimal"
 /*
 /*
 Parses a boolean value from the input string
 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"
 	- true: "1", "t", "T", "true", "TRUE", "True"
 	- false: "0", "f", "F", "false", "FALSE", "False"
 	- false: "0", "f", "F", "false", "FALSE", "False"
 - n: An optional pointer to an int to store the length of the parsed substring (default: nil)
 - 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
 	- If base is not 0, it will be used for parsing regardless of any prefix in the input string
 
 
 Example:
 Example:
-	
+
 	import "core:fmt"
 	import "core:fmt"
 	import "core:strconv"
 	import "core:strconv"
 	parse_uint_example :: proc() {
 	parse_uint_example :: proc() {
@@ -399,14 +399,14 @@ Example:
 		n, ok = strconv.parse_uint("0xffff") // with prefix and inferred base
 		n, ok = strconv.parse_uint("0xffff") // with prefix and inferred base
 		fmt.println(n,ok)
 		fmt.println(n,ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	1234 true
 	1234 true
 	65535 true
 	65535 true
 	65535 true
 	65535 true
 
 
-**Returns**  
+**Returns**
 
 
 value: The parsed uint value
 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
 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
 Parses an integer value from a string in the given base, without any prefix
 
 
-**Inputs**  
+**Inputs**
 - str: The input string containing the integer value
 - str: The input string containing the integer value
 - base: The base (radix) to use for parsing the integer (1-16)
 - 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)
 - 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)
 		n, ok := strconv.parse_i128_of_base("-1234eeee", 10)
 		fmt.println(n,ok)
 		fmt.println(n,ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	-1234 false
 	-1234 false
 
 
-**Returns**  
+**Returns**
 - value: The parsed i128 value
 - 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.
 - 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
 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
 - 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)
 - 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")
 		n, ok = strconv.parse_i128_maybe_prefixed("0xeeee")
 		fmt.println(n, ok)
 		fmt.println(n, ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	1234 true
 	1234 true
 	61166 true
 	61166 true
-	
-**Returns**  
+
+**Returns**
 - value: The parsed i128 value
 - 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.
 - 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
 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
 - str: The input string containing the integer value
 - base: The base (radix) to use for parsing the integer (1-16)
 - 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)
 - 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)
 		n, ok = strconv.parse_u128_of_base("5678eeee", 16)
 		fmt.println(n, ok)
 		fmt.println(n, ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	1234 false
 	1234 false
 	1450766062 true
 	1450766062 true
-	
-**Returns**  
+
+**Returns**
 - value: The parsed u128 value
 - 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.
 - 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
 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
 - 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)
 - 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")
 		n, ok = strconv.parse_u128_maybe_prefixed("5678eeee")
 		fmt.println(n, ok)
 		fmt.println(n, ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	1234 true
 	1234 true
 	5678 false
 	5678 false
-	
-**Returns**  
+
+**Returns**
 - value: The parsed u128 value
 - 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.
 - 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
 Converts a byte to lowercase
 
 
-**Inputs**  
+**Inputs**
 - ch: A byte character to be converted to lowercase.
 - ch: A byte character to be converted to lowercase.
 
 
-**Returns**  
+**Returns**
 - A lowercase byte character.
 - A lowercase byte character.
 */
 */
 @(private)
 @(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
 Parses a 32-bit floating point number from a string
 
 
-**Inputs**  
+**Inputs**
 - s: The input string containing a 32-bit floating point number.
 - 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).
 - 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")
 		n, ok = strconv.parse_f32("5678e2")
 		fmt.printfln("%.3f %v", n, ok)
 		fmt.printfln("%.3f %v", n, ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	0.000 false
 	0.000 false
 	567800.000 true
 	567800.000 true
-	
-**Returns**  
+
+**Returns**
 - value: The parsed 32-bit floating point number.
 - 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.
 - 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
 Parses a 64-bit floating point number from a string
 
 
-**Inputs**  
+**Inputs**
 - str: The input string containing a 64-bit floating point number.
 - 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).
 - 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")
 		n, ok = strconv.parse_f64("5678e2")
 		fmt.printfln("%.3f %v", n, ok)
 		fmt.printfln("%.3f %v", n, ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	0.000 false
 	0.000 false
 	567800.000 true
 	567800.000 true
-	
-**Returns**  
+
+**Returns**
 - value: The parsed 64-bit floating point number.
 - 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.
 - 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
 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.
 - str: The input string containing a 32-bit floating point number.
 
 
 Example:
 Example:
@@ -801,14 +801,14 @@ Example:
 		n, _, ok = strconv.parse_f32_prefix("5678e2")
 		n, _, ok = strconv.parse_f32_prefix("5678e2")
 		fmt.printfln("%.3f %v", n, ok)
 		fmt.printfln("%.3f %v", n, ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	0.000 false
 	0.000 false
 	567800.000 true
 	567800.000 true
-	
 
 
-**Returns**  
+
+**Returns**
 - value: The parsed 32-bit floating point number.
 - value: The parsed 32-bit floating point number.
 - nr: The length of the parsed substring.
 - nr: The length of the parsed substring.
 - ok: A boolean indicating whether the parsing was successful.
 - 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
 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.
 - str: The input string containing a 64-bit floating point number.
 
 
 Example:
 Example:
@@ -846,7 +846,7 @@ Output:
 	1234.000 true
 	1234.000 true
 	13.370 true
 	13.370 true
 
 
-**Returns**  
+**Returns**
 - value: The parsed 64-bit floating point number.
 - value: The parsed 64-bit floating point number.
 - nr: The length of the parsed substring.
 - nr: The length of the parsed substring.
 - ok: `false` if a base 10 float could not be found
 - 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
 Parses a 128-bit complex number from a string
 
 
-**Inputs**  
+**Inputs**
 - str: The input string containing a 128-bit complex number.
 - 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).
 - 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)
 		c, ok = strconv.parse_complex128("5+7i hellope", &n)
 		fmt.printfln("%v %i %t", c, n, ok)
 		fmt.printfln("%v %i %t", c, n, ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	3+1i 4 true
 	3+1i 4 true
 	5+7i 4 false
 	5+7i 4 false
-	
-**Returns**  
+
+**Returns**
 - value: The parsed 128-bit complex number.
 - 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.
 - 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)
 	value = complex(real_value, imag_value)
-	return 
+	return
 }
 }
 /*
 /*
 Parses a 64-bit complex number from a string
 Parses a 64-bit complex number from a string
 
 
-**Inputs**  
+**Inputs**
 - str: The input string containing a 64-bit complex number.
 - 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).
 - 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)
 		c, ok = strconv.parse_complex64("5+7i hellope", &n)
 		fmt.printfln("%v %i %t", c, n, ok)
 		fmt.printfln("%v %i %t", c, n, ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	3+1i 4 true
 	3+1i 4 true
 	5+7i 4 false
 	5+7i 4 false
-	
-**Returns**  
+
+**Returns**
 - value: The parsed 64-bit complex number.
 - 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.
 - 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
 Parses a 32-bit complex number from a string
 
 
-**Inputs**  
+**Inputs**
 - str: The input string containing a 32-bit complex number.
 - 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).
 - 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)
 		c, ok = strconv.parse_complex32("5+7i hellope", &n)
 		fmt.printfln("%v %i %t", c, n, ok)
 		fmt.printfln("%v %i %t", c, n, ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	3+1i 4 true
 	3+1i 4 true
 	5+7i 4 false
 	5+7i 4 false
-	
-**Returns**  
+
+**Returns**
 - value: The parsed 32-bit complex number.
 - 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.
 - 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
 Parses a 256-bit quaternion from a string
 
 
-**Inputs**  
+**Inputs**
 - str: The input string containing a 256-bit quaternion.
 - 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).
 - 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)
 		q, ok = strconv.parse_quaternion256("1+2i+3j+4k hellope", &n)
 		fmt.printfln("%v %i %t", q, n, ok)
 		fmt.printfln("%v %i %t", q, n, ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	1+2i+3j+4k 10 true
 	1+2i+3j+4k 10 true
 	1+2i+3j+4k 10 false
 	1+2i+3j+4k 10 false
-	
-**Returns**  
+
+**Returns**
 - value: The parsed 256-bit quaternion.
 - 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.
 - 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
 Parses a 128-bit quaternion from a string
 
 
-**Inputs**  
+**Inputs**
 - str: The input string containing a 128-bit quaternion.
 - 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).
 - 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)
 		q, ok = strconv.parse_quaternion128("1+2i+3j+4k hellope", &n)
 		fmt.printfln("%v %i %t", q, n, ok)
 		fmt.printfln("%v %i %t", q, n, ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	1+2i+3j+4k 10 true
 	1+2i+3j+4k 10 true
 	1+2i+3j+4k 10 false
 	1+2i+3j+4k 10 false
-	
-**Returns**  
+
+**Returns**
 - value: The parsed 128-bit quaternion.
 - 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.
 - 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
 Parses a 64-bit quaternion from a string
 
 
-**Inputs**  
+**Inputs**
 - str: The input string containing a 64-bit quaternion.
 - 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).
 - 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)
 		q, ok = strconv.parse_quaternion64("1+2i+3j+4k hellope", &n)
 		fmt.printfln("%v %i %t", q, n, ok)
 		fmt.printfln("%v %i %t", q, n, ok)
 	}
 	}
-	
+
 Output:
 Output:
 
 
 	1+2i+3j+4k 10 true
 	1+2i+3j+4k 10 true
 	1+2i+3j+4k 10 false
 	1+2i+3j+4k 10 false
-	
-**Returns**  
+
+**Returns**
 - value: The parsed 64-bit quaternion.
 - 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.
 - 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)
 	v, ok = parse_quaternion256(str, n)
 	return cast(quaternion64)v, ok
 	return cast(quaternion64)v, ok
 }
 }
-/* 
-Appends a boolean value as a string to the given buffer
+/*
+Writes a boolean value as a string to the given buffer
 
 
-**Inputs**  
-- 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:
 Example:
 
 
 	import "core:fmt"
 	import "core:fmt"
 	import "core:strconv"
 	import "core:strconv"
-	append_bool_example :: proc() {
+	write_bool_example :: proc() {
 		buf: [6]byte
 		buf: [6]byte
-		result := strconv.append_bool(buf[:], true)
+		result := strconv.write_bool(buf[:], true)
 		fmt.println(result, buf)
 		fmt.println(result, buf)
 	}
 	}
 
 
@@ -1471,10 +1471,10 @@ Output:
 
 
 	true [116, 114, 117, 101, 0, 0]
 	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
 	n := 0
 	if b {
 	if b {
 		n = copy(buf, "true")
 		n = copy(buf, "true")
@@ -1483,21 +1483,21 @@ append_bool :: proc(buf: []byte, b: bool) -> string {
 	}
 	}
 	return string(buf[:n])
 	return string(buf[:n])
 }
 }
-/* 
-Appends an unsigned integer value as a string to the given buffer with the specified base
+/*
+Writes an unsigned integer value as a string to the given buffer with the specified base
 
 
-**Inputs**  
-- 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
 - base: The base to use for converting the integer value
 
 
 Example:
 Example:
 
 
 	import "core:fmt"
 	import "core:fmt"
 	import "core:strconv"
 	import "core:strconv"
-	append_uint_example :: proc() {
+	write_uint_example :: proc() {
 		buf: [4]byte
 		buf: [4]byte
-		result := strconv.append_uint(buf[:], 42, 16)
+		result := strconv.write_uint(buf[:], 42, 16)
 		fmt.println(result, buf)
 		fmt.println(result, buf)
 	}
 	}
 
 
@@ -1505,27 +1505,27 @@ Output:
 
 
 	2a [50, 97, 0, 0]
 	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
 - base: The base to use for converting the integer value
 
 
 Example:
 Example:
 
 
 	import "core:fmt"
 	import "core:fmt"
 	import "core:strconv"
 	import "core:strconv"
-	append_int_example :: proc() {
+	write_int_example :: proc() {
 		buf: [4]byte
 		buf: [4]byte
-		result := strconv.append_int(buf[:], -42, 10)
+		result := strconv.write_int(buf[:], -42, 10)
 		fmt.println(result, buf)
 		fmt.println(result, buf)
 	}
 	}
 
 
@@ -1533,23 +1533,23 @@ Output:
 
 
 	-42 [45, 52, 50, 0]
 	-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
 Converts an integer value to a string and stores it in the given buffer
 
 
-**Inputs**  
+**Inputs**
 - buf: The buffer to store the resulting string
 - buf: The buffer to store the resulting string
 - i: The integer value to be converted
 - i: The integer value to be converted
 
 
@@ -1567,16 +1567,16 @@ Output:
 
 
 	42 [52, 50, 0, 0]
 	42 [52, 50, 0, 0]
 
 
-**Returns**  
+**Returns**
 - The resulting string after converting the integer value
 - The resulting string after converting the integer value
 */
 */
 itoa :: proc(buf: []byte, i: int) -> string {
 itoa :: proc(buf: []byte, i: int) -> string {
-	return append_int(buf, i64(i), 10)
+	return write_int(buf, i64(i), 10)
 }
 }
 /*
 /*
 Converts a string to an integer value
 Converts a string to an integer value
 
 
-**Inputs**  
+**Inputs**
 - s: The string to be converted
 - s: The string to be converted
 
 
 Example:
 Example:
@@ -1591,17 +1591,17 @@ Output:
 
 
 	42
 	42
 
 
-**Returns**  
+**Returns**
 - The resulting integer value
 - The resulting integer value
 */
 */
 atoi :: proc(s: string) -> int {
 atoi :: proc(s: string) -> int {
 	v, _ := parse_int(s)
 	v, _ := parse_int(s)
 	return v
 	return v
 }
 }
-/* 
+/*
 Converts a string to a float64 value
 Converts a string to a float64 value
 
 
-**Inputs**  
+**Inputs**
 - s: The string to be converted
 - s: The string to be converted
 
 
 Example:
 Example:
@@ -1616,21 +1616,21 @@ Output:
 
 
 	3.140
 	3.140
 
 
-**Returns**  
+**Returns**
 - The resulting float64 value after converting the string
 - The resulting float64 value after converting the string
 */
 */
 atof :: proc(s: string) -> f64 {
 atof :: proc(s: string) -> f64 {
 	v, _  := parse_f64(s)
 	v, _  := parse_f64(s)
 	return v
 	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
-
-**Inputs**  
-- buf: The buffer to append the float64 value to
-- f: The float64 value to be appended
+// 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 write the float64 value to
+- f: The float64 value to be written
 - fmt: The byte specifying the format to use for the conversion
 - fmt: The byte specifying the format to use for the conversion
 - prec: The precision to use for the conversion
 - prec: The precision to use for the conversion
 - bit_size: The size of the float in bits (32 or 64)
 - bit_size: The size of the float in bits (32 or 64)
@@ -1639,9 +1639,9 @@ Example:
 
 
 	import "core:fmt"
 	import "core:fmt"
 	import "core:strconv"
 	import "core:strconv"
-	append_float_example :: proc() {
+	write_float_example :: proc() {
 		buf: [8]byte
 		buf: [8]byte
-		result := strconv.append_float(buf[:], 3.14159, 'f', 2, 64)
+		result := strconv.write_float(buf[:], 3.14159, 'f', 2, 64)
 		fmt.println(result, buf)
 		fmt.println(result, buf)
 	}
 	}
 
 
@@ -1649,20 +1649,20 @@ Output:
 
 
 	+3.14 [43, 51, 46, 49, 52, 0, 0, 0]
 	+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))
 	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
 - str: The input string to be quoted
 
 
-!! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected  
+!! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected
 
 
 Example:
 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]
 	"'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 {
 quote :: proc(buf: []byte, str: string) -> string {
 	write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) {
 	write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) {
@@ -1719,10 +1719,10 @@ quote :: proc(buf: []byte, str: string) -> string {
 	return string(buf[:i])
 	return string(buf[:i])
 }
 }
 /*
 /*
-Appends a quoted rune representation of the input rune to a given byte slice and returns the result as a string
+Writes a quoted rune representation of the input rune to a given byte slice and returns the result as a string
 
 
-**Inputs**  
-- 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
 - r: The input rune to be quoted
 
 
 Example:
 Example:
@@ -1739,8 +1739,8 @@ Output:
 
 
 	'A' [39, 65, 39, 0]
 	'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 {
 quote_rune :: proc(buf: []byte, r: rune) -> string {
 	write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) {
 	write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) {
@@ -1783,7 +1783,7 @@ quote_rune :: proc(buf: []byte, r: rune) -> string {
 		if r < 32 {
 		if r < 32 {
 			write_string(buf, &i, "\\x")
 			write_string(buf, &i, "\\x")
 			b: [2]byte
 			b: [2]byte
-			s := append_bits(b[:], u64(r), 16, true, 64, digits, nil)
+			s := write_bits(b[:], u64(r), 16, true, 64, digits, nil)
 			switch len(s) {
 			switch len(s) {
 			case 0: write_string(buf, &i, "00")
 			case 0: write_string(buf, &i, "00")
 			case 1: write_rune(buf, &i, '0')
 			case 1: write_rune(buf, &i, '0')
@@ -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
 Unquotes a single character from the input string, considering the given quote character
 
 
-**Inputs**  
+**Inputs**
 - str: The input string containing the character to unquote
 - str: The input string containing the character to unquote
 - quote: The quote character to consider (e.g., '"')
 - quote: The quote character to consider (e.g., '"')
 
 
-Example:  
+Example:
 
 
 	import "core:fmt"
 	import "core:fmt"
 	import "core:strconv"
 	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)
 		fmt.printf("r: <%v>, multiple_bytes:%v, tail_string:<%s>, success:%v\n",r, multiple_bytes, tail_string, success)
 	}
 	}
 
 
-Output:  
+Output:
 
 
 	Source: 'The' raven
 	Source: 'The' raven
 	r: <'>, multiple_bytes:false, tail_string:<The' raven>, success:true
 	r: <'>, multiple_bytes:false, tail_string:<The' raven>, success:true
 
 
-**Returns**  
+**Returns**
 - r: The unquoted rune
 - r: The unquoted rune
 - multiple_bytes: A boolean indicating if the rune has multiple bytes
 - multiple_bytes: A boolean indicating if the rune has multiple bytes
 - tail_string: The remaining portion of the input string after unquoting the character
 - 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
 Unquotes the input string considering any type of quote character and returns the unquoted string
 
 
-**Inputs**  
+**Inputs**
 - lit: The input string to unquote
 - lit: The input string to unquote
 - allocator: (default: context.allocator)
 - allocator: (default: context.allocator)
 
 
 WARNING: This procedure gives unexpected results if the quotes are not the first and last characters.
 WARNING: This procedure gives unexpected results if the quotes are not the first and last characters.
 
 
-Example:  
+Example:
 
 
 	import "core:fmt"
 	import "core:fmt"
 	import "core:strconv"
 	import "core:strconv"
@@ -1947,10 +1947,10 @@ Example:
 		src="The raven \'Huginn\' is black."
 		src="The raven \'Huginn\' is black."
 		s, allocated, ok = strconv.unquote_string(src) // Will produce undesireable results
 		s, allocated, ok = strconv.unquote_string(src) // Will produce undesireable results
 		fmt.println(src)
 		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."
 	"The raven Huginn is black."
 	Unquoted: <The raven Huginn is black.>, alloc:false, ok:true
 	Unquoted: <The raven Huginn is black.>, alloc:false, ok:true
@@ -1961,7 +1961,7 @@ Output:
 	The raven 'Huginn' is black.
 	The raven 'Huginn' is black.
 	Unquoted: <he raven 'Huginn' is black>, alloc:false, ok:true
 	Unquoted: <he raven 'Huginn' is black>, alloc:false, ok:true
 
 
-**Returns**  
+**Returns**
 - res: The resulting unquoted string
 - res: The resulting unquoted string
 - allocated: A boolean indicating if the resulting string was allocated using the provided allocator
 - allocated: A boolean indicating if the resulting string was allocated using the provided allocator
 - success: A boolean indicating whether the unquoting was successful
 - 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
 			return s, false, true
 		}
 		}
 	}
 	}
-	
+
 	context.allocator = allocator
 	context.allocator = allocator
 
 
 	buf_len := 3*len(s) / 2
 	buf_len := 3*len(s) / 2

+ 6 - 6
core/strings/builder.odin

@@ -675,7 +675,7 @@ Returns:
 */
 */
 write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) {
 write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) {
 	buf: [384]byte
 	buf: [384]byte
-	s := strconv.append_float(buf[:], f, fmt, prec, bit_size)
+	s := strconv.write_float(buf[:], f, fmt, prec, bit_size)
 	// If the result starts with a `+` then unless we always want signed results,
 	// If the result starts with a `+` then unless we always want signed results,
 	// we skip it unless it's followed by an `I` (because of +Inf).
 	// we skip it unless it's followed by an `I` (because of +Inf).
 	if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
 	if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
@@ -699,7 +699,7 @@ Returns:
 */
 */
 write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) {
 write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) {
 	buf: [384]byte
 	buf: [384]byte
-	s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
+	s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
 	if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
 	if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
 		s = s[1:]
 		s = s[1:]
 	}
 	}
@@ -739,7 +739,7 @@ Output:
 */
 */
 write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) {
 write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) {
 	buf: [384]byte
 	buf: [384]byte
-	s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
+	s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
 	if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
 	if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
 		s = s[1:]
 		s = s[1:]
 	}
 	}
@@ -761,7 +761,7 @@ Returns:
 */
 */
 write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) {
 write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) {
 	buf: [384]byte
 	buf: [384]byte
-	s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
+	s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
 	if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
 	if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
 		s = s[1:]
 		s = s[1:]
 	}
 	}
@@ -782,7 +782,7 @@ Returns:
 */
 */
 write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
 write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
 	buf: [32]byte
 	buf: [32]byte
-	s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil)
+	s := strconv.write_bits(buf[:], i, base, false, 64, strconv.digits, nil)
 	return write_string(b, s)
 	return write_string(b, s)
 }
 }
 /*
 /*
@@ -800,7 +800,7 @@ Returns:
 */
 */
 write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) {
 write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) {
 	buf: [32]byte
 	buf: [32]byte
-	s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
+	s := strconv.write_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
 	return write_string(b, s)
 	return write_string(b, s)
 }
 }
 /*
 /*

+ 82 - 64
core/strings/strings.odin

@@ -28,24 +28,7 @@ clone :: proc(s: string, allocator := context.allocator, loc := #caller_location
 	copy(c, s)
 	copy(c, s)
 	return string(c), nil
 	return string(c), nil
 }
 }
-/*
-Clones a string safely (returns early with an allocation error on failure)
-
-*Allocates Using Provided Allocator*
-
-Inputs:
-- s: The string to be cloned
-- allocator: (default: context.allocator)
-- loc: The caller location for debugging purposes (default: #caller_location)
 
 
-Returns:
-- res: The cloned string
-- err: An allocator error if one occured, `nil` otherwise
-*/
-@(deprecated="Prefer clone. It now returns an optional allocator error")
-clone_safe :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) {
-	return clone(s, allocator, loc)
-}
 /*
 /*
 Clones a string and appends a null-byte to make it a cstring
 Clones a string and appends a null-byte to make it a cstring
 
 
@@ -66,6 +49,7 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #call
 	c[len(s)] = 0
 	c[len(s)] = 0
 	return cstring(&c[0]), nil
 	return cstring(&c[0]), nil
 }
 }
+
 /*
 /*
 Transmutes a raw pointer into a string. Non-allocating.
 Transmutes a raw pointer into a string. Non-allocating.
 
 
@@ -81,6 +65,7 @@ Returns:
 string_from_ptr :: proc(ptr: ^byte, len: int) -> (res: string) {
 string_from_ptr :: proc(ptr: ^byte, len: int) -> (res: string) {
 	return transmute(string)mem.Raw_String{ptr, len}
 	return transmute(string)mem.Raw_String{ptr, len}
 }
 }
+
 /*
 /*
 Transmutes a raw pointer (null-terminated) into a string. Non-allocating. Searches for a null-byte from `0..<len`, otherwise `len` will be the end size
 Transmutes a raw pointer (null-terminated) into a string. Non-allocating. Searches for a null-byte from `0..<len`, otherwise `len` will be the end size
 
 
@@ -99,20 +84,7 @@ string_from_null_terminated_ptr :: proc "contextless" (ptr: [^]byte, len: int) -
 	s = truncate_to_byte(s, 0)
 	s = truncate_to_byte(s, 0)
 	return s
 	return s
 }
 }
-/*
-Gets the raw byte pointer for the start of a string `str`
 
 
-Inputs:
-- str: The input string
-
-Returns:
-- res: A pointer to the start of the string's bytes
-*/
-@(deprecated="Prefer the builtin raw_data.")
-ptr_from_string :: proc(str: string) -> (res: ^byte) {
-	d := transmute(mem.Raw_String)str
-	return d.data
-}
 /*
 /*
 Converts a string `str` to a cstring
 Converts a string `str` to a cstring
 
 
@@ -128,6 +100,7 @@ unsafe_string_to_cstring :: proc(str: string) -> (res: cstring) {
 	d := transmute(mem.Raw_String)str
 	d := transmute(mem.Raw_String)str
 	return cstring(d.data)
 	return cstring(d.data)
 }
 }
+
 /*
 /*
 Truncates a string `str` at the first occurrence of char/byte `b`
 Truncates a string `str` at the first occurrence of char/byte `b`
 
 
@@ -147,6 +120,7 @@ truncate_to_byte :: proc "contextless" (str: string, b: byte) -> (res: string) {
 	}
 	}
 	return str[:n]
 	return str[:n]
 }
 }
+
 /*
 /*
 Truncates a string `str` at the first occurrence of rune `r` as a slice of the original, entire string if not found
 Truncates a string `str` at the first occurrence of rune `r` as a slice of the original, entire string if not found
 
 
@@ -164,6 +138,7 @@ truncate_to_rune :: proc(str: string, r: rune) -> (res: string) {
 	}
 	}
 	return str[:n]
 	return str[:n]
 }
 }
+
 /*
 /*
 Clones a byte array `s` and appends a null-byte
 Clones a byte array `s` and appends a null-byte
 
 
@@ -184,6 +159,7 @@ clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #call
 	c[len(s)] = 0
 	c[len(s)] = 0
 	return string(c[:len(s)]), nil
 	return string(c[:len(s)]), nil
 }
 }
+
 /*
 /*
 Clones a cstring `s` as a string
 Clones a cstring `s` as a string
 
 
@@ -201,6 +177,7 @@ Returns:
 clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
 clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
 	return clone(string(s), allocator, loc)
 	return clone(string(s), allocator, loc)
 }
 }
+
 /*
 /*
 Clones a string from a byte pointer `ptr` and a byte length `len`
 Clones a string from a byte pointer `ptr` and a byte length `len`
 
 
@@ -222,6 +199,7 @@ clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc
 	s := string_from_ptr(ptr, len)
 	s := string_from_ptr(ptr, len)
 	return clone(s, allocator, loc)
 	return clone(s, allocator, loc)
 }
 }
+
 // Overloaded procedure to clone from a string, `[]byte`, `cstring` or a `^byte` + length
 // Overloaded procedure to clone from a string, `[]byte`, `cstring` or a `^byte` + length
 clone_from :: proc{
 clone_from :: proc{
 	clone,
 	clone,
@@ -229,6 +207,7 @@ clone_from :: proc{
 	clone_from_cstring,
 	clone_from_cstring,
 	clone_from_ptr,
 	clone_from_ptr,
 }
 }
+
 /*
 /*
 Clones a string from a null-terminated cstring `ptr` and a byte length `len`
 Clones a string from a null-terminated cstring `ptr` and a byte length `len`
 
 
@@ -251,6 +230,7 @@ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.
 	s = truncate_to_byte(s, 0)
 	s = truncate_to_byte(s, 0)
 	return clone(s, allocator, loc)
 	return clone(s, allocator, loc)
 }
 }
+
 /*
 /*
 Compares two strings, returning a value representing which one comes first lexicographically.
 Compares two strings, returning a value representing which one comes first lexicographically.
 -1 for `lhs`; 1 for `rhs`, or 0 if they are equal.
 -1 for `lhs`; 1 for `rhs`, or 0 if they are equal.
@@ -265,6 +245,7 @@ Returns:
 compare :: proc "contextless" (lhs, rhs: string) -> (result: int) {
 compare :: proc "contextless" (lhs, rhs: string) -> (result: int) {
 	return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs)
 	return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs)
 }
 }
+
 /*
 /*
 Checks if rune `r` in the string `s`
 Checks if rune `r` in the string `s`
 
 
@@ -283,6 +264,7 @@ contains_rune :: proc(s: string, r: rune) -> (result: bool) {
 	}
 	}
 	return false
 	return false
 }
 }
+
 /*
 /*
 Returns true when the string `substr` is contained inside the string `s`
 Returns true when the string `substr` is contained inside the string `s`
 
 
@@ -314,6 +296,7 @@ Output:
 contains :: proc(s, substr: string) -> (res: bool) {
 contains :: proc(s, substr: string) -> (res: bool) {
 	return index(s, substr) >= 0
 	return index(s, substr) >= 0
 }
 }
+
 /*
 /*
 Returns `true` when the string `s` contains any of the characters inside the string `chars`
 Returns `true` when the string `s` contains any of the characters inside the string `chars`
 
 
@@ -386,6 +369,7 @@ Output:
 rune_count :: proc(s: string) -> (res: int) {
 rune_count :: proc(s: string) -> (res: int) {
 	return utf8.rune_count_in_string(s)
 	return utf8.rune_count_in_string(s)
 }
 }
+
 /*
 /*
 Returns whether the strings `u` and `v` are the same alpha characters, ignoring different casings
 Returns whether the strings `u` and `v` are the same alpha characters, ignoring different casings
 Works with UTF-8 string content
 Works with UTF-8 string content
@@ -508,6 +492,7 @@ prefix_length :: proc "contextless" (a, b: string) -> (n: int) {
 	}
 	}
 	return
 	return
 }
 }
+
 /*
 /*
 Returns the common prefix between strings `a` and `b`
 Returns the common prefix between strings `a` and `b`
 
 
@@ -540,6 +525,7 @@ Output:
 common_prefix :: proc(a, b: string) -> string {
 common_prefix :: proc(a, b: string) -> string {
 	return a[:prefix_length(a, b)]
 	return a[:prefix_length(a, b)]
 }
 }
+
 /*
 /*
 Determines if a string `s` starts with a given `prefix`
 Determines if a string `s` starts with a given `prefix`
 
 
@@ -661,24 +647,7 @@ join :: proc(a: []string, sep: string, allocator := context.allocator, loc := #c
 	}
 	}
 	return string(b), nil
 	return string(b), nil
 }
 }
-/*
-Joins a slice of strings `a` with a `sep` string, returns an error on allocation failure
-
-*Allocates Using Provided Allocator*
 
 
-Inputs:
-- a: A slice of strings to join
-- sep: The separator string
-- allocator: (default is context.allocator)
-
-Returns:
-- str: A combined string from the slice of strings `a` separated with the `sep` string
-- err: An allocator error if one occured, `nil` otherwise
-*/
-@(deprecated="Prefer join. It now returns an optional allocator error")
-join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) {
-	return join(a, sep, allocator)
-}
 /*
 /*
 Returns a combined string from the slice of strings `a` without a separator
 Returns a combined string from the slice of strings `a` without a separator
 
 
@@ -723,22 +692,6 @@ concatenate :: proc(a: []string, allocator := context.allocator, loc := #caller_
 	}
 	}
 	return string(b), nil
 	return string(b), nil
 }
 }
-/*
-Returns a combined string from the slice of strings `a` without a separator, or an error if allocation fails
-
-*Allocates Using Provided Allocator*
-
-Inputs:
-- a: A slice of strings to concatenate
-- allocator: (default is context.allocator)
-
-Returns:
-The concatenated string, and an error if allocation fails
-*/
-@(deprecated="Prefer concatenate. It now returns an optional allocator error")
-concatenate_safe :: proc(a: []string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) {
-	return concatenate(a, allocator)
-}
 
 
 /*
 /*
 Returns a substring of the input string `s` with the specified rune offset and length
 Returns a substring of the input string `s` with the specified rune offset and length
@@ -901,6 +854,7 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato
 
 
 	return res[:i+1], nil
 	return res[:i+1], nil
 }
 }
+
 /*
 /*
 Splits a string into parts based on a separator.
 Splits a string into parts based on a separator.
 
 
@@ -936,6 +890,7 @@ Output:
 split :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
 split :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
 	return _split(s, sep, 0, -1, allocator)
 	return _split(s, sep, 0, -1, allocator)
 }
 }
+
 /*
 /*
 Splits a string into parts based on a separator. If n < count of seperators, the remainder of the string is returned in the last entry.
 Splits a string into parts based on a separator. If n < count of seperators, the remainder of the string is returned in the last entry.
 
 
@@ -972,6 +927,7 @@ Output:
 split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
 split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
 	return _split(s, sep, 0, n, allocator)
 	return _split(s, sep, 0, n, allocator)
 }
 }
+
 /*
 /*
 Splits a string into parts after the separator, retaining it in the substrings.
 Splits a string into parts after the separator, retaining it in the substrings.
 
 
@@ -1007,6 +963,7 @@ Output:
 split_after :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
 split_after :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
 	return _split(s, sep, len(sep), -1, allocator)
 	return _split(s, sep, len(sep), -1, allocator)
 }
 }
+
 /*
 /*
 Splits a string into a total of `n` parts after the separator.
 Splits a string into a total of `n` parts after the separator.
 
 
@@ -1043,6 +1000,7 @@ Output:
 split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
 split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
 	return _split(s, sep, len(sep), n, allocator)
 	return _split(s, sep, len(sep), n, allocator)
 }
 }
+
 /*
 /*
 Searches for the first occurrence of `sep` in the given string and returns the substring
 Searches for the first occurrence of `sep` in the given string and returns the substring
 up to (but not including) the separator, as well as a boolean indicating success.
 up to (but not including) the separator, as well as a boolean indicating success.
@@ -1083,6 +1041,7 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string,
 	}
 	}
 	return
 	return
 }
 }
+
 /*
 /*
 Splits the input string by the byte separator in an iterator fashion.
 Splits the input string by the byte separator in an iterator fashion.
 
 
@@ -1129,6 +1088,7 @@ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) {
 	}
 	}
 	return
 	return
 }
 }
+
 /*
 /*
 Splits the input string by the separator string in an iterator fashion.
 Splits the input string by the separator string in an iterator fashion.
 
 
@@ -1164,6 +1124,7 @@ Output:
 split_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) {
 split_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) {
 	return _split_iterator(s, sep, 0)
 	return _split_iterator(s, sep, 0)
 }
 }
+
 /*
 /*
 Splits the input string after every separator string in an iterator fashion.
 Splits the input string after every separator string in an iterator fashion.
 
 
@@ -1199,6 +1160,7 @@ Output:
 split_after_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) {
 split_after_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) {
 	return _split_iterator(s, sep, len(sep))
 	return _split_iterator(s, sep, len(sep))
 }
 }
+
 /*
 /*
 Trims the carriage return character from the end of the input string.
 Trims the carriage return character from the end of the input string.
 
 
@@ -1220,6 +1182,7 @@ _trim_cr :: proc(s: string) -> (res: string) {
 	}
 	}
 	return s
 	return s
 }
 }
+
 /*
 /*
 Splits the input string at every line break `\n`.
 Splits the input string at every line break `\n`.
 
 
@@ -1257,6 +1220,7 @@ split_lines :: proc(s: string, allocator := context.allocator) -> (res: []string
 	}
 	}
 	return lines, nil
 	return lines, nil
 }
 }
+
 /*
 /*
 Splits the input string at every line break `\n` for `n` parts.
 Splits the input string at every line break `\n` for `n` parts.
 
 
@@ -1297,6 +1261,7 @@ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> (res
 	}
 	}
 	return lines, nil
 	return lines, nil
 }
 }
+
 /*
 /*
 Splits the input string at every line break `\n` leaving the `\n` in the resulting strings.
 Splits the input string at every line break `\n` leaving the `\n` in the resulting strings.
 
 
@@ -1336,6 +1301,7 @@ split_lines_after :: proc(s: string, allocator := context.allocator) -> (res: []
 	}
 	}
 	return lines, nil
 	return lines, nil
 }
 }
+
 /*
 /*
 Splits the input string at every line break `\n` leaving the `\n` in the resulting strings.
 Splits the input string at every line break `\n` leaving the `\n` in the resulting strings.
 Only runs for n parts.
 Only runs for n parts.
@@ -1377,6 +1343,7 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -
 	}
 	}
 	return lines, nil
 	return lines, nil
 }
 }
+
 /*
 /*
 Splits the input string at every line break `\n`.
 Splits the input string at every line break `\n`.
 Returns the current split string every iteration until the string is consumed.
 Returns the current split string every iteration until the string is consumed.
@@ -1411,6 +1378,7 @@ split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
 	line = _split_iterator(s, sep, 0) or_return
 	line = _split_iterator(s, sep, 0) or_return
 	return _trim_cr(line), true
 	return _trim_cr(line), true
 }
 }
+
 /*
 /*
 Splits the input string at every line break `\n`.
 Splits the input string at every line break `\n`.
 Returns the current split string with line breaks included every iteration until the string is consumed.
 Returns the current split string with line breaks included every iteration until the string is consumed.
@@ -1448,6 +1416,7 @@ split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
 	line = _split_iterator(s, sep, len(sep)) or_return
 	line = _split_iterator(s, sep, len(sep)) or_return
 	return _trim_cr(line), true
 	return _trim_cr(line), true
 }
 }
+
 /*
 /*
 Returns the byte offset of the first byte `c` in the string s it finds, -1 when not found.
 Returns the byte offset of the first byte `c` in the string s it finds, -1 when not found.
 NOTE: Can't find UTF-8 based runes.
 NOTE: Can't find UTF-8 based runes.
@@ -1482,6 +1451,7 @@ Output:
 index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) {
 index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) {
 	return #force_inline bytes.index_byte(transmute([]u8)s, c)
 	return #force_inline bytes.index_byte(transmute([]u8)s, c)
 }
 }
+
 /*
 /*
 Returns the byte offset of the last byte `c` in the string `s`, -1 when not found.
 Returns the byte offset of the last byte `c` in the string `s`, -1 when not found.
 
 
@@ -1517,6 +1487,7 @@ Output:
 last_index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) {
 last_index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) {
 	return #force_inline bytes.last_index_byte(transmute([]u8)s, c)
 	return #force_inline bytes.last_index_byte(transmute([]u8)s, c)
 }
 }
+
 /*
 /*
 Returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found.
 Returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found.
 Invalid runes return -1
 Invalid runes return -1
@@ -1657,6 +1628,7 @@ index :: proc "contextless" (s, substr: string) -> (res: int) {
 	}
 	}
 	return -1
 	return -1
 }
 }
+
 /*
 /*
 Returns the last byte offset of the string `substr` in the string `s`, -1 when not found.
 Returns the last byte offset of the string `substr` in the string `s`, -1 when not found.
 
 
@@ -1734,6 +1706,7 @@ last_index :: proc(s, substr: string) -> (res: int) {
 	}
 	}
 	return -1
 	return -1
 }
 }
+
 /*
 /*
 Returns the index of any first char of `chars` found in `s`, -1 if not found.
 Returns the index of any first char of `chars` found in `s`, -1 if not found.
 
 
@@ -1797,6 +1770,7 @@ index_any :: proc(s, chars: string) -> (res: int) {
 	}
 	}
 	return -1
 	return -1
 }
 }
+
 /*
 /*
 Finds the last occurrence of any character in `chars` within `s`. Iterates in reverse.
 Finds the last occurrence of any character in `chars` within `s`. Iterates in reverse.
 
 
@@ -1878,6 +1852,7 @@ last_index_any :: proc(s, chars: string) -> (res: int) {
 	}
 	}
 	return -1
 	return -1
 }
 }
+
 /*
 /*
 Finds the first occurrence of any substring in `substrs` within `s`
 Finds the first occurrence of any substring in `substrs` within `s`
 
 
@@ -1919,6 +1894,7 @@ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) {
 	}
 	}
 	return
 	return
 }
 }
+
 /*
 /*
 Counts the number of non-overlapping occurrences of `substr` in `s`
 Counts the number of non-overlapping occurrences of `substr` in `s`
 
 
@@ -1985,6 +1961,7 @@ count :: proc(s, substr: string) -> (res: int) {
 	}
 	}
 	return n
 	return n
 }
 }
+
 /*
 /*
 Repeats the string `s` `count` times, concatenating the result
 Repeats the string `s` `count` times, concatenating the result
 
 
@@ -2030,6 +2007,7 @@ repeat :: proc(s: string, count: int, allocator := context.allocator, loc := #ca
 	}
 	}
 	return string(b), nil
 	return string(b), nil
 }
 }
+
 /*
 /*
 Replaces all occurrences of `old` in `s` with `new`
 Replaces all occurrences of `old` in `s` with `new`
 
 
@@ -2063,9 +2041,11 @@ Output:
 	zzzz true
 	zzzz true
 
 
 */
 */
+
 replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
 replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
 	return replace(s, old, new, -1, allocator)
 	return replace(s, old, new, -1, allocator)
 }
 }
+
 /*
 /*
 Replaces n instances of old in the string s with the new string
 Replaces n instances of old in the string s with the new string
 
 
@@ -2144,6 +2124,7 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator, loc
 	output = string(t[0:w])
 	output = string(t[0:w])
 	return
 	return
 }
 }
+
 /*
 /*
 Removes the key string `n` times from the `s` string
 Removes the key string `n` times from the `s` string
 
 
@@ -2182,6 +2163,7 @@ Output:
 remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) {
 remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) {
 	return replace(s, key, "", n, allocator)
 	return replace(s, key, "", n, allocator)
 }
 }
+
 /*
 /*
 Removes all the `key` string instances from the `s` string
 Removes all the `key` string instances from the `s` string
 
 
@@ -2217,6 +2199,7 @@ Output:
 remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
 remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
 	return remove(s, key, -1, allocator)
 	return remove(s, key, -1, allocator)
 }
 }
+
 // Returns true if is an ASCII space character ('\t', '\n', '\v', '\f', '\r', ' ')
 // Returns true if is an ASCII space character ('\t', '\n', '\v', '\f', '\r', ' ')
 @(private) _ascii_space := [256]bool{'\t' = true, '\n' = true, '\v' = true, '\f' = true, '\r' = true, ' ' = true}
 @(private) _ascii_space := [256]bool{'\t' = true, '\n' = true, '\v' = true, '\f' = true, '\r' = true, ' ' = true}
 
 
@@ -2320,6 +2303,7 @@ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int
 	}
 	}
 	return -1
 	return -1
 }
 }
+
 // Same as `index_proc`, but the procedure p takes a raw pointer for state
 // Same as `index_proc`, but the procedure p takes a raw pointer for state
 index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) {
 index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) {
 	for r, i in s {
 	for r, i in s {
@@ -2329,6 +2313,7 @@ index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: r
 	}
 	}
 	return -1
 	return -1
 }
 }
+
 // Finds the index of the *last* rune in the string s for which the procedure p returns the same value as truth
 // Finds the index of the *last* rune in the string s for which the procedure p returns the same value as truth
 last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int) {
 last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int) {
 	// TODO(bill): Probably use Rabin-Karp Search
 	// TODO(bill): Probably use Rabin-Karp Search
@@ -2341,6 +2326,7 @@ last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res
 	}
 	}
 	return -1
 	return -1
 }
 }
+
 // Same as `index_proc_with_state`, runs through the string in reverse
 // Same as `index_proc_with_state`, runs through the string in reverse
 last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) {
 last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) {
 	// TODO(bill): Probably use Rabin-Karp Search
 	// TODO(bill): Probably use Rabin-Karp Search
@@ -2353,6 +2339,7 @@ last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta
 	}
 	}
 	return -1
 	return -1
 }
 }
+
 /*
 /*
 Trims the input string `s` from the left until the procedure `p` returns false
 Trims the input string `s` from the left until the procedure `p` returns false
 
 
@@ -2387,6 +2374,7 @@ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) {
 	}
 	}
 	return s[i:]
 	return s[i:]
 }
 }
+
 /*
 /*
 Trims the input string `s` from the left until the procedure `p` with state returns false
 Trims the input string `s` from the left until the procedure `p` with state returns false
 
 
@@ -2405,6 +2393,7 @@ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, stat
 	}
 	}
 	return s[i:]
 	return s[i:]
 }
 }
+
 /*
 /*
 Trims the input string `s` from the right until the procedure `p` returns `false`
 Trims the input string `s` from the right until the procedure `p` returns `false`
 
 
@@ -2442,6 +2431,7 @@ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) {
 	}
 	}
 	return s[0:i]
 	return s[0:i]
 }
 }
+
 /*
 /*
 Trims the input string `s` from the right until the procedure `p` with state returns `false`
 Trims the input string `s` from the right until the procedure `p` with state returns `false`
 
 
@@ -2463,6 +2453,7 @@ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta
 	}
 	}
 	return s[0:i]
 	return s[0:i]
 }
 }
+
 // Procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison
 // Procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison
 is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) {
 is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) {
 	cutset := (^string)(state)^
 	cutset := (^string)(state)^
@@ -2473,6 +2464,7 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) {
 	}
 	}
 	return false
 	return false
 }
 }
+
 /*
 /*
 Trims the cutset string from the `s` string
 Trims the cutset string from the `s` string
 
 
@@ -2490,6 +2482,7 @@ trim_left :: proc(s: string, cutset: string) -> (res: string) {
 	state := cutset
 	state := cutset
 	return trim_left_proc_with_state(s, is_in_cutset, &state)
 	return trim_left_proc_with_state(s, is_in_cutset, &state)
 }
 }
+
 /*
 /*
 Trims the cutset string from the `s` string from the right
 Trims the cutset string from the `s` string from the right
 
 
@@ -2507,6 +2500,7 @@ trim_right :: proc(s: string, cutset: string) -> (res: string) {
 	state := cutset
 	state := cutset
 	return trim_right_proc_with_state(s, is_in_cutset, &state)
 	return trim_right_proc_with_state(s, is_in_cutset, &state)
 }
 }
+
 /*
 /*
 Trims the cutset string from the `s` string, both from left and right
 Trims the cutset string from the `s` string, both from left and right
 
 
@@ -2520,6 +2514,7 @@ Returns:
 trim :: proc(s: string, cutset: string) -> (res: string) {
 trim :: proc(s: string, cutset: string) -> (res: string) {
 	return trim_right(trim_left(s, cutset), cutset)
 	return trim_right(trim_left(s, cutset), cutset)
 }
 }
+
 /*
 /*
 Trims until a valid non-space rune from the left, "\t\txyz\t\t" -> "xyz\t\t"
 Trims until a valid non-space rune from the left, "\t\txyz\t\t" -> "xyz\t\t"
 
 
@@ -2532,6 +2527,7 @@ Returns:
 trim_left_space :: proc(s: string) -> (res: string) {
 trim_left_space :: proc(s: string) -> (res: string) {
 	return trim_left_proc(s, is_space)
 	return trim_left_proc(s, is_space)
 }
 }
+
 /*
 /*
 Trims from the right until a valid non-space rune, "\t\txyz\t\t" -> "\t\txyz"
 Trims from the right until a valid non-space rune, "\t\txyz\t\t" -> "\t\txyz"
 
 
@@ -2544,6 +2540,7 @@ Returns:
 trim_right_space :: proc(s: string) -> (res: string) {
 trim_right_space :: proc(s: string) -> (res: string) {
 	return trim_right_proc(s, is_space)
 	return trim_right_proc(s, is_space)
 }
 }
+
 /*
 /*
 Trims from both sides until a valid non-space rune, "\t\txyz\t\t" -> "xyz"
 Trims from both sides until a valid non-space rune, "\t\txyz\t\t" -> "xyz"
 
 
@@ -2556,6 +2553,7 @@ Returns:
 trim_space :: proc(s: string) -> (res: string) {
 trim_space :: proc(s: string) -> (res: string) {
 	return trim_right_space(trim_left_space(s))
 	return trim_right_space(trim_left_space(s))
 }
 }
+
 /*
 /*
 Trims null runes from the left, "\x00\x00testing\x00\x00" -> "testing\x00\x00"
 Trims null runes from the left, "\x00\x00testing\x00\x00" -> "testing\x00\x00"
 
 
@@ -2568,6 +2566,7 @@ Returns:
 trim_left_null :: proc(s: string) -> (res: string) {
 trim_left_null :: proc(s: string) -> (res: string) {
 	return trim_left_proc(s, is_null)
 	return trim_left_proc(s, is_null)
 }
 }
+
 /*
 /*
 Trims null runes from the right, "\x00\x00testing\x00\x00" -> "\x00\x00testing"
 Trims null runes from the right, "\x00\x00testing\x00\x00" -> "\x00\x00testing"
 
 
@@ -2580,6 +2579,7 @@ Returns:
 trim_right_null :: proc(s: string) -> (res: string) {
 trim_right_null :: proc(s: string) -> (res: string) {
 	return trim_right_proc(s, is_null)
 	return trim_right_proc(s, is_null)
 }
 }
+
 /*
 /*
 Trims null runes from both sides, "\x00\x00testing\x00\x00" -> "testing"
 Trims null runes from both sides, "\x00\x00testing\x00\x00" -> "testing"
 
 
@@ -2591,6 +2591,7 @@ Returns:
 trim_null :: proc(s: string) -> (res: string) {
 trim_null :: proc(s: string) -> (res: string) {
 	return trim_right_null(trim_left_null(s))
 	return trim_right_null(trim_left_null(s))
 }
 }
+
 /*
 /*
 Trims a `prefix` string from the start of the `s` string and returns the trimmed string
 Trims a `prefix` string from the start of the `s` string and returns the trimmed string
 
 
@@ -2623,6 +2624,7 @@ trim_prefix :: proc(s, prefix: string) -> (res: string) {
 	}
 	}
 	return s
 	return s
 }
 }
+
 /*
 /*
 Trims a `suffix` string from the end of the `s` string and returns the trimmed string
 Trims a `suffix` string from the end of the `s` string and returns the trimmed string
 
 
@@ -2655,6 +2657,7 @@ trim_suffix :: proc(s, suffix: string) -> (res: string) {
 	}
 	}
 	return s
 	return s
 }
 }
+
 /*
 /*
 Splits the input string `s` by all possible `substrs` and returns an allocated array of strings
 Splits the input string `s` by all possible `substrs` and returns an allocated array of strings
 
 
@@ -2727,6 +2730,7 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator
 	assert(len(results) == n)
 	assert(len(results) == n)
 	return results[:], nil
 	return results[:], nil
 }
 }
+
 /*
 /*
 Splits the input string `s` by all possible `substrs` in an iterator fashion. The full string is returned if no match.
 Splits the input string `s` by all possible `substrs` in an iterator fashion. The full string is returned if no match.
 
 
@@ -2786,6 +2790,7 @@ split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok:
 	ok = true
 	ok = true
 	return
 	return
 }
 }
+
 /*
 /*
 Replaces invalid UTF-8 characters in the input string with a specified replacement string. Adjacent invalid bytes are only replaced once.
 Replaces invalid UTF-8 characters in the input string with a specified replacement string. Adjacent invalid bytes are only replaced once.
 
 
@@ -2846,6 +2851,7 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) ->
 
 
 	return to_string(b), nil
 	return to_string(b), nil
 }
 }
+
 /*
 /*
 Reverses the input string `s`
 Reverses the input string `s`
 
 
@@ -2889,6 +2895,7 @@ reverse :: proc(s: string, allocator := context.allocator, loc := #caller_locati
 	}
 	}
 	return string(buf), nil
 	return string(buf), nil
 }
 }
+
 /*
 /*
 Expands the input string by replacing tab characters with spaces to align to a specified tab size
 Expands the input string by replacing tab characters with spaces to align to a specified tab size
 
 
@@ -2920,6 +2927,7 @@ Output:
 	abc1    abc2    abc3
 	abc1    abc2    abc3
 
 
 */
 */
+
 expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
 expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
 	if tab_size <= 0 {
 	if tab_size <= 0 {
 		panic("tab size must be positive")
 		panic("tab size must be positive")
@@ -2961,6 +2969,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
 
 
 	return to_string(b), nil
 	return to_string(b), nil
 }
 }
+
 /*
 /*
 Splits the input string `str` by the separator `sep` string and returns 3 parts. The values are slices of the original string.
 Splits the input string `str` by the separator `sep` string and returns 3 parts. The values are slices of the original string.
 
 
@@ -3011,8 +3020,10 @@ partition :: proc(str, sep: string) -> (head, match, tail: string) {
 	tail = str[i+len(sep):]
 	tail = str[i+len(sep):]
 	return
 	return
 }
 }
+
 // Alias for centre_justify
 // Alias for centre_justify
 center_justify :: centre_justify // NOTE(bill): Because Americans exist
 center_justify :: centre_justify // NOTE(bill): Because Americans exist
+
 /*
 /*
 Centers the input string within a field of specified length by adding pad string on both sides, if its length is less than the target length.
 Centers the input string within a field of specified length by adding pad string on both sides, if its length is less than the target length.
 
 
@@ -3048,6 +3059,7 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
 
 
 	return to_string(b), nil
 	return to_string(b), nil
 }
 }
+
 /*
 /*
 Left-justifies the input string within a field of specified length by adding pad string on the right side, if its length is less than the target length.
 Left-justifies the input string within a field of specified length by adding pad string on the right side, if its length is less than the target length.
 
 
@@ -3082,6 +3094,7 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
 
 
 	return to_string(b), nil
 	return to_string(b), nil
 }
 }
+
 /*
 /*
 Right-justifies the input string within a field of specified length by adding pad string on the left side, if its length is less than the target length.
 Right-justifies the input string within a field of specified length by adding pad string on the left side, if its length is less than the target length.
 
 
@@ -3116,6 +3129,7 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex
 
 
 	return to_string(b), nil
 	return to_string(b), nil
 }
 }
+
 /*
 /*
 Writes a given pad string a specified number of times to an `io.Writer`
 Writes a given pad string a specified number of times to an `io.Writer`
 
 
@@ -3142,6 +3156,7 @@ write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) {
 		p = p[width:]
 		p = p[width:]
 	}
 	}
 }
 }
+
 /*
 /*
 Splits a string into a slice of substrings at each instance of one or more consecutive white space characters, as defined by `unicode.is_space`
 Splits a string into a slice of substrings at each instance of one or more consecutive white space characters, as defined by `unicode.is_space`
 
 
@@ -3203,6 +3218,7 @@ fields :: proc(s: string, allocator := context.allocator, loc := #caller_locatio
 	}
 	}
 	return a, nil
 	return a, nil
 }
 }
+
 /*
 /*
 Splits a string into a slice of substrings at each run of unicode code points `r` satisfying the predicate `f(r)`
 Splits a string into a slice of substrings at each run of unicode code points `r` satisfying the predicate `f(r)`
 
 
@@ -3245,6 +3261,7 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc
 
 
 	return substrings[:], nil
 	return substrings[:], nil
 }
 }
+
 /*
 /*
 Retrieves the first non-space substring from a mutable string reference and advances the reference. `s` is advanced from any space after the substring, or be an empty string if the substring was the remaining characters
 Retrieves the first non-space substring from a mutable string reference and advances the reference. `s` is advanced from any space after the substring, or be an empty string if the substring was the remaining characters
 
 
@@ -3283,6 +3300,7 @@ fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) {
 	s^ = s[len(s):]
 	s^ = s[len(s):]
 	return
 	return
 }
 }
+
 /*
 /*
 Computes the Levenshtein edit distance between two strings
 Computes the Levenshtein edit distance between two strings
 
 
@@ -3460,4 +3478,4 @@ substring_to :: proc(s: string, rune_end: int) -> (sub: string, ok: bool) {
 	}
 	}
 
 
 	return internal_substring(s, -1, rune_end)
 	return internal_substring(s, -1, rune_end)
-}
+}

+ 0 - 1
core/sys/darwin/Foundation/NSApplication.odin

@@ -82,7 +82,6 @@ Application_setActivationPolicy :: proc "c" (self: ^Application, activationPolic
 // NOTE: this is technically deprecated but still actively used (Sokol, glfw, SDL, etc.)
 // NOTE: this is technically deprecated but still actively used (Sokol, glfw, SDL, etc.)
 // and has no clear alternative although `activate` is what Apple tells you to use,
 // and has no clear alternative although `activate` is what Apple tells you to use,
 // that does not work the same way.
 // that does not work the same way.
-// @(deprecated="Use NSApplication method activate instead.")
 @(objc_type=Application, objc_name="activateIgnoringOtherApps")
 @(objc_type=Application, objc_name="activateIgnoringOtherApps")
 Application_activateIgnoringOtherApps :: proc "c" (self: ^Application, ignoreOtherApps: BOOL) {
 Application_activateIgnoringOtherApps :: proc "c" (self: ^Application, ignoreOtherApps: BOOL) {
 	msgSend(nil, self, "activateIgnoringOtherApps:", ignoreOtherApps)
 	msgSend(nil, self, "activateIgnoringOtherApps:", ignoreOtherApps)

+ 1 - 1
core/sys/linux/sys.odin

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

+ 0 - 8
core/sys/windows/util.odin

@@ -75,14 +75,6 @@ LANGIDFROMLCID :: #force_inline proc "contextless" (lcid: LCID) -> LANGID {
 	return LANGID(lcid)
 	return LANGID(lcid)
 }
 }
 
 
-// this one gave me trouble as it do not mask the values.
-// the _ in the name is also off comparing to the c code
-// i can't find any usage in the odin repo
-@(deprecated = "use MAKEWORD")
-MAKE_WORD :: #force_inline proc "contextless" (x, y: WORD) -> WORD {
-	return x << 8 | y
-}
-
 utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
 utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
 	if len(s) < 1 {
 	if len(s) < 1 {
 		return nil
 		return nil

+ 3 - 3
core/sys/windows/ws2_32.odin

@@ -243,9 +243,9 @@ foreign ws2_32 {
 	// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohs)
 	// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohs)
 	ntohs :: proc(netshort: c_ushort) -> c_ushort ---
 	ntohs :: proc(netshort: c_ushort) -> c_ushort ---
 	// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htonl)
 	// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htonl)
-	@(deprecated="Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types")
+	// Prefer using endian-specific integers instead, https://odin-lang.org/docs/overview/#basic-types
 	htonl :: proc(hostlong: c_ulong) -> c_ulong ---
 	htonl :: proc(hostlong: c_ulong) -> c_ulong ---
 	// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htons)
 	// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htons)
-	@(deprecated="Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types")
+	// Prefer using endian-specific integers instead, https://odin-lang.org/docs/overview/#basic-types
 	htons :: proc(hostshort: c_ushort) -> c_ushort ---
 	htons :: proc(hostshort: c_ushort) -> c_ushort ---
-}
+}

+ 1 - 6
core/time/timezone/tzif.odin

@@ -577,12 +577,7 @@ parse_tzif :: proc(_buffer: []u8, region_name: string, allocator := context.allo
 	footer_str := string(buffer[:end_idx])
 	footer_str := string(buffer[:end_idx])
 
 
 	// UTC is a special case, we don't need to alloc
 	// UTC is a special case, we don't need to alloc
-	if len(local_time_types) == 1 {
-		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
 		return nil, true
 	}
 	}
 
 

+ 2 - 0
src/check_builtin.cpp

@@ -41,6 +41,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool
 	is_type_enum,
 	is_type_enum,
 	is_type_proc,
 	is_type_proc,
 	is_type_bit_set,
 	is_type_bit_set,
+	is_type_bit_field,
 	is_type_simd_vector,
 	is_type_simd_vector,
 	is_type_matrix,
 	is_type_matrix,
 
 
@@ -6081,6 +6082,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 	case BuiltinProc_type_is_enum:
 	case BuiltinProc_type_is_enum:
 	case BuiltinProc_type_is_proc:
 	case BuiltinProc_type_is_proc:
 	case BuiltinProc_type_is_bit_set:
 	case BuiltinProc_type_is_bit_set:
+	case BuiltinProc_type_is_bit_field:
 	case BuiltinProc_type_is_simd_vector:
 	case BuiltinProc_type_is_simd_vector:
 	case BuiltinProc_type_is_matrix:
 	case BuiltinProc_type_is_matrix:
 	case BuiltinProc_type_is_specialized_polymorphic_record:
 	case BuiltinProc_type_is_specialized_polymorphic_record:

+ 1 - 0
src/check_decl.cpp

@@ -1370,6 +1370,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
 	e->Procedure.has_instrumentation = has_instrumentation;
 	e->Procedure.has_instrumentation = has_instrumentation;
 
 
 	e->Procedure.no_sanitize_address = ac.no_sanitize_address;
 	e->Procedure.no_sanitize_address = ac.no_sanitize_address;
+	e->Procedure.no_sanitize_memory  = ac.no_sanitize_memory;
 
 
 	e->deprecated_message = ac.deprecated_message;
 	e->deprecated_message = ac.deprecated_message;
 	e->warning_message = ac.warning_message;
 	e->warning_message = ac.warning_message;

+ 14 - 0
src/check_stmt.cpp

@@ -1593,6 +1593,20 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
 			error_line("\tSuggestion: Was '#partial switch' wanted?\n");
 			error_line("\tSuggestion: Was '#partial switch' wanted?\n");
 		}
 		}
 	}
 	}
+
+	if (build_context.strict_style) {
+		Token stok = ss->token;
+		for_array(i, bs->stmts) {
+			Ast *stmt = bs->stmts[i];
+			if (stmt->kind != Ast_CaseClause) {
+				continue;
+			}
+			Token ctok = stmt->CaseClause.token;
+			if (ctok.pos.column > stok.pos.column) {
+				error(ctok, "With '-strict-style', 'case' statements must share the same column as the 'switch' token");
+			}
+		}
+	}
 }
 }
 
 
 gb_internal void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body)  {
 gb_internal void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body)  {

+ 9 - 3
src/checker.cpp

@@ -2054,7 +2054,7 @@ gb_internal void add_type_info_type(CheckerContext *c, Type *t) {
 }
 }
 
 
 gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
 gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
-	if (t == nullptr) {
+	if (t == nullptr || c == nullptr) {
 		return;
 		return;
 	}
 	}
 
 
@@ -3776,6 +3776,12 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 		}
 		}
 		ac->no_sanitize_address = true;
 		ac->no_sanitize_address = true;
 		return true;
 		return true;
+	} else if (name == "no_sanitize_memory") {
+		if (value != nullptr) {
+			error(value, "'%.*s' expects no parameter", LIT(name));
+		}
+		ac->no_sanitize_memory = true;
+		return true;
 	}
 	}
 	return false;
 	return false;
 }
 }
@@ -6672,7 +6678,7 @@ gb_internal void check_sort_init_and_fini_procedures(Checker *c) {
 gb_internal void add_type_info_for_type_definitions(Checker *c) {
 gb_internal void add_type_info_for_type_definitions(Checker *c) {
 	for_array(i, c->info.definitions) {
 	for_array(i, c->info.definitions) {
 		Entity *e = c->info.definitions[i];
 		Entity *e = c->info.definitions[i];
-		if (e->kind == Entity_TypeName && e->type != nullptr) {
+		if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) {
 			i64 align = type_align_of(e->type);
 			i64 align = type_align_of(e->type);
 			if (align > 0 && ptr_set_exists(&c->info.minimum_dependency_set, e)) {
 			if (align > 0 && ptr_set_exists(&c->info.minimum_dependency_set, e)) {
 				add_type_info_type(&c->builtin_ctx, e->type);
 				add_type_info_type(&c->builtin_ctx, e->type);
@@ -6794,7 +6800,7 @@ gb_internal void check_parsed_files(Checker *c) {
 	// NOTE(bill): Check for illegal cyclic type declarations
 	// NOTE(bill): Check for illegal cyclic type declarations
 	for_array(i, c->info.definitions) {
 	for_array(i, c->info.definitions) {
 		Entity *e = c->info.definitions[i];
 		Entity *e = c->info.definitions[i];
-		if (e->kind == Entity_TypeName && e->type != nullptr) {
+		if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) {
 			(void)type_align_of(e->type);
 			(void)type_align_of(e->type);
 		} else if (e->kind == Entity_Procedure) {
 		} else if (e->kind == Entity_Procedure) {
 			DeclInfo *decl = e->decl_info;
 			DeclInfo *decl = e->decl_info;

+ 1 - 0
src/checker.hpp

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

+ 2 - 0
src/checker_builtin_procs.hpp

@@ -240,6 +240,7 @@ BuiltinProc__type_begin,
 
 
 BuiltinProc__type_simple_boolean_begin,
 BuiltinProc__type_simple_boolean_begin,
 	BuiltinProc_type_is_boolean,
 	BuiltinProc_type_is_boolean,
+	BuiltinProc_type_is_bit_field,
 	BuiltinProc_type_is_integer,
 	BuiltinProc_type_is_integer,
 	BuiltinProc_type_is_rune,
 	BuiltinProc_type_is_rune,
 	BuiltinProc_type_is_float,
 	BuiltinProc_type_is_float,
@@ -630,6 +631,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 	{STR_LIT("type_is_enum"),              1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_enum"),              1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_proc"),              1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_proc"),              1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_bit_set"),           1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_bit_set"),           1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+	{STR_LIT("type_is_bit_field"),         1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_simd_vector"),       1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_simd_vector"),       1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_matrix"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("type_is_matrix"),            1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
 

+ 1 - 0
src/entity.cpp

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

+ 11 - 0
src/llvm_backend_const.cpp

@@ -1054,9 +1054,20 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb
 					}
 					}
 				}
 				}
 
 
+				res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
+				return res;
+			} else if (value.value_compound->tav.type == elem_type) {
+				// Compound is of array item type; expand its value to all items in array.
+				LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count);
+
+				for (isize i = 0; i < type->Array.count; i++) {
+					values[i] = lb_const_value(m, elem_type, value, cc).value;
+				}
+
 				res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
 				res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc);
 				return res;
 				return res;
 			} else {
 			} else {
+				// Assume that compound value is an array literal
 				GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count);
 				GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count);
 
 
 				LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count);
 				LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count);

+ 1 - 1
src/llvm_backend_proc.cpp

@@ -345,7 +345,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
 		if (build_context.sanitizer_flags & SanitizerFlag_Address && !entity->Procedure.no_sanitize_address) {
 		if (build_context.sanitizer_flags & SanitizerFlag_Address && !entity->Procedure.no_sanitize_address) {
 			lb_add_attribute_to_proc(m, p->value, "sanitize_address");
 			lb_add_attribute_to_proc(m, p->value, "sanitize_address");
 		}
 		}
-		if (build_context.sanitizer_flags & SanitizerFlag_Memory) {
+		if (build_context.sanitizer_flags & SanitizerFlag_Memory && !entity->Procedure.no_sanitize_memory) {
 			lb_add_attribute_to_proc(m, p->value, "sanitize_memory");
 			lb_add_attribute_to_proc(m, p->value, "sanitize_memory");
 		}
 		}
 		if (build_context.sanitizer_flags & SanitizerFlag_Thread) {
 		if (build_context.sanitizer_flags & SanitizerFlag_Thread) {

+ 52 - 52
src/main.cpp

@@ -1996,39 +1996,39 @@ gb_internal void show_timings(Checker *c, Timings *t) {
 
 
 	if (build_context.show_debug_messages && build_context.show_more_timings) {
 	if (build_context.show_debug_messages && build_context.show_more_timings) {
 		{
 		{
-			gb_printf("\n");
-			gb_printf("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;
 			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;
 			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 = {};
 			TimeStamp ts = {};
@@ -2041,16 +2041,16 @@ gb_internal void show_timings(Checker *c, Timings *t) {
 			GB_ASSERT(ts.label == "parse files");
 			GB_ASSERT(ts.label == "parse files");
 
 
 			f64 parse_time = time_stamp_as_s(ts, t->freq);
 			f64 parse_time = time_stamp_as_s(ts, t->freq);
-			gb_printf("Parse pass\n");
-			gb_printf("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 = {};
 			TimeStamp ts = {};
@@ -2071,27 +2071,27 @@ gb_internal void show_timings(Checker *c, Timings *t) {
 			ts.finish = ts_end.finish;
 			ts.finish = ts_end.finish;
 
 
 			f64 parse_time = time_stamp_as_s(ts, t->freq);
 			f64 parse_time = time_stamp_as_s(ts, t->freq);
-			gb_printf("Checker pass\n");
-			gb_printf("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;
 			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");
 		}
 		}
 	}
 	}
 }
 }

+ 2 - 2
src/parser.cpp

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

+ 2 - 2
src/timings.cpp

@@ -197,7 +197,7 @@ gb_internal void timings_print_all(Timings *t, TimingUnit unit = TimingUnit_Mill
 
 
 	f64 total_time = time_stamp(t->total, t->freq, unit);
 	f64 total_time = time_stamp(t->total, t->freq, unit);
 
 
-	gb_printf("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
+	gb_printf_err("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
 	          LIT(t->total.label),
 	          LIT(t->total.label),
 	          cast(int)(max_len-t->total.label.len), SPACES,
 	          cast(int)(max_len-t->total.label.len), SPACES,
 	          total_time,
 	          total_time,
@@ -207,7 +207,7 @@ gb_internal void timings_print_all(Timings *t, TimingUnit unit = TimingUnit_Mill
 	for_array(i, t->sections) {
 	for_array(i, t->sections) {
 		TimeStamp ts = t->sections[i];
 		TimeStamp ts = t->sections[i];
 		f64 section_time = time_stamp(ts, t->freq, unit);
 		f64 section_time = time_stamp(ts, t->freq, unit);
-		gb_printf("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
+		gb_printf_err("%.*s%.*s - % 9.3f %s - %6.2f%%\n",
 		          LIT(ts.label),
 		          LIT(ts.label),
 	              cast(int)(max_len-ts.label.len), SPACES,
 	              cast(int)(max_len-ts.label.len), SPACES,
 		          section_time,
 		          section_time,

+ 28 - 0
tests/core/encoding/cbor/test_core_cbor.odin

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

+ 1 - 1
tests/core/math/test_core_math.odin

@@ -1238,7 +1238,7 @@ test_count_digits :: proc(t: ^testing.T) {
 		buf: [64]u8
 		buf: [64]u8
 		for n in 0..<i64(base*base*base) {
 		for n in 0..<i64(base*base*base) {
 			count := math.count_digits_of_base(n, base)
 			count := math.count_digits_of_base(n, base)
-			str := strconv.append_int(buf[:], n, base)
+			str := strconv.write_int(buf[:], n, base)
 			if !testing.expectf(t,
 			if !testing.expectf(t,
 				len(str) == count,
 				len(str) == count,
 				"decimal %i in base-%i digit count is %i, does not match length %i of %q",
 				"decimal %i in base-%i digit count is %i, does not match length %i of %q",

+ 1 - 0
tests/issues/run.bat

@@ -17,6 +17,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style
 ..\..\..\odin test ..\test_issue_2637.odin %COMMON%  || exit /b
 ..\..\..\odin test ..\test_issue_2637.odin %COMMON%  || exit /b
 ..\..\..\odin test ..\test_issue_2666.odin %COMMON%  || exit /b
 ..\..\..\odin test ..\test_issue_2666.odin %COMMON%  || exit /b
 ..\..\..\odin test ..\test_issue_4210.odin %COMMON%  || exit /b
 ..\..\..\odin test ..\test_issue_4210.odin %COMMON%  || exit /b
+..\..\..\odin test ..\test_issue_4364.odin %COMMON%  || exit /b
 ..\..\..\odin test ..\test_issue_4584.odin %COMMON%  || exit /b
 ..\..\..\odin test ..\test_issue_4584.odin %COMMON%  || exit /b
 ..\..\..\odin build ..\test_issue_5043.odin %COMMON% || exit /b
 ..\..\..\odin build ..\test_issue_5043.odin %COMMON% || exit /b
 ..\..\..\odin build ..\test_issue_5097.odin %COMMON% || exit /b
 ..\..\..\odin build ..\test_issue_5097.odin %COMMON% || exit /b

+ 1 - 0
tests/issues/run.sh

@@ -18,6 +18,7 @@ $ODIN test ../test_issue_2615.odin $COMMON
 $ODIN test ../test_issue_2637.odin $COMMON
 $ODIN test ../test_issue_2637.odin $COMMON
 $ODIN test ../test_issue_2666.odin $COMMON
 $ODIN test ../test_issue_2666.odin $COMMON
 $ODIN test ../test_issue_4210.odin $COMMON
 $ODIN test ../test_issue_4210.odin $COMMON
+$ODIN test ../test_issue_4364.odin $COMMON
 $ODIN test ../test_issue_4584.odin $COMMON
 $ODIN test ../test_issue_4584.odin $COMMON
 if [[ $($ODIN build ../test_issue_2395.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]] ; then
 if [[ $($ODIN build ../test_issue_2395.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]] ; then
 	echo "SUCCESSFUL 1/1"
 	echo "SUCCESSFUL 1/1"

+ 18 - 0
tests/issues/test_issue_4364.odin

@@ -0,0 +1,18 @@
+// Tests issue #4364 https://github.com/odin-lang/Odin/issues/4364
+package test_issues
+
+import "core:testing"
+
+@test
+test_const_array_fill_assignment :: proc(t: ^testing.T) {
+	MAGIC :: 12345
+	Struct :: struct {x: int}
+	CONST_ARR : [4]Struct : Struct{MAGIC}
+	arr := CONST_ARR
+
+	testing.expect_value(t, len(arr), 4)
+	testing.expect_value(t, arr[0], Struct{MAGIC})
+	testing.expect_value(t, arr[1], Struct{MAGIC})
+	testing.expect_value(t, arr[2], Struct{MAGIC})
+	testing.expect_value(t, arr[3], Struct{MAGIC})
+}

+ 3 - 3
vendor/directx/dxc/dxcapi.odin

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