linera_witty/type_traits/implementations/
frunk.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Implementations of the custom traits for types from the standard library.
5
6use std::{borrow::Cow, ops::Add};
7
8use frunk::{HCons, HList, HNil};
9
10use crate::{
11    GuestPointer, InstanceWithMemory, Layout, Memory, Runtime, RuntimeError, RuntimeMemory, Split,
12    WitLoad, WitStore, WitType,
13};
14
15impl WitType for HNil {
16    const SIZE: u32 = 0;
17
18    type Layout = HNil;
19    type Dependencies = HNil;
20
21    fn wit_type_name() -> Cow<'static, str> {
22        "hnil".into()
23    }
24
25    fn wit_type_declaration() -> Cow<'static, str> {
26        "type hnil = unit".into()
27    }
28}
29
30impl WitLoad for HNil {
31    fn load<Instance>(
32        _memory: &Memory<'_, Instance>,
33        _location: GuestPointer,
34    ) -> Result<Self, RuntimeError>
35    where
36        Instance: InstanceWithMemory,
37    {
38        Ok(HNil)
39    }
40
41    fn lift_from<Instance>(
42        HNil: <Self::Layout as Layout>::Flat,
43        _memory: &Memory<'_, Instance>,
44    ) -> Result<Self, RuntimeError>
45    where
46        Instance: InstanceWithMemory,
47    {
48        Ok(HNil)
49    }
50}
51
52impl WitStore for HNil {
53    fn store<Instance>(
54        &self,
55        _memory: &mut Memory<'_, Instance>,
56        _location: GuestPointer,
57    ) -> Result<(), RuntimeError>
58    where
59        Instance: InstanceWithMemory,
60        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
61    {
62        Ok(())
63    }
64
65    fn lower<Instance>(
66        &self,
67        _memory: &mut Memory<'_, Instance>,
68    ) -> Result<<Self::Layout as Layout>::Flat, RuntimeError>
69    where
70        Instance: InstanceWithMemory,
71        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
72    {
73        Ok(HNil)
74    }
75}
76
77impl<Head, Tail> WitType for HCons<Head, Tail>
78where
79    Head: WitType,
80    Tail: WitType + SizeCalculation,
81    Head::Layout: Add<Tail::Layout>,
82    <Head::Layout as Add<Tail::Layout>>::Output: Layout,
83{
84    const SIZE: u32 = {
85        let packed_size = Self::SIZE_STARTING_AT_BYTE_BOUNDARIES[0];
86        let aligned_size = GuestPointer(packed_size).after_padding_for::<Self>();
87
88        aligned_size.0
89    };
90
91    type Layout = <Head::Layout as Add<Tail::Layout>>::Output;
92    type Dependencies = HList![Head, Tail];
93
94    fn wit_type_name() -> Cow<'static, str> {
95        format!("hcons-{}-{}", Head::wit_type_name(), Tail::wit_type_name()).into()
96    }
97
98    fn wit_type_declaration() -> Cow<'static, str> {
99        let head = Head::wit_type_name();
100        let tail = Tail::wit_type_name();
101
102        format!("type hcons-{head}-{tail} = tuple<{head}, {tail}>").into()
103    }
104}
105
106impl<Head, Tail> WitLoad for HCons<Head, Tail>
107where
108    Head: WitLoad,
109    Tail: WitLoad + SizeCalculation,
110    Head::Layout: Add<Tail::Layout>,
111    <Head::Layout as Add<Tail::Layout>>::Output: Layout,
112    <Self::Layout as Layout>::Flat:
113        Split<<Head::Layout as Layout>::Flat, Remainder = <Tail::Layout as Layout>::Flat>,
114{
115    fn load<Instance>(
116        memory: &Memory<'_, Instance>,
117        location: GuestPointer,
118    ) -> Result<Self, RuntimeError>
119    where
120        Instance: InstanceWithMemory,
121        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
122    {
123        Ok(HCons {
124            head: Head::load(memory, location)?,
125            tail: Tail::load(
126                memory,
127                location
128                    .after::<Head>()
129                    .after_padding_for::<Tail::FirstElement>(),
130            )?,
131        })
132    }
133
134    fn lift_from<Instance>(
135        layout: <Self::Layout as Layout>::Flat,
136        memory: &Memory<'_, Instance>,
137    ) -> Result<Self, RuntimeError>
138    where
139        Instance: InstanceWithMemory,
140        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
141    {
142        let (head_layout, tail_layout) = layout.split();
143
144        Ok(HCons {
145            head: Head::lift_from(head_layout, memory)?,
146            tail: Tail::lift_from(tail_layout, memory)?,
147        })
148    }
149}
150
151impl<Head, Tail> WitStore for HCons<Head, Tail>
152where
153    Head: WitStore,
154    Tail: WitStore + SizeCalculation,
155    Head::Layout: Add<Tail::Layout>,
156    <Head::Layout as Add<Tail::Layout>>::Output: Layout,
157    <Head::Layout as Layout>::Flat: Add<<Tail::Layout as Layout>::Flat>,
158    Self::Layout: Layout<
159        Flat = <<Head::Layout as Layout>::Flat as Add<<Tail::Layout as Layout>::Flat>>::Output,
160    >,
161{
162    fn store<Instance>(
163        &self,
164        memory: &mut Memory<'_, Instance>,
165        location: GuestPointer,
166    ) -> Result<(), RuntimeError>
167    where
168        Instance: InstanceWithMemory,
169        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
170    {
171        self.head.store(memory, location)?;
172        self.tail.store(
173            memory,
174            location
175                .after::<Head>()
176                .after_padding_for::<Tail::FirstElement>(),
177        )?;
178
179        Ok(())
180    }
181
182    fn lower<Instance>(
183        &self,
184        memory: &mut Memory<'_, Instance>,
185    ) -> Result<<Self::Layout as Layout>::Flat, RuntimeError>
186    where
187        Instance: InstanceWithMemory,
188        <Instance::Runtime as Runtime>::Memory: RuntimeMemory<Instance>,
189    {
190        let head_layout = self.head.lower(memory)?;
191        let tail_layout = self.tail.lower(memory)?;
192
193        Ok(head_layout + tail_layout)
194    }
195}
196
197/// Helper trait used to calculate the size of a heterogeneous list considering internal alignment.
198///
199/// Assumes the maximum alignment necessary for any type is 8 bytes, which is the alignment for the
200/// largest flat types (`i64` and `f64`).
201trait SizeCalculation {
202    /// The size of the list considering the current size calculation starts at different offsets
203    /// inside an 8-byte window.
204    const SIZE_STARTING_AT_BYTE_BOUNDARIES: [u32; 8];
205
206    /// The type of the first element of the list, used to determine the current necessary
207    /// alignment.
208    type FirstElement: WitType;
209}
210
211impl SizeCalculation for HNil {
212    const SIZE_STARTING_AT_BYTE_BOUNDARIES: [u32; 8] = [0; 8];
213
214    type FirstElement = ();
215}
216
217/// Unrolls a `for`-like loop so that it runs in a `const` context.
218macro_rules! unroll_for {
219    ($binding:ident in [ $($elements:expr),* $(,)? ] $body:tt) => {
220        $(
221            let $binding = $elements;
222            $body
223        )*
224    };
225}
226
227impl<Head, Tail> SizeCalculation for HCons<Head, Tail>
228where
229    Head: WitType,
230    Tail: SizeCalculation,
231{
232    const SIZE_STARTING_AT_BYTE_BOUNDARIES: [u32; 8] = {
233        let mut size_at_boundaries = [0; 8];
234
235        unroll_for!(boundary_offset in [0, 1, 2, 3, 4, 5, 6, 7] {
236            let memory_location = GuestPointer(boundary_offset)
237                .after_padding_for::<Head>()
238                .after::<Head>();
239
240            let tail_size = Tail::SIZE_STARTING_AT_BYTE_BOUNDARIES[memory_location.0 as usize % 8];
241
242            size_at_boundaries[boundary_offset as usize] =
243                memory_location.0 - boundary_offset + tail_size;
244        });
245
246        size_at_boundaries
247    };
248
249    type FirstElement = Head;
250}