Skip to main content

serde_with/
utils.rs

1pub(crate) mod duration;
2
3use crate::prelude::*;
4
5/// Re-Implementation of `serde::private::de::size_hint::cautious`
6#[cfg(feature = "alloc")]
7#[inline]
8pub(crate) fn size_hint_cautious<Element>(hint: Option<usize>) -> usize {
9    const MAX_PREALLOC_BYTES: usize = 1024 * 1024;
10
11    if size_of::<Element>() == 0 {
12        0
13    } else {
14        core::cmp::min(hint.unwrap_or(0), MAX_PREALLOC_BYTES / size_of::<Element>())
15    }
16}
17
18/// Safer version of [`Vec::with_capacity`] that uses `size_hint_cautious` to prevent overflow when calculating the capacity.
19///
20/// Implemented to address crashes due to capacity overflow, e.g., GHSA-7gcf-g7xr-8hxj
21#[cfg(feature = "alloc")]
22#[inline]
23pub(crate) fn vec_with_capacity_cautious<Element>(hint: Option<usize>) -> Vec<Element> {
24    #[allow(clippy::disallowed_methods)]
25    Vec::with_capacity(size_hint_cautious::<Element>(hint))
26}
27
28/// Re-Implementation of `serde::private::de::size_hint::from_bounds`
29#[cfg(feature = "alloc")]
30#[inline]
31pub fn size_hint_from_bounds<I>(iter: &I) -> Option<usize>
32where
33    I: Iterator,
34{
35    fn _size_hint_from_bounds(bounds: (usize, Option<usize>)) -> Option<usize> {
36        match bounds {
37            (lower, Some(upper)) if lower == upper => Some(upper),
38            _ => None,
39        }
40    }
41    _size_hint_from_bounds(iter.size_hint())
42}
43
44pub(crate) const NANOS_PER_SEC: u128 = 1_000_000_000;
45pub(crate) const NANOS_PER_SEC_F64: f64 = 1_000_000_000.0;
46// pub(crate) const NANOS_PER_MILLI: u32 = 1_000_000;
47// pub(crate) const NANOS_PER_MICRO: u32 = 1_000;
48// pub(crate) const MILLIS_PER_SEC: u64 = 1_000;
49// pub(crate) const MICROS_PER_SEC: u64 = 1_000_000;
50pub(crate) const U64_MAX: u128 = u64::MAX as u128;
51
52pub(crate) struct MapIter<'de, A, K, V> {
53    pub(crate) access: A,
54    marker: PhantomData<(&'de (), K, V)>,
55}
56
57impl<'de, A, K, V> MapIter<'de, A, K, V> {
58    pub(crate) fn new(access: A) -> Self
59    where
60        A: MapAccess<'de>,
61    {
62        Self {
63            access,
64            marker: PhantomData,
65        }
66    }
67}
68
69impl<'de, A, K, V> Iterator for MapIter<'de, A, K, V>
70where
71    A: MapAccess<'de>,
72    K: Deserialize<'de>,
73    V: Deserialize<'de>,
74{
75    type Item = Result<(K, V), A::Error>;
76
77    fn next(&mut self) -> Option<Self::Item> {
78        self.access.next_entry().transpose()
79    }
80
81    fn size_hint(&self) -> (usize, Option<usize>) {
82        match self.access.size_hint() {
83            Some(size) => (size, Some(size)),
84            None => (0, None),
85        }
86    }
87}
88
89pub(crate) struct SeqIter<'de, A, T> {
90    access: A,
91    marker: PhantomData<(&'de (), T)>,
92}
93
94impl<'de, A, T> SeqIter<'de, A, T> {
95    pub(crate) fn new(access: A) -> Self
96    where
97        A: SeqAccess<'de>,
98    {
99        Self {
100            access,
101            marker: PhantomData,
102        }
103    }
104}
105
106impl<'de, A, T> Iterator for SeqIter<'de, A, T>
107where
108    A: SeqAccess<'de>,
109    T: Deserialize<'de>,
110{
111    type Item = Result<T, A::Error>;
112
113    fn next(&mut self) -> Option<Self::Item> {
114        self.access.next_element().transpose()
115    }
116
117    fn size_hint(&self) -> (usize, Option<usize>) {
118        match self.access.size_hint() {
119            Some(size) => (size, Some(size)),
120            None => (0, None),
121        }
122    }
123}
124
125pub(crate) fn duration_signed_from_secs_f64(secs: f64) -> Result<DurationSigned, &'static str> {
126    const MAX_NANOS_F64: f64 = ((U64_MAX + 1) * NANOS_PER_SEC) as f64;
127    // TODO why are the seconds converted to nanoseconds first?
128    // Does it make sense to just truncate the value?
129    let mut nanos = secs * NANOS_PER_SEC_F64;
130    if !nanos.is_finite() {
131        return Err("got non-finite value when converting float to duration");
132    }
133    if nanos >= MAX_NANOS_F64 {
134        return Err("overflow when converting float to duration");
135    }
136    let mut sign = Sign::Positive;
137    if nanos < 0.0 {
138        nanos = -nanos;
139        sign = Sign::Negative;
140    }
141    let nanos = nanos as u128;
142    Ok(DurationSigned::new(
143        sign,
144        (nanos / NANOS_PER_SEC) as u64,
145        (nanos % NANOS_PER_SEC) as u32,
146    ))
147}
148
149/// Collect an array of a fixed size from an iterator.
150///
151/// # Safety
152/// The code follow exactly the pattern of initializing an array element-by-element from the standard library.
153/// <https://doc.rust-lang.org/nightly/std/mem/union.MaybeUninit.html#initializing-an-array-element-by-element>
154pub(crate) fn array_from_iterator<I, T, E, const N: usize>(
155    mut iter: I,
156    expected: &dyn Expected,
157) -> Result<[T; N], E>
158where
159    I: Iterator<Item = Result<T, E>>,
160    E: DeError,
161{
162    use core::mem::MaybeUninit;
163
164    fn drop_array_elems<T, const N: usize>(num: usize, mut arr: [MaybeUninit<T>; N]) {
165        arr[..num].iter_mut().for_each(|elem| {
166            // TODO This would be better with assume_init_drop nightly function
167            // https://github.com/rust-lang/rust/issues/63567
168            unsafe { core::ptr::drop_in_place(elem.as_mut_ptr()) };
169        });
170    }
171
172    // Create an uninitialized array of `MaybeUninit`. The `assume_init` is
173    // safe because the type we are claiming to have initialized here is a
174    // bunch of `MaybeUninit`s, which do not require initialization.
175    //
176    // TODO could be simplified with nightly maybe_uninit_uninit_array feature
177    // https://doc.rust-lang.org/nightly/std/mem/union.MaybeUninit.html#method.uninit_array
178
179    let mut arr: [MaybeUninit<T>; N] = unsafe { MaybeUninit::uninit().assume_init() };
180
181    // Dropping a `MaybeUninit` does nothing. Thus using raw pointer
182    // assignment instead of `ptr::write` does not cause the old
183    // uninitialized value to be dropped. Also if there is a panic during
184    // this loop, we have a memory leak, but there is no memory safety
185    // issue.
186    for (idx, elem) in arr[..].iter_mut().enumerate() {
187        *elem = match iter.next() {
188            Some(Ok(value)) => MaybeUninit::new(value),
189            Some(Err(err)) => {
190                drop_array_elems(idx, arr);
191                return Err(err);
192            }
193            None => {
194                drop_array_elems(idx, arr);
195                return Err(DeError::invalid_length(idx, expected));
196            }
197        };
198    }
199
200    // Everything is initialized. Transmute the array to the
201    // initialized type.
202    // A normal transmute is not possible because of:
203    // https://github.com/rust-lang/rust/issues/61956
204    Ok(unsafe { core::mem::transmute_copy::<_, [T; N]>(&arr) })
205}
206
207/// Writer that writes into a `&mut [u8]` while checking the length of the buffer
208struct BufWriter<'a> {
209    bytes: &'a mut [u8],
210    offset: usize,
211}
212
213impl<'a> BufWriter<'a> {
214    fn new(bytes: &'a mut [u8]) -> Self {
215        BufWriter { bytes, offset: 0 }
216    }
217
218    fn into_str(self) -> &'a str {
219        let slice = &self.bytes[..self.offset];
220        str::from_utf8(slice)
221            .unwrap_or("Failed to extract valid string from BufWriter. This should never happen.")
222    }
223}
224
225impl fmt::Write for BufWriter<'_> {
226    fn write_str(&mut self, s: &str) -> fmt::Result {
227        if s.len() > self.bytes.len() - self.offset {
228            Err(fmt::Error)
229        } else {
230            self.bytes[self.offset..self.offset + s.len()].copy_from_slice(s.as_bytes());
231            self.offset += s.len();
232            Ok(())
233        }
234    }
235}
236
237// 58 chars is long enough for any i128 and u128 value
238pub(crate) fn get_unexpected_i128(value: i128, buf: &mut [u8; 58]) -> Unexpected<'_> {
239    let mut writer = BufWriter::new(buf);
240    fmt::Write::write_fmt(&mut writer, format_args!("integer `{value}` as i128")).unwrap();
241    Unexpected::Other(writer.into_str())
242}
243
244// 58 chars is long enough for any i128 and u128 value
245pub(crate) fn get_unexpected_u128(value: u128, buf: &mut [u8; 58]) -> Unexpected<'_> {
246    let mut writer = BufWriter::new(buf);
247    fmt::Write::write_fmt(&mut writer, format_args!("integer `{value}` as u128")).unwrap();
248    Unexpected::Other(writer.into_str())
249}
250
251#[cfg(any(
252    feature = "schemars_0_8",
253    feature = "schemars_0_9",
254    feature = "schemars_1"
255))]
256pub(crate) struct DropGuard<T, F: FnOnce(T)> {
257    value: core::mem::ManuallyDrop<T>,
258    guard: Option<F>,
259}
260
261#[cfg(any(
262    feature = "schemars_0_8",
263    feature = "schemars_0_9",
264    feature = "schemars_1"
265))]
266impl<T, F: FnOnce(T)> DropGuard<T, F> {
267    pub fn new(value: T, guard: F) -> Self {
268        Self {
269            value: core::mem::ManuallyDrop::new(value),
270            guard: Some(guard),
271        }
272    }
273
274    pub fn unguarded(value: T) -> Self {
275        Self {
276            value: core::mem::ManuallyDrop::new(value),
277            guard: None,
278        }
279    }
280}
281
282#[cfg(any(
283    feature = "schemars_0_8",
284    feature = "schemars_0_9",
285    feature = "schemars_1"
286))]
287impl<T, F: FnOnce(T)> core::ops::Deref for DropGuard<T, F> {
288    type Target = T;
289
290    fn deref(&self) -> &Self::Target {
291        &self.value
292    }
293}
294
295#[cfg(any(
296    feature = "schemars_0_8",
297    feature = "schemars_0_9",
298    feature = "schemars_1"
299))]
300impl<T, F: FnOnce(T)> core::ops::DerefMut for DropGuard<T, F> {
301    fn deref_mut(&mut self) -> &mut Self::Target {
302        &mut self.value
303    }
304}
305
306#[cfg(any(
307    feature = "schemars_0_8",
308    feature = "schemars_0_9",
309    feature = "schemars_1"
310))]
311impl<T, F: FnOnce(T)> Drop for DropGuard<T, F> {
312    fn drop(&mut self) {
313        // SAFETY: value is known to be initialized since we only ever remove it here.
314        let value = unsafe { core::mem::ManuallyDrop::take(&mut self.value) };
315
316        if let Some(guard) = self.guard.take() {
317            guard(value);
318        }
319    }
320}
321
322#[cfg(any(feature = "schemars_0_9", feature = "schemars_1"))]
323pub(crate) trait NumberExt: Sized {
324    fn saturating_sub(&self, count: u64) -> Self;
325}
326
327#[cfg(any(feature = "schemars_0_9", feature = "schemars_1"))]
328impl NumberExt for serde_json::Number {
329    fn saturating_sub(&self, count: u64) -> Self {
330        if let Some(v) = self.as_u64() {
331            return v.saturating_sub(count).into();
332        }
333
334        if let Some(v) = self.as_i64() {
335            if count < i64::MAX as u64 {
336                return v.saturating_sub(count as _).into();
337            }
338        }
339
340        if let Some(v) = self.as_f64() {
341            return serde_json::Number::from_f64(v - (count as f64))
342                .expect("saturating_sub resulted in NaN");
343        }
344
345        unreachable!()
346    }
347}