1use core::num::{NonZeroI8, NonZeroU8};
2
3use crate::{
4 boxed::ArchivedBox,
5 niche::{
6 niched_option::NichedOption,
7 niching::{
8 Bool, DefaultNiche, NaN, Niching, Null, SharedNiching, Zero,
9 },
10 },
11 primitive::{
12 ArchivedF32, ArchivedF64, ArchivedI128, ArchivedI16, ArchivedI32,
13 ArchivedI64, ArchivedNonZeroI128, ArchivedNonZeroI16,
14 ArchivedNonZeroI32, ArchivedNonZeroI64, ArchivedNonZeroU128,
15 ArchivedNonZeroU16, ArchivedNonZeroU32, ArchivedNonZeroU64,
16 ArchivedU128, ArchivedU16, ArchivedU32, ArchivedU64,
17 },
18 traits::ArchivePointee,
19 Place, Portable, RelPtr,
20};
21
22macro_rules! impl_default_niche {
23 ($ty:ty, $niche:ty) => {
24 impl Niching<$ty> for DefaultNiche {
25 unsafe fn is_niched(niched: *const $ty) -> bool {
26 unsafe { <$niche as Niching<$ty>>::is_niched(niched) }
27 }
28
29 fn resolve_niched(out: Place<$ty>) {
30 <$niche as Niching<$ty>>::resolve_niched(out)
31 }
32 }
33 };
34}
35
36macro_rules! impl_nonzero_zero_niching {
39 ($nz:ty, $int:ty) => {
40 impl Niching<$nz> for Zero {
41 unsafe fn is_niched(niched: *const $nz) -> bool {
42 let value = unsafe { &*niched.cast::<$int>() };
43 *value == 0
44 }
45
46 fn resolve_niched(out: Place<$nz>) {
47 let out = unsafe { out.cast_unchecked::<$int>() };
48 out.write(0.into());
49 }
50 }
51
52 impl_default_niche!($nz, Zero);
53 };
54}
55
56impl_nonzero_zero_niching!(NonZeroU8, u8);
57impl_nonzero_zero_niching!(ArchivedNonZeroU16, ArchivedU16);
58impl_nonzero_zero_niching!(ArchivedNonZeroU32, ArchivedU32);
59impl_nonzero_zero_niching!(ArchivedNonZeroU64, ArchivedU64);
60impl_nonzero_zero_niching!(ArchivedNonZeroU128, ArchivedU128);
61
62impl_nonzero_zero_niching!(NonZeroI8, i8);
63impl_nonzero_zero_niching!(ArchivedNonZeroI16, ArchivedI16);
64impl_nonzero_zero_niching!(ArchivedNonZeroI32, ArchivedI32);
65impl_nonzero_zero_niching!(ArchivedNonZeroI64, ArchivedI64);
66impl_nonzero_zero_niching!(ArchivedNonZeroI128, ArchivedI128);
67
68macro_rules! impl_float_nan_niching {
71 ($fl:ty, $ar:ty) => {
72 impl Niching<$ar> for NaN {
73 unsafe fn is_niched(niched: *const $ar) -> bool {
74 unsafe { (*niched).to_native().is_nan() }
75 }
76
77 fn resolve_niched(out: Place<$ar>) {
78 out.write(<$fl>::NAN.into());
79 }
80 }
81 };
82}
83
84impl_float_nan_niching!(f32, ArchivedF32);
85impl_float_nan_niching!(f64, ArchivedF64);
86
87impl Niching<bool> for Bool {
90 unsafe fn is_niched(niched: *const bool) -> bool {
91 unsafe { (*niched.cast::<u8>()) > 1 }
92 }
93
94 fn resolve_niched(out: Place<bool>) {
95 unsafe { out.cast_unchecked::<u8>().write(2) };
96 }
97}
98
99impl_default_niche!(bool, Bool);
100
101impl<T> Niching<ArchivedBox<T>> for Null
104where
105 T: ArchivePointee + Portable + ?Sized,
106{
107 unsafe fn is_niched(niched: *const ArchivedBox<T>) -> bool {
108 unsafe { (*niched.cast::<RelPtr<T>>()).is_invalid() }
109 }
110
111 fn resolve_niched(out: Place<ArchivedBox<T>>) {
112 let out = unsafe { out.cast_unchecked::<RelPtr<T>>() };
113 RelPtr::emplace_invalid(out);
114 }
115}
116
117impl<T> Niching<ArchivedBox<T>> for DefaultNiche
118where
119 T: ArchivePointee + Portable + ?Sized,
120{
121 unsafe fn is_niched(niched: *const ArchivedBox<T>) -> bool {
122 unsafe { <Null as Niching<ArchivedBox<T>>>::is_niched(niched) }
123 }
124
125 fn resolve_niched(out: Place<ArchivedBox<T>>) {
126 <Null as Niching<ArchivedBox<T>>>::resolve_niched(out);
127 }
128}
129
130impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
133where
134 T: SharedNiching<N1, N2>,
135 N1: Niching<T>,
136 N2: Niching<T>,
137{
138 unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool {
139 unsafe { <Self as Niching<T>>::is_niched(niched.cast()) }
140 }
141
142 fn resolve_niched(out: Place<NichedOption<T, N1>>) {
143 <Self as Niching<T>>::resolve_niched(unsafe { out.cast_unchecked() })
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use core::num::NonZeroU32;
150
151 use crate::{
152 api::test::{
153 deserialize, roundtrip_with, to_archived, to_archived_from_bytes,
154 to_bytes,
155 },
156 boxed::ArchivedBox,
157 niche::niching::{DefaultNiche, NaN, Zero},
158 with::{AsBox, MapNiche, NicheInto},
159 Archive, Deserialize, Serialize,
160 };
161
162 #[test]
163 fn with_struct() {
164 #[derive(Archive, Serialize, Deserialize, Debug, PartialEq)]
165 #[rkyv(crate, derive(Debug))]
166 struct Nichable {
167 #[rkyv(niche = NaN)]
168 not_nan: f32,
169 #[rkyv(niche = Zero)]
170 int: NonZeroU32,
171 #[rkyv(niche)] boolean: bool,
173 }
174
175 impl Nichable {
176 fn create() -> Self {
177 Nichable {
178 not_nan: 123.456,
179 int: unsafe { NonZeroU32::new_unchecked(789) },
180 boolean: true,
181 }
182 }
183 }
184
185 #[derive(Archive, Serialize, Deserialize, Debug, PartialEq)]
186 #[rkyv(crate, derive(Debug))]
187 struct Middle {
188 #[rkyv(with = NicheInto<Zero>, niche = NaN, niche)]
189 a: Option<Nichable>,
191 #[rkyv(with = NicheInto<NaN>, niche = Zero)]
192 b: Option<Nichable>,
193 }
194
195 #[derive(Archive, Serialize, Deserialize, Debug, PartialEq)]
196 #[rkyv(crate, derive(Debug))]
197 struct Outer {
198 #[rkyv(with = DefaultNiche)]
199 field: Option<Middle>,
200 }
201
202 assert_eq!(
203 size_of::<ArchivedMiddle>(),
204 2 * size_of::<ArchivedNichable>()
205 );
206 assert_eq!(size_of::<ArchivedOuter>(), size_of::<ArchivedMiddle>());
207
208 let values = [
209 Outer { field: None },
210 Outer {
211 field: Some(Middle { a: None, b: None }),
212 },
213 Outer {
214 field: Some(Middle {
215 a: None,
216 b: Some(Nichable::create()),
217 }),
218 },
219 ];
220
221 roundtrip_with(&values[0], |_, archived| {
222 assert!(archived.field.is_none());
223 });
224 roundtrip_with(&values[1], |_, archived| {
225 let middle = archived.field.as_ref().unwrap();
226 assert!(middle.a.is_none());
227 assert!(middle.b.is_none());
228 });
229 roundtrip_with(&values[2], |_, archived| {
230 let middle = archived.field.as_ref().unwrap();
231 assert!(middle.a.is_none());
232 let b = middle.b.as_ref().unwrap();
233 assert_eq!(b.not_nan, 123.456);
234 assert_eq!(b.int.get(), 789);
235 assert_eq!(b.boolean, true);
236 });
237 }
238
239 #[test]
240 fn with_enum() {
241 #[derive(Archive, Serialize, Deserialize, Debug, PartialEq)]
242 #[rkyv(crate, derive(Debug))]
243 enum Nichable {
244 A(#[rkyv(niche)] bool),
245 B {
246 #[rkyv(niche = NaN)]
247 float: f32,
248 },
249 C,
250 }
251
252 #[derive(Archive, Serialize, Deserialize, Debug, PartialEq)]
253 #[rkyv(crate, derive(Debug))]
254 struct Middle {
255 #[rkyv(with = DefaultNiche, niche = NaN)]
256 nichable: Option<Nichable>,
257 }
258
259 #[derive(Archive, Serialize, Deserialize, Debug, PartialEq)]
260 #[rkyv(crate, derive(Debug))]
261 struct Outer {
262 #[rkyv(with = NicheInto<NaN>)]
263 field: Option<Middle>,
264 }
265
266 assert_eq!(size_of::<ArchivedNichable>(), size_of::<ArchivedMiddle>());
267 assert_eq!(size_of::<ArchivedOuter>(), size_of::<ArchivedMiddle>());
268
269 let values = [
270 Outer { field: None },
271 Outer {
272 field: Some(Middle { nichable: None }),
273 },
274 Outer {
275 field: Some(Middle {
276 nichable: Some(Nichable::A(true)),
277 }),
278 },
279 Outer {
280 field: Some(Middle {
281 nichable: Some(Nichable::B { float: f32::NAN }),
282 }),
283 },
284 Outer {
285 field: Some(Middle {
286 nichable: Some(Nichable::B { float: 123.45 }),
287 }),
288 },
289 Outer {
290 field: Some(Middle {
291 nichable: Some(Nichable::C),
292 }),
293 },
294 ];
295
296 roundtrip_with(&values[0], |_, archived| {
297 assert!(archived.field.is_none());
298 });
299 roundtrip_with(&values[1], |_, archived| {
300 let middle = archived.field.as_ref().unwrap();
301 assert!(middle.nichable.is_none());
302 });
303 roundtrip_with(&values[2], |_, archived| {
304 let middle = archived.field.as_ref().unwrap();
305 let nichable = middle.nichable.as_ref().unwrap();
306 match nichable {
307 ArchivedNichable::A(b) => assert!(*b),
308 _ => panic!("expected `ArchivedNichable::A`"),
309 }
310 });
311 to_archived(&values[3], |archived| {
312 assert!(archived.field.is_none());
314 });
315 roundtrip_with(&values[4], |_, archived| {
316 let middle = archived.field.as_ref().unwrap();
317 let nichable = middle.nichable.as_ref().unwrap();
318 match nichable {
319 ArchivedNichable::B { float } => {
320 assert_eq!(float.to_native(), 123.45)
321 }
322 _ => panic!("expected `ArchivedNichable::B`"),
323 }
324 });
325 roundtrip_with(&values[5], |_, archived| {
326 let middle = archived.field.as_ref().unwrap();
327 let nichable = middle.nichable.as_ref().unwrap();
328 match nichable {
329 ArchivedNichable::C => {}
330 _ => panic!("expected `ArchivedNichable::C`"),
331 }
332 });
333 }
334
335 #[test]
336 fn map_niche() {
337 #[derive(Archive, Serialize, Deserialize, Debug, PartialEq)]
338 #[rkyv(crate, derive(Debug))]
339 struct Outer {
340 #[rkyv(with = MapNiche<AsBox>)]
341 opt: Option<NotNichable>,
342 }
343
344 #[derive(Archive, Serialize, Deserialize, Debug, PartialEq)]
345 #[rkyv(crate, derive(Debug))]
346 struct NotNichable {
347 int: i64,
348 }
349
350 let values = &[
351 Outer { opt: None },
352 Outer {
353 opt: Some(NotNichable { int: 42 }),
354 },
355 ];
356
357 to_bytes(&values[0], |bytes| {
358 assert_eq!(
359 bytes.len(),
360 size_of::<ArchivedBox<ArchivedNotNichable>>()
361 );
362 to_archived_from_bytes::<Outer>(bytes, |archived| {
363 assert!(archived.opt.as_ref().is_none());
364 let deserialized: Outer = deserialize(&*archived);
365 assert_eq!(&values[0], &deserialized);
366 });
367 });
368 roundtrip_with(&values[1], |_, archived| {
369 let bar = archived.opt.as_ref().unwrap();
370 assert_eq!(bar.int.to_native(), 42);
371 });
372 }
373}