strict_encoding/
primitives.rs

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