use crate::error::{
encode_buffer_doesnt_match_bits, encode_buffer_too_big, invalid_bits, invalid_ecc_len,
total_encode_len_too_long, HumancodeError,
};
use crate::smallbytebuf::SmallByteBuf;
use core::fmt::{Debug, Display, Formatter};
use libzbase32::low_level_decode::required_octets_buffer_len;
use libzbase32::low_level_encode::{
octets_to_quintets, quintet_to_character, required_quintets_buffer_len,
};
use reed_solomon_32::encoder as reed_solomoon_encoder;
pub const CHUNK_ENCODER_0: ChunkEncoder = ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_0, 0);
pub const CHUNK_ENCODER_1: ChunkEncoder = ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_1, 1);
pub const CHUNK_ENCODER_2: ChunkEncoder = ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_2, 2);
pub const CHUNK_ENCODER_3: ChunkEncoder = ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_3, 3);
pub const CHUNK_ENCODER_4: ChunkEncoder = ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_4, 4);
pub const CHUNK_ENCODER_5: ChunkEncoder = ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_5, 5);
pub const CHUNK_ENCODER_6: ChunkEncoder = ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_6, 6);
pub const CHUNK_ENCODER_7: ChunkEncoder = ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_7, 7);
pub const CHUNK_ENCODER_8: ChunkEncoder = ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_8, 8);
pub const CHUNK_ENCODER_9: ChunkEncoder = ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_9, 9);
pub const CHUNK_ENCODER_10: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_10, 10);
pub const CHUNK_ENCODER_11: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_11, 11);
pub const CHUNK_ENCODER_12: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_12, 12);
pub const CHUNK_ENCODER_13: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_13, 13);
pub const CHUNK_ENCODER_14: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_14, 14);
pub const CHUNK_ENCODER_15: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_15, 15);
pub const CHUNK_ENCODER_16: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_16, 16);
pub const CHUNK_ENCODER_17: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_17, 17);
pub const CHUNK_ENCODER_18: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_18, 18);
pub const CHUNK_ENCODER_19: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_19, 19);
pub const CHUNK_ENCODER_20: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_20, 20);
pub const CHUNK_ENCODER_21: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_21, 21);
pub const CHUNK_ENCODER_22: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_22, 22);
pub const CHUNK_ENCODER_23: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_23, 23);
pub const CHUNK_ENCODER_24: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_24, 24);
pub const CHUNK_ENCODER_25: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_25, 25);
pub const CHUNK_ENCODER_26: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_26, 26);
pub const CHUNK_ENCODER_27: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_27, 27);
pub const CHUNK_ENCODER_28: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_28, 28);
pub const CHUNK_ENCODER_29: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_29, 29);
pub const CHUNK_ENCODER_30: ChunkEncoder =
ChunkEncoder::new(&reed_solomoon_encoder::ENCODER_30, 30);
#[derive(Copy, Clone, Debug)]
pub struct EncodedChunk {
raw: EncodedChunkRaw,
}
impl EncodedChunk {
pub(crate) fn from_quintet_buffer(quintet_buffer: &[u8]) -> EncodedChunk {
let mut encoded_data = [0u8; 31];
assert!(quintet_buffer.len() <= encoded_data.len());
for (x, &y) in encoded_data.iter_mut().zip(quintet_buffer.iter()) {
*x = quintet_to_character(y).expect("quintet_to_character() failed - which shouldn't be possible since we only pass in valid values");
}
EncodedChunk {
raw: EncodedChunkRaw {
buf: SmallByteBuf::new(encoded_data, quintet_buffer.len() as u8),
},
}
}
pub fn raw(self) -> EncodedChunkRaw {
self.raw
}
pub fn pretty(self) -> EncodedChunkPretty {
EncodedChunkPretty::from_raw(self.raw)
}
}
#[derive(Copy, Clone)]
pub struct EncodedChunkRaw {
buf: SmallByteBuf<31>,
}
impl EncodedChunkRaw {
pub fn as_str(&self) -> &str {
core::str::from_utf8(&self.buf.as_bytes())
.expect("Encoded result couldn't be converted to utf-8 - which shouldn't be possible")
}
}
impl AsRef<str> for EncodedChunkRaw {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Debug for EncodedChunkRaw {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl Display for EncodedChunkRaw {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Copy, Clone)]
pub struct EncodedChunkPretty {
buf: SmallByteBuf<38>,
}
impl EncodedChunkPretty {
fn from_raw(raw: EncodedChunkRaw) -> EncodedChunkPretty {
let mut pos = 0;
let mut encoded_data = [0u8; 38];
for &x in raw.buf.as_bytes().iter() {
if (pos + 1) % 5 == 0 {
encoded_data[pos] = b'-';
pos += 1;
}
encoded_data[pos] = x;
pos += 1;
}
EncodedChunkPretty {
buf: SmallByteBuf::new(encoded_data, pos as u8),
}
}
pub fn as_str(&self) -> &str {
core::str::from_utf8(&self.buf.as_bytes())
.expect("Encoded result couldn't be converted to utf-8 - which shouldn't be possible")
}
}
impl AsRef<str> for EncodedChunkPretty {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Debug for EncodedChunkPretty {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl Display for EncodedChunkPretty {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug)]
pub struct ChunkEncoder {
rs_encoder: &'static reed_solomoon_encoder::Encoder,
ecc: u8,
}
impl ChunkEncoder {
const fn new(rs_encoder: &'static reed_solomoon_encoder::Encoder, ecc: u8) -> ChunkEncoder {
ChunkEncoder { rs_encoder, ecc }
}
pub fn encode_chunk(&self, data: &[u8], bits: u8) -> Result<EncodedChunk, HumancodeError> {
if data.len() > 19 {
return Err(encode_buffer_too_big());
}
if bits == 0 || bits > 150 {
return Err(invalid_bits());
}
if data.len()
!= required_octets_buffer_len(bits as u64)
.expect("required_octets_buffer_len() failed - which shouldn't be possible")
{
return Err(encode_buffer_doesnt_match_bits());
}
let data_quintets_len = required_quintets_buffer_len(bits as u64)
.expect("required_quintets_buffer_len() failed - which shouldn't be possible");
let total_len = data_quintets_len + self.ecc as usize;
if total_len > 31 {
return Err(total_encode_len_too_long());
}
let mut quintets_buffer = SmallByteBuf::new([0u8; 31], data_quintets_len as u8);
octets_to_quintets(data, quintets_buffer.as_mut_bytes(), bits as u64)
.expect("octets_to_quintets() failed - which shouldn't be possible");
let rs_encoded_buffer = self
.rs_encoder
.encode(&quintets_buffer.as_bytes())
.expect("Reed Solomon 32 encode failed - which shouldn't be possible");
Ok(EncodedChunk::from_quintet_buffer(&rs_encoded_buffer))
}
}
pub fn encode_chunk(data: &[u8], ecc: u8, bits: u8) -> Result<EncodedChunk, HumancodeError> {
match ecc {
0 => CHUNK_ENCODER_0.encode_chunk(data, bits),
1 => CHUNK_ENCODER_1.encode_chunk(data, bits),
2 => CHUNK_ENCODER_2.encode_chunk(data, bits),
3 => CHUNK_ENCODER_3.encode_chunk(data, bits),
4 => CHUNK_ENCODER_4.encode_chunk(data, bits),
5 => CHUNK_ENCODER_5.encode_chunk(data, bits),
6 => CHUNK_ENCODER_6.encode_chunk(data, bits),
7 => CHUNK_ENCODER_7.encode_chunk(data, bits),
8 => CHUNK_ENCODER_8.encode_chunk(data, bits),
9 => CHUNK_ENCODER_9.encode_chunk(data, bits),
10 => CHUNK_ENCODER_10.encode_chunk(data, bits),
11 => CHUNK_ENCODER_11.encode_chunk(data, bits),
12 => CHUNK_ENCODER_12.encode_chunk(data, bits),
13 => CHUNK_ENCODER_13.encode_chunk(data, bits),
14 => CHUNK_ENCODER_14.encode_chunk(data, bits),
15 => CHUNK_ENCODER_15.encode_chunk(data, bits),
16 => CHUNK_ENCODER_16.encode_chunk(data, bits),
17 => CHUNK_ENCODER_17.encode_chunk(data, bits),
18 => CHUNK_ENCODER_18.encode_chunk(data, bits),
19 => CHUNK_ENCODER_19.encode_chunk(data, bits),
20 => CHUNK_ENCODER_20.encode_chunk(data, bits),
21 => CHUNK_ENCODER_21.encode_chunk(data, bits),
22 => CHUNK_ENCODER_22.encode_chunk(data, bits),
23 => CHUNK_ENCODER_23.encode_chunk(data, bits),
24 => CHUNK_ENCODER_24.encode_chunk(data, bits),
25 => CHUNK_ENCODER_25.encode_chunk(data, bits),
26 => CHUNK_ENCODER_26.encode_chunk(data, bits),
27 => CHUNK_ENCODER_27.encode_chunk(data, bits),
28 => CHUNK_ENCODER_28.encode_chunk(data, bits),
29 => CHUNK_ENCODER_29.encode_chunk(data, bits),
30 => CHUNK_ENCODER_30.encode_chunk(data, bits),
_ => Err(invalid_ecc_len()),
}
}