xd 0.0.4

tool that dumps binary input in a variety of formats
Documentation
#![forbid(unsafe_code)]
#![cfg_attr(all(test, feature = "bench"), feature(test))]
#![cfg_attr(feature = "strict", deny(warnings))]
#![cfg_attr(not(any(test, feature = "std")), no_std)]
// #![warn(missing_docs)]

pub mod table;

extern crate alloc;

use core::fmt::{self, Display, Formatter, Write};

#[cfg(feature = "std")]
use std::io;

use crate::table::{Table, DEFAULT};

pub struct Dumper {
    pub address: usize,
    pub buffer: [u8; 16],
    pub len: usize,
    table: &'static Table,
    use_rich: bool,
}

impl Dumper {
    pub fn new(table: &'static Table, use_rich: bool) -> Self {
        Self {
            address: 0,
            buffer: [0; 16],
            len: 0,
            table,
            use_rich,
        }
    }
}

#[cfg(feature = "std")]
impl Dumper {
    pub fn read(&mut self, source: &mut impl io::Read) -> io::Result<usize> {
        self.address += self.len;
        self.len = 0;

        while self.len < self.buffer.len() {
            self.len += match source.read(&mut self.buffer[self.len..])? {
                0 => return Ok(self.len),
                x => x,
            };
        }

        Ok(self.len)
    }
}

impl Default for Dumper {
    fn default() -> Self {
        Self::new(&DEFAULT, true)
    }
}

impl Display for Dumper {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let mut offset = 0;
        let mut column = 0;

        while offset < self.len {
            write!(f, "{:02X} ", self.buffer[offset])?;
            offset += 1;
            column += 3;

            if offset % 4 == 0 {
                f.write_char(' ')?;
                column += 1;
            }
        }

        // <ff ff ff ff  ff ff ff ff  ff ff ff ff  ff ff ff ff  >
        f.write_str(&"                                                    "[column..])?;

        if self.use_rich {
            for &byte in &self.buffer[..self.len] {
                Display::fmt(&self.table.inner[usize::from(byte)], f)?;
            }
        } else {
            for &byte in &self.buffer[..self.len] {
                Display::fmt(&self.table.inner[usize::from(byte)].scalar, f)?;
            }
        }

        // <0123456789abcdef  >
        f.write_str(&"                  "[self.len..])?;

        write!(f, "{:X}\n", self.address)?;

        Ok(())
    }
}