• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vigna / epserde-rs / 18535034917

15 Oct 2025 04:01PM UTC coverage: 44.27% (-0.05%) from 44.324%
18535034917

push

github

vigna
Static check for zero-copy mismatch (hurrah\!)

0 of 2 new or added lines in 1 file covered. (0.0%)

5 existing lines in 4 files now uncovered.

649 of 1466 relevant lines covered (44.27%)

274.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

35.61
/epserde/src/impls/stdlib.rs
1
/*
2
 * SPDX-FileCopyrightText: 2023 Tommaso Fontana
3
 * SPDX-FileCopyrightText: 2025 Sebastiano Vigna
4
 *
5
 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
6
 */
7

8
//! Implementation for structures from the standard library.
9
//!
10
//! Note that none of this types can be zero-copy (unless they are empty, as in
11
//! the case of [`RangeFull`], because they are not `repr(C)`.
12
use ser::WriteWithNames;
13

14
use crate::prelude::*;
15
use core::hash::Hash;
16
use core::ops::{
17
    Bound, ControlFlow, Range, RangeBounds, RangeFrom, RangeFull, RangeInclusive, RangeTo,
18
    RangeToInclusive,
19
};
20

21
#[cfg(feature = "std")]
22
use std::collections::hash_map::DefaultHasher;
23

24
// This implementation makes it possible to serialize
25
// PhantomData<DefaultHasher>.
26
#[cfg(feature = "std")]
27
impl TypeHash for DefaultHasher {
28
    fn type_hash(hasher: &mut impl core::hash::Hasher) {
1✔
29
        "std::hash::DefaultHasher".hash(hasher);
3✔
30
    }
31
}
32

33
macro_rules! impl_ranges {
34
    ($ty:ident) => {
35
        unsafe impl<Idx> CopyType for $ty<Idx> {
36
            type Copy = Deep;
37
        }
38

39
        impl<Idx: TypeHash> TypeHash for $ty<Idx> {
40
            fn type_hash(hasher: &mut impl core::hash::Hasher) {
20✔
41
                stringify!(core::ops::$ty).hash(hasher);
60✔
42
                Idx::type_hash(hasher);
40✔
43
            }
44
        }
45

46
        impl<Idx: AlignHash> AlignHash for $ty<Idx> {
47
            fn align_hash(hasher: &mut impl core::hash::Hasher, offset_of: &mut usize) {
20✔
48
                Idx::align_hash(hasher, offset_of);
60✔
49
                Idx::align_hash(hasher, offset_of);
60✔
50
            }
51
        }
52
    };
53
}
54

55
impl_ranges!(Range);
56
impl_ranges!(RangeFrom);
57
impl_ranges!(RangeInclusive);
58
impl_ranges!(RangeTo);
59
impl_ranges!(RangeToInclusive);
60

61
// RangeFull is a zero-sized type, so it is always zero-copy.
62

63
unsafe impl CopyType for RangeFull {
64
    type Copy = Zero;
65
}
66

67
impl TypeHash for RangeFull {
68
    fn type_hash(hasher: &mut impl core::hash::Hasher) {
1✔
69
        stringify!(core::ops::RangeFull).hash(hasher);
3✔
70
    }
71
}
72

73
impl AlignHash for RangeFull {
74
    fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {}
2✔
75
}
76

77
impl AlignTo for RangeFull {
78
    fn align_to() -> usize {
×
79
        0
×
80
    }
81
}
82

83
impl<Idx: SerInner> SerInner for Range<Idx> {
84
    type SerType = Range<Idx::SerType>;
85
    const IS_ZERO_COPY: bool = false;
86

87
    #[inline(always)]
88
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
4✔
89
        backend.write("start", &self.start)?;
16✔
90
        backend.write("end", &self.end)?;
4✔
91
        Ok(())
4✔
92
    }
93
}
94

95
impl<Idx: DeserInner> DeserInner for Range<Idx> {
96
    #[inline(always)]
97
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
4✔
98
        let start = unsafe { Idx::_deser_full_inner(backend) }?;
12✔
99
        let end = unsafe { Idx::_deser_full_inner(backend) }?;
4✔
100
        Ok(Range { start, end })
×
101
    }
102
    type DeserType<'a> = Range<DeserType<'a, Idx>>;
103
    #[inline(always)]
104
    unsafe fn _deser_eps_inner<'a>(
2✔
105
        backend: &mut SliceWithPos<'a>,
106
    ) -> deser::Result<Self::DeserType<'a>> {
107
        let start = unsafe { Idx::_deser_eps_inner(backend) }?;
6✔
108
        let end = unsafe { Idx::_deser_eps_inner(backend) }?;
2✔
109
        Ok(Range { start, end })
×
110
    }
111
}
112

113
impl<Idx: SerInner> SerInner for RangeFrom<Idx> {
114
    type SerType = RangeFrom<Idx::SerType>;
115
    const IS_ZERO_COPY: bool = false;
116

117
    #[inline(always)]
118
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
×
119
        backend.write("start", &self.start)?;
×
120
        Ok(())
×
121
    }
122
}
123

124
impl<Idx: DeserInner> DeserInner for RangeFrom<Idx> {
125
    #[inline(always)]
126
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
×
127
        let start = unsafe { Idx::_deser_full_inner(backend) }?;
×
128
        Ok(RangeFrom { start })
×
129
    }
130
    type DeserType<'a> = RangeFrom<DeserType<'a, Idx>>;
131
    #[inline(always)]
132
    unsafe fn _deser_eps_inner<'a>(
×
133
        backend: &mut SliceWithPos<'a>,
134
    ) -> deser::Result<Self::DeserType<'a>> {
135
        let start = unsafe { Idx::_deser_eps_inner(backend) }?;
×
136
        Ok(RangeFrom { start })
×
137
    }
138
}
139

140
impl<Idx: SerInner> SerInner for RangeInclusive<Idx> {
141
    type SerType = RangeInclusive<Idx::SerType>;
142
    const IS_ZERO_COPY: bool = false;
143

144
    #[inline(always)]
145
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
1✔
146
        backend.write("start", self.start())?;
4✔
147
        backend.write("end", self.end())?;
1✔
148
        backend.write("exhausted", &matches!(self.end_bound(), Bound::Excluded(_)))?;
2✔
149
        Ok(())
1✔
150
    }
151
}
152

153
impl<Idx: DeserInner> DeserInner for RangeInclusive<Idx> {
154
    #[inline(always)]
155
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
1✔
156
        let start = unsafe { Idx::_deser_full_inner(backend) }?;
3✔
157
        let end = unsafe { Idx::_deser_full_inner(backend) }?;
1✔
158
        let exhausted = unsafe { bool::_deser_full_inner(backend) }?;
1✔
159
        assert!(!exhausted, "cannot deserialize an exhausted range");
×
160
        Ok(start..=end)
1✔
161
    }
162
    type DeserType<'a> = RangeInclusive<DeserType<'a, Idx>>;
163
    #[inline(always)]
164
    unsafe fn _deser_eps_inner<'a>(
1✔
165
        backend: &mut SliceWithPos<'a>,
166
    ) -> deser::Result<Self::DeserType<'a>> {
167
        let start = unsafe { Idx::_deser_eps_inner(backend) }?;
3✔
168
        let end = unsafe { Idx::_deser_eps_inner(backend) }?;
1✔
169
        let exhausted = unsafe { bool::_deser_full_inner(backend) }?;
1✔
170
        assert!(!exhausted, "cannot deserialize an exhausted range");
×
171
        Ok(start..=end)
1✔
172
    }
173
}
174

175
impl<Idx: SerInner> SerInner for RangeTo<Idx> {
176
    type SerType = RangeTo<Idx::SerType>;
177
    const IS_ZERO_COPY: bool = false;
178

179
    #[inline(always)]
180
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
×
181
        backend.write("end", &self.end)?;
×
182
        Ok(())
×
183
    }
184
}
185

186
impl<Idx: DeserInner> DeserInner for RangeTo<Idx> {
187
    #[inline(always)]
188
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
×
189
        let end = unsafe { Idx::_deser_full_inner(backend) }?;
×
190
        Ok(..end)
×
191
    }
192
    type DeserType<'a> = RangeTo<DeserType<'a, Idx>>;
193
    #[inline(always)]
194
    unsafe fn _deser_eps_inner<'a>(
×
195
        backend: &mut SliceWithPos<'a>,
196
    ) -> deser::Result<Self::DeserType<'a>> {
197
        let end = unsafe { Idx::_deser_eps_inner(backend) }?;
×
198
        Ok(..end)
×
199
    }
200
}
201

202
impl<Idx: SerInner> SerInner for RangeToInclusive<Idx> {
203
    type SerType = RangeToInclusive<Idx::SerType>;
204
    const IS_ZERO_COPY: bool = false;
205

206
    #[inline(always)]
207
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
×
208
        backend.write("end", &self.end)?;
×
209
        Ok(())
×
210
    }
211
}
212

213
impl<Idx: DeserInner> DeserInner for RangeToInclusive<Idx> {
214
    #[inline(always)]
215
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
×
216
        let end = unsafe { Idx::_deser_full_inner(backend) }?;
×
217
        Ok(..=end)
×
218
    }
219
    type DeserType<'a> = RangeToInclusive<DeserType<'a, Idx>>;
220
    #[inline(always)]
221
    unsafe fn _deser_eps_inner<'a>(
×
222
        backend: &mut SliceWithPos<'a>,
223
    ) -> deser::Result<Self::DeserType<'a>> {
224
        let end = unsafe { Idx::_deser_eps_inner(backend) }?;
×
225
        Ok(..=end)
×
226
    }
227
}
228

229
impl SerInner for RangeFull {
230
    type SerType = RangeFull;
231
    const IS_ZERO_COPY: bool = false;
232

233
    #[inline(always)]
234
    unsafe fn _ser_inner(&self, _backend: &mut impl WriteWithNames) -> ser::Result<()> {
×
235
        Ok(())
×
236
    }
237
}
238

239
impl DeserInner for RangeFull {
240
    #[inline(always)]
241
    unsafe fn _deser_full_inner(_backend: &mut impl ReadWithPos) -> deser::Result<Self> {
×
242
        Ok(RangeFull)
×
243
    }
244
    type DeserType<'a> = RangeFull;
245
    #[inline(always)]
246
    unsafe fn _deser_eps_inner<'a>(
×
247
        _backend: &mut SliceWithPos<'a>,
248
    ) -> deser::Result<Self::DeserType<'a>> {
249
        Ok(RangeFull)
×
250
    }
251
}
252

253
unsafe impl<T> CopyType for Bound<T> {
254
    type Copy = Deep;
255
}
256

257
impl<T: TypeHash> TypeHash for Bound<T> {
258
    fn type_hash(hasher: &mut impl core::hash::Hasher) {
1✔
259
        stringify!(core::ops::Bound).hash(hasher);
3✔
260
        T::type_hash(hasher);
2✔
261
    }
262
}
263

264
impl<T> AlignHash for Bound<T> {
265
    fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {}
2✔
266
}
267

268
impl<T: SerInner> SerInner for Bound<T> {
269
    type SerType = Bound<T::SerType>;
270
    const IS_ZERO_COPY: bool = false;
271

UNCOV
272
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
×
273
        match self {
×
274
            Bound::Unbounded => backend.write("Tag", &0_u8),
×
275
            Bound::Included(val) => {
×
276
                backend.write("Tag", &1_u8)?;
×
277
                backend.write("Included", val)
×
278
            }
279
            Bound::Excluded(val) => {
×
280
                backend.write("Tag", &2_u8)?;
×
281
                backend.write("Excluded", val)
×
282
            }
283
        }
284
    }
285
}
286

287
impl<T: DeserInner> DeserInner for Bound<T> {
288
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
×
289
        let tag = unsafe { u8::_deser_full_inner(backend) }?;
×
290
        match tag {
×
291
            0 => Ok(Bound::Unbounded),
×
292
            1 => Ok(Bound::Included(unsafe { T::_deser_full_inner(backend) }?)),
×
293
            2 => Ok(Bound::Excluded(unsafe { T::_deser_full_inner(backend) }?)),
×
294
            _ => Err(deser::Error::InvalidTag(tag as usize)),
×
295
        }
296
    }
297

298
    type DeserType<'a> = Bound<DeserType<'a, T>>;
299

300
    unsafe fn _deser_eps_inner<'a>(
×
301
        backend: &mut SliceWithPos<'a>,
302
    ) -> deser::Result<Self::DeserType<'a>> {
303
        let tag = unsafe { u8::_deser_full_inner(backend) }?;
×
304
        match tag {
×
305
            0 => Ok(Bound::Unbounded),
×
306
            1 => Ok(Bound::Included(unsafe { T::_deser_eps_inner(backend) }?)),
×
307
            2 => Ok(Bound::Excluded(unsafe { T::_deser_eps_inner(backend) }?)),
×
308
            _ => Err(deser::Error::InvalidTag(tag as usize)),
×
309
        }
310
    }
311
}
312

313
unsafe impl<B, C> CopyType for ControlFlow<B, C> {
314
    type Copy = Deep;
315
}
316

317
impl<B: TypeHash, C: TypeHash> TypeHash for ControlFlow<B, C> {
318
    fn type_hash(hasher: &mut impl core::hash::Hasher) {
1✔
319
        stringify!(core::ops::ControlFlow).hash(hasher);
3✔
320
        B::type_hash(hasher);
2✔
321
        C::type_hash(hasher);
2✔
322
    }
323
}
324

325
impl<B: AlignHash, C: AlignHash> AlignHash for ControlFlow<B, C> {
326
    fn align_hash(hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {
1✔
327
        B::align_hash(hasher, &mut 0);
3✔
328
        C::align_hash(hasher, &mut 0);
3✔
329
    }
330
}
331

332
impl<B: SerInner, C: SerInner> SerInner for ControlFlow<B, C> {
333
    type SerType = ControlFlow<B::SerType, C::SerType>;
334
    const IS_ZERO_COPY: bool = false;
335

UNCOV
336
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
×
337
        match self {
×
338
            ControlFlow::Break(br) => {
×
339
                backend.write("Tag", &0_u8)?;
×
340
                backend.write("Break", br)
×
341
            }
342
            ControlFlow::Continue(val) => {
×
343
                backend.write("Tag", &1_u8)?;
×
344
                backend.write("Continue", val)
×
345
            }
346
        }
347
    }
348
}
349

350
impl<B: DeserInner, C: DeserInner> DeserInner for ControlFlow<B, C> {
351
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
×
352
        let tag = unsafe { u8::_deser_full_inner(backend) }?;
×
353
        match tag {
×
354
            1 => Ok(ControlFlow::Break(unsafe {
×
355
                B::_deser_full_inner(backend)
×
356
            }?)),
357
            2 => Ok(ControlFlow::Continue(unsafe {
×
358
                C::_deser_full_inner(backend)
×
359
            }?)),
360
            _ => Err(deser::Error::InvalidTag(tag as usize)),
×
361
        }
362
    }
363

364
    type DeserType<'a> = ControlFlow<DeserType<'a, B>, DeserType<'a, C>>;
365

366
    unsafe fn _deser_eps_inner<'a>(
×
367
        backend: &mut SliceWithPos<'a>,
368
    ) -> deser::Result<Self::DeserType<'a>> {
369
        let tag = unsafe { u8::_deser_full_inner(backend) }?;
×
370
        match tag {
×
371
            1 => Ok(ControlFlow::Break(unsafe { B::_deser_eps_inner(backend) }?)),
×
372
            2 => Ok(ControlFlow::Continue(unsafe {
×
373
                C::_deser_eps_inner(backend)
×
374
            }?)),
375
            _ => Err(deser::Error::InvalidTag(tag as usize)),
×
376
        }
377
    }
378
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc