gprimitives/
utils.rs

1// This file is part of Gear.
2
3// Copyright (C) 2021-2025 Gear Technologies Inc.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Primitives' utils
20
21use core::{
22    fmt,
23    ops::{Index, RangeFrom, RangeTo},
24    str,
25};
26#[cfg(feature = "serde")]
27use {
28    core::{marker::PhantomData, str::FromStr},
29    serde::de,
30};
31
32/// `[u8]` formatter.
33///
34/// So it looks like `0x12ae..ff80`.
35pub enum ByteSliceFormatter<'a> {
36    /// Fixed-size array so it can be formatted on stack.
37    Array(&'a [u8; 32]),
38    /// Slice of any size.
39    ///
40    /// If the size is less or equal to 32, it is formatted on stack,
41    /// on heap otherwise.
42    Dynamic(&'a [u8]),
43}
44
45impl ByteSliceFormatter<'_> {
46    fn len(&self) -> usize {
47        match self {
48            ByteSliceFormatter::Array(arr) => arr.len(),
49            ByteSliceFormatter::Dynamic(slice) => slice.len(),
50        }
51    }
52}
53
54impl Index<RangeTo<usize>> for ByteSliceFormatter<'_> {
55    type Output = <[u8] as Index<RangeTo<usize>>>::Output;
56
57    fn index(&self, index: RangeTo<usize>) -> &Self::Output {
58        match self {
59            ByteSliceFormatter::Array(arr) => &arr[index],
60            ByteSliceFormatter::Dynamic(slice) => &slice[index],
61        }
62    }
63}
64
65impl Index<RangeFrom<usize>> for ByteSliceFormatter<'_> {
66    type Output = <[u8] as Index<RangeFrom<usize>>>::Output;
67
68    fn index(&self, index: RangeFrom<usize>) -> &Self::Output {
69        match self {
70            ByteSliceFormatter::Array(arr) => &arr[index],
71            ByteSliceFormatter::Dynamic(slice) => &slice[index],
72        }
73    }
74}
75
76impl fmt::Display for ByteSliceFormatter<'_> {
77    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78        const STACK_LEN: usize = 32;
79
80        let len = self.len();
81        let median = len.div_ceil(2);
82
83        let mut e1 = median;
84        let mut s2 = median;
85
86        if let Some(precision) = f.precision()
87            && precision < median
88        {
89            e1 = precision;
90            s2 = len - precision;
91        }
92
93        let out1_len = e1 * 2;
94        let out2_len = (len - s2) * 2;
95
96        let mut out1_vec;
97        let mut out2_vec;
98
99        let (out1, out2) = match self {
100            ByteSliceFormatter::Array(_arr) => (
101                &mut [0u8; STACK_LEN] as &mut [u8],
102                &mut [0u8; STACK_LEN] as &mut [u8],
103            ),
104            ByteSliceFormatter::Dynamic(slice) if slice.len() <= STACK_LEN => (
105                &mut [0u8; STACK_LEN] as &mut [u8],
106                &mut [0u8; STACK_LEN] as &mut [u8],
107            ),
108            ByteSliceFormatter::Dynamic(_slice) => {
109                out1_vec = alloc::vec![0u8; out1_len];
110                out2_vec = alloc::vec![0u8; out2_len];
111                (&mut out1_vec[..], &mut out2_vec[..])
112            }
113        };
114
115        let _ = hex::encode_to_slice(&self[..e1], &mut out1[..out1_len]);
116        let _ = hex::encode_to_slice(&self[s2..], &mut out2[..out2_len]);
117
118        let p1 = unsafe { str::from_utf8_unchecked(&out1[..out1_len]) };
119        let p2 = unsafe { str::from_utf8_unchecked(&out2[..out2_len]) };
120        let sep = if e1.ne(&s2) { ".." } else { Default::default() };
121
122        write!(f, "0x{p1}{sep}{p2}")
123    }
124}
125
126#[cfg(feature = "serde")]
127pub(crate) struct HexStrVisitor<T: FromStr>(PhantomData<T>);
128
129#[cfg(feature = "serde")]
130impl<T: FromStr> HexStrVisitor<T> {
131    pub fn new() -> Self {
132        Self(PhantomData)
133    }
134}
135
136#[cfg(feature = "serde")]
137impl<T: FromStr> de::Visitor<'_> for HexStrVisitor<T> {
138    type Value = T;
139
140    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
141        formatter.write_str("a string in hex format starting with 0x")
142    }
143
144    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
145    where
146        E: de::Error,
147    {
148        value
149            .parse()
150            .map_err(|_| de::Error::invalid_value(de::Unexpected::Str(value), &self))
151    }
152}