tezos_data_encoding/
encoding.rs

1// Copyright (c) SimpleStaking, Viable Systems, Nomadic Labs and Tezedge Contributors
2// SPDX-CopyrightText: 2022-2023 TriliTech <contact@trili.tech>
3// SPDX-License-Identifier: MIT
4
5//! Schema used for serialization and deserialization.
6
7use std::collections::HashMap;
8
9pub use tezos_data_encoding_derive::HasEncoding;
10
11#[derive(Debug, Clone)]
12pub struct Field {
13    name: String,
14    encoding: Encoding,
15}
16
17impl Field {
18    pub fn new(name: &str, encoding: Encoding) -> Field {
19        Field {
20            name: String::from(name),
21            encoding,
22        }
23    }
24
25    pub fn get_name(&self) -> &String {
26        &self.name
27    }
28
29    pub fn get_encoding(&self) -> &Encoding {
30        &self.encoding
31    }
32}
33
34pub type Schema = Vec<Field>;
35
36#[derive(Debug, Clone)]
37pub struct Tag {
38    id: u16,
39    variant: String,
40    encoding: Encoding,
41}
42
43impl Tag {
44    pub fn new(id: u16, variant: &str, encoding: Encoding) -> Tag {
45        Tag {
46            id,
47            variant: String::from(variant),
48            encoding,
49        }
50    }
51
52    pub fn get_id(&self) -> u16 {
53        self.id
54    }
55
56    pub fn get_encoding(&self) -> &Encoding {
57        &self.encoding
58    }
59
60    pub fn get_variant(&self) -> &String {
61        &self.variant
62    }
63}
64
65#[derive(Debug, Clone)]
66pub struct TagMap {
67    id_to_tag: HashMap<u16, Tag>,
68    variant_to_id: HashMap<String, u16>,
69}
70
71impl TagMap {
72    pub fn new(tags: Vec<Tag>) -> TagMap {
73        let mut id_to_tag = HashMap::new();
74        let mut variant_to_id = HashMap::new();
75
76        for tag in tags {
77            let tag_id = tag.get_id();
78            let variant = tag.get_variant().to_string();
79            let prev_item = id_to_tag.insert(tag_id, tag);
80            debug_assert!(
81                prev_item.is_none(),
82                "Tag id: 0x{:X} is already present in TagMap",
83                tag_id
84            );
85            variant_to_id.insert(variant, tag_id);
86        }
87
88        TagMap {
89            id_to_tag,
90            variant_to_id,
91        }
92    }
93
94    pub fn find_by_id(&self, id: u16) -> Option<&Tag> {
95        self.id_to_tag.get(&id)
96    }
97
98    pub fn find_by_variant(&self, variant: &str) -> Option<&Tag> {
99        self.variant_to_id
100            .get(variant)
101            .and_then(|tag_id| self.id_to_tag.get(tag_id))
102    }
103
104    pub fn tags(&self) -> impl Iterator<Item = &Tag> {
105        self.id_to_tag.values()
106    }
107}
108
109/// Represents schema used for encoding a data into a json or a binary form.
110#[derive(Debug, Clone)]
111pub enum Encoding {
112    /// Encoded as nothing in binary or null in json
113    Unit,
114    /// Signed 8 bit integer (data is encoded as a byte in binary and an integer in JSON).
115    Int8,
116    /// Unsigned 8 bit integer (data is encoded as a byte in binary and an integer in JSON).
117    Uint8,
118    /// Signed 16 bit integer (data is encoded as a short in binary and an integer in JSON).
119    Int16,
120    /// Unsigned 16 bit integer (data is encoded as a short in binary and an integer in JSON).
121    Uint16,
122    /// Signed 31 bit integer, which corresponds to type int on 32-bit OCaml systems (data is encoded as a 32 bit int in binary and an integer in JSON).
123    Int31,
124    /// Signed 32 bit integer (data is encoded as a 32-bit int in binary and an integer in JSON).
125    Int32,
126    /// Unsigned 32 bit integer (data is encoded as a 32-bit int in binary and an integer in JSON).
127    Uint32,
128    /// Signed 64 bit integer (data is encoded as a 64-bit int in binary and a decimal string in JSON).
129    Int64,
130    /// Integer with bounds in a given range. Both bounds are inclusive.
131    RangedInt,
132    ///  Big number
133    ///  In JSON, data is encoded as a decimal string.
134    ///  In binary, data is encoded as a variable length sequence of
135    ///  bytes, with a running unary size bit: the most significant bit of
136    ///  each byte tells is this is the last byte in the sequence (0) or if
137    /// there is more to read (1). The second most significant bit of the
138    /// first byte is reserved for the sign (positive if zero). Binary_size and
139    /// sign bits ignored, data is then the binary representation of the
140    /// absolute value of the number in little-endian order.
141    Z,
142    /// Big number
143    /// Almost identical to [Encoding::Z], but does not contain the sign bit in the second most
144    /// significant bit of the first byte
145    Mutez,
146    /// Encoding of floating point number (encoded as a floating point number in JSON and a double in binary).
147    Float,
148    /// Float with bounds in a given range. Both bounds are inclusive.
149    RangedFloat,
150    /// Encoding of a boolean (data is encoded as a byte in binary and a boolean in JSON).
151    Bool,
152    /// Encoding of a string
153    /// - encoded as a byte sequence in binary prefixed by the length
154    /// of the string
155    /// - encoded as a string in JSON.
156    String,
157    /// Encoding of a string
158    /// - encoded as a byte sequence in binary prefixed by the length
159    /// of the string
160    /// - encoded as a string in JSON.
161    BoundedString(usize),
162    /// Encoding of arbitrary sized bytes (encoded via hex in JSON and directly as a sequence byte in binary).
163    Bytes,
164    /// Tag is prefixed by tag id and followed by encoded bytes
165    /// First argument represents size of the tag marker in bytes.
166    Tags(usize, TagMap),
167    /// List combinator. It's behavior is similar to [Encoding::Greedy] encoding. Main distinction
168    /// is that we are expecting list of items instead of a single item to be contained in binary data.
169    /// - encoded as an array in JSON
170    /// - encoded as the concatenation of all the element in binary
171    List(Box<Encoding>),
172    /// Encode enumeration via association list
173    ///  - represented as a string in JSON and
174    ///  - represented as an integer representing the element's position in the list in binary. The integer size depends on the list size.
175    BoundedList(usize, Box<Encoding>),
176    /// Encode enumeration via association list
177    ///  - represented as a string in JSON and
178    ///  - represented as an integer representing the element's position in the list in binary. The integer size depends on the list size.
179    Enum,
180    /// Combinator to make an optional value
181    /// (represented as a 1-byte tag followed by the data (or nothing) in binary
182    ///  and either the raw value or an empty object in JSON).
183    ///
184    /// Compatible with ocaml usage: (req "arg" (Data_encoding.option string))
185    Option(Box<Encoding>),
186    /// TE-172 - combinator to make an optional field, this is not the same as Encoding::Option
187    /// (req "arg" (Data_encoding.option string)) is not the same as (opt "arg" string))
188    ///
189    /// Compatible with ocaml usage: (opt "arg" string)
190    OptionalField(Box<Encoding>),
191    /// Is the collection of fields.
192    /// not prefixed by anything in binary, encoded as the concatenation of all the element in binary
193    Obj(&'static str, Schema),
194    /// Heterogeneous collection of values.
195    /// Similar to [Encoding::Obj], but schema can be any types, not just fields.
196    /// Encoded as continuous binary representation.
197    Tup(Vec<Encoding>),
198    /// Is the collection of fields.
199    /// prefixed its length in bytes (1 Bytes), encoded as the concatenation of all the element in binary
200    ShortDynamic(Box<Encoding>),
201    /// Is the collection of fields.
202    /// prefixed its length in bytes (4 Byte), encoded as the concatenation of all the element in binary
203    Dynamic(Box<Encoding>),
204    /// Is the collection of fields.
205    /// prefixed its length in bytes (4 Bytes), encoded as the concatenation of all the element in binary
206    BoundedDynamic(usize, Box<Encoding>),
207    /// Represents fixed size block in binary encoding.
208    Sized(usize, Box<Encoding>),
209    /// Represents bounded block in binary encoding
210    /// (one with a length that cannot exceed the upper value).
211    Bounded(usize, Box<Encoding>),
212    /// Almost same as [Encoding::Dynamic] but without bytes size information prefix.
213    /// It assumes that encoding passed as argument will process rest of the available data.
214    Greedy(Box<Encoding>),
215    /// Decode various types of hashes. Hash has it's own predefined length and prefix.
216    /// This is controller by a hash implementation.
217    Hash(&'static str),
218    /// Timestamp encoding.
219    /// - encoded as RFC 3339 in json
220    /// - encoded as [Encoding::Int64] in binary
221    Timestamp,
222    /// This is used to perform encoding using custom function
223    /// rather than basing on schema. Used to get rid of recursion
224    /// while encoding/decoding recursive types.
225    Custom,
226}
227
228impl Encoding {
229    /// Utility function to construct [Encoding::List] without the need
230    /// to manually create new [Box].
231    #[inline]
232    pub fn list(encoding: Encoding) -> Encoding {
233        Encoding::List(Box::new(encoding))
234    }
235
236    /// Utility function to construct [Encoding::List] without the need
237    /// to manually create new [Box].
238    #[inline]
239    pub fn bounded_list(max: usize, encoding: Encoding) -> Encoding {
240        Encoding::BoundedList(max, Box::new(encoding))
241    }
242
243    /// Utility function to construct [Encoding::Sized] without the need
244    /// to manually create new [Box].
245    #[inline]
246    pub fn sized(bytes_sz: usize, encoding: Encoding) -> Encoding {
247        Encoding::Sized(bytes_sz, Box::new(encoding))
248    }
249
250    /// Utility function to construct [Encoding::Sized] without the need
251    /// to manually create new [Box].
252    #[inline]
253    pub fn bounded(max: usize, encoding: Encoding) -> Encoding {
254        Encoding::Bounded(max, Box::new(encoding))
255    }
256
257    /// Utility function to construct [Encoding::Greedy] without the need
258    /// to manually create new [Box].
259    #[inline]
260    pub fn greedy(encoding: Encoding) -> Encoding {
261        Encoding::Greedy(Box::new(encoding))
262    }
263
264    /// Utility function to construct [Encoding::ShortDynamic] without the need
265    /// to manually create new [Box].
266    #[inline]
267    pub fn short_dynamic(encoding: Encoding) -> Encoding {
268        Encoding::Dynamic(Box::new(encoding))
269    }
270
271    /// Utility function to construct [Encoding::Dynamic] without the need
272    /// to manually create new [Box].
273    #[inline]
274    pub fn dynamic(encoding: Encoding) -> Encoding {
275        Encoding::Dynamic(Box::new(encoding))
276    }
277
278    /// Utility function to construct [Encoding::Dynamic] without the need
279    /// to manually create new [Box].
280    #[inline]
281    pub fn bounded_dynamic(max: usize, encoding: Encoding) -> Encoding {
282        Encoding::BoundedDynamic(max, Box::new(encoding))
283    }
284
285    /// Utility function to construct [Encoding::Option] without the need
286    /// to manually create new [Box].
287    #[inline]
288    pub fn option(encoding: Encoding) -> Encoding {
289        Encoding::Option(Box::new(encoding))
290    }
291
292    /// Utility function to construct [Encoding::OptionalField] without the need
293    /// to manually create new [Box].
294    #[inline]
295    pub fn option_field(encoding: Encoding) -> Encoding {
296        Encoding::OptionalField(Box::new(encoding))
297    }
298}
299
300/// Indicates that type has its own ser/de schema.
301pub trait HasEncoding {
302    fn encoding() -> Encoding;
303}
304
305/// Creates impl HasEncoding for given struct backed by lazy_static ref instance with encoding.
306#[macro_export]
307macro_rules! has_encoding {
308    ($struct_name:ident, $enc_ref_name:ident, $code:block) => {
309        impl HasEncoding for $struct_name {
310            fn encoding() -> Encoding {
311                $code
312            }
313        }
314    };
315}