///|
priv struct ReverseDecoder {
style : Style
cols : Int32
base : Int64
mut want : Int64
mut column : Int32
mut ignore_garbage : Bool
mut skip_line : Bool
mut first : Int32
mut second : Int32
mut third : Int32
mut bits : Int32
mut bit_count : Int32
}
///|
fn ReverseDecoder::new(
style : Style,
cols : Int32,
base : Int64,
) -> ReverseDecoder {
{
style,
cols,
base,
want: 0L,
column: cols,
ignore_garbage: true,
skip_line: false,
first: -1,
second: 0,
third: 0,
bits: 0,
bit_count: 0,
}
}
///|
fn ReverseDecoder::newline(self : ReverseDecoder) -> Unit {
if !(self.style is Plain) {
self.want = 0L
}
self.column = self.cols
self.ignore_garbage = true
self.skip_line = false
}
///|
fn ReverseDecoder::feed(
self : ReverseDecoder,
byte : Byte,
) -> (Int64, Byte?)? raise CliError {
if byte == b'\r' {
return None
}
if self.skip_line {
if byte == b'\n' {
self.newline()
}
return None
}
if self.style is Plain && byte is (b' ' | b'\n' | b'\t') {
return None
}
if self.style is Bits {
self.first = hex_val(byte)
if self.first < 0 && self.ignore_garbage {
return None
}
if byte is (b'0' | b'1') {
self.bits = (self.bits << 1) | (byte.to_int() - 0x30)
self.bit_count += 1
}
} else {
self.third = self.second
self.second = self.first
self.first = hex_val(byte)
if self.first < 0 && self.ignore_garbage {
return None
}
}
self.ignore_garbage = false
if !(self.style is Plain) && self.column >= self.cols {
if self.first < 0 {
self.column = 0
if self.style is Bits {
self.bit_count = 0
}
} else {
if self.want > (9223372036854775807L - self.first.to_int64()) / 16L {
raise CliError(5, "xxd: addressed offset overflow.")
}
self.want = self.want * 16L + self.first.to_int64()
}
return None
}
let target = checked_add(
self.base,
self.want,
5,
"xxd: addressed offset overflow.",
)
let mut result : Byte? = None
if self.style is Bits {
if self.bit_count == 8 {
result = Some(self.bits.to_byte())
self.bits = 0
self.bit_count = 0
}
} else if self.second >= 0 && self.first >= 0 {
result = Some(((self.second << 4) | self.first).to_byte())
self.first = -1
} else if self.first < 0 && self.second < 0 && self.third < 0 {
self.skip_line = true
}
if result is Some(_) {
self.want = checked_add(self.want, 1L, 5, "xxd: addressed offset overflow.")
if !(self.style is Plain) {
self.column += 1
if self.column >= self.cols {
self.skip_line = true
}
}
}
if byte == b'\n' {
self.newline()
}
Some((target, result))
}
///|
async fn flush_reverse(output : Output, pending : Array[Byte]) -> Unit {
if !pending.is_empty() {
output.write(Bytes::from_array(pending))
pending.clear()
}
}
///|
async fn reverse_stream(
reader : &@io.Reader,
output : Output,
opts : Options,
) -> Unit {
if opts.style is Little || opts.style is Include {
raise CliError(255, "xxd: Sorry, cannot revert this type of hexdump")
}
let base = match opts.seek {
Some(seek) => if seek.negative { -seek.amount } else { seek.amount }
None => 0L
}
let decoder = ReverseDecoder::new(opts.style, opts.cols, base)
let pending : Array[Byte] = []
while @stream.read_chunk(reader) is Some(chunk) {
try {
for byte in chunk {
if decoder.feed(byte) is Some((target, value)) {
let current = checked_add(
output.position,
pending.length().to_int64(),
5,
"xxd: output offset overflow.",
)
if target != current {
flush_reverse(output, pending)
output.seek(target)
}
if value is Some(value) {
pending.push(value)
}
}
}
} catch {
CliError(status, message) => {
if status == 5 {
flush_reverse(output, pending)
}
raise CliError(status, message)
}
error => raise error
}
flush_reverse(output, pending)
}
}