rkyv/with.rs
1//! Wrapper type support and commonly used wrappers.
2//!
3//! Wrappers can be applied with the `#[rkyv(with = ..)]` attribute in the
4//! [`Archive`](macro@crate::Archive) macro.
5
6// mod impls;
7
8use core::{fmt, marker::PhantomData};
9
10use rancor::Fallible;
11
12#[doc(inline)]
13pub use crate::niche::niching::DefaultNiche;
14use crate::{Archive, Deserialize, Place, Portable, Serialize};
15
16/// A variant of [`Archive`] that works with wrappers.
17///
18/// Creating a wrapper allows users to customize how fields are archived easily
19/// without changing the unarchived type.
20///
21/// This trait allows wrapper types to transparently change the archive
22/// behaviors for struct and enum fields. When a field is serialized, it may use
23/// the implementations for the wrapper type and the given field instead of the
24/// implementation for the type itself.
25///
26/// Only a single implementation of [`Archive`] may be written
27/// for each type, but multiple implementations of ArchiveWith can be written
28/// for the same type because it is parametric over the wrapper type. This is
29/// used with the `#[rkyv(with = ..)]` macro attribute to provide a more
30/// flexible interface for serialization.
31///
32/// # Example
33///
34/// ```
35/// use rkyv::{
36/// access_unchecked, deserialize,
37/// rancor::{Error, Fallible, Infallible, ResultExt as _},
38/// to_bytes,
39/// with::{ArchiveWith, DeserializeWith, SerializeWith},
40/// Archive, Archived, Deserialize, Place, Resolver, Serialize,
41/// };
42///
43/// struct Incremented;
44///
45/// // For the archived representation, [u8; 4] is used to illustrate
46/// // how archived types can differ from non-archived types.
47/// // An `i32` could be used here as well (which would be more natural).
48/// type IncBytes = [u8; 4];
49///
50/// impl ArchiveWith<i32> for Incremented {
51/// type Archived = Archived<IncBytes>;
52/// type Resolver = Resolver<IncBytes>;
53///
54/// fn resolve_with(
55/// field: &i32,
56/// resolver: Self::Resolver,
57/// out: Place<Self::Archived>,
58/// ) {
59/// let incremented = (field + 1).to_le_bytes();
60/// incremented.resolve(resolver, out);
61/// }
62/// }
63///
64/// impl<S> SerializeWith<i32, S> for Incremented
65/// where
66/// S: Fallible + ?Sized,
67/// IncBytes: Serialize<S>,
68/// {
69/// fn serialize_with(
70/// field: &i32,
71/// serializer: &mut S,
72/// ) -> Result<Self::Resolver, S::Error> {
73/// let incremented = (field + 1).to_le_bytes();
74/// incremented.serialize(serializer)
75/// }
76/// }
77///
78/// impl<D> DeserializeWith<Archived<IncBytes>, i32, D> for Incremented
79/// where
80/// D: Fallible + ?Sized,
81/// Archived<i32>: Deserialize<i32, D>,
82/// {
83/// fn deserialize_with(
84/// field: &Archived<IncBytes>,
85/// deserializer: &mut D,
86/// ) -> Result<i32, D::Error> {
87/// let incremented: IncBytes = field.deserialize(deserializer)?;
88/// Ok(i32::from_le_bytes(incremented) - 1)
89/// }
90/// }
91///
92/// #[derive(Archive, Deserialize, Serialize)]
93/// struct Example {
94/// #[rkyv(with = Incremented)]
95/// a: i32,
96/// // Another i32 field, but not incremented this time
97/// b: i32,
98/// }
99///
100/// let value = Example { a: 4, b: 9 };
101///
102/// let buf = to_bytes::<Error>(&value).unwrap();
103///
104/// let archived =
105/// unsafe { access_unchecked::<Archived<Example>>(buf.as_ref()) };
106/// // The wrapped field has been incremented
107/// assert_eq!(i32::from_le_bytes(archived.a), 5);
108/// // ... and the unwrapped field has not
109/// assert_eq!(archived.b, 9);
110///
111/// let deserialized = deserialize::<Example, Infallible>(archived).always_ok();
112/// // The wrapped field is back to normal
113/// assert_eq!(deserialized.a, 4);
114/// // ... and the unwrapped field is unchanged
115/// assert_eq!(deserialized.b, 9);
116/// ```
117pub trait ArchiveWith<F: ?Sized> {
118 /// The archived type of `Self` with `F`.
119 type Archived: Portable;
120 /// The resolver of a `Self` with `F`.
121 type Resolver;
122
123 /// Resolves the archived type using a reference to the field type `F`.
124 fn resolve_with(
125 field: &F,
126 resolver: Self::Resolver,
127 out: Place<Self::Archived>,
128 );
129}
130
131/// A variant of `Serialize` for "with" types.
132///
133/// See [ArchiveWith] for more details.
134pub trait SerializeWith<F: ?Sized, S: Fallible + ?Sized>:
135 ArchiveWith<F>
136{
137 /// Serializes the field type `F` using the given serializer.
138 fn serialize_with(
139 field: &F,
140 serializer: &mut S,
141 ) -> Result<Self::Resolver, S::Error>;
142}
143
144/// A variant of `Deserialize` for "with" types.
145///
146/// See [ArchiveWith] for more details.
147pub trait DeserializeWith<F: ?Sized, T, D: Fallible + ?Sized> {
148 /// Deserializes the field type `F` using the given deserializer.
149 fn deserialize_with(field: &F, deserializer: &mut D)
150 -> Result<T, D::Error>;
151}
152
153/// A transparent wrapper which applies a "with" type.
154///
155/// `With` wraps a reference to a type and applies the specified wrapper type
156/// when serializing and deserializing.
157#[repr(transparent)]
158pub struct With<F: ?Sized, W> {
159 _phantom: PhantomData<W>,
160 field: F,
161}
162
163impl<F: ?Sized, W> With<F, W> {
164 /// Casts a `With` reference from a reference to the underlying field.
165 pub fn cast(field: &F) -> &Self {
166 // SAFETY: `With` is `repr(transparent)` and so a reference to `F` can
167 // always be transmuted into a reference to `With<F, W>`.
168 unsafe { ::core::mem::transmute::<&F, &Self>(field) }
169 }
170}
171
172impl<F: ?Sized, W: ArchiveWith<F>> Archive for With<F, W> {
173 type Archived = <W as ArchiveWith<F>>::Archived;
174 type Resolver = <W as ArchiveWith<F>>::Resolver;
175
176 fn resolve(&self, resolver: Self::Resolver, out: Place<Self::Archived>) {
177 W::resolve_with(&self.field, resolver, out);
178 }
179}
180
181impl<S, F, W> Serialize<S> for With<F, W>
182where
183 S: Fallible + ?Sized,
184 F: ?Sized,
185 W: SerializeWith<F, S>,
186{
187 fn serialize(
188 &self,
189 serializer: &mut S,
190 ) -> Result<Self::Resolver, <S as Fallible>::Error> {
191 W::serialize_with(&self.field, serializer)
192 }
193}
194
195impl<T, D, F, W> Deserialize<T, D> for With<F, W>
196where
197 D: Fallible + ?Sized,
198 F: ?Sized,
199 W: DeserializeWith<F, T, D>,
200{
201 fn deserialize(
202 &self,
203 deserializer: &mut D,
204 ) -> Result<T, <D as Fallible>::Error> {
205 W::deserialize_with(&self.field, deserializer)
206 }
207}
208
209/// A wrapper that applies another wrapper to the values contained in a type.
210/// This can be applied to a vector to map each element, or an option to map any
211/// contained value.
212///
213/// See [ArchiveWith] for more details.
214///
215/// # Example
216///
217/// ```
218/// use rkyv::{
219/// with::{InlineAsBox, Map},
220/// Archive,
221/// };
222///
223/// #[derive(Archive)]
224/// struct Example<'a> {
225/// // This will apply `InlineAsBox` to the `&i32` contained in this option
226/// #[rkyv(with = Map<InlineAsBox>)]
227/// option: Option<&'a i32>,
228/// // This will apply `InlineAsBox` to each `&i32` contained in this vector
229/// #[rkyv(with = Map<InlineAsBox>)]
230/// vec: Vec<&'a i32>,
231/// }
232/// ```
233pub struct Map<T> {
234 _phantom: PhantomData<T>,
235}
236
237/// A wrapper that applies key and value wrappers to the key-value pairs
238/// contained in a type. This can be applied to a hash map or B-tree map to map
239/// the key-value pairs.
240///
241/// # Example
242/// ```
243/// use std::collections::HashMap;
244///
245/// use rkyv::{
246/// with::{Inline, InlineAsBox, MapKV},
247/// Archive,
248/// };
249///
250/// #[derive(Archive)]
251/// struct Example<'a> {
252/// // This will apply `InlineAsBox` to the `&str` key, and `Inline` to the
253/// // `&str` value.
254/// #[rkyv(with = MapKV<InlineAsBox, Inline>)]
255/// hash_map: HashMap<&'a str, &'a str>,
256/// }
257/// ```
258pub struct MapKV<K, V> {
259 _phantom: PhantomData<(K, V)>,
260}
261
262/// A type indicating relaxed atomic loads.
263pub struct Relaxed;
264
265/// A type indicating acquire atomic loads.
266pub struct Acquire;
267
268/// A type indicating sequentially-consistent atomic loads.
269pub struct SeqCst;
270
271/// A wrapper that archives an atomic by loading its value with a particular
272/// ordering.
273///
274/// When serializing, the specified ordering will be used to load the value from
275/// the source atomic. The underlying archived type is still a non-atomic value.
276///
277/// # Example
278///
279/// ```
280/// # #[cfg(target_has_atomic = "32")]
281/// use core::sync::atomic::AtomicU32;
282///
283/// use rkyv::{
284/// with::{AtomicLoad, Relaxed},
285/// Archive,
286/// };
287///
288/// # #[cfg(target_has_atomic = "32")]
289/// #[derive(Archive)]
290/// struct Example {
291/// #[rkyv(with = AtomicLoad<Relaxed>)]
292/// a: AtomicU32,
293/// }
294/// ```
295#[derive(Debug)]
296pub struct AtomicLoad<SO> {
297 _phantom: PhantomData<SO>,
298}
299
300/// A wrapper that serializes a reference inline.
301///
302/// References serialized with `Inline` cannot be deserialized because the
303/// struct cannot own the deserialized value.
304///
305/// # Example
306///
307/// ```
308/// use rkyv::{with::Inline, Archive};
309///
310/// #[derive(Archive)]
311/// struct Example<'a> {
312/// #[rkyv(with = Inline)]
313/// a: &'a i32,
314/// }
315/// ```
316#[derive(Debug)]
317pub struct Inline;
318
319/// A wrapper that serializes a field into a box.
320///
321/// This functions similarly to [`InlineAsBox`], but is for regular fields
322/// instead of references.
323///
324/// # Example
325///
326/// ```
327/// use rkyv::{with::AsBox, Archive};
328///
329/// #[derive(Archive)]
330/// struct Example {
331/// #[rkyv(with = AsBox)]
332/// a: i32,
333/// #[rkyv(with = AsBox)]
334/// b: str,
335/// }
336/// ```
337#[derive(Debug)]
338pub struct AsBox;
339
340/// A wrapper that serializes a reference as if it were boxed.
341///
342/// Unlike [`Inline`], unsized references can be serialized with `InlineAsBox`.
343///
344/// References serialized with `InlineAsBox` cannot be deserialized because the
345/// struct cannot own the deserialized value.
346///
347/// # Example
348///
349/// ```
350/// use rkyv::{with::InlineAsBox, Archive};
351///
352/// #[derive(Archive)]
353/// struct Example<'a> {
354/// #[rkyv(with = InlineAsBox)]
355/// a: &'a i32,
356/// #[rkyv(with = InlineAsBox)]
357/// b: &'a str,
358/// }
359/// ```
360#[derive(Debug)]
361pub struct InlineAsBox;
362
363/// A wrapper that attempts to convert a type to and from UTF-8.
364///
365/// Types like `OsString` and `PathBuf` aren't guaranteed to be encoded as
366/// UTF-8, but they usually are anyway. Using this wrapper will archive them as
367/// if they were regular `String`s.
368///
369/// It also allows `&str` to be archived like an owned `String`. However, note
370/// that `&str` cannot be *deserialized* this way.
371///
372/// # Example
373///
374/// ```
375/// use std::{ffi::OsString, path::PathBuf};
376///
377/// use rkyv::{with::AsString, Archive};
378///
379/// #[derive(Archive)]
380/// struct Example<'a> {
381/// #[rkyv(with = AsString)]
382/// os_string: OsString,
383/// #[rkyv(with = AsString)]
384/// path: PathBuf,
385/// #[rkyv(with = AsString)]
386/// reference: &'a str,
387/// }
388/// ```
389#[derive(Debug)]
390pub struct AsString;
391
392/// A wrapper that locks a lock and serializes the value immutably.
393///
394/// This wrapper can panic under very specific circumstances when:
395///
396/// 1. `serialize_with` is called and succeeds in locking the value to serialize
397/// it.
398/// 2. Another thread locks the value and panics, poisoning the lock
399/// 3. `resolve_with` is called and gets a poisoned value.
400///
401/// Unfortunately, it's not possible to work around this issue internally. Users
402/// must ensure this doesn't happen on their own through manual synchronization
403/// or guaranteeing that panics do not occur while holding locks.
404///
405/// # Example
406///
407/// ```
408/// use std::sync::Mutex;
409///
410/// use rkyv::{with::Lock, Archive};
411///
412/// #[derive(Archive)]
413/// struct Example {
414/// #[rkyv(with = Lock)]
415/// a: Mutex<i32>,
416/// }
417/// ```
418#[derive(Debug)]
419pub struct Lock;
420
421/// A wrapper that serializes a `Cow` as if it were owned.
422///
423/// # Example
424///
425/// ```
426/// use std::borrow::Cow;
427///
428/// use rkyv::{with::AsOwned, Archive};
429///
430/// #[derive(Archive)]
431/// struct Example<'a> {
432/// #[rkyv(with = AsOwned)]
433/// a: Cow<'a, str>,
434/// }
435/// ```
436#[derive(Debug)]
437pub struct AsOwned;
438
439/// A wrapper that serializes associative containers as a `Vec` of key-value
440/// pairs.
441///
442/// This provides faster serialization for containers like `HashMap` and
443/// `BTreeMap` by serializing the key-value pairs directly instead of building a
444/// data structure in the buffer.
445///
446/// It also allows `&[T]` to be archived like an owned `Vec`. However, note
447/// that `&[T]` cannot be *deserialized* this way.
448///
449/// # Example
450///
451/// ```
452/// use std::collections::HashMap;
453///
454/// use rkyv::{with::AsVec, Archive};
455///
456/// #[derive(Archive)]
457/// struct Example<'a> {
458/// #[rkyv(with = AsVec)]
459/// values: HashMap<String, u32>,
460/// #[rkyv(with = AsVec)]
461/// slice: &'a [u32],
462/// }
463/// ```
464#[derive(Debug)]
465pub struct AsVec;
466
467/// A wrapper that niches some type combinations.
468///
469/// A common type combination is `Option<Box<T>>`. By using a null pointer, the
470/// archived version can save some space on-disk.
471///
472/// # Example
473///
474/// ```
475/// use core::mem::size_of;
476///
477/// use rkyv::{with::Niche, Archive, Archived};
478///
479/// #[derive(Archive)]
480/// struct BasicExample {
481/// value: Option<Box<str>>,
482/// }
483///
484/// #[derive(Archive)]
485/// struct NichedExample {
486/// #[rkyv(with = Niche)]
487/// value: Option<Box<str>>,
488/// }
489///
490/// assert!(
491/// size_of::<Archived<BasicExample>>()
492/// > size_of::<Archived<NichedExample>>()
493/// );
494/// ```
495#[derive(Debug)]
496pub struct Niche;
497
498/// A wrapper that niches based on a generic [`Niching`].
499///
500/// A common type combination is `Option<Box<T>>`. By niching `None` into the
501/// null pointer, the archived version can save some space on-disk.
502///
503/// # Example
504///
505/// ```
506/// use core::mem::size_of;
507///
508/// use rkyv::{
509/// niche::niching::{NaN, Null},
510/// with::NicheInto,
511/// Archive, Archived,
512/// };
513///
514/// #[derive(Archive)]
515/// struct BasicExample {
516/// maybe_box: Option<Box<str>>,
517/// maybe_non_nan: Option<f32>,
518/// }
519///
520/// #[derive(Archive)]
521/// struct NichedExample {
522/// #[rkyv(with = NicheInto<Null>)]
523/// maybe_box: Option<Box<str>>,
524/// #[rkyv(with = NicheInto<NaN>)]
525/// maybe_non_nan: Option<f32>,
526/// }
527///
528/// assert!(
529/// size_of::<Archived<BasicExample>>()
530/// > size_of::<Archived<NichedExample>>()
531/// );
532/// ```
533///
534/// [`Niching`]: crate::niche::niching::Niching
535pub struct NicheInto<N: ?Sized>(PhantomData<N>);
536
537impl<N: ?Sized> Default for NicheInto<N> {
538 fn default() -> Self {
539 Self(PhantomData)
540 }
541}
542
543impl<N: ?Sized> fmt::Debug for NicheInto<N> {
544 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
545 f.write_str("NicheInto")
546 }
547}
548
549/// A wrapper that first applies another wrapper `W` to the value inside an
550/// `Option` and then niches the result based on the [`Niching`] `N`.
551///
552/// # Example
553///
554/// ```
555/// use rkyv::{
556/// with::{AsBox, MapNiche},
557/// Archive, Serialize,
558/// };
559///
560/// #[derive(Archive, Serialize)]
561/// struct BasicExample {
562/// option: Option<HugeType>,
563/// }
564///
565/// #[derive(Archive, Serialize)]
566/// struct NichedExample {
567/// #[rkyv(with = MapNiche<AsBox>)]
568/// option: Option<HugeType>,
569/// }
570///
571/// #[derive(Archive, Serialize)]
572/// struct HugeType([u8; 1024]);
573///
574/// # fn main() -> Result<(), rkyv::rancor::Error> {
575/// let basic_value = BasicExample { option: None };
576/// let basic_bytes = rkyv::to_bytes(&basic_value)?;
577/// assert_eq!(basic_bytes.len(), 1 + 1024);
578///
579/// let niched_value = NichedExample { option: None };
580/// let niched_bytes = rkyv::to_bytes(&niched_value)?;
581/// assert_eq!(niched_bytes.len(), 4); // size_of::<ArchivedBox<_>>()
582/// # Ok(()) }
583/// ```
584///
585/// [`Niching`]: crate::niche::niching::Niching
586pub struct MapNiche<W: ?Sized, N: ?Sized = DefaultNiche> {
587 _map: PhantomData<W>,
588 _niching: PhantomData<N>,
589}
590
591impl<W: ?Sized, N: ?Sized> Default for MapNiche<W, N> {
592 fn default() -> Self {
593 Self {
594 _map: PhantomData,
595 _niching: PhantomData,
596 }
597 }
598}
599
600impl<W: ?Sized, N: ?Sized> fmt::Debug for MapNiche<W, N> {
601 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
602 f.write_str("MapNiche")
603 }
604}
605
606/// A wrapper that converts a [`SystemTime`](std::time::SystemTime) to a
607/// [`Duration`](std::time::Duration) since
608/// [`UNIX_EPOCH`](std::time::UNIX_EPOCH).
609///
610/// If the serialized time occurs before the UNIX epoch, serialization will
611/// panic during `resolve`. The resulting archived time will be an
612/// [`ArchivedDuration`](crate::time::ArchivedDuration) relative to the UNIX
613/// epoch.
614///
615/// # Example
616///
617/// ```
618/// use rkyv::{Archive, with::AsUnixTime};
619/// use std::time::SystemTime;
620///
621/// #[derive(Archive)]
622/// struct Example {
623/// #[rkyv(with = AsUnixTime)]
624/// time: SystemTime,
625/// }
626#[derive(Debug)]
627pub struct AsUnixTime;
628
629/// A wrapper that allows serialize-unsafe types to be serialized.
630///
631/// Types like `Cell` and `UnsafeCell` may contain serializable types, but have
632/// unsafe access semantics due to interior mutability. They may be safe to
633/// serialize, but only under conditions that rkyv is unable to guarantee.
634///
635/// This wrapper enables serializing these types, and places the burden of
636/// verifying that their access semantics are used safely on the user.
637///
638/// # Safety
639///
640/// Using this wrapper on types with interior mutability can create races
641/// conditions or allow access to data in an invalid state if access semantics
642/// are not followed properly. During serialization, the data must not be
643/// modified.
644///
645/// # Example
646///
647/// ```
648/// use core::cell::{Cell, UnsafeCell};
649///
650/// use rkyv::{with::Unsafe, Archive};
651///
652/// #[derive(Archive)]
653/// struct Example {
654/// #[rkyv(with = Unsafe)]
655/// cell: Cell<String>,
656/// #[rkyv(with = Unsafe)]
657/// unsafe_cell: UnsafeCell<String>,
658/// }
659/// ```
660#[derive(Debug)]
661pub struct Unsafe;
662
663/// A wrapper that skips serializing a field.
664///
665/// Skipped fields must implement `Default` to be deserialized.
666///
667/// # Example
668///
669/// ```
670/// use rkyv::{with::Skip, Archive};
671///
672/// #[derive(Archive)]
673/// struct Example {
674/// #[rkyv(with = Skip)]
675/// a: u32,
676/// }
677/// ```
678#[derive(Debug)]
679pub struct Skip;
680
681/// A wrapper that clones the contents of `Arc` and `Rc` pointers.
682#[derive(Debug)]
683pub struct Unshare;
684
685/// A no-op wrapper which uses the default impls for the type.
686///
687/// This is most useful for wrappers like [`MapKV`] when you only want to apply
688/// a wrapper to either the key or the value.
689///
690/// # Example
691///
692/// ```
693/// use std::collections::HashMap;
694///
695/// use rkyv::{
696/// with::{Identity, Inline, MapKV},
697/// Archive,
698/// };
699///
700/// #[derive(Archive)]
701/// struct Example<'a> {
702/// #[rkyv(with = MapKV<Identity, Inline>)]
703/// a: HashMap<u32, &'a u32>,
704/// }
705/// ```
706#[derive(Debug)]
707pub struct Identity;