arc_slice/
layout.rs

1//! The different layouts used by [`ArcSlice`] and [`ArcSliceMut`].
2//!
3//! A layout defines how the data is stored, impacting memory size and the behavior
4//! of some operations like `clone`.
5//!
6//! Almost all layouts are compatible with each other: [`ArcSlice::with_layout`] and
7//! [`ArcSliceMut::with_layout`] allows to update the layout used, often at zero cost. See
8//! [`FromLayout`].
9//!
10//! [`ArcSlice`] and [`ArcSliceMut`] have a default layout, which can be overridden using
11//! [crate features](crate#features).
12//!
13//! ## Which layout to choose
14//!
15//! The default layout, [`ArcLayout`], should support most of the use cases efficiently.
16//! The other layouts are designed to support some particular buffers without allocating an inner
17//! Arc:
18//! - [`BoxedSliceLayout`] and [`VecLayout`] are intended for boxed slice/vector buffers,
19//!   and should be used only when clones are unlikely;
20//! - [`RawLayout`] should be used with [`Arc`] and other raw buffers.
21//!
22//! Since layout primarily affects [`ArcSlice`]/[`ArcSliceMut`] instantiation, libraries generally
23//! don’t need to worry about it: they can either accept the default layout or use a generic one
24//! in public APIs, and expose the most appropriate layout in their return types. Libraries should not
25//! override the default layout using [crate features], as it would impact every other crates.
26//! <br>
27//! Binaries should use the default layout, adapting it to the use case using [crate features].
28//!
29//! In any case, layout choice is primarily a performance concern and should be supported by
30//! measurement.
31//!
32//! ## Layouts summary
33//!
34//! | Layout             | `ArcSlice` size          | static/empty slices support | arbitrary buffers support | cloning may allocate | optimized for      |
35//! |--------------------|--------------------------|-----------------------------|---------------------------|----------------------|--------------------|
36//! | `ArcLayout`        | `3 * size_of::<usize>()` | yes (optional)              | yes (optional)            | no                   | regular `ArcSlice` |
37//! | `BoxedSliceLayout` | `3 * size_of::<usize>()` | yes                         | yes                       | yes                  | `Box<[T]>`         |
38//! | `VecLayout`        | `4 * size_of::<usize>()` | yes                         | yes                       | yes                  | `Vec<T>`           |
39//! | `RawLayout`        | `4 * size_of::<usize>()` | yes                         | yes                       | no                   | `RawBuffer`        |
40//!
41//! [crate feature]: crate#features
42//! [`Arc`]: alloc::sync::Arc
43
44#[cfg(doc)]
45use crate::{slice::ArcSlice, slice_mut::ArcSliceMut};
46
47/// A layout, which defines how [`ArcSlice`] data is stored.
48pub trait Layout: private::Layout {}
49/// A layout, which defines how [`ArcSliceMut`] data is stored.
50pub trait LayoutMut: Layout + private::LayoutMut {}
51
52/// A layout that supports arbitrary buffers, such as [`Vec`](alloc::vec::Vec),
53/// shared memory regions, ffi buffers, etc.
54///
55/// It enables [`ArcSlice::from_buffer`]/[`ArcSliceMut::from_buffer`] and derived methods.
56pub trait AnyBufferLayout: Layout {}
57/// A layout that supports static slices without inner Arc allocation.
58///
59/// It enables [`ArcSlice::new`] and [`ArcSlice::from_static`]. Additionally, empty subslices are
60/// stored as static slices to avoid Arc clone/drop overhead.
61pub trait StaticLayout: Layout {}
62/// A layout that supports [`clone`](ArcSlice::clone) without allocating.
63pub trait CloneNoAllocLayout: Layout {}
64/// A layout that supports [`truncate`](ArcSlice::truncate) without allocating.
65pub trait TruncateNoAllocLayout: Layout {}
66
67/// The default and most optimized layout.
68///
69/// It aims to be more performant than other layouts for supported operations,
70/// though other layouts may support a broader range of use cases.
71/// It takes two generic boolean parameters, whose defaults can be overridden via compilation
72/// features:
73/// - `ANY_BUFFER`, default to false, if it supports arbitrary buffer;
74/// - `STATIC`, default to false, if it supports static slices without allocations; it
75///   enables [`Default`] implementation for [`ArcSlice`], as well as const constructors.
76///
77/// Other layouts support arbitrary buffers and static slices out of the box, but this flexibility
78/// comes at a cost. `ArcLayout` focuses instead on providing the most optimized implementation
79/// adapted to each use case.
80/// ```rust
81/// # use core::mem::size_of;
82/// # use arc_slice::{layout::ArcLayout, ArcBytes, ArcBytesMut};
83/// assert_eq!(size_of::<ArcBytes<ArcLayout>>(), 3 * size_of::<usize>());
84/// assert_eq!(size_of::<ArcBytesMut<ArcLayout>>(), 4 * size_of::<usize>());
85/// ```
86#[derive(Debug)]
87pub struct ArcLayout<
88    const ANY_BUFFER: bool = { cfg!(feature = "default-layout-any-buffer") },
89    const STATIC: bool = { cfg!(feature = "default-layout-static") },
90>;
91impl<const ANY_BUFFER: bool, const STATIC: bool> Layout for ArcLayout<ANY_BUFFER, STATIC> {}
92impl<const STATIC: bool> AnyBufferLayout for ArcLayout<true, STATIC> {}
93impl<const ANY_BUFFER: bool> StaticLayout for ArcLayout<ANY_BUFFER, true> {}
94impl<const ANY_BUFFER: bool, const STATIC: bool> CloneNoAllocLayout
95    for ArcLayout<ANY_BUFFER, STATIC>
96{
97}
98impl<const ANY_BUFFER: bool, const STATIC: bool> TruncateNoAllocLayout
99    for ArcLayout<ANY_BUFFER, STATIC>
100{
101}
102impl<const ANY_BUFFER: bool, const STATIC: bool> LayoutMut for ArcLayout<ANY_BUFFER, STATIC> {}
103
104/// Enables storing a boxed slice into an [`ArcSlice`] without requiring the allocation of an inner
105/// Arc, as long as there is a single instance.
106///
107/// As soon as the [`ArcSlice`] is cloned (or subsliced), then an inner Arc is allocated. As a
108/// consequence, when [`oom-handling` feature](crate#features) is not enabled,
109/// `ArcSlice<S, BoxedSliceLayout>` doesn't implement [`Clone`].
110/// <br>
111/// When initializing an [`ArcSlice`] with a vector, there will be no Arc allocation if the
112/// vector has no spare capacity.
113/// ```rust
114/// # use core::mem::size_of;
115/// # use arc_slice::{layout::BoxedSliceLayout, ArcBytes};
116/// assert_eq!(
117///     size_of::<ArcBytes<BoxedSliceLayout>>(),
118///     3 * size_of::<usize>()
119/// );
120/// ```
121#[derive(Debug)]
122pub struct BoxedSliceLayout;
123impl Layout for BoxedSliceLayout {}
124impl AnyBufferLayout for BoxedSliceLayout {}
125impl StaticLayout for BoxedSliceLayout {}
126
127/// Enables storing a vector into an [`ArcSlice`] without requiring the allocation of an inner Arc,
128/// as long as there is a single instance.
129///
130/// As soon as the [`ArcSlice`] is cloned (or subsliced), then an inner Arc is allocated. As a
131/// consequence, when [`oom-handling` feature](crate#features) is not enabled,
132/// `ArcSlice<S, VecLayout>` doesn't implement [`Clone`].
133/// ```rust
134/// # use core::mem::size_of;
135/// # use arc_slice::{layout::VecLayout, ArcBytes, ArcBytesMut};
136/// assert_eq!(size_of::<ArcBytes<VecLayout>>(), 4 * size_of::<usize>());
137/// assert_eq!(size_of::<ArcBytesMut<VecLayout>>(), 4 * size_of::<usize>());
138/// ```
139#[derive(Debug)]
140pub struct VecLayout;
141impl Layout for VecLayout {}
142impl AnyBufferLayout for VecLayout {}
143impl StaticLayout for VecLayout {}
144impl TruncateNoAllocLayout for VecLayout {}
145impl LayoutMut for VecLayout {}
146
147/// Enables storing a [`RawBuffer`], without requiring the allocation of an inner Arc.
148/// ```rust
149/// # use core::mem::size_of;
150/// # use arc_slice::{layout::RawLayout, ArcBytes};
151/// assert_eq!(size_of::<ArcBytes<RawLayout>>(), 4 * size_of::<usize>());
152/// ```
153///
154/// [`RawBuffer`]: crate::buffer::RawBuffer
155#[cfg(feature = "raw-buffer")]
156#[derive(Debug)]
157pub struct RawLayout;
158#[cfg(feature = "raw-buffer")]
159impl Layout for RawLayout {}
160#[cfg(feature = "raw-buffer")]
161impl StaticLayout for RawLayout {}
162#[cfg(feature = "raw-buffer")]
163impl AnyBufferLayout for RawLayout {}
164#[cfg(feature = "raw-buffer")]
165impl CloneNoAllocLayout for RawLayout {}
166#[cfg(feature = "raw-buffer")]
167impl TruncateNoAllocLayout for RawLayout {}
168
169/// A layout that can be converted from another one.
170///
171/// As long as a layout implement [`AnyBufferLayout`], every other layout can be converted to it.
172/// This conversion is often very cheap, when the default `ArcSlice` buffer is used, but it may
173/// require to allocate an Arc when to arbitrary buffers specially supported by the input layout.
174/// For example, converting `ArcSlice<S, BoxedSliceLayout>` to `ArcSlice<S, ArcLayout<true>>` might
175/// allocate an `Arc` if a boxed slice was store inlined.
176///
177/// `ArcLayout<false>` doesn't implement [`AnyBufferLayout`], so it cannot straightforwardly be
178/// converted from other layouts, as it may not support the underlying buffer. However, the actual
179/// underlying buffer may be compatible, for example, an `ArcSlice<S, VecLayout>` backed by an
180/// Arc buffer can in fact be converted  to an`ArcSlice<S, ArcLayout<false>>`. Fallible conversions
181/// like [`ArcSlice::try_with_layout`]/[`ArcSliceMut::try_freeze`]/etc. can be used to handle this
182/// edge case.
183pub trait FromLayout<L: Layout>: Layout {}
184
185impl<const STATIC: bool, L: Layout> FromLayout<ArcLayout<false, STATIC>> for L {}
186impl<L1: AnyBufferLayout, L2: AnyBufferLayout> FromLayout<L1> for L2 {}
187
188macro_rules! default_layout {
189    ($layout:ty) => {
190        /// Default layout used by [`ArcSlice`].
191        ///
192        /// Can be overridden with [crate features](crate#features).
193        pub type DefaultLayout = $layout;
194    };
195}
196cfg_if::cfg_if! {
197    if #[cfg(feature = "default-layout-raw")] {
198        default_layout!(RawLayout);
199    } else if #[cfg(feature = "default-layout-vec")] {
200        default_layout!(VecLayout);
201    } else if #[cfg(feature = "default-layout-boxed-slice")] {
202        default_layout!(BoxedSliceLayout);
203    } else {
204        default_layout!(ArcLayout);
205    }
206}
207
208macro_rules! default_layout_mut {
209    ($layout:ty) => {
210        /// Default layout used by [`ArcSliceMut`].
211        ///
212        /// Can be overridden with [crate features](crate#features).
213        pub type DefaultLayoutMut = $layout;
214    };
215}
216cfg_if::cfg_if! {
217    if #[cfg(feature = "default-layout-mut-vec")] {
218        default_layout_mut!(VecLayout);
219    } else {
220        default_layout_mut!(ArcLayout<{ cfg!(feature = "default-layout-mut-any-buffer") }>);
221    }
222}
223
224#[cfg(not(feature = "inlined"))]
225mod private {
226    pub use crate::{slice::ArcSliceLayout as Layout, slice_mut::ArcSliceMutLayout as LayoutMut};
227}
228
229#[cfg(feature = "inlined")]
230mod private {
231    use crate::{inlined::InlinedLayout, slice::ArcSliceLayout};
232
233    pub trait Layout: ArcSliceLayout + InlinedLayout {}
234    impl<L> Layout for L where L: ArcSliceLayout + InlinedLayout {}
235
236    pub use crate::slice_mut::ArcSliceMutLayout as LayoutMut;
237}