1#![cfg_attr(not(feature = "std"), no_std)]
54#![deny(
55 missing_docs,
56 rustdoc::missing_crate_level_docs,
57 rust_2018_compatibility,
58 rust_2018_idioms,
59 future_incompatible,
60 nonstandard_style,
61 unused,
62 clippy::all
63)]
64
65#[macro_use]
66mod impl_struct;
67#[macro_use]
68mod impl_traits;
69#[cfg(feature = "validation")]
70#[macro_use]
71mod impl_validation;
72#[cfg(feature = "bytemuck")]
73mod impl_bytemuck;
74
75#[cfg(feature = "validation")]
76use bytecheck::{CharCheckError, CheckBytes, NonZeroCheckError};
77#[cfg(feature = "validation")]
78use core::convert::Infallible;
79#[cfg(has_atomics)]
80use core::sync::atomic::{AtomicI16, AtomicI32, AtomicU16, AtomicU32, Ordering};
81#[cfg(has_atomics_64)]
82use core::sync::atomic::{AtomicI64, AtomicU64};
83use core::{
84 hash::{Hash, Hasher},
85 num::{
86 NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroU128, NonZeroU16, NonZeroU32,
87 NonZeroU64,
88 },
89};
90
91pub unsafe trait Primitive {
93 type Storage;
96}
97
98#[derive(Clone, Copy)]
103#[cfg_attr(feature = "bytemuck", derive(bytemuck::Zeroable, bytemuck::Pod))]
104#[repr(transparent)]
105pub struct NativeEndian<T> {
106 pub value: T,
108}
109
110#[derive(Clone, Copy)]
112#[repr(transparent)]
113pub struct LittleEndian<T: Primitive> {
114 value: T::Storage,
115}
116
117#[derive(Clone, Copy)]
119#[repr(transparent)]
120pub struct BigEndian<T: Primitive> {
121 value: T::Storage,
122}
123
124macro_rules! swap_endian {
125 (@NativeEndian $expr:expr) => {{
126 $expr
127 }};
128 (@LittleEndian $expr:expr) => {{
129 #[cfg(target_endian = "little")]
130 {
131 $expr
132 }
133 #[cfg(target_endian = "big")]
134 {
135 $expr.swap_bytes()
136 }
137 }};
138 (@BigEndian $expr:expr) => {{
139 #[cfg(target_endian = "little")]
140 {
141 $expr.swap_bytes()
142 }
143 #[cfg(target_endian = "big")]
144 {
145 $expr
146 }
147 }};
148}
149
150macro_rules! swap_bytes {
151 (@signed_int $endian:ident<$ne:ty> $value:expr) => {
152 swap_endian!(@$endian $value)
153 };
154 (@unsigned_int $endian:ident<$ne:ty> $value:expr) => {
155 swap_endian!(@$endian $value)
156 };
157 (@float $endian:ident<$ne:ty> $value:expr) => {
158 <$ne>::from_bits(swap_endian!(@$endian $value.to_bits()))
159 };
160 (@char $endian:ident<$ne:ty> $value:expr) => {
161 swap_endian!(@$endian $value)
162 };
163 (@nonzero $endian:ident<$ne:ty> $value:expr) => {
164 unsafe { <$ne>::new_unchecked(swap_endian!(@$endian $value.get())) }
165 };
166 (@atomic $endian:ident<$ne:ty> $value:expr) => {
167 swap_endian!(@$endian $value)
168 };
169}
170
171macro_rules! from_native {
172 (@char NativeEndian<$ne:ty> $value:expr) => {
173 $value
174 };
175 (@char $endian:ident<$ne:ty> $value:expr) => {
176 $value as u32
177 };
178 (@$class:ident $endian:ident<$ne:ty> $value:expr) => {
179 $value
180 };
181}
182
183macro_rules! to_native {
184 (@char NativeEndian<$ne:ty> $value:expr) => {
185 $value
186 };
187 (@char $endian:ident<$ne:ty> $value:expr) => {
188 unsafe { char::from_u32_unchecked($value) }
189 };
190 (@$class:ident $endian:ident<$ne:ty> $value:expr) => {
191 $value
192 };
193}
194
195macro_rules! impl_endian {
196 (
197 @$class:ident $native:ty $(= $prim:ty)?,
198 $ne:ident = $ne_doc:literal,
199 $le:ident = $le_doc:literal,
200 $be:ident = $be_doc:literal
201 ) => {
202 impl_endian!(@$class $ne_doc NativeEndian<$native> as $ne $(= $prim)?);
203 impl_endian!(@$class $le_doc LittleEndian<$native> as $le $(= $prim)?);
204 impl_endian!(@$class $be_doc BigEndian<$native> as $be $(= $prim)?);
205 };
206 (@$class:ident $doc:literal $endian:ident<$ne:ty> as $alias:ident $(= $prim:ty)?) => {
207 impl_struct!(@$class $endian<$ne> $(= $prim)?);
208 #[cfg(feature = "validation")]
209 impl_validation!(@$class $endian<$ne> $(= $prim)?);
210 #[doc = "Alias for "]
211 #[doc = $doc]
212 #[doc = "."]
213 #[allow(non_camel_case_types)]
214 pub type $alias = $endian<$ne>;
215 };
216}
217
218macro_rules! impl_primitive {
219 ($($ty:ty),+ $(,)?) => {
220 $(
221 unsafe impl Primitive for $ty {
222 type Storage = $ty;
223 }
224 )+
225 };
226}
227
228impl_primitive!(
229 i16,
230 i32,
231 i64,
232 i128,
233 u16,
234 u32,
235 u64,
236 u128,
237 f32,
238 f64,
239 NonZeroI16,
240 NonZeroI32,
241 NonZeroI64,
242 NonZeroI128,
243 NonZeroU16,
244 NonZeroU32,
245 NonZeroU64,
246 NonZeroU128,
247);
248
249#[cfg(has_atomics)]
250impl_primitive!(AtomicI16, AtomicI32, AtomicU16, AtomicU32);
251
252#[cfg(has_atomics_64)]
253impl_primitive!(AtomicI64, AtomicU64);
254
255unsafe impl Primitive for char {
256 type Storage = u32;
257}
258
259impl_endian!(
260 @signed_int i16,
261 i16_ne = "`NativeEndian<i16>`",
262 i16_le = "`LittleEndian<i16>`",
263 i16_be = "`BigEndian<i16>`"
264);
265impl_endian!(
266 @signed_int i32,
267 i32_ne = "`NativeEndian<i32>`",
268 i32_le = "`LittleEndian<i32>`",
269 i32_be = "`BigEndian<i32>`"
270);
271impl_endian!(
272 @signed_int i64,
273 i64_ne = "`NativeEndian<i64>`",
274 i64_le = "`LittleEndian<i64>`",
275 i64_be = "`BigEndian<i64>`"
276);
277impl_endian!(
278 @signed_int i128,
279 i128_ne = "`NativeEndian<i128>`",
280 i128_le = "`LittleEndian<i128>`",
281 i128_be = "`BigEndian<i128>`"
282);
283impl_endian!(
284 @unsigned_int u16,
285 u16_ne = "`NativeEndian<u16>`",
286 u16_le = "`LittleEndian<u16>`",
287 u16_be = "`BigEndian<u16>`"
288);
289impl_endian!(
290 @unsigned_int u32,
291 u32_ne = "`NativeEndian<u32>`",
292 u32_le = "`LittleEndian<u32>`",
293 u32_be = "`BigEndian<u32>`"
294);
295impl_endian!(
296 @unsigned_int u64,
297 u64_ne = "`NativeEndian<u64>`",
298 u64_le = "`LittleEndian<u64>`",
299 u64_be = "`BigEndian<u64>`"
300);
301impl_endian!(
302 @unsigned_int u128,
303 u128_ne = "`NativeEndian<u128>`",
304 u128_le = "`LittleEndian<u128>`",
305 u128_be = "`BigEndian<u128>`"
306);
307
308impl_endian!(
309 @float f32,
310 f32_ne = "`NativeEndian<f32>`",
311 f32_le = "`LittleEndian<f32>`",
312 f32_be = "`BigEndian<f32>`"
313);
314impl_endian!(
315 @float f64,
316 f64_ne = "`NativeEndian<f64>`",
317 f64_le = "`LittleEndian<f64>`",
318 f64_be = "`BigEndian<f64>`"
319);
320
321impl_endian!(
322 @char char,
323 char_ne = "`NativeEndian<char>`",
324 char_le = "`LittleEndian<char>`",
325 char_be = "`BigEndian<char>`"
326);
327
328impl_endian!(
329 @nonzero NonZeroI16 = i16,
330 NonZeroI16_ne = "`NativeEndian<NonZeroI16>`",
331 NonZeroI16_le = "`LittleEndian<NonZeroI16>`",
332 NonZeroI16_be = "`BigEndian<NonZeroI16>`"
333);
334impl_endian!(
335 @nonzero NonZeroI32 = i32,
336 NonZeroI32_ne = "`NativeEndian<NonZeroI32>`",
337 NonZeroI32_le = "`LittleEndian<NonZeroI32>`",
338 NonZeroI32_be = "`BigEndian<NonZeroI32>`"
339);
340impl_endian!(
341 @nonzero NonZeroI64 = i64,
342 NonZeroI64_ne = "`NativeEndian<NonZeroI64>`",
343 NonZeroI64_le = "`LittleEndian<NonZeroI64>`",
344 NonZeroI64_be = "`BigEndian<NonZeroI64>`"
345);
346impl_endian!(
347 @nonzero NonZeroI128 = i128,
348 NonZeroI128_ne = "`NativeEndian<NonZeroI128>`",
349 NonZeroI128_le = "`LittleEndian<NonZeroI128>`",
350 NonZeroI128_be = "`BigEndian<NonZeroI128>`"
351);
352impl_endian!(
353 @nonzero NonZeroU16 = u16,
354 NonZeroU16_ne = "`NativeEndian<NonZeroU16>`",
355 NonZeroU16_le = "`LittleEndian<NonZeroU16>`",
356 NonZeroU16_be = "`BigEndian<NonZeroU16>`"
357);
358impl_endian!(
359 @nonzero NonZeroU32 = u32,
360 NonZeroU32_ne = "`NativeEndian<NonZeroU32>`",
361 NonZeroU32_le = "`LittleEndian<NonZeroU32>`",
362 NonZeroU32_be = "`BigEndian<NonZeroU32>`"
363);
364impl_endian!(
365 @nonzero NonZeroU64 = u64,
366 NonZeroU64_ne = "`NativeEndian<NonZeroU64>`",
367 NonZeroU64_le = "`LittleEndian<NonZeroU64>`",
368 NonZeroU64_be = "`BigEndian<NonZeroU64>`"
369);
370impl_endian!(
371 @nonzero NonZeroU128 = u128,
372 NonZeroU128_ne = "`NativeEndian<NonZeroU128>`",
373 NonZeroU128_le = "`LittleEndian<NonZeroU128>`",
374 NonZeroU128_be = "`BigEndian<NonZeroU128>`"
375);
376
377#[cfg(has_atomics)]
378impl_endian!(
379 @atomic AtomicI16 = i16,
380 AtomicI16_ne = "`NativeEndian<AtomicI16>`",
381 AtomicI16_le = "`LittleEndian<AtomicI16>`",
382 AtomicI16_be = "`BigEndian<AtomicI16>`"
383);
384#[cfg(has_atomics)]
385impl_endian!(
386 @atomic AtomicI32 = i32,
387 AtomicI32_ne = "`NativeEndian<AtomicI32>`",
388 AtomicI32_le = "`LittleEndian<AtomicI32>`",
389 AtomicI32_be = "`BigEndian<AtomicI32>`"
390);
391#[cfg(has_atomics_64)]
392impl_endian!(
393 @atomic AtomicI64 = i64,
394 AtomicI64_ne = "`NativeEndian<AtomicI64>`",
395 AtomicI64_le = "`LittleEndian<AtomicI64>`",
396 AtomicI64_be = "`BigEndian<AtomicI64>`"
397);
398#[cfg(has_atomics)]
399impl_endian!(
400 @atomic AtomicU16 = u16,
401 AtomicU16_ne = "`NativeEndian<AtomicU16>`",
402 AtomicU16_le = "`LittleEndian<AtomicU16>`",
403 AtomicU16_be = "`BigEndian<AtomicU16>`"
404);
405#[cfg(has_atomics)]
406impl_endian!(
407 @atomic AtomicU32 = u32,
408 AtomicU32_ne = "`NativeEndian<AtomicU32>`",
409 AtomicU32_le = "`LittleEndian<AtomicU32>`",
410 AtomicU32_be = "`BigEndian<AtomicU32>`"
411);
412#[cfg(has_atomics_64)]
413impl_endian!(
414 @atomic AtomicU64 = u64,
415 AtomicU64_ne = "`NativeEndian<AtomicU64>`",
416 AtomicU64_le = "`LittleEndian<AtomicU64>`",
417 AtomicU64_be = "`BigEndian<AtomicU64>`"
418);
419
420#[cfg(test)]
421mod tests {
422 use crate::*;
423 use core::mem;
424
425 #[test]
426 fn endian_representation() {
427 unsafe {
428 assert_eq!(
430 [0x01, 0x02],
431 mem::transmute::<_, [u8; 2]>(i16_be::new(0x0102))
432 );
433 assert_eq!(
434 [0x02, 0x01],
435 mem::transmute::<_, [u8; 2]>(i16_le::new(0x0102))
436 );
437
438 assert_eq!(
440 [0x01, 0x02, 0x03, 0x04],
441 mem::transmute::<_, [u8; 4]>(i32_be::new(0x01020304))
442 );
443 assert_eq!(
444 [0x04, 0x03, 0x02, 0x01],
445 mem::transmute::<_, [u8; 4]>(i32_le::new(0x01020304))
446 );
447
448 assert_eq!(
450 [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
451 mem::transmute::<_, [u8; 8]>(i64_be::new(0x0102030405060708))
452 );
453 assert_eq!(
454 [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
455 mem::transmute::<_, [u8; 8]>(i64_le::new(0x0102030405060708))
456 );
457
458 assert_eq!(
460 [
461 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
462 0x0e, 0x0f, 0x10
463 ],
464 mem::transmute::<_, [u8; 16]>(i128_be::new(0x0102030405060708090a0b0c0d0e0f10))
465 );
466 assert_eq!(
467 [
468 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04,
469 0x03, 0x02, 0x01
470 ],
471 mem::transmute::<_, [u8; 16]>(i128_le::new(0x0102030405060708090a0b0c0d0e0f10))
472 );
473
474 assert_eq!(
476 [0x01, 0x02],
477 mem::transmute::<_, [u8; 2]>(u16_be::new(0x0102))
478 );
479 assert_eq!(
480 [0x02, 0x01],
481 mem::transmute::<_, [u8; 2]>(u16_le::new(0x0102))
482 );
483
484 assert_eq!(
486 [0x01, 0x02, 0x03, 0x04],
487 mem::transmute::<_, [u8; 4]>(u32_be::new(0x01020304))
488 );
489 assert_eq!(
490 [0x04, 0x03, 0x02, 0x01],
491 mem::transmute::<_, [u8; 4]>(u32_le::new(0x01020304))
492 );
493
494 assert_eq!(
496 [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
497 mem::transmute::<_, [u8; 8]>(u64_be::new(0x0102030405060708))
498 );
499 assert_eq!(
500 [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
501 mem::transmute::<_, [u8; 8]>(u64_le::new(0x0102030405060708))
502 );
503
504 assert_eq!(
506 [
507 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
508 0x0e, 0x0f, 0x10
509 ],
510 mem::transmute::<_, [u8; 16]>(u128_be::new(0x0102030405060708090a0b0c0d0e0f10))
511 );
512 assert_eq!(
513 [
514 0x10, 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04,
515 0x03, 0x02, 0x01
516 ],
517 mem::transmute::<_, [u8; 16]>(u128_le::new(0x0102030405060708090a0b0c0d0e0f10))
518 );
519
520 assert_eq!(
522 [0x40, 0x49, 0x0f, 0xdb],
523 mem::transmute::<_, [u8; 4]>(f32_be::new(core::f32::consts::PI))
524 );
525 assert_eq!(
526 [0xdb, 0x0f, 0x49, 0x40],
527 mem::transmute::<_, [u8; 4]>(f32_le::new(core::f32::consts::PI))
528 );
529
530 assert_eq!(
532 [0x40, 0x09, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18],
533 mem::transmute::<_, [u8; 8]>(f64_be::new(core::f64::consts::PI))
534 );
535 assert_eq!(
536 [0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40],
537 mem::transmute::<_, [u8; 8]>(f64_le::new(core::f64::consts::PI))
538 );
539
540 assert_eq!(
542 [0x00, 0x01, 0xf3, 0x89],
543 mem::transmute::<_, [u8; 4]>(char_be::new('🎉'))
544 );
545 assert_eq!(
546 [0x89, 0xf3, 0x01, 0x00],
547 mem::transmute::<_, [u8; 4]>(char_le::new('🎉'))
548 );
549
550 #[cfg(has_atomics)]
552 assert_eq!(
553 [0x01, 0x02],
554 mem::transmute::<_, [u8; 2]>(AtomicU16_be::new(0x0102))
555 );
556 #[cfg(has_atomics)]
557 assert_eq!(
558 [0x02, 0x01],
559 mem::transmute::<_, [u8; 2]>(AtomicU16_le::new(0x0102))
560 );
561
562 #[cfg(has_atomics)]
564 assert_eq!(
565 [0x01, 0x02, 0x03, 0x04],
566 mem::transmute::<_, [u8; 4]>(AtomicU32_be::new(0x01020304))
567 );
568 #[cfg(has_atomics)]
569 assert_eq!(
570 [0x04, 0x03, 0x02, 0x01],
571 mem::transmute::<_, [u8; 4]>(AtomicU32_le::new(0x01020304))
572 );
573
574 #[cfg(has_atomics_64)]
576 assert_eq!(
577 [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08],
578 mem::transmute::<_, [u8; 8]>(AtomicU64_be::new(0x0102030405060708))
579 );
580 #[cfg(has_atomics_64)]
581 assert_eq!(
582 [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01],
583 mem::transmute::<_, [u8; 8]>(AtomicU64_le::new(0x0102030405060708))
584 );
585 }
586 }
587}