///|
fn input_integer(data : BytesView, position : Int32, count : Int32) -> UInt64 {
let mut value = 0UL
for index in 0..<(if count < 8 { count } else { 8 }) {
let byte = data.get(position + index).unwrap_or(b'\x00')
value = value | (byte.to_int().to_uint64() << (index * 8))
}
value
}
///|
fn padded_bytes(value : BytesView, width : Int32, minus : Bool) -> Bytes {
let output = @buffer.Buffer()
let padding = if width > value.length() { width - value.length() } else { 0 }
if !minus {
output.write_string_utf8(spaces(padding))
}
output.write_bytes(value)
if minus {
output.write_string_utf8(spaces(padding))
}
output.to_bytes()
}
///|
fn padded_number(
prefix : String,
digits : String,
atom : PrintAtom,
zero : Bool,
) -> Bytes {
let padding = if atom.width > prefix.length() + digits.length() {
atom.width - prefix.length() - digits.length()
} else {
0
}
let output = @buffer.Buffer()
if !atom.minus && !zero {
output.write_string_utf8(spaces(padding))
}
output.write_string_utf8(prefix)
if !atom.minus && zero {
for _ in 0.. Bytes {
let signed = conversion is ('d' | 'i')
let negative = signed && count > 0 && (bits & (1UL << (count * 8 - 1))) != 0UL
let magnitude = if negative {
if count == 8 {
0UL - bits
} else {
(1UL << (count * 8)) - bits
}
} else {
bits
}
let radix = match conversion {
'o' => 8
'x' | 'X' => 16
_ => 10
}
let mut digits = if magnitude == 0UL && atom.precision == Some(0) {
""
} else {
magnitude.to_string(radix~)
}
if conversion == 'X' {
digits = digits.to_upper()
}
let precision = atom.precision.unwrap_or(0)
if digits.length() < precision {
let padded = StringBuilder()
for _ in digits.length().. String {
let names = [
"nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", "bs", "ht", "lf", "vt",
"ff", "cr", "so", "si", "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
"can", "em", "sub", "esc", "fs", "gs", "rs", "us",
]
let value = byte.to_int()
if value < 32 {
names[value]
} else if value == 127 {
"del"
} else if value >= 128 {
number(value.to_int64(), 16, 0)
} else {
value.unsafe_to_char().to_string()
}
}
///|
fn special_string(text : String, atom : PrintAtom) -> Bytes {
let bytes = @utf8.encode(text)
let length = atom.precision.unwrap_or(bytes.length()).min(bytes.length())
padded_bytes(bytes[:length], atom.width, atom.minus)
}
///|
fn atom_value(
atom : PrintAtom,
data : BytesView,
position : Int32,
address : Int64,
blank : Bool,
) -> Bytes {
if blank {
return @utf8.encode(spaces(atom.width))
}
match atom.conversion {
Some(Integer(conversion)) =>
integer_atom(
input_integer(data, position, atom.count),
atom.count,
conversion,
atom,
)
Some(Address(conversion, _)) =>
integer_atom(address.reinterpret_as_uint64(), 8, conversion, atom)
Some(Floating(conversion)) => {
let end = if position + atom.count < data.length() {
position + atom.count
} else {
data.length()
}
let bytes = if position < data.length() {
data[position:end]
} else {
b""[:]
}
let profile = if atom.count == 4 {
@numfmt.Binary32
} else {
@numfmt.Binary64
}
let value = @numfmt.decode(bytes, profile)
let negative = (
data.get(position + atom.count - 1).unwrap_or(b'\x00') & b'\x80'
) !=
b'\x00'
let prefix = if negative {
"-"
} else if atom.plus {
"+"
} else if atom.blank {
" "
} else {
""
}
let digits = @numfmt.format_magnitude(
value,
conversion,
atom.precision,
atom.alternate,
profile,
)
padded_number(
prefix,
digits,
atom,
atom.zero && value is @numfmt.Finite(_, _, _) && !atom.minus,
)
}
Some(StringValue) => {
let available = if position < data.length() {
data.length() - position
} else {
0
}
let length = atom.precision.unwrap_or(available)
let limit = if length < available { length } else { available }
let mut end = 0
while end < limit && data[position + end] != b'\x00' {
end += 1
}
padded_bytes(data[position:position + end], atom.width, atom.minus)
}
Some(Character) =>
padded_bytes(
Bytes::from_array([data.get(position).unwrap_or(b'\x00')])[:],
atom.width,
atom.minus,
)
Some(EscapedCharacter) => {
let byte = data.get(position).unwrap_or(b'\x00')
if byte >= b' ' && byte <= b'~' {
padded_bytes(Bytes::from_array([byte])[:], atom.width, atom.minus)
} else {
special_string(escaped_character(byte), atom)
}
}
Some(PrintableCharacter) => {
let byte = data.get(position).unwrap_or(b'\x00')
padded_bytes(
Bytes::from_array([
if byte >= b' ' && byte <= b'~' {
byte
} else {
b'.'
},
])[:],
atom.width,
atom.minus,
)
}
Some(AsciiName) => {
let byte = data.get(position).unwrap_or(b'\x00')
if byte >= b'\x80' {
integer_atom(byte.to_int().to_uint64(), 1, 'x', atom)
} else if byte >= b' ' && byte < b'\x7f' {
padded_bytes(Bytes::from_array([byte])[:], atom.width, atom.minus)
} else {
special_string(ascii_name(byte), atom)
}
}
None => b""
}
}
///|
fn render_custom(
format : CustomFormat,
data : BytesView,
offset : Int64,
block_size : Int32,
) -> Bytes {
let output = @buffer.Buffer()
let mut position = 0
for unit in format.units {
if unit.final_address {
break
}
if unit.atoms.is_empty() {
continue
}
for repetition in 0.. 1 &&
repetition + 1 == unit.reps &&
index + 1 == unit.atoms.length() &&
unit.trim_tail &&
atom.conversion is None
let prefix = if trim {
atom.prefix[:atom.prefix.length() - 1]
} else {
atom.prefix[:]
}
let sequence = colored_sequence(
atom,
data,
position,
offset + position.to_int64(),
)
if sequence is Some(sequence) {
output.write_string_utf8(sequence)
}
output.write_bytes(prefix)
let blank = data.length() < block_size &&
position >= data.length() &&
atom.conversion is Some(_)
output.write_bytes(
atom_value(atom, data, position, offset + position.to_int64(), blank)[:],
)
if sequence is Some(_) {
output.write_string_utf8("\u001b[0m")
}
position += atom.count
}
}
}
output.to_bytes()
}
///|
fn render_final(unit : FormatUnit, address : Int64) -> Bytes {
let output = @buffer.Buffer()
for atom in unit.atoms {
match atom.conversion {
Some(Address(_, _)) => {
let sequence = colored_sequence(atom, b""[:], 0, address)
if sequence is Some(sequence) {
output.write_string_utf8(sequence)
}
output.write_bytes(atom.prefix[:])
output.write_bytes(atom_value(atom, b""[:], 0, address, false)[:])
if sequence is Some(_) {
output.write_string_utf8("\u001b[0m")
}
}
None => output.write_bytes(atom.prefix[:])
_ => ()
}
}
output.to_bytes()
}