use core::iter::{FusedIterator, Iterator};
pub struct BCDBuffer {
pub bytes: [u8; 20],
pub i: u8,
}
impl BCDBuffer {
#[inline]
pub fn new() -> Self {
BCDBuffer {
bytes: [0; 20],
i: 0,
}
}
pub fn push_str(&mut self, s: &str) {
debug_assert!(s.is_ascii(), "non-ascii passed into BCDBuffer::push_str");
s.bytes().for_each(|b| self.push_digit_u8(b));
}
pub fn push_ascii_bytes(&mut self, bytes: &[u8]) {
debug_assert!(
bytes.is_ascii(),
"non-ascii passed into BCDBuffer::push_ascii_bytes"
);
bytes.iter().for_each(|b| self.push_digit_u8(*b));
}
pub fn push_digit_u8(&mut self, b: u8) {
debug_assert!(
b.is_ascii_digit(),
"non-ascii digit passed into BCDBuffer::push_digit_u8"
);
let nybble: u8 = b.saturating_sub(0x30);
self.push_nybble(nybble);
}
pub fn push_nybble(&mut self, n: u8) {
let byte_index = self.i >> 1;
if (self.i % 2) > 0 {
self.bytes[byte_index as usize] |= n;
} else {
self.bytes[byte_index as usize] |= n << 4;
}
self.i += 1;
self.i = self.i.clamp(0, 39);
}
pub fn push_byte(&mut self, byte: u8) {
let byte_index = self.len_in_bytes();
self.bytes[byte_index] = byte;
self.i += if (self.i % 2) > 0 { 3 } else { 2 };
self.i = self.i.clamp(0, 39);
}
pub fn len_in_bytes(&self) -> usize {
((self.i >> 1) + (self.i % 2)) as usize
}
}
impl AsRef<[u8]> for BCDBuffer {
fn as_ref(&self) -> &[u8] {
&self.bytes[0..self.len_in_bytes()]
}
}
#[derive(Debug, Clone)]
pub struct BCDDigitsIter<'a> {
bytes: &'a [u8],
least_sig_nybble: bool,
leading_0_sig: bool,
processing_leading_digits: bool,
ignore_last_nybble: bool,
}
impl<'a> BCDDigitsIter<'a> {
#[inline]
pub fn new(
bytes: &'a [u8],
leading_0_sig: bool,
ignore_last_nybble: bool,
least_sig_nybble: bool,
processing_leading_digits: bool,
) -> BCDDigitsIter<'a> {
BCDDigitsIter {
bytes,
leading_0_sig,
ignore_last_nybble,
processing_leading_digits, least_sig_nybble, }
}
}
pub type ShouldBeASCIIDigit = u8;
impl<'a> Iterator for BCDDigitsIter<'a> {
type Item = ShouldBeASCIIDigit;
fn next(&mut self) -> Option<Self::Item> {
while self.bytes.len() > 0 {
let nybble: u8 = if self.least_sig_nybble {
self.bytes[0] & 0b0000_1111
} else {
(self.bytes[0] & 0b1111_0000) >> 4
};
if self.least_sig_nybble {
self.least_sig_nybble = false;
self.bytes = &self.bytes[1..];
} else {
self.least_sig_nybble = true;
}
if self.processing_leading_digits {
let leading_digit: u8 = if self.leading_0_sig { 1 } else { 0 };
if nybble == leading_digit {
continue;
} else {
self.processing_leading_digits = false;
}
}
if self.bytes.len() == 0 && (nybble == 0b1111 || self.ignore_last_nybble) {
return None;
}
return Some(nybble);
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
let mut max_digits = self.bytes.len() << 1; if self.least_sig_nybble {
max_digits = max_digits.saturating_sub(1);
}
if self.ignore_last_nybble {
max_digits = max_digits.saturating_sub(1);
}
(0, Some(max_digits))
}
}
impl<'a> FusedIterator for BCDDigitsIter<'a> {}
#[cfg(test)]
mod tests {
use crate::bcd::BCDBuffer;
#[test]
fn test_bcd_buffer_1() {
let mut bcd = BCDBuffer::new();
assert_eq!(bcd.len_in_bytes(), 0);
bcd.push_digit_u8(b'9');
assert_eq!(bcd.len_in_bytes(), 1);
bcd.push_digit_u8(0x37);
assert_eq!(bcd.len_in_bytes(), 1);
bcd.push_nybble(0x05);
assert_eq!(bcd.len_in_bytes(), 2);
bcd.push_byte(0x33);
assert_eq!(bcd.len_in_bytes(), 3);
assert_eq!(bcd.as_ref(), [0x97, 0x50, 0x33].as_slice());
assert_eq!(bcd.len_in_bytes(), 3);
}
#[test]
fn test_bcd_buffer_2() {
let mut bcd = BCDBuffer::new();
assert_eq!(bcd.len_in_bytes(), 0);
bcd.push_digit_u8(0x39);
assert_eq!(bcd.len_in_bytes(), 1);
bcd.push_ascii_bytes([0x37, 0x35].as_slice());
assert_eq!(bcd.len_in_bytes(), 2);
bcd.push_str("31");
assert_eq!(bcd.len_in_bytes(), 3);
bcd.push_nybble(0xF);
assert_eq!(bcd.len_in_bytes(), 3);
assert_eq!(bcd.as_ref(), [0x97, 0x53, 0x1F].as_slice());
assert_eq!(bcd.len_in_bytes(), 3);
}
}