1#[cfg(feature = "bytemuck")]
6use bytemuck_derive::{Pod, Zeroable};
7#[cfg(feature = "serde")]
8use serde_derive::{Deserialize, Serialize};
9#[cfg(feature = "wincode")]
10use wincode::{SchemaRead, SchemaWrite};
11#[cfg(feature = "borsh")]
12use {
13 alloc::string::ToString,
14 borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
15};
16
17#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
21#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
22#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
23#[cfg_attr(feature = "serde", serde(from = "bool", into = "bool"))]
24#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
25#[derive(Clone, Copy, Debug, Default, PartialEq)]
26#[repr(transparent)]
27pub struct Bool(pub u8);
28impl Bool {
29 pub const fn from_bool(b: bool) -> Self {
30 Self(if b { 1 } else { 0 })
31 }
32}
33
34impl From<bool> for Bool {
35 fn from(b: bool) -> Self {
36 Self::from_bool(b)
37 }
38}
39
40impl From<&bool> for Bool {
41 fn from(b: &bool) -> Self {
42 Self(if *b { 1 } else { 0 })
43 }
44}
45
46impl From<&Bool> for bool {
47 fn from(b: &Bool) -> Self {
48 b.0 != 0
49 }
50}
51
52impl From<Bool> for bool {
53 fn from(b: Bool) -> Self {
54 b.0 != 0
55 }
56}
57
58#[macro_export]
65macro_rules! impl_int_conversion {
66 ($P:ty, $I:ty) => {
67 impl $P {
68 pub const fn from_primitive(n: $I) -> Self {
69 Self(n.to_le_bytes())
70 }
71 }
72 impl From<$I> for $P {
73 fn from(n: $I) -> Self {
74 Self::from_primitive(n)
75 }
76 }
77 impl From<$P> for $I {
78 fn from(unaligned: $P) -> Self {
79 Self::from_le_bytes(unaligned.0)
80 }
81 }
82 };
83}
84
85#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
87#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
88#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
89#[cfg_attr(feature = "serde", serde(from = "u16", into = "u16"))]
90#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
91#[derive(Clone, Copy, Debug, Default, PartialEq)]
92#[repr(transparent)]
93pub struct U16(pub [u8; 2]);
94impl_int_conversion!(U16, u16);
95
96#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
98#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
99#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
100#[cfg_attr(feature = "serde", serde(from = "i16", into = "i16"))]
101#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
102#[derive(Clone, Copy, Debug, Default, PartialEq)]
103#[repr(transparent)]
104pub struct I16(pub [u8; 2]);
105impl_int_conversion!(I16, i16);
106
107#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
109#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
110#[cfg_attr(
111 feature = "borsh",
112 derive(BorshDeserialize, BorshSerialize, BorshSchema)
113)]
114#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
115#[cfg_attr(feature = "serde", serde(from = "u32", into = "u32"))]
116#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
117#[derive(Clone, Copy, Debug, Default, PartialEq)]
118#[repr(transparent)]
119pub struct U32(pub [u8; 4]);
120impl_int_conversion!(U32, u32);
121
122#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
124#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
125#[cfg_attr(
126 feature = "borsh",
127 derive(BorshDeserialize, BorshSerialize, BorshSchema)
128)]
129#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
130#[cfg_attr(feature = "serde", serde(from = "u64", into = "u64"))]
131#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
132#[derive(Clone, Copy, Debug, Default, PartialEq)]
133#[repr(transparent)]
134pub struct U64(pub [u8; 8]);
135impl_int_conversion!(U64, u64);
136
137#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
139#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
140#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
141#[cfg_attr(feature = "serde", serde(from = "i64", into = "i64"))]
142#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
143#[derive(Clone, Copy, Debug, Default, PartialEq)]
144#[repr(transparent)]
145pub struct I64([u8; 8]);
146impl_int_conversion!(I64, i64);
147
148#[cfg(not(target_arch = "bpf"))]
150#[cfg_attr(feature = "wincode", derive(SchemaRead, SchemaWrite))]
151#[cfg_attr(feature = "wincode", wincode(assert_zero_copy))]
152#[cfg_attr(
153 feature = "borsh",
154 derive(BorshDeserialize, BorshSerialize, BorshSchema)
155)]
156#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
157#[cfg_attr(feature = "serde", serde(from = "u128", into = "u128"))]
158#[cfg_attr(feature = "bytemuck", derive(Pod, Zeroable))]
159#[derive(Clone, Copy, Debug, Default, PartialEq)]
160#[repr(transparent)]
161pub struct U128(pub [u8; 16]);
162#[cfg(not(target_arch = "bpf"))]
163impl_int_conversion!(U128, u128);
164
165macro_rules! impl_usize_conversion {
168 ($UnalignedType:ty, $PrimitiveType:ty) => {
169 impl TryFrom<usize> for $UnalignedType {
170 type Error = core::num::TryFromIntError;
171
172 fn try_from(val: usize) -> Result<Self, Self::Error> {
173 let primitive_val = <$PrimitiveType>::try_from(val)?;
174 Ok(primitive_val.into())
175 }
176 }
177
178 impl From<$UnalignedType> for usize {
179 fn from(unaligned_val: $UnalignedType) -> Self {
180 let primitive_val = <$PrimitiveType>::from(unaligned_val);
181 Self::try_from(primitive_val)
182 .expect("value out of range for usize on this platform")
183 }
184 }
185 };
186}
187
188impl_usize_conversion!(U16, u16);
189impl_usize_conversion!(U32, u32);
190impl_usize_conversion!(U64, u64);
191#[cfg(not(target_arch = "bpf"))]
192impl_usize_conversion!(U128, u128);
193
194#[cfg(test)]
195mod tests {
196 use super::*;
197
198 #[cfg(feature = "bytemuck")]
199 #[test]
200 fn test_bool() {
201 assert!(bytemuck::try_from_bytes::<Bool>(&[]).is_err());
202 assert!(bytemuck::try_from_bytes::<Bool>(&[0, 0]).is_err());
203
204 for i in 0..=u8::MAX {
205 assert_eq!(i != 0, bool::from(*bytemuck::from_bytes::<Bool>(&[i])));
206 }
207 }
208
209 #[cfg(feature = "serde")]
210 #[test]
211 fn test_bool_serde() {
212 let unaligned_false: Bool = false.into();
213 let unaligned_true: Bool = true.into();
214
215 let serialized_false = serde_json::to_string(&unaligned_false).unwrap();
216 let serialized_true = serde_json::to_string(&unaligned_true).unwrap();
217 assert_eq!(&serialized_false, "false");
218 assert_eq!(&serialized_true, "true");
219
220 let deserialized_false = serde_json::from_str::<Bool>(&serialized_false).unwrap();
221 let deserialized_true = serde_json::from_str::<Bool>(&serialized_true).unwrap();
222 assert_eq!(unaligned_false, deserialized_false);
223 assert_eq!(unaligned_true, deserialized_true);
224 }
225
226 #[cfg(feature = "bytemuck")]
227 #[test]
228 fn test_u16() {
229 assert!(bytemuck::try_from_bytes::<U16>(&[]).is_err());
230 assert_eq!(1u16, u16::from(*bytemuck::from_bytes::<U16>(&[1, 0])));
231 }
232
233 #[cfg(feature = "serde")]
234 #[test]
235 fn test_u16_serde() {
236 let unaligned_u16: U16 = u16::MAX.into();
237
238 let serialized = serde_json::to_string(&unaligned_u16).unwrap();
239 assert_eq!(&serialized, "65535");
240
241 let deserialized = serde_json::from_str::<U16>(&serialized).unwrap();
242 assert_eq!(unaligned_u16, deserialized);
243 }
244
245 #[cfg(feature = "bytemuck")]
246 #[test]
247 fn test_i16() {
248 assert!(bytemuck::try_from_bytes::<I16>(&[]).is_err());
249 assert_eq!(-1i16, i16::from(*bytemuck::from_bytes::<I16>(&[255, 255])));
250 }
251
252 #[cfg(feature = "serde")]
253 #[test]
254 fn test_i16_serde() {
255 let unaligned_i16: I16 = i16::MAX.into();
256 let serialized = serde_json::to_string(&unaligned_i16).unwrap();
257 assert_eq!(&serialized, "32767");
258
259 let deserialized = serde_json::from_str::<I16>(&serialized).unwrap();
260 assert_eq!(unaligned_i16, deserialized);
261 }
262
263 #[cfg(feature = "bytemuck")]
264 #[test]
265 fn test_u64() {
266 assert!(bytemuck::try_from_bytes::<U64>(&[]).is_err());
267 assert_eq!(
268 1u64,
269 u64::from(*bytemuck::from_bytes::<U64>(&[1, 0, 0, 0, 0, 0, 0, 0]))
270 );
271 }
272
273 #[cfg(feature = "serde")]
274 #[test]
275 fn test_u64_serde() {
276 let unaligned_u64: U64 = u64::MAX.into();
277
278 let serialized = serde_json::to_string(&unaligned_u64).unwrap();
279 assert_eq!(&serialized, "18446744073709551615");
280
281 let deserialized = serde_json::from_str::<U64>(&serialized).unwrap();
282 assert_eq!(unaligned_u64, deserialized);
283 }
284
285 #[cfg(feature = "bytemuck")]
286 #[test]
287 fn test_i64() {
288 assert!(bytemuck::try_from_bytes::<I64>(&[]).is_err());
289 assert_eq!(
290 -1i64,
291 i64::from(*bytemuck::from_bytes::<I64>(&[
292 255, 255, 255, 255, 255, 255, 255, 255
293 ]))
294 );
295 }
296
297 #[cfg(feature = "serde")]
298 #[test]
299 fn test_i64_serde() {
300 let unaligned_i64: I64 = i64::MAX.into();
301
302 let serialized = serde_json::to_string(&unaligned_i64).unwrap();
303 assert_eq!(&serialized, "9223372036854775807");
304
305 let deserialized = serde_json::from_str::<I64>(&serialized).unwrap();
306 assert_eq!(unaligned_i64, deserialized);
307 }
308
309 #[cfg(not(target_arch = "bpf"))]
310 #[cfg(feature = "bytemuck")]
311 #[test]
312 fn test_u128() {
313 assert!(bytemuck::try_from_bytes::<U128>(&[]).is_err());
314 assert_eq!(
315 1u128,
316 u128::from(*bytemuck::from_bytes::<U128>(&[
317 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
318 ]))
319 );
320 }
321
322 #[cfg(not(target_arch = "bpf"))]
323 #[cfg(feature = "serde")]
324 #[test]
325 fn test_u128_serde() {
326 let unaligned_u128: U128 = u128::MAX.into();
327
328 let serialized = serde_json::to_string(&unaligned_u128).unwrap();
329 assert_eq!(&serialized, "340282366920938463463374607431768211455");
330
331 let deserialized = serde_json::from_str::<U128>(&serialized).unwrap();
332 assert_eq!(unaligned_u128, deserialized);
333 }
334
335 macro_rules! test_usize_roundtrip {
336 ($test_name:ident, $UnalignedType:ty, $max:expr) => {
337 #[test]
338 fn $test_name() {
339 let unaligned = <$UnalignedType>::try_from(0usize).unwrap();
341 assert_eq!(usize::from(unaligned), 0);
342
343 let unaligned = <$UnalignedType>::try_from(42usize).unwrap();
345 assert_eq!(usize::from(unaligned), 42);
346
347 let max = $max as usize;
349 let unaligned = <$UnalignedType>::try_from(max).unwrap();
350 assert_eq!(usize::from(unaligned), max);
351 }
352 };
353 }
354
355 test_usize_roundtrip!(test_usize_roundtrip_u16, U16, u16::MAX);
356 test_usize_roundtrip!(test_usize_roundtrip_u32, U32, u32::MAX);
357 test_usize_roundtrip!(test_usize_roundtrip_u64, U64, u64::MAX);
358 #[cfg(not(target_arch = "bpf"))]
359 test_usize_roundtrip!(test_usize_roundtrip_u128, U128, u128::MAX);
360
361 #[cfg(feature = "wincode")]
362 mod wincode_tests {
363 use {super::*, test_case::test_case};
364
365 #[test_case(Bool::from_bool(true))]
366 #[test_case(Bool::from_bool(false))]
367 #[test_case(U16::from_primitive(u16::MAX))]
368 #[test_case(I16::from_primitive(i16::MIN))]
369 #[test_case(U32::from_primitive(u32::MAX))]
370 #[test_case(U64::from_primitive(u64::MAX))]
371 #[test_case(I64::from_primitive(i64::MIN))]
372 #[cfg(not(target_arch = "bpf"))]
373 #[test_case(U128::from_primitive(u128::MAX))]
374 fn wincode_roundtrip<
375 T: PartialEq
376 + core::fmt::Debug
377 + wincode::ZeroCopy
378 + for<'de> wincode::SchemaRead<'de, wincode::config::DefaultConfig, Dst = T>
379 + wincode::SchemaWrite<wincode::config::DefaultConfig, Src = T>,
380 >(
381 value: T,
382 ) {
383 let size = wincode::serialized_size(&value).unwrap() as usize;
384 let mut bytes = [0u8; 32];
385 assert!(size <= bytes.len());
386 wincode::serialize_into(&mut bytes[..size], &value).unwrap();
387
388 let deserialized: T = wincode::deserialize(&bytes[..size]).unwrap();
389 assert_eq!(value, deserialized);
390
391 let zero_copy_ref = <T as wincode::ZeroCopy>::from_bytes(&bytes[..size]).unwrap();
392 assert_eq!(&value, zero_copy_ref);
393 }
394 }
395}