diff options
author | Canop <cano.petrole@gmail.com> | 2020-07-12 21:28:04 +0200 |
---|---|---|
committer | Canop <cano.petrole@gmail.com> | 2020-07-12 21:28:04 +0200 |
commit | 4d61940e26ad624806adf0ca24dc96c832100ea1 (patch) | |
tree | 96f1420213482a681b52cf2bceca6aac01011195 /src/hex | |
parent | b429fd0b235aa5c9317d8c132fd1bfc80be7acd7 (diff) |
preview panel [still more like a POC than a full feature]
Diffstat (limited to 'src/hex')
-rw-r--r-- | src/hex/byte.rs | 60 | ||||
-rw-r--r-- | src/hex/hex_view.rs | 106 | ||||
-rw-r--r-- | src/hex/mod.rs | 9 |
3 files changed, 175 insertions, 0 deletions
diff --git a/src/hex/byte.rs b/src/hex/byte.rs new file mode 100644 index 0000000..1195ff4 --- /dev/null +++ b/src/hex/byte.rs @@ -0,0 +1,60 @@ +use { + crate::{ + skin::StyleMap, + }, + termimad::CompoundStyle, +}; + +pub enum ByteCategory { + Null, + AsciiGraphic, + AsciiWhitespace, + AsciiOther, + NonAscii, +} + +#[derive(Clone, Copy)] +pub struct Byte(u8); + +impl From<u8> for Byte { + fn from(u: u8) -> Self { + Self(u) + } +} + +impl Byte { + pub fn category(self) -> ByteCategory { + if self.0 == 0x00 { + ByteCategory::Null + } else if self.0.is_ascii_graphic() { + ByteCategory::AsciiGraphic + } else if self.0.is_ascii_whitespace() { + ByteCategory::AsciiWhitespace + } else if self.0.is_ascii() { + ByteCategory::AsciiOther + } else { + ByteCategory::NonAscii + } + } + + pub fn style(self, styles: & StyleMap) -> & CompoundStyle { + match self.category() { + ByteCategory::Null => &styles.hex_null, + ByteCategory::AsciiGraphic => &styles.hex_ascii_graphic, + ByteCategory::AsciiWhitespace => &styles.hex_ascii_whitespace, + ByteCategory::AsciiOther => &styles.hex_ascii_other, + ByteCategory::NonAscii => &styles.hex_non_ascii, + } + } + + pub fn as_char(self) -> char { + match self.category() { + ByteCategory::Null => '0', + ByteCategory::AsciiGraphic => self.0 as char, + ByteCategory::AsciiWhitespace if self.0 == 0x20 => ' ', + ByteCategory::AsciiWhitespace => '_', + ByteCategory::AsciiOther => '•', + ByteCategory::NonAscii => '×', + } + } +} diff --git a/src/hex/hex_view.rs b/src/hex/hex_view.rs new file mode 100644 index 0000000..fd9eea9 --- /dev/null +++ b/src/hex/hex_view.rs @@ -0,0 +1,106 @@ + +use { + super::{ + byte::Byte, + }, + crate::{ + display::{CropWriter, LONG_SPACE, Screen, W}, + errors::ProgramError, + skin::PanelSkin, + }, + crossterm::{ + cursor, + QueueableCommand, + }, + memmap::Mmap, + std::{ + fs::File, + io, + path::PathBuf, + }, + termimad::{Area}, +}; + + +pub struct HexLine { + pub bytes: Vec<u8>, // from 1 to 16 bytes +} + +pub struct HexView { + pub path: PathBuf, + pub scroll: usize, + pub len: usize, +} + +impl HexView { + pub fn new(path: PathBuf) -> io::Result<Self> { + let len = path.metadata()?.len() as usize; + Ok(Self { + path, + scroll: 0, + len, + }) + } + pub fn line_count(&self) -> usize { + self.len / 16 + } + pub fn get_page(&mut self, start_line_idx: usize, line_count: usize) -> io::Result<Vec<HexLine>> { + // I'm not sure a memmap is the best solution here but at least it's easy + let file = File::open(&self.path)?; + let mmap = unsafe { Mmap::map(&file)? }; + let new_len = mmap.len(); + if new_len != self.len { + warn!("previewed file len changed from {} to {}", self.len, new_len); + self.len = new_len; + } + let mut lines = Vec::new(); + let mut start_idx = 16 * start_line_idx; + while start_idx < self.len { + let line_len = 16.min(self.len - start_idx); + let mut bytes: Vec<u8> = vec![0; line_len]; + bytes[0..line_len].copy_from_slice(&mmap[start_idx..start_idx+line_len]); + lines.push(HexLine{bytes}); + if lines.len() >= line_count { + break; + } + start_idx += line_len; + } + Ok(lines) + } + pub fn display( + &mut self, + w: &mut W, + _screen: &Screen, + panel_skin: &PanelSkin, + area: &Area, + ) -> Result<(), ProgramError> { + let line_count = area.height as usize; + let page = self.get_page(self.scroll, line_count)?; + let styles = &panel_skin.styles; + let mut show_middle_space = false; + if area.width > 50 { + show_middle_space = true; + } + for y in 0..line_count { + w.queue(cursor::MoveTo(area.left, y as u16 + area.top))?; + let mut cw = CropWriter::new(w, area.width as usize); + let cw = &mut cw; + if y < page.len() { + let line = &page[y]; + for x in 0..16 { + if x==8 && show_middle_space { + cw.queue_char(&styles.default, ' ')?; + } + if let Some(b) = line.bytes.get(x) { + let byte = Byte::from(*b); + cw.queue_string(byte.style(styles), format!(" {:02x}", b))?; + } else { + cw.queue_str(&styles.default, " ")?; + } + } + cw.fill(&styles.default, LONG_SPACE)?; + } + } + Ok(()) + } +} diff --git a/src/hex/mod.rs b/src/hex/mod.rs new file mode 100644 index 0000000..378e56f --- /dev/null +++ b/src/hex/mod.rs @@ -0,0 +1,9 @@ + + +mod byte; +mod hex_view; + +pub use { + hex_view::HexView, +}; + |