strict_encoding/
primitives.rs

1// Strict encoding library for deterministic binary serialization.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2019-2024 by
6//     Dr. Maxim Orlovsky <orlovsky@ubideco.org>
7//
8// Copyright 2022-2024 UBIDECO Labs
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14//     http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21
22use std::fmt::{self, Display, Formatter, Write};
23use std::hash::Hash;
24
25use amplify::Wrapper;
26
27use crate::STRICT_TYPES_LIB;
28
29#[derive(Wrapper, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, From)]
30#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "serde_crate"))]
31pub struct Primitive(u8);
32
33impl Primitive {
34    pub const U8: Primitive = Primitive::unsigned(1);
35    pub const U16: Primitive = Primitive::unsigned(2);
36    pub const U24: Primitive = Primitive::unsigned(3);
37    pub const U32: Primitive = Primitive::unsigned(4);
38    pub const U40: Primitive = Primitive::unsigned(5);
39    pub const U48: Primitive = Primitive::unsigned(6);
40    pub const U56: Primitive = Primitive::unsigned(7);
41    pub const U64: Primitive = Primitive::unsigned(8);
42    pub const U128: Primitive = Primitive::unsigned(16);
43    pub const U160: Primitive = Primitive::unsigned(20);
44    pub const U256: Primitive = Primitive::unsigned(32);
45    pub const U512: Primitive = Primitive::unsigned(64);
46    pub const U1024: Primitive = Primitive::unsigned(128);
47
48    pub const I8: Primitive = Primitive::signed(1);
49    pub const I16: Primitive = Primitive::signed(2);
50    pub const I24: Primitive = Primitive::signed(3);
51    pub const I32: Primitive = Primitive::signed(4);
52    pub const I40: Primitive = Primitive::signed(5);
53    pub const I48: Primitive = Primitive::signed(6);
54    pub const I56: Primitive = Primitive::signed(7);
55    pub const I64: Primitive = Primitive::signed(8);
56    pub const I128: Primitive = Primitive::signed(16);
57    pub const I256: Primitive = Primitive::signed(32);
58    pub const I512: Primitive = Primitive::signed(64);
59    pub const I1024: Primitive = Primitive::signed(128);
60
61    pub const N8: Primitive = Primitive::non_zero(1);
62    pub const N16: Primitive = Primitive::non_zero(2);
63    pub const N24: Primitive = Primitive::non_zero(3);
64    pub const N32: Primitive = Primitive::non_zero(4);
65    pub const N48: Primitive = Primitive::non_zero(6);
66    pub const N64: Primitive = Primitive::non_zero(8);
67    pub const N128: Primitive = Primitive::non_zero(16);
68
69    pub const F16: Primitive = Primitive::float(2);
70    pub const F32: Primitive = Primitive::float(4);
71    pub const F64: Primitive = Primitive::float(8);
72    pub const F80: Primitive = Primitive::float(10);
73    pub const F128: Primitive = Primitive::float(16);
74    pub const F256: Primitive = Primitive::float(32);
75
76    pub const UNIT: Primitive = Primitive(0x00);
77    pub const BYTE: Primitive = Primitive(0x40);
78    pub const RESERVED: Primitive = Primitive(0x80);
79    pub const F16B: Primitive = Primitive(0xC0);
80
81    pub const FLOAT_RESERVED_1: Primitive = Primitive(0xC1);
82    pub const FLOAT_RESERVED_2: Primitive = Primitive(0xC3);
83    pub const FLOAT_RESERVED_3: Primitive = Primitive(0xC5);
84    pub const FLOAT_RESERVED_4: Primitive = Primitive(0xC6);
85    pub const FLOAT_RESERVED_5: Primitive = Primitive(0xC7);
86    pub const FLOAT_RESERVED_6: Primitive = Primitive(0xC9);
87    pub const FLOAT_RESERVED_7: Primitive = Primitive(0xCB);
88    pub const FLOAT_RESERVED_8: Primitive = Primitive(0xCC);
89    pub const FLOAT_RESERVED_9: Primitive = Primitive(0xCD);
90    pub const FLOAT_RESERVED_10: Primitive = Primitive(0xCE);
91    pub const FLOAT_RESERVED_11: Primitive = Primitive(0xCF);
92    pub const FLOAT_RESERVED_12: Primitive = Primitive(0xD1);
93    pub const FLOAT_RESERVED_13: Primitive = Primitive(0xD2);
94    pub const FLOAT_RESERVED_14: Primitive = Primitive(0xD3);
95    pub const FLOAT_RESERVED_15: Primitive = Primitive(0xD4);
96    pub const FLOAT_RESERVED_16: Primitive = Primitive(0xD5);
97    pub const FLOAT_RESERVED_17: Primitive = Primitive(0xD6);
98    pub const FLOAT_RESERVED_18: Primitive = Primitive(0xD7);
99    pub const FLOAT_RESERVED_19: Primitive = Primitive(0xD8);
100    pub const FLOAT_RESERVED_20: Primitive = Primitive(0xD9);
101    pub const FLOAT_RESERVED_21: Primitive = Primitive(0xDA);
102    pub const FLOAT_RESERVED_22: Primitive = Primitive(0xDB);
103    pub const FLOAT_RESERVED_23: Primitive = Primitive(0xDC);
104    pub const FLOAT_RESERVED_24: Primitive = Primitive(0xDE);
105    pub const FLOAT_RESERVED_25: Primitive = Primitive(0xDF);
106
107    pub const FLOAT_RESERVED_26: Primitive = Primitive(0xE1);
108    pub const FLOAT_RESERVED_27: Primitive = Primitive(0xE2);
109    pub const FLOAT_RESERVED_28: Primitive = Primitive(0xE3);
110    pub const FLOAT_RESERVED_29: Primitive = Primitive(0xE4);
111    pub const FLOAT_RESERVED_30: Primitive = Primitive(0xE5);
112    pub const FLOAT_RESERVED_31: Primitive = Primitive(0xE6);
113    pub const FLOAT_RESERVED_32: Primitive = Primitive(0xE7);
114    pub const FLOAT_RESERVED_33: Primitive = Primitive(0xE8);
115    pub const FLOAT_RESERVED_34: Primitive = Primitive(0xE9);
116    pub const FLOAT_RESERVED_35: Primitive = Primitive(0xEA);
117    pub const FLOAT_RESERVED_36: Primitive = Primitive(0xEB);
118    pub const FLOAT_RESERVED_37: Primitive = Primitive(0xEC);
119    pub const FLOAT_RESERVED_38: Primitive = Primitive(0xEE);
120    pub const FLOAT_RESERVED_39: Primitive = Primitive(0xEF);
121
122    pub const FLOAT_RESERVED_40: Primitive = Primitive(0xF0);
123    pub const FLOAT_RESERVED_41: Primitive = Primitive(0xF1);
124    pub const FLOAT_RESERVED_42: Primitive = Primitive(0xF2);
125    pub const FLOAT_RESERVED_43: Primitive = Primitive(0xF3);
126    pub const FLOAT_RESERVED_44: Primitive = Primitive(0xF4);
127    pub const FLOAT_RESERVED_45: Primitive = Primitive(0xF5);
128    pub const FLOAT_RESERVED_46: Primitive = Primitive(0xF6);
129    pub const FLOAT_RESERVED_47: Primitive = Primitive(0xF7);
130    pub const FLOAT_RESERVED_48: Primitive = Primitive(0xF8);
131    pub const FLOAT_RESERVED_49: Primitive = Primitive(0xF9);
132    pub const FLOAT_RESERVED_50: Primitive = Primitive(0xFA);
133    pub const FLOAT_RESERVED_51: Primitive = Primitive(0xFB);
134    pub const FLOAT_RESERVED_52: Primitive = Primitive(0xFC);
135    pub const FLOAT_RESERVED_53: Primitive = Primitive(0xFE);
136    pub const FLOAT_RESERVED_54: Primitive = Primitive(0xFF);
137
138    pub const fn unsigned(bytes: u16) -> Self {
139        Primitive(
140            NumInfo {
141                ty: NumCls::Unsigned,
142                size: NumSize::from_bytes(bytes),
143            }
144            .into_code(),
145        )
146    }
147
148    pub const fn signed(bytes: u16) -> Self {
149        Primitive(
150            NumInfo {
151                ty: NumCls::Signed,
152                size: NumSize::from_bytes(bytes),
153            }
154            .into_code(),
155        )
156    }
157
158    pub const fn non_zero(bytes: u16) -> Self {
159        Primitive(
160            NumInfo {
161                ty: NumCls::NonZero,
162                size: NumSize::from_bytes(bytes),
163            }
164            .into_code(),
165        )
166    }
167
168    pub const fn float(bytes: u16) -> Self {
169        Primitive(
170            NumInfo {
171                ty: NumCls::Float,
172                size: NumSize::from_bytes(bytes),
173            }
174            .into_code(),
175        )
176    }
177
178    pub const fn from_code(code: u8) -> Self { Primitive(code) }
179    pub const fn into_code(self) -> u8 { self.0 }
180
181    pub const fn info(self) -> NumInfo { NumInfo::from_code(self.0) }
182
183    pub const fn byte_size(self) -> u16 { self.info().byte_size() }
184}
185
186impl Display for Primitive {
187    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
188        match *self {
189            Primitive::UNIT => return f.write_str("()"),
190            Primitive::BYTE => return f.write_str("Byte"),
191            Primitive::F16B => return f.write_str("F16b"),
192            Primitive::RESERVED => unreachable!("reserved primitive value"),
193            _ => {}
194        }
195
196        let info = self.info();
197        f.write_char(match info.ty {
198            NumCls::Unsigned => 'U',
199            NumCls::Signed => 'I',
200            NumCls::NonZero => 'N',
201            NumCls::Float => 'F',
202        })?;
203
204        write!(f, "{}", info.byte_size() * 8)
205    }
206}
207
208impl_strict_newtype!(Primitive, STRICT_TYPES_LIB);
209
210/// Information about numeric type
211#[derive(Copy, Clone, PartialEq, Eq, Debug)]
212pub struct NumInfo {
213    /// Class of the number
214    pub ty: NumCls,
215    /// Size of the number, in bytes
216    pub size: NumSize,
217}
218
219impl NumInfo {
220    pub const fn from_code(id: u8) -> Self {
221        NumInfo {
222            ty: NumCls::from_code(id),
223            size: NumSize::from_code(id),
224        }
225    }
226
227    pub const fn into_code(self) -> u8 { self.ty.into_code() | self.size.into_code() }
228
229    pub const fn byte_size(self) -> u16 { self.size.byte_size() }
230}
231
232#[derive(Copy, Clone, PartialEq, Eq, Debug)]
233pub struct NumSize(NumSizeInner);
234
235impl NumSize {
236    pub(super) const fn from_bytes(bytes: u16) -> Self {
237        NumSize(if bytes < 0x20 {
238            NumSizeInner::Bytes(bytes as u8)
239        } else if bytes % 16 != 0 {
240            unreachable!()
241        } else {
242            NumSizeInner::Factored((bytes / 16 - 2) as u8)
243        })
244    }
245
246    pub(super) const fn from_code(id: u8) -> Self {
247        let code = id & 0x1F;
248        NumSize(match (id & 0x20) / 0x20 {
249            0 if id == Primitive::BYTE.0 => NumSizeInner::Bytes(1),
250            0 if id == Primitive::F16B.0 => NumSizeInner::Bytes(2),
251            0 => NumSizeInner::Bytes(code),
252            1 => NumSizeInner::Factored(code),
253            _ => unreachable!(),
254        })
255    }
256
257    pub(super) const fn into_code(self) -> u8 {
258        match self.0 {
259            NumSizeInner::Bytes(bytes) => bytes,
260            NumSizeInner::Factored(factor) => factor | 0x20,
261        }
262    }
263
264    pub const fn byte_size(self) -> u16 {
265        match self.0 {
266            NumSizeInner::Bytes(bytes) => bytes as u16,
267            NumSizeInner::Factored(factor) => 16 * (factor as u16 + 2),
268        }
269    }
270}
271
272/// The way how the size is computed and encoded in the type id
273#[derive(Copy, Clone, PartialEq, Eq, Debug)]
274enum NumSizeInner {
275    /// Lowest 5 bits contain type size in bytes
276    Bytes(u8),
277    /// Lowest 5 bits contain a factor defining the size according to the
278    /// equation `16 * (2 + factor)`
279    // TODO: Ensure that U256 doesn't have two encodings with both variants
280    Factored(u8),
281}
282
283/// Class of the number type
284#[derive(Copy, Clone, PartialEq, Eq, Debug)]
285#[repr(u8)]
286pub enum NumCls {
287    Unsigned = 0x00,
288    Signed = 0x40,
289    NonZero = 0x80,
290    Float = 0xC0,
291}
292
293impl NumCls {
294    pub const fn from_code(id: u8) -> Self {
295        match id & 0xC0 {
296            x if x == NumCls::Unsigned as u8 => NumCls::Unsigned,
297            x if x == NumCls::Signed as u8 => NumCls::Signed,
298            x if x == NumCls::NonZero as u8 => NumCls::NonZero,
299            x if x == NumCls::Float as u8 => NumCls::Float,
300            _ => unreachable!(),
301        }
302    }
303
304    pub const fn into_code(self) -> u8 { self as u8 }
305}
306
307#[cfg(test)]
308mod test {
309    use crate::Primitive;
310
311    #[test]
312    fn unsigned_byte_size() {
313        assert_eq!(Primitive::U8.byte_size(), 1);
314        assert_eq!(Primitive::U16.byte_size(), 2);
315        assert_eq!(Primitive::U24.byte_size(), 3);
316        assert_eq!(Primitive::U32.byte_size(), 4);
317        assert_eq!(Primitive::U40.byte_size(), 5);
318        assert_eq!(Primitive::U48.byte_size(), 6);
319        assert_eq!(Primitive::U56.byte_size(), 7);
320        assert_eq!(Primitive::U64.byte_size(), 8);
321        assert_eq!(Primitive::U128.byte_size(), 16);
322        assert_eq!(Primitive::U160.byte_size(), 20);
323        assert_eq!(Primitive::U256.byte_size(), 32);
324        assert_eq!(Primitive::U512.byte_size(), 64);
325        assert_eq!(Primitive::U1024.byte_size(), 128);
326    }
327
328    #[test]
329    fn signed_byte_size() {
330        assert_eq!(Primitive::I8.byte_size(), 1);
331        assert_eq!(Primitive::I16.byte_size(), 2);
332        assert_eq!(Primitive::I24.byte_size(), 3);
333        assert_eq!(Primitive::I32.byte_size(), 4);
334        assert_eq!(Primitive::I40.byte_size(), 5);
335        assert_eq!(Primitive::I48.byte_size(), 6);
336        assert_eq!(Primitive::I56.byte_size(), 7);
337        assert_eq!(Primitive::I64.byte_size(), 8);
338        assert_eq!(Primitive::I128.byte_size(), 16);
339        assert_eq!(Primitive::I256.byte_size(), 32);
340        assert_eq!(Primitive::I512.byte_size(), 64);
341        assert_eq!(Primitive::I1024.byte_size(), 128);
342    }
343
344    #[test]
345    fn nonzero_byte_size() {
346        assert_eq!(Primitive::N8.byte_size(), 1);
347        assert_eq!(Primitive::N16.byte_size(), 2);
348        assert_eq!(Primitive::N24.byte_size(), 3);
349        assert_eq!(Primitive::N32.byte_size(), 4);
350        assert_eq!(Primitive::N48.byte_size(), 6);
351        assert_eq!(Primitive::N64.byte_size(), 8);
352        assert_eq!(Primitive::N128.byte_size(), 16);
353    }
354
355    #[test]
356    fn float_byte_size() {
357        assert_eq!(Primitive::F16.byte_size(), 2);
358        assert_eq!(Primitive::F16B.byte_size(), 2);
359        assert_eq!(Primitive::F32.byte_size(), 4);
360        assert_eq!(Primitive::F64.byte_size(), 8);
361        assert_eq!(Primitive::F80.byte_size(), 10);
362        assert_eq!(Primitive::F128.byte_size(), 16);
363        assert_eq!(Primitive::F256.byte_size(), 32);
364    }
365
366    #[test]
367    fn spec_byte_size() {
368        assert_eq!(Primitive::UNIT.byte_size(), 0);
369        assert_eq!(Primitive::BYTE.byte_size(), 1);
370    }
371}