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

vigna / epserde-rs / 22098498149

17 Feb 2026 12:28PM UTC coverage: 52.283% (-1.7%) from 54.0%
22098498149

push

github

vigna
New covariance-checking infrastructure

7 of 61 new or added lines in 11 files covered. (11.48%)

1 existing line in 1 file now uncovered.

790 of 1511 relevant lines covered (52.28%)

482.26 hits per line

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

63.53
/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::{BuildHasherDefault, 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::hash::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) {
4✔
29
        "std::hash::DefaultHasher".hash(hasher);
12✔
30
    }
31
}
32

33
unsafe impl<H> CopyType for BuildHasherDefault<H> {
34
    type Copy = Zero;
35
}
36

37
impl<H> AlignTo for BuildHasherDefault<H> {
38
    fn align_to() -> usize {
×
39
        0
×
40
    }
41
}
42

43
impl<H: TypeHash> TypeHash for BuildHasherDefault<H> {
44
    fn type_hash(hasher: &mut impl core::hash::Hasher) {
3✔
45
        "core::hash::BuildHasherDefault".hash(hasher);
9✔
46
        H::type_hash(hasher);
6✔
47
    }
48
}
49

50
impl<H> AlignHash for BuildHasherDefault<H> {
51
    fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {}
6✔
52
}
53

54
impl<H> SerInner for BuildHasherDefault<H> {
55
    type SerType = BuildHasherDefault<H>;
56
    const IS_ZERO_COPY: bool = true;
57

58
    unsafe fn _ser_inner(&self, _backend: &mut impl WriteWithNames) -> ser::Result<()> {
1✔
59
        Ok(())
1✔
60
    }
61
}
62

63
impl<H> DeserInner for BuildHasherDefault<H> {
NEW
64
    fn __check_covariance<'__long: '__short, '__short>(
×
65
        p: deser::CovariantProof<Self::DeserType<'__long>>,
66
    ) -> deser::CovariantProof<Self::DeserType<'__short>> {
NEW
67
        p
×
68
    }
69
    unsafe fn _deser_full_inner(_backend: &mut impl ReadWithPos) -> deser::Result<Self> {
1✔
70
        Ok(BuildHasherDefault::default())
1✔
71
    }
72

73
    type DeserType<'a> = BuildHasherDefault<H>;
74

75
    unsafe fn _deser_eps_inner<'a>(
1✔
76
        _backend: &mut SliceWithPos<'a>,
77
    ) -> deser::Result<Self::DeserType<'a>> {
78
        Ok(BuildHasherDefault::default())
1✔
79
    }
80
}
81

82
macro_rules! impl_ranges {
83
    ($ty:ident) => {
84
        unsafe impl<Idx> CopyType for $ty<Idx> {
85
            type Copy = Deep;
86
        }
87

88
        impl<Idx: TypeHash> TypeHash for $ty<Idx> {
89
            fn type_hash(hasher: &mut impl core::hash::Hasher) {
20✔
90
                stringify!(core::ops::$ty).hash(hasher);
60✔
91
                Idx::type_hash(hasher);
40✔
92
            }
93
        }
94
    };
95
}
96

97
impl_ranges!(Range);
98
impl_ranges!(RangeFrom);
99
impl_ranges!(RangeInclusive);
100
impl_ranges!(RangeTo);
101
impl_ranges!(RangeToInclusive);
102

103
impl<Idx: AlignHash> AlignHash for Range<Idx> {
104
    fn align_hash(hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {
13✔
105
        Idx::align_hash(hasher, &mut 0);
39✔
106
        Idx::align_hash(hasher, &mut 0);
39✔
107
    }
108
}
109

110
impl<Idx: AlignHash> AlignHash for RangeFrom<Idx> {
111
    fn align_hash(hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {
1✔
112
        Idx::align_hash(hasher, &mut 0);
3✔
113
    }
114
}
115

116
impl<Idx: AlignHash> AlignHash for RangeInclusive<Idx> {
117
    fn align_hash(hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {
4✔
118
        Idx::align_hash(hasher, &mut 0);
12✔
119
        Idx::align_hash(hasher, &mut 0);
12✔
120
        bool::align_hash(hasher, &mut 0);
12✔
121
    }
122
}
123

124
impl<Idx: AlignHash> AlignHash for RangeTo<Idx> {
125
    fn align_hash(hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {
1✔
126
        Idx::align_hash(hasher, &mut 0);
3✔
127
    }
128
}
129

130
impl<Idx: AlignHash> AlignHash for RangeToInclusive<Idx> {
131
    fn align_hash(hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {
1✔
132
        Idx::align_hash(hasher, &mut 0);
3✔
133
    }
134
}
135

136
// RangeFull is a zero-sized type, so it is always zero-copy.
137

138
unsafe impl CopyType for RangeFull {
139
    type Copy = Zero;
140
}
141

142
impl TypeHash for RangeFull {
143
    fn type_hash(hasher: &mut impl core::hash::Hasher) {
1✔
144
        stringify!(core::ops::RangeFull).hash(hasher);
3✔
145
    }
146
}
147

148
impl AlignHash for RangeFull {
149
    fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {}
2✔
150
}
151

152
impl AlignTo for RangeFull {
153
    fn align_to() -> usize {
×
154
        0
×
155
    }
156
}
157

158
impl<Idx: SerInner> SerInner for Range<Idx> {
159
    type SerType = Range<Idx::SerType>;
160
    const IS_ZERO_COPY: bool = false;
161

162
    #[inline(always)]
163
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
4✔
164
        backend.write("start", &self.start)?;
16✔
165
        backend.write("end", &self.end)?;
16✔
166
        Ok(())
4✔
167
    }
168
}
169

170
impl<Idx: DeserInner> DeserInner for Range<Idx> {
NEW
171
    fn __check_covariance<'__long: '__short, '__short>(
×
172
        p: deser::CovariantProof<Self::DeserType<'__long>>,
173
    ) -> deser::CovariantProof<Self::DeserType<'__short>> {
174
        // SAFETY: Range is covariant in Idx, and Idx::DeserType is covariant
175
        // (enforced by Idx's own __check_covariance).
NEW
176
        unsafe { core::mem::transmute(p) }
×
177
    }
178
    #[inline(always)]
179
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
4✔
180
        let start = unsafe { Idx::_deser_full_inner(backend) }?;
12✔
181
        let end = unsafe { Idx::_deser_full_inner(backend) }?;
12✔
182
        Ok(Range { start, end })
4✔
183
    }
184
    type DeserType<'a> = Range<DeserType<'a, Idx>>;
185
    #[inline(always)]
186
    unsafe fn _deser_eps_inner<'a>(
2✔
187
        backend: &mut SliceWithPos<'a>,
188
    ) -> deser::Result<Self::DeserType<'a>> {
189
        let start = unsafe { Idx::_deser_eps_inner(backend) }?;
6✔
190
        let end = unsafe { Idx::_deser_eps_inner(backend) }?;
6✔
191
        Ok(Range { start, end })
2✔
192
    }
193
}
194

195
impl<Idx: SerInner> SerInner for RangeFrom<Idx> {
196
    type SerType = RangeFrom<Idx::SerType>;
197
    const IS_ZERO_COPY: bool = false;
198

199
    #[inline(always)]
200
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
×
201
        backend.write("start", &self.start)?;
×
202
        Ok(())
×
203
    }
204
}
205

206
impl<Idx: DeserInner> DeserInner for RangeFrom<Idx> {
NEW
207
    fn __check_covariance<'__long: '__short, '__short>(
×
208
        p: deser::CovariantProof<Self::DeserType<'__long>>,
209
    ) -> deser::CovariantProof<Self::DeserType<'__short>> {
210
        // SAFETY: RangeFrom is covariant in Idx, and Idx::DeserType is covariant
211
        // (enforced by Idx's own __check_covariance).
NEW
212
        unsafe { core::mem::transmute(p) }
×
213
    }
214
    #[inline(always)]
215
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
×
216
        let start = unsafe { Idx::_deser_full_inner(backend) }?;
×
217
        Ok(RangeFrom { start })
×
218
    }
219
    type DeserType<'a> = RangeFrom<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 start = unsafe { Idx::_deser_eps_inner(backend) }?;
×
225
        Ok(RangeFrom { start })
×
226
    }
227
}
228

229
impl<Idx: SerInner> SerInner for RangeInclusive<Idx> {
230
    type SerType = RangeInclusive<Idx::SerType>;
231
    const IS_ZERO_COPY: bool = false;
232

233
    #[inline(always)]
234
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
1✔
235
        backend.write("start", self.start())?;
4✔
236
        backend.write("end", self.end())?;
4✔
237
        backend.write("exhausted", &matches!(self.end_bound(), Bound::Excluded(_)))?;
5✔
238
        Ok(())
1✔
239
    }
240
}
241

242
impl<Idx: DeserInner> DeserInner for RangeInclusive<Idx> {
NEW
243
    fn __check_covariance<'__long: '__short, '__short>(
×
244
        p: deser::CovariantProof<Self::DeserType<'__long>>,
245
    ) -> deser::CovariantProof<Self::DeserType<'__short>> {
246
        // SAFETY: RangeInclusive is covariant in Idx, and Idx::DeserType is
247
        // covariant (enforced by Idx's own __check_covariance).
NEW
248
        unsafe { core::mem::transmute(p) }
×
249
    }
250
    #[inline(always)]
251
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
1✔
252
        let start = unsafe { Idx::_deser_full_inner(backend) }?;
3✔
253
        let end = unsafe { Idx::_deser_full_inner(backend) }?;
3✔
254
        let exhausted = unsafe { bool::_deser_full_inner(backend) }?;
3✔
255
        assert!(!exhausted, "cannot deserialize an exhausted range");
2✔
256
        Ok(start..=end)
1✔
257
    }
258
    type DeserType<'a> = RangeInclusive<DeserType<'a, Idx>>;
259
    #[inline(always)]
260
    unsafe fn _deser_eps_inner<'a>(
1✔
261
        backend: &mut SliceWithPos<'a>,
262
    ) -> deser::Result<Self::DeserType<'a>> {
263
        let start = unsafe { Idx::_deser_eps_inner(backend) }?;
3✔
264
        let end = unsafe { Idx::_deser_eps_inner(backend) }?;
3✔
265
        let exhausted = unsafe { bool::_deser_full_inner(backend) }?;
3✔
266
        assert!(!exhausted, "cannot deserialize an exhausted range");
2✔
267
        Ok(start..=end)
1✔
268
    }
269
}
270

271
impl<Idx: SerInner> SerInner for RangeTo<Idx> {
272
    type SerType = RangeTo<Idx::SerType>;
273
    const IS_ZERO_COPY: bool = false;
274

275
    #[inline(always)]
276
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
×
277
        backend.write("end", &self.end)?;
×
278
        Ok(())
×
279
    }
280
}
281

282
impl<Idx: DeserInner> DeserInner for RangeTo<Idx> {
NEW
283
    fn __check_covariance<'__long: '__short, '__short>(
×
284
        p: deser::CovariantProof<Self::DeserType<'__long>>,
285
    ) -> deser::CovariantProof<Self::DeserType<'__short>> {
286
        // SAFETY: RangeTo is covariant in Idx, and Idx::DeserType is covariant
287
        // (enforced by Idx's own __check_covariance).
NEW
288
        unsafe { core::mem::transmute(p) }
×
289
    }
290
    #[inline(always)]
291
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
×
292
        let end = unsafe { Idx::_deser_full_inner(backend) }?;
×
293
        Ok(..end)
×
294
    }
295
    type DeserType<'a> = RangeTo<DeserType<'a, Idx>>;
296
    #[inline(always)]
297
    unsafe fn _deser_eps_inner<'a>(
×
298
        backend: &mut SliceWithPos<'a>,
299
    ) -> deser::Result<Self::DeserType<'a>> {
300
        let end = unsafe { Idx::_deser_eps_inner(backend) }?;
×
301
        Ok(..end)
×
302
    }
303
}
304

305
impl<Idx: SerInner> SerInner for RangeToInclusive<Idx> {
306
    type SerType = RangeToInclusive<Idx::SerType>;
307
    const IS_ZERO_COPY: bool = false;
308

309
    #[inline(always)]
310
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
×
311
        backend.write("end", &self.end)?;
×
312
        Ok(())
×
313
    }
314
}
315

316
impl<Idx: DeserInner> DeserInner for RangeToInclusive<Idx> {
NEW
317
    fn __check_covariance<'__long: '__short, '__short>(
×
318
        p: deser::CovariantProof<Self::DeserType<'__long>>,
319
    ) -> deser::CovariantProof<Self::DeserType<'__short>> {
320
        // SAFETY: RangeToInclusive is covariant in Idx, and Idx::DeserType is
321
        // covariant (enforced by Idx's own __check_covariance).
NEW
322
        unsafe { core::mem::transmute(p) }
×
323
    }
324
    #[inline(always)]
325
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
×
326
        let end = unsafe { Idx::_deser_full_inner(backend) }?;
×
327
        Ok(..=end)
×
328
    }
329
    type DeserType<'a> = RangeToInclusive<DeserType<'a, Idx>>;
330
    #[inline(always)]
331
    unsafe fn _deser_eps_inner<'a>(
×
332
        backend: &mut SliceWithPos<'a>,
333
    ) -> deser::Result<Self::DeserType<'a>> {
334
        let end = unsafe { Idx::_deser_eps_inner(backend) }?;
×
335
        Ok(..=end)
×
336
    }
337
}
338

339
impl SerInner for RangeFull {
340
    type SerType = RangeFull;
341
    const IS_ZERO_COPY: bool = true;
342

343
    #[inline(always)]
344
    unsafe fn _ser_inner(&self, _backend: &mut impl WriteWithNames) -> ser::Result<()> {
×
345
        Ok(())
×
346
    }
347
}
348

349
impl DeserInner for RangeFull {
NEW
350
    fn __check_covariance<'__long: '__short, '__short>(
×
351
        p: deser::CovariantProof<Self::DeserType<'__long>>,
352
    ) -> deser::CovariantProof<Self::DeserType<'__short>> {
NEW
353
        p
×
354
    }
355
    #[inline(always)]
356
    unsafe fn _deser_full_inner(_backend: &mut impl ReadWithPos) -> deser::Result<Self> {
×
357
        Ok(RangeFull)
×
358
    }
359
    type DeserType<'a> = RangeFull;
360
    #[inline(always)]
361
    unsafe fn _deser_eps_inner<'a>(
×
362
        _backend: &mut SliceWithPos<'a>,
363
    ) -> deser::Result<Self::DeserType<'a>> {
364
        Ok(RangeFull)
×
365
    }
366
}
367

368
unsafe impl<T> CopyType for Bound<T> {
369
    type Copy = Deep;
370
}
371

372
impl<T: TypeHash> TypeHash for Bound<T> {
373
    fn type_hash(hasher: &mut impl core::hash::Hasher) {
19✔
374
        stringify!(core::ops::Bound).hash(hasher);
57✔
375
        T::type_hash(hasher);
38✔
376
    }
377
}
378

379
impl<T> AlignHash for Bound<T> {
380
    fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {}
38✔
381
}
382

383
impl<T: SerInner> SerInner for Bound<T> {
384
    type SerType = Bound<T::SerType>;
385
    const IS_ZERO_COPY: bool = false;
386

387
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
6✔
388
        match self {
6✔
389
            Bound::Unbounded => backend.write("Tag", &0_u8),
8✔
390
            Bound::Included(val) => {
2✔
391
                backend.write("Tag", &1_u8)?;
8✔
392
                backend.write("Included", val)
8✔
393
            }
394
            Bound::Excluded(val) => {
2✔
395
                backend.write("Tag", &2_u8)?;
8✔
396
                backend.write("Excluded", val)
8✔
397
            }
398
        }
399
    }
400
}
401

402
impl<T: DeserInner> DeserInner for Bound<T> {
NEW
403
    fn __check_covariance<'__long: '__short, '__short>(
×
404
        p: deser::CovariantProof<Self::DeserType<'__long>>,
405
    ) -> deser::CovariantProof<Self::DeserType<'__short>> {
406
        // SAFETY: Bound is covariant in T, and T::DeserType is covariant
407
        // (enforced by T's own __check_covariance).
NEW
408
        unsafe { core::mem::transmute(p) }
×
409
    }
410
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
6✔
411
        let tag = unsafe { u8::_deser_full_inner(backend) }?;
18✔
412
        match tag {
6✔
413
            0 => Ok(Bound::Unbounded),
2✔
414
            1 => Ok(Bound::Included(unsafe { T::_deser_full_inner(backend) }?)),
4✔
415
            2 => Ok(Bound::Excluded(unsafe { T::_deser_full_inner(backend) }?)),
4✔
416
            _ => Err(deser::Error::InvalidTag(tag as usize)),
×
417
        }
418
    }
419

420
    type DeserType<'a> = Bound<DeserType<'a, T>>;
421

422
    unsafe fn _deser_eps_inner<'a>(
6✔
423
        backend: &mut SliceWithPos<'a>,
424
    ) -> deser::Result<Self::DeserType<'a>> {
425
        let tag = unsafe { u8::_deser_full_inner(backend) }?;
18✔
426
        match tag {
6✔
427
            0 => Ok(Bound::Unbounded),
2✔
428
            1 => Ok(Bound::Included(unsafe { T::_deser_eps_inner(backend) }?)),
4✔
429
            2 => Ok(Bound::Excluded(unsafe { T::_deser_eps_inner(backend) }?)),
4✔
430
            _ => Err(deser::Error::InvalidTag(tag as usize)),
×
431
        }
432
    }
433
}
434

435
unsafe impl<B, C> CopyType for ControlFlow<B, C> {
436
    type Copy = Deep;
437
}
438

439
impl<B: TypeHash, C: TypeHash> TypeHash for ControlFlow<B, C> {
440
    fn type_hash(hasher: &mut impl core::hash::Hasher) {
13✔
441
        stringify!(core::ops::ControlFlow).hash(hasher);
39✔
442
        B::type_hash(hasher);
26✔
443
        C::type_hash(hasher);
26✔
444
    }
445
}
446

447
impl<B, C> AlignHash for ControlFlow<B, C> {
448
    fn align_hash(_hasher: &mut impl core::hash::Hasher, _offset_of: &mut usize) {}
26✔
449
}
450

451
impl<B: SerInner, C: SerInner> SerInner for ControlFlow<B, C> {
452
    type SerType = ControlFlow<B::SerType, C::SerType>;
453
    const IS_ZERO_COPY: bool = false;
454

455
    unsafe fn _ser_inner(&self, backend: &mut impl WriteWithNames) -> ser::Result<()> {
4✔
456
        match self {
4✔
457
            ControlFlow::Break(br) => {
2✔
458
                backend.write("Tag", &0_u8)?;
8✔
459
                backend.write("Break", br)
8✔
460
            }
461
            ControlFlow::Continue(val) => {
2✔
462
                backend.write("Tag", &1_u8)?;
8✔
463
                backend.write("Continue", val)
8✔
464
            }
465
        }
466
    }
467
}
468

469
impl<B: DeserInner, C: DeserInner> DeserInner for ControlFlow<B, C> {
NEW
470
    fn __check_covariance<'__long: '__short, '__short>(
×
471
        p: deser::CovariantProof<Self::DeserType<'__long>>,
472
    ) -> deser::CovariantProof<Self::DeserType<'__short>> {
473
        // SAFETY: ControlFlow is covariant in B and C, and both B::DeserType
474
        // and C::DeserType are covariant (enforced by their own __check_covariance).
NEW
475
        unsafe { core::mem::transmute(p) }
×
476
    }
477
    unsafe fn _deser_full_inner(backend: &mut impl ReadWithPos) -> deser::Result<Self> {
4✔
478
        let tag = unsafe { u8::_deser_full_inner(backend) }?;
12✔
479
        match tag {
4✔
480
            0 => Ok(ControlFlow::Break(unsafe {
×
481
                B::_deser_full_inner(backend)
4✔
482
            }?)),
483
            1 => Ok(ControlFlow::Continue(unsafe {
×
484
                C::_deser_full_inner(backend)
4✔
485
            }?)),
486
            _ => Err(deser::Error::InvalidTag(tag as usize)),
×
487
        }
488
    }
489

490
    type DeserType<'a> = ControlFlow<DeserType<'a, B>, DeserType<'a, C>>;
491

492
    unsafe fn _deser_eps_inner<'a>(
4✔
493
        backend: &mut SliceWithPos<'a>,
494
    ) -> deser::Result<Self::DeserType<'a>> {
495
        let tag = unsafe { u8::_deser_full_inner(backend) }?;
12✔
496
        match tag {
4✔
497
            0 => Ok(ControlFlow::Break(unsafe { B::_deser_eps_inner(backend) }?)),
4✔
498
            1 => Ok(ControlFlow::Continue(unsafe {
×
499
                C::_deser_eps_inner(backend)
4✔
500
            }?)),
501
            _ => Err(deser::Error::InvalidTag(tag as usize)),
×
502
        }
503
    }
504
}
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