use std::ops::{Bound, RangeBounds};
#[derive(Debug, Clone, Copy)]
pub struct HexdOptions {
pub base: Base,
pub autoskip: bool,
pub uppercase: bool,
pub print_ascii: bool,
pub align: bool,
pub grouping: Grouping,
pub print_range: HexdRange,
pub index_offset: IndexOffset,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Base {
Hex,
Decimal(LeadingZeroChar),
Octal(LeadingZeroChar),
Binary,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum LeadingZeroChar {
Space,
Zero,
Underscore,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Endianness {
BigEndian,
LittleEndian,
}
#[derive(Debug, Clone, Copy)]
pub struct HexdRange {
pub skip: usize,
pub limit: Option<usize>,
}
impl HexdRange {
pub fn full() -> Self {
Self {
skip: 0,
limit: None,
}
}
pub fn new<R: RangeBounds<usize>>(r: R) -> Self {
let skip = match r.start_bound() {
Bound::Unbounded => 0usize,
Bound::Included(s) => *s,
Bound::Excluded(s) => s + 1,
};
let limit = match r.end_bound() {
Bound::Unbounded => None,
Bound::Included(s) => Some(*s + 1),
Bound::Excluded(s) => Some(*s),
};
Self { skip, limit }
}
pub fn length(&self) -> Option<usize> {
self.limit.map(|lim| lim - self.skip)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum IndexOffset {
Relative(usize),
Absolute(usize),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Grouping {
Ungrouped { byte_count: usize, spacing: Spacing },
Grouped {
group_size: GroupSize,
byte_spacing: Spacing,
num_groups: usize,
group_spacing: Spacing,
},
}
impl Grouping {
pub fn elt_width(&self) -> usize {
match self {
&Grouping::Ungrouped {
byte_count,
spacing: _,
} => byte_count,
&Grouping::Grouped {
group_size,
num_groups,
byte_spacing: _,
group_spacing: _,
} => group_size.element_count() * num_groups,
}
}
pub fn spacing_for_index(&self, index: usize) -> Spacing {
match self {
&Grouping::Ungrouped {
byte_count: _,
spacing,
} => spacing,
&Grouping::Grouped {
group_size,
num_groups: _,
byte_spacing,
group_spacing,
} => {
let elt_count = group_size.element_count();
if index % elt_count == elt_count - 1 {
group_spacing
} else {
byte_spacing
}
}
}
}
}
impl Default for Grouping {
fn default() -> Self {
Self::Grouped {
group_size: GroupSize::Short,
byte_spacing: Spacing::None,
num_groups: 8,
group_spacing: Spacing::Normal,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum GroupSize {
Byte,
Short,
Int,
Long,
ULong,
}
impl GroupSize {
pub fn element_count(self) -> usize {
match self {
Self::Byte => 1,
Self::Short => 2,
Self::Int => 4,
Self::Long => 8,
Self::ULong => 16,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Spacing {
None,
Normal,
Wide,
UltraWide,
}
impl Spacing {
pub fn as_spaces(&self) -> &'static [u8] {
match self {
Self::None => &[],
Self::Normal => " ".as_bytes(),
Self::Wide => " ".as_bytes(),
Self::UltraWide => " ".as_bytes(),
}
}
}
impl Default for HexdOptions {
fn default() -> Self {
Self {
base: Base::Hex,
autoskip: true,
uppercase: true,
print_ascii: true,
align: true,
grouping: Grouping::default(),
print_range: HexdRange {
skip: 0,
limit: None,
},
index_offset: IndexOffset::Relative(0),
}
}
}
pub trait HexdOptionsBuilder: Sized {
fn map_options<F: FnOnce(HexdOptions) -> HexdOptions>(self, f: F) -> Self;
fn with_options(self, o: HexdOptions) -> Self {
self.map_options(|_| o)
}
fn base(self, base: Base) -> Self {
self.map_options(|o| HexdOptions { base, ..o })
}
fn hexadecimal(self) -> Self {
self.base(Base::Hex).grouping(Grouping::default())
}
fn decimal(self) -> Self {
self.base(Base::Decimal(LeadingZeroChar::Space))
.ungrouped(8, Spacing::Normal)
}
fn octal(self) -> Self {
self.base(Base::Octal(LeadingZeroChar::Zero))
.ungrouped(8, Spacing::Normal)
}
fn binary(self) -> Self {
self.base(Base::Binary).ungrouped(4, Spacing::Normal)
}
fn range<R: RangeBounds<usize>>(self, range: R) -> Self {
self.map_options(|o| HexdOptions {
print_range: HexdRange::new(range),
..o
})
}
fn aligned(self, align: bool) -> Self {
self.map_options(|o| HexdOptions { align, ..o })
}
fn uppercase(self, uppercase: bool) -> Self {
self.map_options(|o| HexdOptions { uppercase, ..o })
}
fn grouping(self, grouping: Grouping) -> Self {
self.map_options(|o| HexdOptions { grouping, ..o })
}
fn ungrouped(self, num_bytes: usize, spacing: Spacing) -> Self {
self.map_options(|o| HexdOptions {
grouping: Grouping::Ungrouped {
byte_count: num_bytes,
spacing,
},
..o
})
}
fn grouped(
self,
(group_size, byte_spacing): (GroupSize, Spacing),
(num_groups, group_spacing): (usize, Spacing),
) -> Self {
self.map_options(|o| HexdOptions {
grouping: Grouping::Grouped {
group_size,
num_groups,
byte_spacing,
group_spacing,
},
..o
})
}
fn autoskip(self, autoskip: bool) -> Self {
self.map_options(|o| HexdOptions { autoskip, ..o })
}
fn offset(self, index_offset: IndexOffset) -> Self {
self.map_options(|o| HexdOptions { index_offset, ..o })
}
fn relative_offset(self, offset: usize) -> Self {
self.map_options(|o| HexdOptions {
index_offset: IndexOffset::Relative(offset),
..o
})
}
fn absolute_offset(self, offset: usize) -> Self {
self.map_options(|o| HexdOptions {
index_offset: IndexOffset::Absolute(offset),
..o
})
}
}
impl HexdOptionsBuilder for HexdOptions {
fn map_options<F: FnOnce(HexdOptions) -> HexdOptions>(self, f: F) -> Self {
f(self)
}
}
impl HexdOptions {
pub fn elt_width(&self) -> usize {
self.grouping.elt_width()
}
}