bee_ternary/
b1t6.rs

1// Copyright 2020-2021 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4extern crate alloc;
5
6use alloc::vec::Vec;
7
8use crate::{Btrit, RawEncoding, RawEncodingBuf, TritBuf, Trits, Tryte};
9
10const TRYTES_PER_BYTE: usize = 2;
11const TRITS_PER_TRYTE: usize = 3;
12const TRITS_PER_BYTE: usize = TRYTES_PER_BYTE * TRITS_PER_TRYTE;
13
14/// An error that may be emitted when decoding a B1T6 trit slice.
15#[derive(Debug)]
16pub enum DecodeError {
17    /// Two trits had an invalid B1T6 representation.
18    InvalidTrytes([Tryte; 2]),
19}
20
21/// Decode a series of trits into bytes.
22pub fn decode(src: &Trits) -> Result<Vec<u8>, DecodeError> {
23    assert!(src.len() % TRITS_PER_BYTE == 0);
24    src.iter_trytes()
25        .step_by(TRYTES_PER_BYTE)
26        .zip(src[TRITS_PER_TRYTE..].iter_trytes().step_by(TRYTES_PER_BYTE))
27        .map(|(a, b)| decode_group(a, b).ok_or(DecodeError::InvalidTrytes([a, b])))
28        .collect()
29}
30
31fn decode_group(t1: Tryte, t2: Tryte) -> Option<u8> {
32    Some(i8::try_from(t1 as isize + t2 as isize * 27).ok()? as u8)
33}
34
35/// Encode a series of bytes into trits.
36pub fn encode<T: RawEncodingBuf>(bytes: &[u8]) -> TritBuf<T>
37where
38    T::Slice: RawEncoding<Trit = Btrit>,
39{
40    let mut trits = TritBuf::with_capacity(bytes.len() * TRITS_PER_BYTE);
41
42    for byte in bytes {
43        let (t1, t2) = encode_group(*byte);
44        [t1, t2]
45            .iter()
46            // Unwrap is safe, `encode_group` is valid for all inputs
47            .for_each(|b| trits.append(Tryte::try_from(*b).unwrap().as_trits()));
48    }
49
50    trits
51}
52
53fn encode_group(byte: u8) -> (i8, i8) {
54    let v = (byte as i8) as i16 + (27 / 2) * 27 + 27 / 2;
55    let quo = (v / 27) as i8;
56    let rem = (v % 27) as i8;
57
58    (rem + Tryte::MIN_VALUE as i8, quo + Tryte::MIN_VALUE as i8)
59}