strict_encoding/
util.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::cmp::Ordering;
25use std::fmt::{self, Display, Formatter};
26use std::hash::{Hash, Hasher};
27use std::io;
28
29use crate::{ReadStruct, VariantName, WriteStruct, STRICT_TYPES_LIB};
30
31// TODO: Control that min > max!
32#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
33#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
34pub struct Sizing {
35    pub min: u64,
36    pub max: u64,
37}
38impl_strict_struct!(Sizing, STRICT_TYPES_LIB; min, max);
39
40impl Sizing {
41    // TODO: Remove (in strict-types use Array with size 1 for char types instead).
42    pub const ONE: Sizing = Sizing { min: 1, max: 1 };
43
44    pub const U8: Sizing = Sizing {
45        min: 0,
46        max: u8::MAX as u64,
47    };
48
49    pub const U16: Sizing = Sizing {
50        min: 0,
51        max: u16::MAX as u64,
52    };
53
54    pub const U8_NONEMPTY: Sizing = Sizing {
55        min: 1,
56        max: u8::MAX as u64,
57    };
58
59    pub const U16_NONEMPTY: Sizing = Sizing {
60        min: 1,
61        max: u16::MAX as u64,
62    };
63
64    pub const fn new(min: u64, max: u64) -> Self { Sizing { min, max } }
65
66    pub const fn fixed(len: u64) -> Self { Sizing { min: len, max: len } }
67
68    pub const fn is_fixed(&self) -> bool { self.min == self.max }
69
70    pub const fn check(&self, len: usize) -> bool {
71        let len = len as u64;
72        len >= self.min && len <= self.max
73    }
74}
75
76impl Display for Sizing {
77    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
78        match (self.min, self.max) {
79            (0, 0xFFFF) => Ok(()),
80            (min, max) if min == max => write!(f, " ^ {min}"),
81            (0, max) => write!(f, " ^ ..{max:#x}"),
82            (min, 0xFFFF) => write!(f, " ^ {min}.."),
83            (min, max) => write!(f, " ^ {min}..{max:#x}"),
84        }
85    }
86}
87
88#[derive(Clone, Eq, Debug)]
89pub struct Variant {
90    pub name: VariantName,
91    pub tag: u8,
92}
93impl_strict_struct!(Variant, STRICT_TYPES_LIB; name, tag);
94
95#[cfg(feature = "serde")]
96// The manual serde implementation is needed due to `Variant` bein used as a key in maps (like enum
97// or union fields), and serde text implementations such as JSON can't serialize map keys if they
98// are not strings. This solves the issue, by putting string serialization of `Variant` for
99// human-readable serializers
100mod _serde {
101    use std::str::FromStr;
102
103    use serde::ser::SerializeStruct;
104    use serde::{Deserialize, Deserializer, Serialize, Serializer};
105
106    use super::*;
107
108    impl Serialize for Variant {
109        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
110        where S: Serializer {
111            if serializer.is_human_readable() {
112                serializer.serialize_str(&format!("{}:{}", self.name, self.tag))
113            } else {
114                let mut s = serializer.serialize_struct("Variant", 2)?;
115                s.serialize_field("name", &self.name)?;
116                s.serialize_field("tag", &self.tag)?;
117                s.end()
118            }
119        }
120    }
121
122    impl<'de> Deserialize<'de> for Variant {
123        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
124        where D: Deserializer<'de> {
125            if deserializer.is_human_readable() {
126                let s = String::deserialize(deserializer)?;
127                let mut split = s.split(':');
128                let (name, tag) = (split.next(), split.next());
129                if split.next().is_some() {
130                    return Err(serde::de::Error::custom(format!(
131                        "Invalid variant format: '{}'. Expected 'name:tag'",
132                        s
133                    )));
134                }
135                match (name, tag) {
136                    (Some(name), Some(tag)) => {
137                        let name = VariantName::from_str(name).map_err(|e| {
138                            serde::de::Error::custom(format!("Invalid variant name: {}", e))
139                        })?;
140                        let tag = tag.parse::<u8>().map_err(|e| {
141                            serde::de::Error::custom(format!("Invalid variant tag: {}", e))
142                        })?;
143                        Ok(Variant { name, tag })
144                    }
145                    _ => Err(serde::de::Error::custom(format!(
146                        "Invalid variant format: '{}'. Expected 'name:tag'",
147                        s
148                    ))),
149                }
150            } else {
151                #[cfg_attr(feature = "serde", derive(Deserialize), serde(rename = "Variant"))]
152                struct VariantFields {
153                    name: VariantName,
154                    tag: u8,
155                }
156                let VariantFields { name, tag } = VariantFields::deserialize(deserializer)?;
157                Ok(Variant { name, tag })
158            }
159        }
160    }
161}
162
163impl Variant {
164    pub fn named(tag: u8, name: VariantName) -> Variant { Variant { name, tag } }
165
166    pub fn none() -> Variant {
167        Variant {
168            name: vname!("none"),
169            tag: 0,
170        }
171    }
172    pub fn some() -> Variant {
173        Variant {
174            name: vname!("some"),
175            tag: 1,
176        }
177    }
178}
179
180impl PartialEq for Variant {
181    fn eq(&self, other: &Self) -> bool { self.tag == other.tag || self.name == other.name }
182}
183
184impl PartialOrd for Variant {
185    fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
186}
187
188impl Hash for Variant {
189    fn hash<H: Hasher>(&self, state: &mut H) {
190        self.tag.hash(state);
191        self.name.hash(state);
192    }
193}
194
195impl Ord for Variant {
196    fn cmp(&self, other: &Self) -> Ordering {
197        if self == other {
198            return Ordering::Equal;
199        }
200        self.tag.cmp(&other.tag)
201    }
202}
203
204impl Display for Variant {
205    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
206        write!(f, "{}", self.name)?;
207        Ok(())
208    }
209}
210
211#[cfg(test)]
212mod test {
213    #![allow(unused)]
214
215    use std::io::Cursor;
216
217    use crate::*;
218
219    #[cfg(feature = "serde")]
220    #[test]
221    fn variant_serde_roundtrip() {
222        let variant_orig = Variant::strict_dumb();
223
224        // CBOR
225        let mut buf = Vec::new();
226        ciborium::into_writer(&variant_orig, &mut buf).unwrap();
227        let variant_post: Variant = ciborium::from_reader(Cursor::new(&buf)).unwrap();
228        assert_eq!(variant_orig, variant_post);
229
230        // JSON
231        let variant_str = serde_json::to_string(&variant_orig).unwrap();
232        let variant_post: Variant = serde_json::from_str(&variant_str).unwrap();
233        assert_eq!(variant_orig, variant_post);
234
235        // YAML
236        let variant_str = serde_yaml::to_string(&variant_orig).unwrap();
237        let variant_post: Variant = serde_yaml::from_str(&variant_str).unwrap();
238        assert_eq!(variant_orig, variant_post);
239    }
240}