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