eosio_scale_info/
registry.rs

1// Copyright 2019-2022 Parity Technologies (UK) Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The registry stores type definitions in a space-efficient manner.
16//!
17//! This is done by deduplicating common types in order to reuse their
18//! definitions which otherwise can grow arbitrarily large. A type is uniquely
19//! identified by its type identifier that is therefore used to refer to types
20//! and their definitions.
21//!
22//! Types with the same name are uniquely identifiable by introducing
23//! namespaces. The normal Rust namespace of a type is used, except for the Rust
24//! prelude types that live in the so-called root namespace which is empty.
25
26use crate::prelude::{
27    any::TypeId,
28    collections::BTreeMap,
29    fmt::Debug,
30    vec::Vec,
31};
32
33use crate::{
34    form::{
35        Form,
36        PortableForm,
37    },
38    interner::{
39        Interner,
40        UntrackedSymbol,
41    },
42    meta_type::MetaType,
43    Type,
44};
45use scale::Encode;
46
47/// Convert the type definition into the portable form using a registry.
48pub trait IntoPortable {
49    /// The portable version of `Self`.
50    type Output;
51
52    /// Convert `self` to the portable form by using the registry for caching.
53    fn into_portable(self, registry: &mut Registry) -> Self::Output;
54}
55
56impl IntoPortable for &'static str {
57    type Output = <PortableForm as Form>::String;
58
59    fn into_portable(self, _registry: &mut Registry) -> Self::Output {
60        self.into()
61    }
62}
63
64/// The registry for space-efficient storage of type identifiers and
65/// definitions.
66///
67/// The registry consists of a cache for type identifiers and definitions.
68///
69/// When adding a type to  the registry, all of its sub-types are registered
70/// recursively as well. A type is considered a sub-type of another type if it
71/// is used by its identifier or structure.
72///
73/// # Note
74///
75/// A type can be a sub-type of itself. In this case the registry has a builtin
76/// mechanism to stop recursion and avoid going into an infinite loop.
77#[derive(Debug, PartialEq, Eq)]
78pub struct Registry {
79    /// The cache for already registered types.
80    ///
81    /// This is just an accessor to the actual database
82    /// for all types found in the `types` field.
83    type_table: Interner<TypeId>,
84    /// The database where registered types reside.
85    ///
86    /// The contents herein is used for serlialization.
87    types: BTreeMap<UntrackedSymbol<core::any::TypeId>, Type<PortableForm>>,
88}
89
90impl Default for Registry {
91    fn default() -> Self {
92        Self::new()
93    }
94}
95
96impl Registry {
97    /// Creates a new empty registry.
98    pub fn new() -> Self {
99        Self {
100            type_table: Interner::new(),
101            types: BTreeMap::new(),
102        }
103    }
104
105    /// Registers the given type ID into the registry.
106    ///
107    /// Returns `false` as the first return value if the type ID has already
108    /// been registered into this registry.
109    /// Returns the associated type ID symbol as second return value.
110    ///
111    /// # Note
112    ///
113    /// This is an internal API and should not be called directly from the
114    /// outside.
115    fn intern_type_id(&mut self, type_id: TypeId) -> (bool, UntrackedSymbol<TypeId>) {
116        let (inserted, symbol) = self.type_table.intern_or_get(type_id);
117        (inserted, symbol.into_untracked())
118    }
119
120    /// Registers the given type into the registry and returns
121    /// its associated type ID symbol.
122    ///
123    /// # Note
124    ///
125    /// Due to safety requirements the returns type ID symbol cannot
126    /// be used later to resolve back to the associated type definition.
127    /// However, since this facility is going to be used for serialization
128    /// purposes this functionality isn't needed anyway.
129    pub fn register_type(&mut self, ty: &MetaType) -> UntrackedSymbol<TypeId> {
130        let (inserted, symbol) = self.intern_type_id(ty.type_id());
131        if inserted {
132            let portable_id = ty.type_info().into_portable(self);
133            self.types.insert(symbol, portable_id);
134        }
135        symbol
136    }
137
138    /// Calls `register_type` for each `MetaType` in the given `iter`.
139    pub fn register_types<I>(&mut self, iter: I) -> Vec<UntrackedSymbol<TypeId>>
140    where
141        I: IntoIterator<Item = MetaType>,
142    {
143        iter.into_iter()
144            .map(|i| self.register_type(&i))
145            .collect::<Vec<_>>()
146    }
147
148    /// Converts an iterator into a Vec of the equivalent portable
149    /// representations.
150    pub fn map_into_portable<I, T>(&mut self, iter: I) -> Vec<T::Output>
151    where
152        I: IntoIterator<Item = T>,
153        T: IntoPortable,
154    {
155        iter.into_iter()
156            .map(|i| i.into_portable(self))
157            .collect::<Vec<_>>()
158    }
159}
160
161/// A read-only registry containing types in their portable form for serialization.
162#[cfg_attr(feature = "serde", derive(serde::Serialize))]
163#[cfg_attr(all(feature = "serde", feature = "decode"), derive(serde::Deserialize))]
164#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
165#[derive(Clone, Debug, PartialEq, Eq, Encode)]
166pub struct PortableRegistry {
167    types: Vec<PortableType>,
168}
169
170impl From<Registry> for PortableRegistry {
171    fn from(registry: Registry) -> Self {
172        PortableRegistry {
173            types: registry
174                .types
175                .iter()
176                .map(|(k, v)| {
177                    PortableType {
178                        id: k.id(),
179                        ty: v.clone(),
180                    }
181                })
182                .collect::<Vec<_>>(),
183        }
184    }
185}
186
187impl PortableRegistry {
188    /// Returns the type definition for the given identifier, `None` if no type found for that ID.
189    pub fn resolve(&self, id: u32) -> Option<&Type<PortableForm>> {
190        self.types.get(id as usize).map(|ty| ty.ty())
191    }
192
193    /// Returns all types with their associated identifiers.
194    pub fn types(&self) -> &[PortableType] {
195        &self.types
196    }
197}
198
199#[cfg_attr(feature = "serde", derive(serde::Serialize))]
200#[cfg_attr(all(feature = "serde", feature = "decode"), derive(serde::Deserialize))]
201#[cfg_attr(any(feature = "std", feature = "decode"), derive(scale::Decode))]
202#[derive(Clone, Debug, PartialEq, Eq, Encode)]
203pub struct PortableType {
204    #[codec(compact)]
205    id: u32,
206    #[cfg_attr(feature = "serde", serde(rename = "type"))]
207    ty: Type<PortableForm>,
208}
209
210impl PortableType {
211    /// Returns the index of the [`PortableType`].
212    pub fn id(&self) -> u32 {
213        self.id
214    }
215
216    /// Returns the type of the [`PortableType`].
217    pub fn ty(&self) -> &Type<PortableForm> {
218        &self.ty
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use super::*;
225    use crate::{
226        build::Fields,
227        meta_type,
228        Path,
229        TypeDef,
230        TypeInfo,
231    };
232
233    #[test]
234    fn readonly_type_ids() {
235        let mut registry = Registry::new();
236        registry.register_type(&MetaType::new::<u32>());
237        registry.register_type(&MetaType::new::<bool>());
238        registry.register_type(&MetaType::new::<Option<(u32, bool)>>());
239
240        let readonly: PortableRegistry = registry.into();
241
242        assert_eq!(4, readonly.types().len());
243
244        for (expected, ty) in readonly.types().iter().enumerate() {
245            assert_eq!(expected as u32, ty.id());
246        }
247    }
248
249    #[test]
250    fn recursive_struct_with_references() {
251        #[allow(unused)]
252        struct RecursiveRefs<'a> {
253            boxed: Box<RecursiveRefs<'a>>,
254            reference: &'a RecursiveRefs<'a>,
255            mutable_reference: &'a mut RecursiveRefs<'a>,
256        }
257
258        impl TypeInfo for RecursiveRefs<'static> {
259            type Identity = Self;
260
261            fn type_info() -> Type {
262                Type::builder()
263                    .path(Path::new("RecursiveRefs", module_path!()))
264                    .composite(
265                        Fields::named()
266                            .field(|f| {
267                                f.ty::<Box<RecursiveRefs>>()
268                                    .name("boxed")
269                                    .type_name("Box < RecursiveRefs >")
270                            })
271                            .field(|f| {
272                                f.ty::<&'static RecursiveRefs<'static>>()
273                                    .name("reference")
274                                    .type_name("&RecursiveRefs")
275                            })
276                            .field(|f| {
277                                f.ty::<&'static mut RecursiveRefs<'static>>()
278                                    .name("mutable_reference")
279                                    .type_name("&mut RecursiveRefs")
280                            }),
281                    )
282            }
283        }
284
285        let mut registry = Registry::new();
286        let type_id = registry.register_type(&meta_type::<RecursiveRefs>());
287
288        let recursive = registry.types.get(&type_id).unwrap();
289        if let TypeDef::Composite(composite) = recursive.type_def() {
290            for field in composite.fields() {
291                assert_eq!(*field.ty(), type_id)
292            }
293        } else {
294            panic!("Should be a composite type definition")
295        }
296    }
297}