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}