bytes_fmt.nx source
↩ module page · 123 lines · 3905 B
1// bytes_fmt.nx -- human-readable byte-size formatter.
2//
3// Renders byte counts as "1.2 KB" / "5.3 MB" / "12.0 GB" for
4// logs + UI. Binary (1024-based, KiB/MiB/GiB) + decimal (1000-
5// based, KB/MB/GB) variants both provided.
6//
7// Used by: fs stat reporters, HTTP Content-Length logs, storage
8// quota displays.
9//
10// Invariants:
11// BF1 Output is deterministic: same input bytes -> same output.
12// BF2 Always produces exactly one significant decimal digit
13// (unless the unit is B, where decimals make no sense).
14// BF3 Negative byte counts produce "-<formatted>" (only the
15// magnitude is unit-scaled).
16
17import "syscalls.nx"
18
19// Emit decimal integer into out starting at pos. Returns digits
20// written. Used by both variants.
21func bf_emit_uint(out: *u8, pos: i64, n: i64) -> i64 {
22 if n == 0 {
23 out[pos] = 0x30
24 return 1
25 }
26 let buf: *u8 = sys_mmap(32)
27 var v: i64 = n
28 var k: i64 = 0
29 while v > 0 {
30 buf[k] = 0x30 + (v % 10)
31 v = v / 10
32 k = k + 1
33 }
34 var i: i64 = 0
35 while i < k {
36 out[pos + i] = buf[k - 1 - i]
37 i = i + 1
38 }
39 return k
40}
41
42// Shared formatter: writes "<whole>.<tenth> <unit>" to out starting
43// at pos. `scale` = 1024 (binary) or 1000 (decimal).
44// Returns total bytes written.
45func bf_fmt_scaled(bytes: i64, scale: i64, binary_units: i64,
46 out: *u8, pos: i64) -> i64 {
47 var p: i64 = pos
48 var abs: i64 = bytes
49 if abs < 0 {
50 out[p] = 0x2D // '-'
51 p = p + 1
52 abs = 0 - abs
53 }
54 // Select unit by order-of-magnitude.
55 var value: i64 = abs
56 var remainder: i64 = 0
57 var unit: i64 = 0 // 0 = B, 1 = K, 2 = M, 3 = G, 4 = T, 5 = P
58 while value >= scale {
59 remainder = value % scale
60 value = value / scale
61 unit = unit + 1
62 if unit >= 5 { unit = 5 }
63 }
64
65 // Whole part.
66 p = p + bf_emit_uint(out, p, value)
67
68 // Fractional part: one digit = (remainder * 10) / scale.
69 // Only for non-byte units.
70 if unit > 0 {
71 out[p] = 0x2E // '.'
72 p = p + 1
73 let tenth: i64 = (remainder * 10) / scale
74 out[p] = 0x30 + tenth
75 p = p + 1
76 }
77
78 // Space + unit suffix.
79 out[p] = 0x20
80 p = p + 1
81 if unit == 0 { out[p] = 0x42; p = p + 1; return p - pos } // "B"
82 // Unit letter.
83 if unit == 1 { out[p] = 0x4B; p = p + 1 } // 'K'
84 if unit == 2 { out[p] = 0x4D; p = p + 1 } // 'M'
85 if unit == 3 { out[p] = 0x47; p = p + 1 } // 'G'
86 if unit == 4 { out[p] = 0x54; p = p + 1 } // 'T'
87 if unit == 5 { out[p] = 0x50; p = p + 1 } // 'P'
88 if binary_units == 1 {
89 out[p] = 0x69 // 'i' -- KiB/MiB
90 p = p + 1
91 }
92 out[p] = 0x42 // 'B'
93 p = p + 1
94 return p - pos
95}
96
97// Binary (1024-based, KiB / MiB / GiB). Preferred for storage
98// display.
99func bytes_fmt_binary(bytes: i64, out: *u8, pos: i64) -> i64 {
100 return bf_fmt_scaled(bytes, 1024, 1, out, pos)
101}
102
103// Decimal (1000-based, KB / MB / GB). Preferred for marketing /
104// network throughput reporting (matches vendor specs).
105func bytes_fmt_decimal(bytes: i64, out: *u8, pos: i64) -> i64 {
106 return bf_fmt_scaled(bytes, 1000, 0, out, pos)
107}
108
109// Compile-only smoke: 2048 bytes -> "2.0 KiB" (binary) or
110// "2.0 KB" (decimal)... actually 2048 / 1000 = 2, remainder 48;
111// tenth = 480/1000 = 0 -> "2.0 KB".
112func main() -> i64 {
113 let out: *u8 = sys_mmap(32)
114 let n: i64 = bytes_fmt_binary(2048, out, 0)
115 // "2.0 KiB" = 7 chars
116 if n != 7 { return 1 }
117 if out[0] != 0x32 { return 2 } // '2'
118 if out[1] != 0x2E { return 3 } // '.'
119 if out[4] != 0x4B { return 4 } // 'K'
120 if out[5] != 0x69 { return 5 } // 'i'
121 if out[6] != 0x42 { return 6 } // 'B'
122 return 0
123}