iota_conversion/
trinary.rs

1use crate::Result;
2use iota_constants::{TRINARY_RADIX, TRITS_PER_BYTE, TRITS_PER_TRYTE};
3
4lazy_static! {
5    /// Provides a byte to trits mapping
6    pub static ref BYTE_TO_TRITS_MAPPINGS: [[i8; TRITS_PER_BYTE]; 243] = {
7        let mut trits: [i8; TRITS_PER_BYTE] = [0; TRITS_PER_BYTE];
8        let mut tmp = [[0; TRITS_PER_BYTE]; 243];
9        tmp.iter_mut().for_each(|tmp_entry| {
10            tmp_entry.copy_from_slice(&trits[0..TRITS_PER_BYTE]);
11            increment(&mut trits, TRITS_PER_BYTE);
12        });
13        tmp
14    };
15    /// Provides a trytes to trits mapping
16    pub static ref TRYTE_TO_TRITS_MAPPINGS: [[i8; TRITS_PER_TRYTE]; 27] = {
17        let mut trits: [i8; TRITS_PER_BYTE] = [0; TRITS_PER_BYTE];
18        let mut tmp = [[0; TRITS_PER_TRYTE]; 27];
19        tmp.iter_mut().for_each(|tmp_entry| {
20            tmp_entry.copy_from_slice(&trits[0..TRITS_PER_TRYTE]);
21            increment(&mut trits, TRITS_PER_TRYTE);
22        });
23        tmp
24    };
25}
26
27/// Trait used to enable conversion to trinary types
28pub trait Trinary {
29    /// Provides the trit vector representation of the value
30    fn trits(&self) -> Vec<Trit>;
31    /// Provides the trit vector representation of the value with given length, padding with `0` if required
32    fn trits_with_length(&self, length: usize) -> Vec<Trit>;
33    /// Provides the tryte string representation of the value
34    fn trytes(&self) -> Result<Trytes>;
35}
36
37/// Type alias for `i8`
38pub type Trit = i8;
39/// Type alias for `String`
40pub type Trytes = String;
41
42impl Trinary for i64 {
43    fn trits(&self) -> Vec<Trit> {
44        let mut trits = Vec::new();
45        let mut abs = self.abs();
46        while abs > 0 {
47            let mut remainder = (abs % i64::from(TRINARY_RADIX as i8)) as i8;
48            abs /= i64::from(TRINARY_RADIX as i8);
49            if remainder > iota_constants::MAX_TRIT_VALUE {
50                remainder = iota_constants::MIN_TRIT_VALUE;
51                abs += 1;
52            }
53            trits.push(remainder);
54        }
55        if *self < 0 {
56            trits.iter_mut().for_each(|trit| *trit = -*trit);
57        }
58        trits
59    }
60
61    fn trits_with_length(&self, length: usize) -> Vec<Trit> {
62        trits_with_length(&self.trits(), length)
63    }
64
65    fn trytes(&self) -> Result<Trytes> {
66        self.trits().trytes()
67    }
68}
69
70impl Trinary for Vec<Trit> {
71    fn trits(&self) -> Vec<Trit> {
72        self.to_vec()
73    }
74    fn trits_with_length(&self, length: usize) -> Vec<Trit> {
75        trits_with_length(&self.trits(), length)
76    }
77    fn trytes(&self) -> Result<Trytes> {
78        trytes(self)
79    }
80}
81
82impl Trinary for &[Trit] {
83    fn trits(&self) -> Vec<Trit> {
84        self.to_vec()
85    }
86    fn trits_with_length(&self, length: usize) -> Vec<Trit> {
87        trits_with_length(&self.trits(), length)
88    }
89    fn trytes(&self) -> Result<Trytes> {
90        trytes(self)
91    }
92}
93
94impl Trinary for [Trit; 243] {
95    fn trits(&self) -> Vec<Trit> {
96        self.to_vec()
97    }
98    fn trits_with_length(&self, length: usize) -> Vec<Trit> {
99        trits_with_length(&self.trits(), length)
100    }
101    fn trytes(&self) -> Result<Trytes> {
102        ensure!(self.len() % 3 == 0, "Invalid trit length.");
103
104        self.chunks(iota_constants::TRITS_PER_TRYTE)
105            .map(trits_to_char)
106            .collect()
107    }
108}
109
110impl Trinary for Trytes {
111    fn trits(&self) -> Vec<Trit> {
112        self.chars().flat_map(char_to_trits).cloned().collect()
113    }
114    fn trits_with_length(&self, length: usize) -> Vec<Trit> {
115        trits_with_length(&self.trits(), length)
116    }
117    fn trytes(&self) -> Result<Trytes> {
118        Ok(self.clone())
119    }
120}
121
122impl Trinary for &str {
123    fn trits(&self) -> Vec<Trit> {
124        self.chars().flat_map(char_to_trits).cloned().collect()
125    }
126    fn trits_with_length(&self, length: usize) -> Vec<Trit> {
127        trits_with_length(&self.trits(), length)
128    }
129    fn trytes(&self) -> Result<Trytes> {
130        Ok((*self).to_string())
131    }
132}
133
134/// Increments a trit slice in place, only considering trits until index `size`
135fn increment(trit_array: &mut [Trit], size: usize) {
136    for trit in trit_array.iter_mut().take(size) {
137        *trit += 1;
138        if *trit > iota_constants::MAX_TRIT_VALUE {
139            *trit = iota_constants::MIN_TRIT_VALUE;
140        } else {
141            break;
142        }
143    }
144}
145
146fn char_to_trits(tryte: char) -> &'static [Trit] {
147    match iota_constants::TRYTE_ALPHABET
148        .iter()
149        .position(|&x| x == tryte)
150    {
151        Some(p) => &TRYTE_TO_TRITS_MAPPINGS[p],
152        None => &TRYTE_TO_TRITS_MAPPINGS[0],
153    }
154}
155
156fn trits_to_char(trits: &[Trit]) -> Result<char> {
157    ensure!(
158        trits.len() <= iota_constants::TRITS_PER_TRYTE,
159        "Provided trit slice is too long: {:?}",
160        trits
161    );
162    Ok(
163        match TRYTE_TO_TRITS_MAPPINGS.iter().position(|&x| x == trits) {
164            Some(p) => iota_constants::TRYTE_ALPHABET[p],
165            None => '-',
166        },
167    )
168}
169
170/// Temporary usage to convert trits to tryte strings
171pub fn trytes(trits: &[Trit]) -> Result<Trytes> {
172    ensure!(trits.len() % 3 == 0, "Invalid trit length.");
173
174    trits
175        .chunks(iota_constants::TRITS_PER_TRYTE)
176        .map(trits_to_char)
177        .collect()
178}
179
180fn trits_with_length(trits: &[Trit], length: usize) -> Vec<Trit> {
181    if trits.len() < length {
182        let mut result = vec![0; length];
183        result[..trits.len()].copy_from_slice(&trits);
184        result
185    } else {
186        trits[..length].to_vec()
187    }
188}