crypto/encoding/ternary/
t3b1.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::ops::Range;
8
9use crate::encoding::ternary::{Btrit, RawEncoding, RawEncodingBuf, ShiftTernary, Utrit};
10
11const TRITS_PER_BYTE: usize = 3;
12// Number required to push a byte between balanced and unbalanced representations
13const BALANCE_DIFF: i8 = 13;
14
15/// An encoding scheme slice that uses a single byte to represent three trits.
16#[repr(transparent)]
17pub struct T3B1([()]);
18
19impl T3B1 {
20    pub(crate) unsafe fn make(ptr: *const i8, offset: usize, len: usize) -> *const Self {
21        let len = (len << 2) | (offset % TRITS_PER_BYTE);
22        core::mem::transmute((ptr.add(offset / TRITS_PER_BYTE), len))
23    }
24
25    unsafe fn ptr(&self, index: usize) -> *const i8 {
26        let byte_offset = (self.len_offset().1 + index) / TRITS_PER_BYTE;
27        (self.0.as_ptr() as *const i8).add(byte_offset)
28    }
29
30    fn len_offset(&self) -> (usize, usize) {
31        (self.0.len() >> 2, self.0.len() & 0b11)
32    }
33}
34
35fn extract(x: i8, elem: usize) -> Btrit {
36    debug_assert!(
37        elem < TRITS_PER_BYTE,
38        "Attempted to extract invalid element {} from balanced T4B1 trit",
39        elem
40    );
41    Utrit::from_u8((((x + BALANCE_DIFF) / 3i8.pow(elem as u32)) % 3) as u8).shift()
42}
43
44fn insert(x: i8, elem: usize, trit: Btrit) -> i8 {
45    debug_assert!(
46        elem < TRITS_PER_BYTE,
47        "Attempted to insert invalid element {} into balanced T3B1 trit",
48        elem
49    );
50    let utrit = trit.shift();
51    debug_assert!((-13..=13).contains(&x));
52    let ux = x + BALANCE_DIFF;
53    let ux = ux + (utrit.into_u8() as i8 - (ux / 3i8.pow(elem as u32)) % 3) * 3i8.pow(elem as u32);
54    debug_assert!(ux - BALANCE_DIFF >= -13 && ux - BALANCE_DIFF <= 13);
55    ux - BALANCE_DIFF
56}
57
58impl RawEncoding for T3B1 {
59    type Trit = Btrit;
60    type Buf = T3B1Buf;
61
62    const TRITS_PER_BYTE: usize = TRITS_PER_BYTE;
63
64    fn empty() -> &'static Self {
65        unsafe { &*Self::make(&[] as *const _, 0, 0) }
66    }
67
68    fn len(&self) -> usize {
69        self.len_offset().0
70    }
71
72    fn as_i8_slice(&self) -> &[i8] {
73        assert!(self.len_offset().1 == 0);
74        unsafe {
75            core::slice::from_raw_parts(
76                self as *const _ as *const _,
77                (self.len() + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE,
78            )
79        }
80    }
81
82    unsafe fn as_i8_slice_mut(&mut self) -> &mut [i8] {
83        assert!(self.len_offset().1 == 0);
84        core::slice::from_raw_parts_mut(
85            self as *mut _ as *mut _,
86            (self.len() + TRITS_PER_BYTE - 1) / TRITS_PER_BYTE,
87        )
88    }
89
90    unsafe fn get_unchecked(&self, index: usize) -> Self::Trit {
91        let b = self.ptr(index).read();
92        extract(b, (self.len_offset().1 + index) % TRITS_PER_BYTE)
93    }
94
95    unsafe fn set_unchecked(&mut self, index: usize, trit: Self::Trit) {
96        let b = self.ptr(index).read();
97        let b = insert(b, (self.len_offset().1 + index) % TRITS_PER_BYTE, trit);
98        (self.ptr(index) as *mut i8).write(b);
99    }
100
101    unsafe fn slice_unchecked(&self, range: Range<usize>) -> &Self {
102        &*Self::make(
103            self.ptr(range.start),
104            (self.len_offset().1 + range.start) % TRITS_PER_BYTE,
105            range.end - range.start,
106        )
107    }
108
109    unsafe fn slice_unchecked_mut(&mut self, range: Range<usize>) -> &mut Self {
110        &mut *(Self::make(
111            self.ptr(range.start),
112            (self.len_offset().1 + range.start) % TRITS_PER_BYTE,
113            range.end - range.start,
114        ) as *mut Self)
115    }
116
117    fn is_valid(b: i8) -> bool {
118        (-BALANCE_DIFF..=BALANCE_DIFF).contains(&b)
119    }
120
121    unsafe fn from_raw_unchecked(b: &[i8], num_trits: usize) -> &Self {
122        assert!(num_trits <= b.len() * TRITS_PER_BYTE);
123        &*Self::make(b.as_ptr() as *const _, 0, num_trits)
124    }
125
126    unsafe fn from_raw_unchecked_mut(b: &mut [i8], num_trits: usize) -> &mut Self {
127        assert!(num_trits <= b.len() * TRITS_PER_BYTE);
128        &mut *(Self::make(b.as_ptr() as *const _, 0, num_trits) as *mut _)
129    }
130}
131
132/// An encoding scheme buffer that uses a single byte to represent three trits.
133#[derive(Clone)]
134pub struct T3B1Buf(Vec<i8>, usize);
135
136impl RawEncodingBuf for T3B1Buf {
137    type Slice = T3B1;
138
139    fn new() -> Self {
140        Self(Vec::new(), 0)
141    }
142
143    fn with_capacity(cap: usize) -> Self {
144        let cap = (cap / TRITS_PER_BYTE) + (cap % TRITS_PER_BYTE != 0) as usize;
145        Self(Vec::with_capacity(cap), 0)
146    }
147
148    fn clear(&mut self) {
149        self.0.clear();
150        self.1 = 0;
151    }
152
153    fn push(&mut self, trit: <Self::Slice as RawEncoding>::Trit) {
154        if self.1 % TRITS_PER_BYTE == 0 {
155            let b = insert(0, 0, trit);
156            debug_assert!(T3B1::is_valid(b));
157            self.0.push(b);
158        } else {
159            let last_index = self.0.len() - 1;
160            let b = unsafe { self.0.get_unchecked_mut(last_index) };
161            *b = insert(*b, self.1 % TRITS_PER_BYTE, trit);
162            debug_assert!(T3B1::is_valid(*b));
163        }
164        self.1 += 1;
165    }
166
167    fn pop(&mut self) -> Option<<Self::Slice as RawEncoding>::Trit> {
168        let val = if self.1 == 0 {
169            return None;
170        } else if self.1 % TRITS_PER_BYTE == 1 {
171            self.0.pop().map(|b| extract(b, 0))
172        } else {
173            let last_index = self.0.len() - 1;
174            unsafe {
175                Some(extract(
176                    *self.0.get_unchecked(last_index),
177                    (self.1 + TRITS_PER_BYTE - 1) % TRITS_PER_BYTE,
178                ))
179            }
180        };
181        self.1 -= 1;
182        val
183    }
184
185    fn as_slice(&self) -> &Self::Slice {
186        unsafe { &*Self::Slice::make(self.0.as_ptr() as _, 0, self.1) }
187    }
188
189    fn as_slice_mut(&mut self) -> &mut Self::Slice {
190        unsafe { &mut *(Self::Slice::make(self.0.as_ptr() as _, 0, self.1) as *mut _) }
191    }
192
193    fn capacity(&self) -> usize {
194        self.0.capacity() * TRITS_PER_BYTE
195    }
196}