crypto/encoding/ternary/
tryte.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::{
8    convert::TryFrom,
9    fmt,
10    iter::FromIterator,
11    ops::{Deref, DerefMut},
12    str::FromStr,
13};
14
15use crate::encoding::ternary::{Btrit, Error, Trits, T3B1};
16
17/// A ternary tryte. Equivalent to 3 trits.
18#[derive(Copy, Clone, Eq, Hash, PartialEq)]
19#[repr(i8)]
20#[allow(missing_docs)]
21pub enum Tryte {
22    N = -13,
23    O = -12,
24    P = -11,
25    Q = -10,
26    R = -9,
27    S = -8,
28    T = -7,
29    U = -6,
30    V = -5,
31    W = -4,
32    X = -3,
33    Y = -2,
34    Z = -1,
35    Nine = 0,
36    A = 1,
37    B = 2,
38    C = 3,
39    D = 4,
40    E = 5,
41    F = 6,
42    G = 7,
43    H = 8,
44    I = 9,
45    J = 10,
46    K = 11,
47    L = 12,
48    M = 13,
49}
50
51impl Tryte {
52    /// The minimum value that this [`Tryte`] can hold.
53    pub const MIN_VALUE: Self = Tryte::N;
54
55    /// The maximum value that this [`Tryte`] can hold.
56    pub const MAX_VALUE: Self = Tryte::M;
57
58    /// Turn an array of three [`Btrit`]s into a [`Tryte`].
59    pub fn from_trits(trits: [Btrit; 3]) -> Self {
60        let x = i8::from(trits[0]) + i8::from(trits[1]) * 3 + i8::from(trits[2]) * 9;
61        Tryte::try_from(x).unwrap()
62    }
63
64    /// Interpret this tryte as a [`T3B1`] trit slice with exactly 3 elements.
65    pub fn as_trits(&self) -> &Trits<T3B1> {
66        unsafe { &*(T3B1::make(self as *const _ as *const _, 0, 3) as *const _) }
67    }
68
69    /// Interpret this tryte as a mutable [`T3B1`] trit slice with exactly 3 elements.
70    pub fn as_trits_mut(&mut self) -> &mut Trits<T3B1> {
71        unsafe { &mut *(T3B1::make(self as *const _ as *const _, 0, 3) as *mut _) }
72    }
73}
74
75impl From<Tryte> for char {
76    fn from(tryte: Tryte) -> char {
77        match tryte as i8 {
78            0 => '9',
79            -13..=-1 => (((tryte as i8 + 13) as u8) + b'N') as char,
80            1..=13 => (((tryte as i8 - 1) as u8) + b'A') as char,
81            x => unreachable!("Tried to decode Tryte with variant {}", x),
82        }
83    }
84}
85
86impl From<[Btrit; 3]> for Tryte {
87    fn from(trits: [Btrit; 3]) -> Self {
88        Self::from_trits(trits)
89    }
90}
91
92impl fmt::Debug for Tryte {
93    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94        write!(f, "{:?}", char::from(*self))
95    }
96}
97
98impl fmt::Display for Tryte {
99    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
100        write!(f, "{}", char::from(*self))
101    }
102}
103
104impl TryFrom<char> for Tryte {
105    type Error = Error;
106
107    fn try_from(c: char) -> Result<Self, Self::Error> {
108        match c {
109            '9' => Ok(Tryte::Nine),
110            'N'..='Z' => Ok(unsafe { core::mem::transmute::<i8, Self>((c as u8 - b'N') as i8 - 13) }),
111            'A'..='M' => Ok(unsafe { core::mem::transmute::<i8, Self>((c as u8 - b'A') as i8 + 1) }),
112            _ => Err(Error::InvalidRepr),
113        }
114    }
115}
116
117impl TryFrom<i8> for Tryte {
118    type Error = Error;
119
120    fn try_from(x: i8) -> Result<Self, Self::Error> {
121        match x {
122            -13..=13 => Ok(unsafe { core::mem::transmute::<i8, Self>(x) }),
123            _ => Err(Error::InvalidRepr),
124        }
125    }
126}
127
128/// A buffer of [`Tryte`]s.
129/// Analogous to [`Vec`].
130#[derive(Default)]
131pub struct TryteBuf {
132    inner: Vec<Tryte>,
133}
134
135impl TryteBuf {
136    /// Create a new empty buffer.
137    pub fn new() -> Self {
138        Self::default()
139    }
140
141    /// Create a new empty buffer with room for `cap` trytes.
142    pub fn with_capacity(cap: usize) -> Self {
143        Self {
144            inner: Vec::with_capacity(cap),
145        }
146    }
147
148    /// Attempt to parse a string into a tryte buffer.
149    pub fn try_from_str(s: &str) -> Result<Self, Error> {
150        s.chars().map(Tryte::try_from).collect()
151    }
152
153    /// Returns `true` if the buffer is empty.
154    pub fn is_empty(&self) -> bool {
155        self.len() == 0
156    }
157
158    /// Returns the number of trytes in the buffer.
159    pub fn len(&self) -> usize {
160        self.inner.len()
161    }
162
163    /// Push a new tryte to the end of the buffer.
164    pub fn push(&mut self, tryte: Tryte) {
165        self.inner.push(tryte);
166    }
167
168    /// Attempt to pop a tryte from the end of the buffer.
169    pub fn pop(&mut self) -> Option<Tryte> {
170        self.inner.pop()
171    }
172
173    /// Safely interpret this tryte buffer as a [`T3B1`] trit slice.
174    pub fn as_trits(&self) -> &Trits<T3B1> {
175        unsafe { &*(T3B1::make(self.as_ptr() as *const _, 0, self.len() * 3) as *const _) }
176    }
177
178    /// Safely interpret this tryte buffer as a mutable [`T3B1`] trit slice.
179    pub fn as_trits_mut(&mut self) -> &mut Trits<T3B1> {
180        unsafe { &mut *(T3B1::make(self.as_ptr() as *const _, 0, self.len() * 3) as *mut _) }
181    }
182}
183
184impl Deref for TryteBuf {
185    type Target = [Tryte];
186    fn deref(&self) -> &Self::Target {
187        &self.inner
188    }
189}
190
191impl DerefMut for TryteBuf {
192    fn deref_mut(&mut self) -> &mut Self::Target {
193        &mut self.inner
194    }
195}
196
197impl FromIterator<Tryte> for TryteBuf {
198    fn from_iter<I: IntoIterator<Item = Tryte>>(iter: I) -> Self {
199        let iter = iter.into_iter();
200        let mut this = Self::with_capacity(iter.size_hint().0);
201        for tryte in iter {
202            this.push(tryte);
203        }
204        this
205    }
206}
207
208impl<'a> From<&'a [Tryte]> for TryteBuf {
209    fn from(trytes: &'a [Tryte]) -> Self {
210        Self { inner: trytes.to_vec() }
211    }
212}
213
214impl FromStr for TryteBuf {
215    type Err = Error;
216
217    fn from_str(s: &str) -> Result<Self, Self::Err> {
218        Self::try_from_str(s)
219    }
220}
221
222impl fmt::Debug for TryteBuf {
223    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224        write!(f, "{:?}", self.inner)
225    }
226}
227
228impl fmt::Display for TryteBuf {
229    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
230        for tryte in self.iter() {
231            write!(f, "{}", tryte)?;
232        }
233        Ok(())
234    }
235}