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::{
27    form::Form,
28    prelude::{any::TypeId, collections::BTreeMap, fmt::Debug, vec::Vec},
29};
30
31use crate::{
32    form::PortableForm,
33    interner::{Interner, UntrackedSymbol},
34    meta_type::MetaType,
35    Type,
36};
37
38/// Convert the type definition into the portable form using a registry.
39pub trait IntoPortable {
40    /// The portable version of `Self`.
41    type Output;
42
43    /// Convert `self` to the portable form by using the registry for caching.
44    fn into_portable(self, registry: &mut Registry) -> Self::Output;
45}
46
47impl IntoPortable for &'static str {
48    type Output = <PortableForm as Form>::String;
49
50    fn into_portable(self, _registry: &mut Registry) -> Self::Output {
51        self.into()
52    }
53}
54
55/// The registry for space-efficient storage of type identifiers and
56/// definitions.
57///
58/// The registry consists of a cache for type identifiers and definitions.
59///
60/// When adding a type to  the registry, all of its sub-types are registered
61/// recursively as well. A type is considered a sub-type of another type if it
62/// is used by its identifier or structure.
63///
64/// # Note
65///
66/// A type can be a sub-type of itself. In this case the registry has a builtin
67/// mechanism to stop recursion and avoid going into an infinite loop.
68#[derive(Debug, PartialEq, Eq)]
69pub struct Registry {
70    /// The cache for already registered types.
71    ///
72    /// This is just an accessor to the actual database
73    /// for all types found in the `types` field.
74    type_table: Interner<TypeId>,
75    /// The database where registered types reside.
76    ///
77    /// The contents herein is used for serlialization.
78    types: BTreeMap<UntrackedSymbol<TypeId>, Type<PortableForm>>,
79}
80
81impl Default for Registry {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87impl Registry {
88    /// Creates a new empty registry.
89    pub fn new() -> Self {
90        Self {
91            type_table: Interner::new(),
92            types: BTreeMap::new(),
93        }
94    }
95
96    /// Registers the given type ID into the registry.
97    ///
98    /// Returns `false` as the first return value if the type ID has already
99    /// been registered into this registry.
100    /// Returns the associated type ID symbol as second return value.
101    ///
102    /// # Note
103    ///
104    /// This is an internal API and should not be called directly from the
105    /// outside.
106    fn intern_type_id(&mut self, type_id: TypeId) -> (bool, UntrackedSymbol<TypeId>) {
107        let (inserted, symbol) = self.type_table.intern_or_get(type_id);
108        (inserted, symbol.into_untracked())
109    }
110
111    /// Registers the given type into the registry and returns
112    /// its associated type ID symbol.
113    ///
114    /// # Note
115    ///
116    /// Due to safety requirements the returns type ID symbol cannot
117    /// be used later to resolve back to the associated type definition.
118    /// However, since this facility is going to be used for serialization
119    /// purposes this functionality isn't needed anyway.
120    pub fn register_type(&mut self, ty: &MetaType) -> UntrackedSymbol<TypeId> {
121        let (inserted, symbol) = self.intern_type_id(ty.type_id());
122        if inserted {
123            let portable_id = ty.type_info().into_portable(self);
124            self.types.insert(symbol, portable_id);
125        }
126        symbol
127    }
128
129    /// Calls `register_type` for each `MetaType` in the given `iter`.
130    pub fn register_types<I>(&mut self, iter: I) -> Vec<UntrackedSymbol<TypeId>>
131    where
132        I: IntoIterator<Item = MetaType>,
133    {
134        iter.into_iter()
135            .map(|i| self.register_type(&i))
136            .collect::<Vec<_>>()
137    }
138
139    /// Converts an iterator into a Vec of the equivalent portable
140    /// representations.
141    pub fn map_into_portable<I, T>(&mut self, iter: I) -> Vec<T::Output>
142    where
143        I: IntoIterator<Item = T>,
144        T: IntoPortable,
145    {
146        iter.into_iter()
147            .map(|i| i.into_portable(self))
148            .collect::<Vec<_>>()
149    }
150
151    /// Returns an iterator over the types with their keys
152    pub fn types(&self) -> impl Iterator<Item = (&UntrackedSymbol<TypeId>, &Type<PortableForm>)> {
153        self.types.iter()
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::*;
160    use crate::{build::Fields, meta_type, Path, TypeDef, TypeInfo};
161
162    #[test]
163    fn recursive_struct_with_references() {
164        #[allow(unused)]
165        struct RecursiveRefs<'a> {
166            boxed: Box<RecursiveRefs<'a>>,
167            reference: &'a RecursiveRefs<'a>,
168            mutable_reference: &'a mut RecursiveRefs<'a>,
169        }
170
171        impl TypeInfo for RecursiveRefs<'static> {
172            type Identity = Self;
173
174            fn type_info() -> Type {
175                Type::builder()
176                    .path(Path::new("RecursiveRefs", module_path!()))
177                    .composite(
178                        Fields::named()
179                            .field(|f| {
180                                f.ty::<Box<RecursiveRefs>>()
181                                    .name("boxed")
182                                    .type_name("Box < RecursiveRefs >")
183                            })
184                            .field(|f| {
185                                f.ty::<&'static RecursiveRefs<'static>>()
186                                    .name("reference")
187                                    .type_name("&RecursiveRefs")
188                            })
189                            .field(|f| {
190                                f.ty::<&'static mut RecursiveRefs<'static>>()
191                                    .name("mutable_reference")
192                                    .type_name("&mut RecursiveRefs")
193                            }),
194                    )
195            }
196        }
197
198        let mut registry = Registry::new();
199        let type_id = registry.register_type(&meta_type::<RecursiveRefs>());
200
201        let recursive = registry.types.get(&type_id).unwrap();
202        if let TypeDef::Composite(composite) = &recursive.type_def {
203            for field in &composite.fields {
204                assert_eq!(field.ty, type_id)
205            }
206        } else {
207            panic!("Should be a composite type definition")
208        }
209    }
210}