use crate::error::{
input_buffer_doesnt_match_bits, invalid_quintet, output_buffer_doesnt_match_bits,
};
use crate::stateful_encoder::{
octet_has_valid_trailing_bits, HaveQuintets, NeedOctets, NextQuintetResult, ProvideOctetResult,
};
use crate::tables::QUINTET_TO_CHARACTER;
use crate::util::{required_octets_buffer_len, required_quintets_buffer_len};
use crate::ZBase32Error;
use core::iter::Peekable;
enum OctetsToQuintetsIterState {
Initial(NeedOctets),
HaveQuintets(HaveQuintets),
}
struct OctetsToQuintetsIter<I>
where
I: Iterator<Item = u8>,
{
octet_iter: Peekable<I>,
state: Option<OctetsToQuintetsIterState>,
}
impl<I> OctetsToQuintetsIter<I>
where
I: Iterator<Item = u8>,
{
fn new(octet_iter: I, need_octets: NeedOctets) -> OctetsToQuintetsIter<I> {
OctetsToQuintetsIter {
octet_iter: octet_iter.peekable(),
state: Some(OctetsToQuintetsIterState::Initial(need_octets)),
}
}
}
fn refill<I>(
octet_iter: &mut Peekable<I>,
mut need_octets: NeedOctets,
) -> Result<Option<OctetsToQuintetsIterState>, ZBase32Error>
where
I: Iterator<Item = u8>,
{
loop {
let octet = octet_iter.next().unwrap();
let last_octet = octet_iter.peek().is_none();
match need_octets.provide_octet(octet, last_octet)? {
ProvideOctetResult::NeedOctets(need_more) => need_octets = need_more,
ProvideOctetResult::HaveQuintets(have_quintets) => {
return Ok(Some(OctetsToQuintetsIterState::HaveQuintets(have_quintets)))
}
}
}
}
impl<I> Iterator for OctetsToQuintetsIter<I>
where
I: Iterator<Item = u8>,
{
type Item = Result<u8, ZBase32Error>;
fn next(&mut self) -> Option<<Self as Iterator>::Item> {
loop {
if self.state.is_none() {
return None;
}
match self.state.take().unwrap() {
OctetsToQuintetsIterState::Initial(need_octets) => {
if self.octet_iter.peek().is_some() {
match refill(&mut self.octet_iter, need_octets) {
Ok(new_state) => self.state = new_state,
Err(err) => return Some(Err(err)),
}
} else {
}
}
OctetsToQuintetsIterState::HaveQuintets(have_quintets) => {
match have_quintets.next_quintet() {
NextQuintetResult::Quintet(quintet, have_quintets) => {
self.state =
Some(OctetsToQuintetsIterState::HaveQuintets(have_quintets));
return Some(Ok(quintet));
}
NextQuintetResult::NeedOctets(need_octets) => {
match refill(&mut self.octet_iter, need_octets) {
Ok(new_state) => self.state = new_state,
Err(err) => return Some(Err(err)),
}
}
NextQuintetResult::Complete => {}
}
}
}
}
}
}
pub fn quintet_to_character(quintet: u8) -> Result<u8, ZBase32Error> {
if quintet as usize > QUINTET_TO_CHARACTER.len() {
return Err(invalid_quintet());
}
Ok(QUINTET_TO_CHARACTER[quintet as usize])
}
fn calc_last_octet_bits(bits: u64) -> Option<u8> {
if bits == 0 {
None
} else {
match bits % 8 {
0 => Some(8),
x => Some(x as u8),
}
}
}
pub fn is_last_octet_valid(bits: u64, octet: u8) -> bool {
if let Some(last_octet_bits) = calc_last_octet_bits(bits) {
octet_has_valid_trailing_bits(last_octet_bits, octet)
} else {
false
}
}
pub fn octets_to_quintets(
in_octets: &[u8],
out_quintets: &mut [u8],
bits: u64,
) -> Result<(), ZBase32Error> {
if in_octets.len() != required_octets_buffer_len(bits)? {
return Err(input_buffer_doesnt_match_bits().into());
}
if out_quintets.len() != required_quintets_buffer_len(bits)? {
return Err(output_buffer_doesnt_match_bits().into());
}
let last_octet_bits = if let Some(x) = calc_last_octet_bits(bits) {
x
} else {
return Ok(());
};
let quintet_iter = OctetsToQuintetsIter::new(
in_octets.iter().map(|&x| x),
NeedOctets::new(last_octet_bits),
);
for (out_quintet, in_quintet) in out_quintets.iter_mut().zip(quintet_iter) {
*out_quintet = in_quintet?;
}
Ok(())
}
pub fn encode_slices(
in_octets: &[u8],
out_characters: &mut [u8],
bits: u64,
) -> Result<(), ZBase32Error> {
if in_octets.len() != required_octets_buffer_len(bits)? {
return Err(input_buffer_doesnt_match_bits().into());
}
if out_characters.len() != required_quintets_buffer_len(bits)? {
return Err(output_buffer_doesnt_match_bits().into());
}
let last_octet_bits = if let Some(x) = calc_last_octet_bits(bits) {
x
} else {
return Ok(());
};
let quintet_iter = OctetsToQuintetsIter::new(
in_octets.iter().map(|&x| x),
NeedOctets::new(last_octet_bits),
);
for (out_quintet, in_quintet) in out_characters.iter_mut().zip(quintet_iter) {
*out_quintet = quintet_to_character(in_quintet?)?;
}
Ok(())
}
#[cfg(feature = "std")]
pub fn encode(input: &[u8], output: &mut String, bits: u64) -> Result<(), ZBase32Error> {
if input.len() != required_octets_buffer_len(bits)? {
return Err(input_buffer_doesnt_match_bits().into());
}
let last_octet_bits = if let Some(x) = calc_last_octet_bits(bits) {
x
} else {
return Ok(());
};
let needed_quintets = required_quintets_buffer_len(bits)?;
output.reserve(needed_quintets);
let quintet_iter =
OctetsToQuintetsIter::new(input.iter().map(|&x| x), NeedOctets::new(last_octet_bits));
for quintet in quintet_iter {
output.push(quintet_to_character(quintet?)? as char);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::encode;
use crate::test_data::{TestCase, RANDOM_TEST_DATA, STANDARD_TEST_DATA};
fn run_tests(test_cases: &[TestCase]) {
let mut buffer = String::new();
for test in test_cases {
buffer.clear();
encode(test.unencoded, &mut buffer, test.bits).unwrap();
assert_eq!(&buffer, test.encoded);
}
}
#[test]
fn test_encode_standard() {
run_tests(STANDARD_TEST_DATA);
}
#[test]
fn test_encode_random() {
run_tests(RANDOM_TEST_DATA);
}
}