///|
priv suberror OdError {
OdError(String)
}
///|
struct Format {
kind : Char
size : Int
trailer : Bool
}
///|
fn number(
value : UInt64,
radix : Int,
width : Int,
fill? : Char = '0',
) -> String {
let digits = "0123456789abcdef"
let out : Array[Char] = []
let mut n = value
for ;; {
out.push(digits[(n % radix.to_uint64()).to_int()].to_int().unsafe_to_char())
n /= radix.to_uint64()
if n == 0UL {
break
}
}
while out.length() < width {
out.push(fill)
}
let sb = StringBuilder()
for i = out.length() - 1; i >= 0; i = i - 1 {
sb.write_char(out[i])
}
sb.to_string()
}
///|
fn spaces(count : Int) -> String {
let sb = StringBuilder()
for _ in 0.. Array[Format] raise OdError {
let result : Array[Format] = []
let mut i = 0
while i < text.length() {
let kind = text[i].to_int().unsafe_to_char()
if kind != 'a' &&
kind != 'c' &&
kind != 'd' &&
kind != 'u' &&
kind != 'o' &&
kind != 'x' {
raise OdError(
"unsupported format '\{kind}'; use a, c, or integer d/u/o/x formats",
)
}
i += 1
let mut size = if kind == 'a' || kind == 'c' { 1 } else { 4 }
if kind != 'a' && kind != 'c' && i < text.length() {
match text[i] {
'1' | 'C' => {
size = 1
i += 1
}
'2' | 'S' => {
size = 2
i += 1
}
'4' | 'I' => {
size = 4
i += 1
}
'8' | 'L' => {
size = 8
i += 1
}
_ => ()
}
}
let trailer = i < text.length() && text[i] == 'z'
if trailer {
i += 1
}
result.push({ kind, size, trailer, })
}
if result.is_empty() {
raise OdError("empty output format")
}
result
}
///|
fn Format::width(self : Format) -> Int {
match self.kind {
'a' | 'c' => 3
'x' => self.size * 2
'o' => (self.size * 8 + 2) / 3
'u' =>
match self.size {
1 => 3
2 => 5
4 => 10
_ => 20
}
_ =>
match self.size {
1 => 4
2 => 6
4 => 11
_ => 20
}
}
}
///|
fn character(byte : Byte, named : Bool) -> String {
let names = [
"nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", "bs", "ht", "nl", "vt",
"ff", "cr", "so", "si", "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
"can", "em", "sub", "esc", "fs", "gs", "rs", "us", "sp",
]
let code = if named { byte.to_int() & 127 } else { byte.to_int() }
let text = if named && code <= 32 {
names[code]
} else if named && code == 127 {
"del"
} else if code >= 32 && code <= 126 {
code.unsafe_to_char().to_string()
} else {
match code {
0 => "\\0"
7 => "\\a"
8 => "\\b"
9 => "\\t"
10 => "\\n"
11 => "\\v"
12 => "\\f"
13 => "\\r"
_ => number(code.to_uint64(), 8, 3)
}
}
spaces(3 - text.length()) + text
}
///|
fn value_at(data : Bytes, start : Int, size : Int, big : Bool) -> UInt64 {
let mut result = 0UL
for i in 0.. String {
if self.kind == 'a' || self.kind == 'c' {
return character(data[start], self.kind == 'a')
}
let value = value_at(data, start, self.size, big)
let width = self.width()
match self.kind {
'x' => number(value, 16, width)
'o' => number(value, 8, width)
'u' => number(value, 10, width, fill=' ')
_ => {
let signed = if self.size == 8 {
value.reinterpret_as_int64()
} else {
let bits = self.size * 8
if (value & (1UL << (bits - 1))) != 0UL {
value.reinterpret_as_int64() - (1L << bits)
} else {
value.reinterpret_as_int64()
}
}
let text = signed.to_string()
spaces(width - text.length()) + text
}
}
}
///|
fn address(offset : Int64, base : Int) -> String {
if base == 0 {
""
} else {
number(offset.reinterpret_as_uint64(), base, if base == 16 { 6 } else { 7 })
}
}
///|
fn render_row(
data : Bytes,
offset : Int64,
width : Int,
formats : Array[Format],
base : Int,
big : Bool,
) -> String {
let mut line_width = 0
for format in formats {
line_width = line_width.max((format.width() + 1) * (width / format.size))
}
let out = StringBuilder()
for index, format in formats {
let prefix = address(offset, base)
out.write_string(if index == 0 { prefix } else { spaces(prefix.length()) })
let fields = width / format.size
let padding = line_width - fields * format.width()
let count = (data.length() + format.size - 1) / format.size
for field in 0..")
for byte in data {
out.write_char(
if byte >= b' ' && byte <= b'~' {
byte.to_int().unsafe_to_char()
} else {
'.'
},
)
}
out.write_char('<')
}
out.write_char('\n')
}
out.to_string()
}