mirror of
https://github.com/odin-lang/Odin.git
synced 2026-02-17 16:38:22 +00:00
Add formatting of bytes into the best unit of measurement
This commit is contained in:
@@ -35,6 +35,8 @@ Floating-point, complex numbers, and quaternions:
|
||||
%F synonym for %f
|
||||
%h hexadecimal (lower-case) representation with 0h prefix (0h01234abcd)
|
||||
%H hexadecimal (upper-case) representation with 0H prefix (0h01234ABCD)
|
||||
%m number of bytes in the best unit of measurement, e.g. 123.45mb
|
||||
%M number of bytes in the best unit of measurement, e.g. 123.45MB
|
||||
String and slice of bytes
|
||||
%s the uninterpreted bytes of the string or slice
|
||||
%q a double-quoted string safely escaped with Odin syntax
|
||||
@@ -85,6 +87,7 @@ Other flags:
|
||||
add leading 0z for dozenal (%#z)
|
||||
add leading 0x or 0X for hexadecimal (%#x or %#X)
|
||||
remove leading 0x for %p (%#p)
|
||||
add a space between bytes and the unit of measurement (%#m or %#M)
|
||||
' ' (space) leave a space for elided sign in numbers (% d)
|
||||
0 pad with leading zeros rather than spaces
|
||||
|
||||
|
||||
@@ -1048,6 +1048,65 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
|
||||
fi.zero = false
|
||||
_pad(fi, s)
|
||||
}
|
||||
// Units of measurements:
|
||||
__MEMORY_LOWER := " b kb mb gb tb pb eb"
|
||||
__MEMORY_UPPER := " B KB MB GB TB PB EB"
|
||||
// Formats an integer value as bytes with the best representation.
|
||||
//
|
||||
// Inputs:
|
||||
// - fi: A pointer to an Info structure
|
||||
// - u: The integer value to format
|
||||
// - is_signed: A boolean indicating if the integer is signed
|
||||
// - bit_size: The bit size of the integer
|
||||
// - digits: A string containing the digits for formatting
|
||||
//
|
||||
_fmt_memory :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, units: string) {
|
||||
abs, neg := strconv.is_integer_negative(u, is_signed, bit_size)
|
||||
|
||||
// Default to a precision of 2, but if less than a kb, 0
|
||||
prec := fi.prec if (fi.prec_set || abs < mem.Kilobyte) else 2
|
||||
|
||||
div, off, unit_len := 1, 0, 1
|
||||
for n := abs; n >= mem.Kilobyte; n /= mem.Kilobyte {
|
||||
div *= mem.Kilobyte
|
||||
off += 3
|
||||
|
||||
// First iteration is slightly different because you go from
|
||||
// units of length 1 to units of length 2.
|
||||
if unit_len == 1 {
|
||||
off = 2
|
||||
unit_len = 2
|
||||
}
|
||||
}
|
||||
|
||||
// If hash, we add a space between the value and the suffix.
|
||||
if fi.hash {
|
||||
unit_len += 1
|
||||
} else {
|
||||
off += 1
|
||||
}
|
||||
|
||||
amt := f64(abs) / f64(div)
|
||||
if neg {
|
||||
amt = -amt
|
||||
}
|
||||
|
||||
buf: [256]byte
|
||||
str := strconv.append_float(buf[:], amt, 'f', prec, 64)
|
||||
|
||||
// Add the unit at the end.
|
||||
copy(buf[len(str):], units[off:off+unit_len])
|
||||
str = string(buf[:len(str)+unit_len])
|
||||
|
||||
if !fi.plus {
|
||||
// Strip sign from "+<value>" but not "+Inf".
|
||||
if str[0] == '+' && str[1] != 'I' {
|
||||
str = str[1:]
|
||||
}
|
||||
}
|
||||
|
||||
_pad(fi, str)
|
||||
}
|
||||
// Hex Values:
|
||||
__DIGITS_LOWER := "0123456789abcdefx"
|
||||
__DIGITS_UPPER := "0123456789ABCDEFX"
|
||||
@@ -1096,6 +1155,8 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) {
|
||||
io.write_string(fi.writer, "U+", &fi.n)
|
||||
_fmt_int(fi, u, 16, false, bit_size, __DIGITS_UPPER)
|
||||
}
|
||||
case 'm': _fmt_memory(fi, u, is_signed, bit_size, __MEMORY_LOWER)
|
||||
case 'M': _fmt_memory(fi, u, is_signed, bit_size, __MEMORY_UPPER)
|
||||
|
||||
case:
|
||||
fmt_bad_verb(fi, verb)
|
||||
|
||||
@@ -8,6 +8,8 @@ Kilobyte :: runtime.Kilobyte
|
||||
Megabyte :: runtime.Megabyte
|
||||
Gigabyte :: runtime.Gigabyte
|
||||
Terabyte :: runtime.Terabyte
|
||||
Petabyte :: runtime.Petabyte
|
||||
Exabyte :: runtime.Exabyte
|
||||
|
||||
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
|
||||
return runtime.memset(data, i32(value), len)
|
||||
|
||||
@@ -337,6 +337,8 @@ Kilobyte :: 1024 * Byte
|
||||
Megabyte :: 1024 * Kilobyte
|
||||
Gigabyte :: 1024 * Megabyte
|
||||
Terabyte :: 1024 * Gigabyte
|
||||
Petabyte :: 1024 * Terabyte
|
||||
Exabyte :: 1024 * Petabyte
|
||||
|
||||
// Logging stuff
|
||||
|
||||
|
||||
@@ -57,3 +57,6 @@ c_libc_test:
|
||||
|
||||
net_test:
|
||||
$(ODIN) run net -out:test_core_net
|
||||
|
||||
fmt_test:
|
||||
$(ODIN) run fmt -out:test_core_fmt
|
||||
|
||||
59
tests/core/fmt/test_core_fmt.odin
Normal file
59
tests/core/fmt/test_core_fmt.odin
Normal file
@@ -0,0 +1,59 @@
|
||||
package test_core_fmt
|
||||
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "core:testing"
|
||||
import "core:mem"
|
||||
|
||||
TEST_count := 0
|
||||
TEST_fail := 0
|
||||
|
||||
when ODIN_TEST {
|
||||
expect :: testing.expect
|
||||
log :: testing.log
|
||||
} else {
|
||||
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
|
||||
TEST_count += 1
|
||||
if !condition {
|
||||
TEST_fail += 1
|
||||
fmt.printf("[%v] %v\n", loc, message)
|
||||
return
|
||||
}
|
||||
}
|
||||
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
|
||||
fmt.printf("[%v] ", loc)
|
||||
fmt.printf("log: %v\n", v)
|
||||
}
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
t := testing.T{}
|
||||
test_fmt_memory(&t)
|
||||
|
||||
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
|
||||
if TEST_fail > 0 {
|
||||
os.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
test_fmt_memory :: proc(t: ^testing.T) {
|
||||
check :: proc(t: ^testing.T, exp: string, format: string, args: ..any, loc := #caller_location) {
|
||||
got := fmt.tprintf(format, ..args)
|
||||
expect(t, got == exp, fmt.tprintf("(%q, %v): %q != %q", format, args, got, exp), loc)
|
||||
}
|
||||
|
||||
check(t, "5b", "%m", 5)
|
||||
check(t, "5B", "%M", 5)
|
||||
check(t, "-5B", "%M", -5)
|
||||
check(t, "3.00kb", "%m", mem.Kilobyte * 3)
|
||||
check(t, "3kb", "%.0m", mem.Kilobyte * 3)
|
||||
check(t, "3KB", "%.0M", mem.Kilobyte * 3)
|
||||
check(t, "3.000 mb", "%#.3m", mem.Megabyte * 3)
|
||||
check(t, "3.50 gb", "%#m", u32(mem.Gigabyte * 3.5))
|
||||
check(t, "001tb", "%5.0m", mem.Terabyte)
|
||||
check(t, "-001tb", "%5.0m", -mem.Terabyte)
|
||||
check(t, "2.50 pb", "%#5.m", uint(mem.Petabyte * 2.5))
|
||||
check(t, "1.00 EB", "%#M", mem.Exabyte)
|
||||
check(t, "255 B", "%#M", u8(255))
|
||||
check(t, "0b", "%m", u8(0))
|
||||
}
|
||||
Reference in New Issue
Block a user