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