Skip to main content

tycho_vm/
util.rs

1use tycho_types::dict::DictKey;
2use tycho_types::error::Error;
3use tycho_types::prelude::*;
4
5/// A wrapper around [`CellSliceParts`] extending its lifetime.
6#[derive(Default, Debug, Clone)]
7#[repr(transparent)]
8pub struct OwnedCellSlice(CellSliceParts);
9
10impl OwnedCellSlice {
11    #[inline]
12    pub fn new_allow_exotic(cell: Cell) -> Self {
13        Self(CellSliceParts::from(cell))
14    }
15
16    pub fn apply(&self) -> CellSlice<'_> {
17        self.range().apply_allow_exotic(self.cell())
18    }
19
20    #[inline]
21    pub fn range(&self) -> CellSliceRange {
22        self.0.0
23    }
24
25    #[inline]
26    pub fn range_mut(&mut self) -> &mut CellSliceRange {
27        &mut self.0.0
28    }
29
30    #[inline]
31    pub fn cell(&self) -> &Cell {
32        &self.0.1
33    }
34
35    #[inline]
36    pub fn set_range(&mut self, range: CellSliceRange) {
37        self.0.0 = range
38    }
39
40    pub fn fits_into(&self, builder: &CellBuilder) -> bool {
41        let range = self.range();
42        let bits = range.size_bits();
43        let refs = range.size_refs();
44        builder.has_capacity(bits, refs)
45    }
46}
47
48impl std::fmt::Display for OwnedCellSlice {
49    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50        let cs = CellSlice::apply_allow_exotic(&self.0);
51        let cs = cs.display_as_stack_value();
52        std::fmt::Display::fmt(&cs, f)
53    }
54}
55
56pub(crate) trait CellSliceExt<'a> {
57    fn display_as_stack_value(&self) -> impl std::fmt::Display + 'a;
58}
59
60impl<'a> CellSliceExt<'a> for CellSlice<'a> {
61    fn display_as_stack_value(&self) -> impl std::fmt::Display + 'a {
62        #[repr(transparent)]
63        struct Display<'a>(CellSlice<'a>);
64
65        impl std::fmt::Display for Display<'_> {
66            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67                let cs = &self.0;
68                let refs = cs.size_refs();
69                if refs != 0 {
70                    ok!(write!(f, "("));
71                }
72                ok!(write!(f, "x{:X}", cs.display_data()));
73                if refs != 0 {
74                    write!(f, ",{refs})")
75                } else {
76                    Ok(())
77                }
78            }
79        }
80
81        Display(*self)
82    }
83}
84
85impl From<CellSliceParts> for OwnedCellSlice {
86    #[inline]
87    fn from(parts: CellSliceParts) -> Self {
88        Self(parts)
89    }
90}
91
92impl From<OwnedCellSlice> for CellSliceParts {
93    #[inline]
94    fn from(value: OwnedCellSlice) -> Self {
95        value.0
96    }
97}
98
99impl PartialEq<CellSlice<'_>> for OwnedCellSlice {
100    fn eq(&self, right: &CellSlice<'_>) -> bool {
101        let left = self.apply();
102        matches!(left.contents_eq(right), Ok(true))
103    }
104}
105
106#[repr(transparent)]
107pub struct Uint4(pub usize);
108
109impl DictKey for Uint4 {
110    const BITS: u16 = 4;
111}
112
113impl LoadDictKey for Uint4 {
114    #[inline]
115    fn load_from_data(data: &CellDataBuilder) -> Option<Self> {
116        let raw_data = data.raw_data();
117        Some(Self((raw_data[0] & 0xf) as usize))
118    }
119}
120
121impl StoreDictKey for Uint4 {
122    #[inline]
123    fn store_into_data(&self, data: &mut CellDataBuilder) -> Result<(), Error> {
124        if self.0 > 0xf {
125            return Err(Error::IntOverflow);
126        }
127        data.store_small_uint(self.0 as _, 4)
128    }
129}
130
131impl Store for Uint4 {
132    #[inline]
133    fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
134        self.store_into_data(builder.as_mut())
135    }
136}
137
138impl Load<'_> for Uint4 {
139    #[inline]
140    fn load_from(slice: &mut CellSlice<'_>) -> Result<Self, Error> {
141        Ok(Self(ok!(slice.load_small_uint(4)) as usize))
142    }
143}
144
145pub const fn shift_ceil_price(value: u128) -> u128 {
146    let r = value & 0xffff != 0;
147    (value >> 16) + r as u128
148}
149
150pub fn ensure_empty_slice(slice: &CellSlice) -> Result<(), Error> {
151    if slice.is_data_empty() && slice.is_refs_empty() {
152        Ok(())
153    } else {
154        Err(Error::InvalidData)
155    }
156}
157
158pub fn load_uint_leq(cs: &mut CellSlice, upper_bound: u32) -> Result<u64, Error> {
159    let bits = 32 - upper_bound.leading_zeros();
160    let result = cs.load_uint(bits as u16)?;
161    if result > upper_bound as u64 {
162        return Err(Error::IntOverflow);
163    };
164    Ok(result)
165}
166
167pub fn remove_trailing(slice: &mut CellSlice<'_>) -> Result<(), tycho_types::error::Error> {
168    let bits = slice.size_bits();
169    if bits == 0 {
170        return Ok(());
171    }
172
173    let n = ok!(slice.count_trailing(false));
174    // NOTE: Skip one additional bit for non-empty slice
175    slice.skip_last(n + (n != bits) as u16, 0)
176}