summaryrefslogtreecommitdiffstats
path: root/src/hex
diff options
context:
space:
mode:
authorCanop <cano.petrole@gmail.com>2020-07-12 21:28:04 +0200
committerCanop <cano.petrole@gmail.com>2020-07-12 21:28:04 +0200
commit4d61940e26ad624806adf0ca24dc96c832100ea1 (patch)
tree96f1420213482a681b52cf2bceca6aac01011195 /src/hex
parentb429fd0b235aa5c9317d8c132fd1bfc80be7acd7 (diff)
preview panel [still more like a POC than a full feature]
Diffstat (limited to 'src/hex')
-rw-r--r--src/hex/byte.rs60
-rw-r--r--src/hex/hex_view.rs106
-rw-r--r--src/hex/mod.rs9
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,
+};
+