Skip to main content

everscale_types/cell/cell_impl/
sync.rs

1use std::alloc::Layout;
2use std::borrow::Borrow;
3use std::sync::atomic::AtomicUsize;
4use std::sync::{Arc, OnceLock, Weak};
5
6use super::{
7    EmptyOrdinaryCell, HeaderWithData, LibraryReference, OrdinaryCell, OrdinaryCellHeader,
8    PrunedBranch, PrunedBranchHeader, VirtualCell, ALL_ONES_CELL, ALL_ZEROS_CELL,
9};
10use crate::cell::cell_context::{CellContext, CellParts, LoadMode};
11use crate::cell::{CellFamily, CellImpl, CellType, DynCell, HashBytes};
12use crate::error::Error;
13use crate::util::TryAsMut;
14
15/// Thread-safe cell.
16#[derive(Clone, Eq)]
17#[repr(transparent)]
18pub struct Cell(pub(crate) CellInner);
19
20/// Inner representation of the cell.
21pub type CellInner<T = DynCell> = Arc<T>;
22
23impl Cell {
24    /// Unwraps the root cell from the usage tracking.
25    #[inline]
26    pub fn untrack(self) -> Self {
27        self.0.untrack()
28    }
29
30    /// Creates a new [`WeakCell`] reference to this cell.
31    pub fn downgrade(this: &Cell) -> WeakCell {
32        WeakCell(Arc::downgrade(&this.0))
33    }
34}
35
36impl Default for Cell {
37    #[inline]
38    fn default() -> Self {
39        Cell::empty_cell()
40    }
41}
42
43impl std::ops::Deref for Cell {
44    type Target = DynCell;
45
46    #[inline]
47    fn deref(&self) -> &Self::Target {
48        self.0.as_ref()
49    }
50}
51
52impl AsRef<DynCell> for Cell {
53    #[inline]
54    fn as_ref(&self) -> &DynCell {
55        self.0.as_ref()
56    }
57}
58
59impl Borrow<DynCell> for Cell {
60    #[inline]
61    fn borrow(&self) -> &DynCell {
62        self.0.borrow()
63    }
64}
65
66impl std::fmt::Debug for Cell {
67    #[inline]
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        std::fmt::Debug::fmt(self.0.as_ref(), f)
70    }
71}
72
73impl PartialEq for Cell {
74    fn eq(&self, other: &Self) -> bool {
75        self.0.as_ref() == other.0.as_ref()
76    }
77}
78
79impl From<Cell> for CellInner {
80    #[inline]
81    fn from(value: Cell) -> Self {
82        value.0
83    }
84}
85
86impl From<CellInner> for Cell {
87    #[inline]
88    fn from(value: CellInner) -> Self {
89        Self(value)
90    }
91}
92
93impl<T: CellImpl + Send + Sync + 'static> From<CellInner<T>> for Cell {
94    #[inline]
95    fn from(value: CellInner<T>) -> Self {
96        Self(value as CellInner)
97    }
98}
99
100impl CellFamily for Cell {
101    type EmptyCellContext = EmptyCellContext;
102
103    fn empty_cell() -> Cell {
104        static EMPTY_CELL: OnceLock<Cell> = OnceLock::new();
105        EMPTY_CELL
106            .get_or_init(|| Cell(Arc::new(EmptyOrdinaryCell)))
107            .clone()
108    }
109
110    #[inline]
111    fn empty_cell_ref() -> &'static DynCell {
112        &EmptyOrdinaryCell
113    }
114
115    #[inline]
116    fn empty_context() -> Self::EmptyCellContext {
117        EmptyCellContext
118    }
119
120    #[inline]
121    fn all_zeros_ref() -> &'static DynCell {
122        &ALL_ZEROS_CELL
123    }
124
125    #[inline]
126    fn all_ones_ref() -> &'static DynCell {
127        &ALL_ONES_CELL
128    }
129
130    fn virtualize(cell: Cell) -> Cell {
131        let descriptor = cell.as_ref().descriptor();
132        if descriptor.level_mask().is_empty() {
133            cell
134        } else {
135            Cell(Arc::new(VirtualCell(cell)))
136        }
137    }
138}
139
140impl<T: ?Sized> TryAsMut<T> for Arc<T> {
141    #[inline]
142    fn try_as_mut(&mut self) -> Option<&mut T> {
143        Arc::get_mut(self)
144    }
145}
146
147impl TryAsMut<DynCell> for Cell {
148    #[inline]
149    fn try_as_mut(&mut self) -> Option<&mut DynCell> {
150        Arc::get_mut(&mut self.0)
151    }
152}
153
154/// A non-owning reference to a [`Cell`].
155#[derive(Clone)]
156#[repr(transparent)]
157pub struct WeakCell(Weak<DynCell>);
158
159impl WeakCell {
160    /// Attempts to upgrade the `WeakCell` to a [`Cell`],
161    /// extending the lifetime of the data.
162    ///
163    /// Returns [`None`] if the inner value has since been dropped.
164    #[inline]
165    pub fn upgrade(&self) -> Option<Cell> {
166        self.0.upgrade().map(Cell)
167    }
168}
169
170/// Empty context for thread-safe cells.
171#[derive(Debug, Default, Clone, Copy)]
172pub struct EmptyCellContext;
173
174impl CellContext for EmptyCellContext {
175    fn finalize_cell(&mut self, ctx: CellParts) -> Result<Cell, Error> {
176        let hashes = ok!(ctx.compute_hashes());
177        // SAFETY: ctx now represents a well-formed cell
178        Ok(unsafe { make_cell(ctx, hashes) })
179    }
180
181    #[inline]
182    fn load_cell(&mut self, cell: Cell, _: LoadMode) -> Result<Cell, Error> {
183        Ok(cell)
184    }
185
186    #[inline]
187    fn load_dyn_cell<'a>(&mut self, cell: &'a DynCell, _: LoadMode) -> Result<&'a DynCell, Error> {
188        Ok(cell)
189    }
190}
191
192unsafe fn make_cell(ctx: CellParts, hashes: Vec<(HashBytes, u16)>) -> Cell {
193    match ctx.descriptor.cell_type() {
194        CellType::PrunedBranch => {
195            debug_assert!(hashes.len() == 1);
196            let repr = hashes.get_unchecked(0);
197
198            make_pruned_branch(
199                PrunedBranchHeader {
200                    repr_hash: repr.0,
201                    level: ctx.descriptor.level_mask().level(),
202                    descriptor: ctx.descriptor,
203                },
204                ctx.data,
205            )
206        }
207        CellType::LibraryReference => {
208            debug_assert!(hashes.len() == 1);
209            let repr = hashes.get_unchecked(0);
210
211            debug_assert!(ctx.descriptor.byte_len() == 33);
212            debug_assert!(ctx.data.len() == 33);
213
214            Cell(Arc::new(LibraryReference {
215                repr_hash: repr.0,
216                descriptor: ctx.descriptor,
217                data: *(ctx.data.as_ptr() as *const [u8; 33]),
218            }))
219        }
220        CellType::Ordinary if ctx.descriptor.d1 == 0 && ctx.descriptor.d2 == 0 => {
221            Cell(Arc::new(EmptyOrdinaryCell))
222        }
223        _ => make_ordinary_cell(
224            OrdinaryCellHeader {
225                bit_len: ctx.bit_len,
226                #[cfg(feature = "stats")]
227                stats: ctx.stats,
228                hashes,
229                descriptor: ctx.descriptor,
230                references: ctx.references.into_inner(),
231                without_first: false,
232            },
233            ctx.data,
234        ),
235    }
236}
237
238/// Constructs an `ArcCell` from well-formed cell header and data.
239///
240/// # Safety
241///
242/// The following must be true:
243/// - Header references array must be consistent with the descriptor.
244/// - Data length in bytes must be in range 0..=128.
245unsafe fn make_ordinary_cell(header: OrdinaryCellHeader, data: &[u8]) -> Cell {
246    define_gen_vtable_ptr!((const N: usize) => OrdinaryCell<N>);
247
248    const VTABLES: [*const (); 9] = [
249        gen_vtable_ptr::<0>(),
250        gen_vtable_ptr::<8>(), // 1, aligned to 8
251        gen_vtable_ptr::<8>(), // 2, aligned to 8
252        gen_vtable_ptr::<8>(), // 4, aligned to 8
253        gen_vtable_ptr::<8>(),
254        gen_vtable_ptr::<16>(),
255        gen_vtable_ptr::<32>(),
256        gen_vtable_ptr::<64>(),
257        gen_vtable_ptr::<128>(),
258    ];
259
260    type EmptyCell = OrdinaryCell<0>;
261
262    // Clamp data to 0..=128 bytes range
263    let raw_data_len = data.len();
264    debug_assert!(raw_data_len <= 128);
265
266    // Compute nearest target data length and vtable
267    let (target_data_len, vtable) = if raw_data_len == 0 {
268        (0, VTABLES[0])
269    } else {
270        let len = std::cmp::max(raw_data_len, 8).next_power_of_two();
271        let vtable = *VTABLES.get_unchecked(1 + len.trailing_zeros() as usize);
272        (len, vtable)
273    };
274    debug_assert!(raw_data_len <= target_data_len);
275
276    // Compute object layout
277    type InnerOrdinaryCell<const N: usize> = ArcInner<AtomicUsize, OrdinaryCell<N>>;
278
279    const ALIGN: usize = std::mem::align_of::<InnerOrdinaryCell<0>>();
280    const _: () = assert!(
281        ALIGN == std::mem::align_of::<InnerOrdinaryCell<8>>()
282            && ALIGN == std::mem::align_of::<InnerOrdinaryCell<16>>()
283            && ALIGN == std::mem::align_of::<InnerOrdinaryCell<32>>()
284            && ALIGN == std::mem::align_of::<InnerOrdinaryCell<64>>()
285            && ALIGN == std::mem::align_of::<InnerOrdinaryCell<128>>()
286    );
287
288    const ARC_DATA_OFFSET: usize =
289        offset_of!(ArcInner<usize, EmptyCell>, obj) + offset_of!(EmptyCell, data);
290
291    let size = (ARC_DATA_OFFSET + target_data_len + ALIGN - 1) & !(ALIGN - 1);
292    let layout = Layout::from_size_align_unchecked(size, ALIGN).pad_to_align();
293
294    // Make ArcCell
295    make_arc_cell::<OrdinaryCellHeader, 0>(layout, header, data.as_ptr(), raw_data_len, vtable)
296}
297
298unsafe fn make_pruned_branch(header: PrunedBranchHeader, data: &[u8]) -> Cell {
299    define_gen_vtable_ptr!((const N: usize) => PrunedBranch<N>);
300
301    const LENGTHS: [usize; 3] = [
302        PrunedBranchHeader::cell_data_len(1),
303        PrunedBranchHeader::cell_data_len(2),
304        PrunedBranchHeader::cell_data_len(3),
305    ];
306
307    const VTABLES: [*const (); 3] = [
308        gen_vtable_ptr::<{ LENGTHS[0] }>(),
309        gen_vtable_ptr::<{ LENGTHS[1] }>(),
310        gen_vtable_ptr::<{ LENGTHS[2] }>(),
311    ];
312
313    type EmptyCell = PrunedBranch<{ LENGTHS[0] }>;
314
315    // Compute nearest target data length and vtable
316    let data_len = PrunedBranchHeader::cell_data_len(header.level as usize);
317    debug_assert!((1..=3).contains(&header.level));
318    debug_assert_eq!(data_len, data.len());
319    debug_assert_eq!(data_len, header.descriptor.byte_len() as usize);
320
321    let vtable = *VTABLES.get_unchecked((header.level - 1) as usize);
322
323    // Compute object layout
324    type InnerPrunedBranch<const N: usize> = ArcInner<AtomicUsize, PrunedBranch<N>>;
325
326    const ALIGN: usize = std::mem::align_of::<InnerPrunedBranch<{ LENGTHS[0] }>>();
327    const _: () = assert!(
328        ALIGN == std::mem::align_of::<InnerPrunedBranch<{ LENGTHS[1] }>>()
329            && ALIGN == std::mem::align_of::<InnerPrunedBranch<{ LENGTHS[2] }>>()
330    );
331
332    const ARC_DATA_OFFSET: usize =
333        offset_of!(ArcInner<usize, EmptyCell>, obj) + offset_of!(EmptyCell, data);
334
335    let size = (ARC_DATA_OFFSET + data_len + ALIGN - 1) & !(ALIGN - 1);
336    let layout = Layout::from_size_align_unchecked(size, ALIGN).pad_to_align();
337
338    // Make ArcCell
339    make_arc_cell::<PrunedBranchHeader, { LENGTHS[0] }>(
340        layout,
341        header,
342        data.as_ptr(),
343        data_len,
344        vtable,
345    )
346}
347
348#[inline]
349unsafe fn make_arc_cell<H, const N: usize>(
350    layout: Layout,
351    header: H,
352    data_ptr: *const u8,
353    data_len: usize,
354    vtable: *const (),
355) -> Cell
356where
357    HeaderWithData<H, N>: CellImpl,
358{
359    // Allocate memory for the object
360    let buffer = std::alloc::alloc(layout);
361    if buffer.is_null() {
362        std::alloc::handle_alloc_error(layout);
363    }
364
365    // Initialize object data
366    let ptr = buffer as *mut ArcInner<AtomicUsize, HeaderWithData<H, N>>;
367    std::ptr::write(std::ptr::addr_of_mut!((*ptr).strong), AtomicUsize::new(1));
368    std::ptr::write(std::ptr::addr_of_mut!((*ptr).weak), AtomicUsize::new(1));
369    std::ptr::write(std::ptr::addr_of_mut!((*ptr).obj.header), header);
370    std::ptr::copy_nonoverlapping(
371        data_ptr,
372        std::ptr::addr_of_mut!((*ptr).obj.data) as *mut u8,
373        data_len,
374    );
375
376    // Construct fat pointer with vtable info
377    let data = std::ptr::addr_of!((*ptr).obj) as *const ();
378    let ptr: *const DynCell = std::mem::transmute([data, vtable]);
379
380    // Construct Arc
381    Cell(Arc::from_raw(ptr))
382}
383
384/// Internal Arc representation.
385#[repr(C)]
386struct ArcInner<A, T: ?Sized> {
387    strong: A,
388    weak: A,
389    obj: T,
390}