Skip to main content

qubit_codec_binary/codec/
binary_codec.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8
9use core::{
10    convert::Infallible,
11    marker::PhantomData,
12    ptr,
13};
14
15use qubit_codec::Codec;
16
17use crate::{
18    BigEndian,
19    LittleEndian,
20};
21
22/// Type-level unchecked binary codec for one scalar type and one byte order.
23///
24/// `BinaryCodec` is intentionally a zero-sized codec type. It exposes
25/// type-level unchecked helpers for direct hot-path use and also implements
26/// [`Codec`] for generic codec pipelines. Callers must validate buffer lengths
27/// before entering the hot path.
28///
29/// # Type Parameters
30///
31/// - `T`: Scalar value type to decode from bytes and encode into bytes.
32/// - `O`: Type-level byte order marker. Multi-byte scalar implementations use
33///   [`BigEndian`] or [`LittleEndian`]. Single-byte scalar implementations
34///   accept any marker because byte order does not affect one-byte values.
35///
36/// # Examples
37///
38/// ```
39/// use qubit_codec_binary::{
40///     BigEndian,
41///     BinaryCodec,
42/// };
43///
44/// let mut output = [0_u8; BinaryCodec::<u32, BigEndian>::MAX_UNITS_PER_VALUE];
45/// let written = unsafe {
46///     BinaryCodec::<u32, BigEndian>::encode_unchecked(0x0102_0304, &mut output, 0)
47/// };
48/// assert_eq!(4, written);
49/// assert_eq!([1, 2, 3, 4], output);
50///
51/// let (decoded, consumed) = unsafe {
52///     BinaryCodec::<u32, BigEndian>::decode_unchecked(&output, 0)
53/// };
54/// assert_eq!(0x0102_0304, decoded);
55/// assert_eq!(4, consumed.get());
56/// ```
57#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
58pub struct BinaryCodec<T, O> {
59    marker: PhantomData<fn() -> (T, O)>,
60}
61
62impl<O> BinaryCodec<u8, O> {
63    /// Minimum number of bytes required to encode or decode this type.
64    pub const MIN_UNITS_PER_VALUE: usize = 1;
65
66    /// Maximum number of bytes required to encode or decode this type.
67    pub const MAX_UNITS_PER_VALUE: usize = Self::MIN_UNITS_PER_VALUE;
68
69    /// Decodes a value from `input` starting at `index` without bounds checks.
70    ///
71    /// # Parameters
72    ///
73    /// - `input`: Source byte buffer.
74    /// - `index`: Start index in `input`.
75    ///
76    /// # Returns
77    ///
78    /// Returns the decoded value and the non-zero number of consumed bytes.
79    ///
80    /// # Safety
81    ///
82    /// The caller must guarantee that `input.as_ptr().add(index)` is valid to
83    /// read [`Self::MIN_UNITS_PER_VALUE`] bytes.
84    #[must_use]
85    #[inline(always)]
86    pub unsafe fn decode_unchecked(
87        input: &[u8],
88        index: usize,
89    ) -> (u8, core::num::NonZeroUsize) {
90        debug_assert!(index + Self::MIN_UNITS_PER_VALUE <= input.len());
91
92        // SAFETY: The caller guarantees that the indexed byte is readable.
93        (
94            unsafe { *input.as_ptr().add(index) },
95            // SAFETY: `u8` is one byte wide.
96            unsafe {
97                core::num::NonZeroUsize::new_unchecked(
98                    Self::MIN_UNITS_PER_VALUE,
99                )
100            },
101        )
102    }
103
104    /// Encodes `value` into `output` starting at `index` without bounds checks.
105    ///
106    /// # Parameters
107    ///
108    /// - `value`: Value to encode.
109    /// - `output`: Destination byte buffer.
110    /// - `index`: Start index in `output`.
111    ///
112    /// # Safety
113    ///
114    /// The caller must guarantee that `output.as_mut_ptr().add(index)` is valid
115    /// to write [`Self::MAX_UNITS_PER_VALUE`] bytes.
116    #[inline(always)]
117    pub unsafe fn encode_unchecked(
118        value: u8,
119        output: &mut [u8],
120        index: usize,
121    ) -> usize {
122        debug_assert!(index + Self::MAX_UNITS_PER_VALUE <= output.len());
123
124        // SAFETY: The caller guarantees that the indexed byte is writable.
125        unsafe {
126            *output.as_mut_ptr().add(index) = value;
127        }
128        Self::MAX_UNITS_PER_VALUE
129    }
130}
131
132unsafe impl<O> Codec for BinaryCodec<u8, O> {
133    type Value = u8;
134    type Unit = u8;
135    type DecodeError = Infallible;
136    type EncodeError = Infallible;
137
138    #[inline(always)]
139    fn min_units_per_value(&self) -> core::num::NonZeroUsize {
140        // SAFETY: `u8` is one byte wide.
141        unsafe {
142            core::num::NonZeroUsize::new_unchecked(Self::MIN_UNITS_PER_VALUE)
143        }
144    }
145
146    #[inline(always)]
147    fn max_units_per_value(&self) -> core::num::NonZeroUsize {
148        // SAFETY: `u8` is one byte wide.
149        unsafe {
150            core::num::NonZeroUsize::new_unchecked(Self::MAX_UNITS_PER_VALUE)
151        }
152    }
153
154    #[inline(always)]
155    unsafe fn decode_unchecked(
156        &self,
157        input: &[u8],
158        index: usize,
159    ) -> Result<(u8, core::num::NonZeroUsize), Self::DecodeError> {
160        // SAFETY: The caller upholds the `Codec::decode_unchecked` contract.
161        Ok(unsafe { Self::decode_unchecked(input, index) })
162    }
163
164    #[inline(always)]
165    unsafe fn encode_unchecked(
166        &self,
167        value: &u8,
168        output: &mut [u8],
169        index: usize,
170    ) -> Result<usize, Self::EncodeError> {
171        // SAFETY: The caller upholds the `Codec::encode_unchecked` contract.
172        Ok(unsafe { Self::encode_unchecked(*value, output, index) })
173    }
174}
175
176impl<O> BinaryCodec<i8, O> {
177    /// Minimum number of bytes required to encode or decode this type.
178    pub const MIN_UNITS_PER_VALUE: usize = 1;
179
180    /// Maximum number of bytes required to encode or decode this type.
181    pub const MAX_UNITS_PER_VALUE: usize = Self::MIN_UNITS_PER_VALUE;
182
183    /// Decodes a value from `input` starting at `index` without bounds checks.
184    ///
185    /// # Parameters
186    ///
187    /// - `input`: Source byte buffer.
188    /// - `index`: Start index in `input`.
189    ///
190    /// # Returns
191    ///
192    /// Returns the decoded value and the non-zero number of consumed bytes.
193    ///
194    /// # Safety
195    ///
196    /// The caller must guarantee that `input.as_ptr().add(index)` is valid to
197    /// read [`Self::MIN_UNITS_PER_VALUE`] bytes.
198    #[must_use]
199    #[inline(always)]
200    pub unsafe fn decode_unchecked(
201        input: &[u8],
202        index: usize,
203    ) -> (i8, core::num::NonZeroUsize) {
204        debug_assert!(index + Self::MIN_UNITS_PER_VALUE <= input.len());
205
206        // SAFETY: The caller guarantees that the indexed byte is readable.
207        (
208            unsafe { *input.as_ptr().add(index) as i8 },
209            // SAFETY: `i8` is one byte wide.
210            unsafe {
211                core::num::NonZeroUsize::new_unchecked(
212                    Self::MIN_UNITS_PER_VALUE,
213                )
214            },
215        )
216    }
217
218    /// Encodes `value` into `output` starting at `index` without bounds checks.
219    ///
220    /// # Parameters
221    ///
222    /// - `value`: Value to encode.
223    /// - `output`: Destination byte buffer.
224    /// - `index`: Start index in `output`.
225    ///
226    /// # Safety
227    ///
228    /// The caller must guarantee that `output.as_mut_ptr().add(index)` is valid
229    /// to write [`Self::MAX_UNITS_PER_VALUE`] bytes.
230    #[inline(always)]
231    pub unsafe fn encode_unchecked(
232        value: i8,
233        output: &mut [u8],
234        index: usize,
235    ) -> usize {
236        debug_assert!(index + Self::MAX_UNITS_PER_VALUE <= output.len());
237
238        // SAFETY: The caller guarantees that the indexed byte is writable.
239        unsafe {
240            *output.as_mut_ptr().add(index) = value as u8;
241        }
242        Self::MAX_UNITS_PER_VALUE
243    }
244}
245
246unsafe impl<O> Codec for BinaryCodec<i8, O> {
247    type Value = i8;
248    type Unit = u8;
249    type DecodeError = Infallible;
250    type EncodeError = Infallible;
251
252    #[inline(always)]
253    fn min_units_per_value(&self) -> core::num::NonZeroUsize {
254        // SAFETY: `i8` is one byte wide.
255        unsafe {
256            core::num::NonZeroUsize::new_unchecked(Self::MIN_UNITS_PER_VALUE)
257        }
258    }
259
260    #[inline(always)]
261    fn max_units_per_value(&self) -> core::num::NonZeroUsize {
262        // SAFETY: `i8` is one byte wide.
263        unsafe {
264            core::num::NonZeroUsize::new_unchecked(Self::MAX_UNITS_PER_VALUE)
265        }
266    }
267
268    #[inline(always)]
269    unsafe fn decode_unchecked(
270        &self,
271        input: &[u8],
272        index: usize,
273    ) -> Result<(i8, core::num::NonZeroUsize), Self::DecodeError> {
274        // SAFETY: The caller upholds the `Codec::decode_unchecked` contract.
275        Ok(unsafe { Self::decode_unchecked(input, index) })
276    }
277
278    #[inline(always)]
279    unsafe fn encode_unchecked(
280        &self,
281        value: &i8,
282        output: &mut [u8],
283        index: usize,
284    ) -> Result<usize, Self::EncodeError> {
285        // SAFETY: The caller upholds the `Codec::encode_unchecked` contract.
286        Ok(unsafe { Self::encode_unchecked(*value, output, index) })
287    }
288}
289
290macro_rules! impl_integer_binary_codec {
291    ($ty:ty, $len:expr) => {
292        impl BinaryCodec<$ty, BigEndian> {
293            /// Minimum number of bytes required to encode or decode this type.
294            pub const MIN_UNITS_PER_VALUE: usize = $len;
295
296            /// Maximum number of bytes required to encode or decode this type.
297            pub const MAX_UNITS_PER_VALUE: usize = Self::MIN_UNITS_PER_VALUE;
298
299            /// Decodes a value from `input` starting at `index` without bounds
300            /// checks.
301            ///
302            /// This function is intended for hot binary codec paths where the
303            /// caller has already validated the buffer length externally.
304            ///
305            /// # Parameters
306            ///
307            /// - `input`: Source byte buffer.
308            /// - `index`: Start byte index in `input`.
309            ///
310            /// # Returns
311            ///
312            /// Returns the decoded value and the non-zero number of consumed
313            /// bytes.
314            ///
315            /// # Safety
316            ///
317            /// The caller must guarantee that:
318            ///
319            /// - `index + Self::MIN_UNITS_PER_VALUE <= input.len()`
320            /// - `input[index..index + Self::MIN_UNITS_PER_VALUE]` is valid for
321            ///   reading.
322            #[must_use]
323            #[inline(always)]
324            pub unsafe fn decode_unchecked(
325                input: &[u8],
326                index: usize,
327            ) -> ($ty, core::num::NonZeroUsize) {
328                debug_assert!(index + Self::MIN_UNITS_PER_VALUE <= input.len());
329
330                // SAFETY:
331                // The caller guarantees that the readable range is fully
332                // in-bounds. `read_unaligned` permits unaligned memory
333                // access.
334                let pointer =
335                    unsafe { input.as_ptr().add(index).cast::<$ty>() };
336
337                // SAFETY:
338                // The pointer is valid for an unaligned integer load.
339                let raw = unsafe { ptr::read_unaligned(pointer) };
340
341                (
342                    <$ty>::from_be(raw),
343                    // SAFETY: `$ty` is a concrete primitive integer type with
344                    // non-zero size.
345                    unsafe {
346                        core::num::NonZeroUsize::new_unchecked(
347                            Self::MIN_UNITS_PER_VALUE,
348                        )
349                    },
350                )
351            }
352
353            /// Encodes `value` into `output` starting at `index`
354            /// without bounds checks.
355            ///
356            /// This function is intended for hot binary codec paths where the
357            /// caller has already validated the buffer length externally.
358            ///
359            /// # Parameters
360            ///
361            /// - `value`: Value to encode.
362            /// - `output`: Destination byte buffer.
363            /// - `index`: Start byte index in `output`.
364            ///
365            /// # Safety
366            ///
367            /// The caller must guarantee that:
368            ///
369            /// - `index + Self::MAX_UNITS_PER_VALUE <= output.len()`
370            /// - `output[index..index + Self::MAX_UNITS_PER_VALUE]` is valid
371            ///   for writing.
372            #[inline(always)]
373            pub unsafe fn encode_unchecked(
374                value: $ty,
375                output: &mut [u8],
376                index: usize,
377            ) -> usize {
378                debug_assert!(
379                    index + Self::MAX_UNITS_PER_VALUE <= output.len()
380                );
381
382                let raw = value.to_be();
383
384                // SAFETY:
385                // The caller guarantees that the writable range is fully
386                // in-bounds. `write_unaligned` permits unaligned memory
387                // access.
388                let pointer =
389                    unsafe { output.as_mut_ptr().add(index).cast::<$ty>() };
390
391                // SAFETY:
392                // The pointer is valid for an unaligned integer store.
393                unsafe {
394                    ptr::write_unaligned(pointer, raw);
395                }
396                Self::MAX_UNITS_PER_VALUE
397            }
398        }
399
400        unsafe impl Codec for BinaryCodec<$ty, BigEndian> {
401            type Value = $ty;
402            type Unit = u8;
403            type DecodeError = Infallible;
404            type EncodeError = Infallible;
405
406            #[inline(always)]
407            fn min_units_per_value(&self) -> core::num::NonZeroUsize {
408                // SAFETY: `$ty` is a concrete primitive integer type with
409                // non-zero size.
410                unsafe {
411                    core::num::NonZeroUsize::new_unchecked(
412                        Self::MIN_UNITS_PER_VALUE,
413                    )
414                }
415            }
416
417            #[inline(always)]
418            fn max_units_per_value(&self) -> core::num::NonZeroUsize {
419                // SAFETY: `$ty` is a concrete primitive integer type with
420                // non-zero size.
421                unsafe {
422                    core::num::NonZeroUsize::new_unchecked(
423                        Self::MAX_UNITS_PER_VALUE,
424                    )
425                }
426            }
427
428            #[inline(always)]
429            unsafe fn decode_unchecked(
430                &self,
431                input: &[u8],
432                index: usize,
433            ) -> Result<($ty, core::num::NonZeroUsize), Self::DecodeError> {
434                // SAFETY: The caller upholds the `Codec::decode_unchecked`
435                // contract.
436                Ok(unsafe { Self::decode_unchecked(input, index) })
437            }
438
439            #[inline(always)]
440            unsafe fn encode_unchecked(
441                &self,
442                value: &$ty,
443                output: &mut [u8],
444                index: usize,
445            ) -> Result<usize, Self::EncodeError> {
446                // SAFETY: The caller upholds the `Codec::encode_unchecked`
447                // contract.
448                Ok(unsafe { Self::encode_unchecked(*value, output, index) })
449            }
450        }
451
452        impl BinaryCodec<$ty, LittleEndian> {
453            /// Minimum number of bytes required to encode or decode this type.
454            pub const MIN_UNITS_PER_VALUE: usize = $len;
455
456            /// Maximum number of bytes required to encode or decode this type.
457            pub const MAX_UNITS_PER_VALUE: usize = Self::MIN_UNITS_PER_VALUE;
458
459            /// Decodes a value from `input` starting at `index` without bounds
460            /// checks.
461            ///
462            /// This function is intended for hot binary codec paths where the
463            /// caller has already validated the buffer length externally.
464            ///
465            /// # Parameters
466            ///
467            /// - `input`: Source byte buffer.
468            /// - `index`: Start byte index in `input`.
469            ///
470            /// # Returns
471            ///
472            /// Returns the decoded value and the non-zero number of consumed
473            /// bytes.
474            ///
475            /// # Safety
476            ///
477            /// The caller must guarantee that:
478            ///
479            /// - `index + Self::MIN_UNITS_PER_VALUE <= input.len()`
480            /// - `input[index..index + Self::MIN_UNITS_PER_VALUE]` is valid for
481            ///   reading.
482            #[must_use]
483            #[inline(always)]
484            pub unsafe fn decode_unchecked(
485                input: &[u8],
486                index: usize,
487            ) -> ($ty, core::num::NonZeroUsize) {
488                debug_assert!(index + Self::MIN_UNITS_PER_VALUE <= input.len());
489
490                // SAFETY:
491                // The caller guarantees that the readable range is fully
492                // in-bounds. `read_unaligned` permits unaligned memory
493                // access.
494                let pointer =
495                    unsafe { input.as_ptr().add(index).cast::<$ty>() };
496
497                // SAFETY:
498                // The pointer is valid for an unaligned integer load.
499                let raw = unsafe { ptr::read_unaligned(pointer) };
500
501                (
502                    <$ty>::from_le(raw),
503                    // SAFETY: `$ty` is a concrete primitive integer type with
504                    // non-zero size.
505                    unsafe {
506                        core::num::NonZeroUsize::new_unchecked(
507                            Self::MIN_UNITS_PER_VALUE,
508                        )
509                    },
510                )
511            }
512
513            /// Encodes `value` into `output` starting at `index`
514            /// without bounds checks.
515            ///
516            /// This function is intended for hot binary codec paths where the
517            /// caller has already validated the buffer length externally.
518            ///
519            /// # Parameters
520            ///
521            /// - `value`: Value to encode.
522            /// - `output`: Destination byte buffer.
523            /// - `index`: Start byte index in `output`.
524            ///
525            /// # Safety
526            ///
527            /// The caller must guarantee that:
528            ///
529            /// - `index + Self::MAX_UNITS_PER_VALUE <= output.len()`
530            /// - `output[index..index + Self::MAX_UNITS_PER_VALUE]` is valid
531            ///   for writing.
532            #[inline(always)]
533            pub unsafe fn encode_unchecked(
534                value: $ty,
535                output: &mut [u8],
536                index: usize,
537            ) -> usize {
538                debug_assert!(
539                    index + Self::MAX_UNITS_PER_VALUE <= output.len()
540                );
541
542                let raw = value.to_le();
543
544                // SAFETY:
545                // The caller guarantees that the writable range is fully
546                // in-bounds. `write_unaligned` permits unaligned memory
547                // access.
548                let pointer =
549                    unsafe { output.as_mut_ptr().add(index).cast::<$ty>() };
550
551                // SAFETY:
552                // The pointer is valid for an unaligned integer store.
553                unsafe {
554                    ptr::write_unaligned(pointer, raw);
555                }
556                Self::MAX_UNITS_PER_VALUE
557            }
558        }
559
560        unsafe impl Codec for BinaryCodec<$ty, LittleEndian> {
561            type Value = $ty;
562            type Unit = u8;
563            type DecodeError = Infallible;
564            type EncodeError = Infallible;
565
566            #[inline(always)]
567            fn min_units_per_value(&self) -> core::num::NonZeroUsize {
568                // SAFETY: `$ty` is a concrete primitive integer type with
569                // non-zero size.
570                unsafe {
571                    core::num::NonZeroUsize::new_unchecked(
572                        Self::MIN_UNITS_PER_VALUE,
573                    )
574                }
575            }
576
577            #[inline(always)]
578            fn max_units_per_value(&self) -> core::num::NonZeroUsize {
579                // SAFETY: `$ty` is a concrete primitive integer type with
580                // non-zero size.
581                unsafe {
582                    core::num::NonZeroUsize::new_unchecked(
583                        Self::MAX_UNITS_PER_VALUE,
584                    )
585                }
586            }
587
588            #[inline(always)]
589            unsafe fn decode_unchecked(
590                &self,
591                input: &[u8],
592                index: usize,
593            ) -> Result<($ty, core::num::NonZeroUsize), Self::DecodeError> {
594                // SAFETY: The caller upholds the `Codec::decode_unchecked`
595                // contract.
596                Ok(unsafe { Self::decode_unchecked(input, index) })
597            }
598
599            #[inline(always)]
600            unsafe fn encode_unchecked(
601                &self,
602                value: &$ty,
603                output: &mut [u8],
604                index: usize,
605            ) -> Result<usize, Self::EncodeError> {
606                // SAFETY: The caller upholds the `Codec::encode_unchecked`
607                // contract.
608                Ok(unsafe { Self::encode_unchecked(*value, output, index) })
609            }
610        }
611    };
612}
613
614macro_rules! impl_float_binary_codec {
615    ($ty:ty, $bits:ty, $len:expr) => {
616        impl BinaryCodec<$ty, BigEndian> {
617            /// Minimum number of bytes required to encode or decode this type.
618            pub const MIN_UNITS_PER_VALUE: usize = $len;
619
620            /// Maximum number of bytes required to encode or decode this type.
621            pub const MAX_UNITS_PER_VALUE: usize = Self::MIN_UNITS_PER_VALUE;
622
623            /// Decodes a value from `input` starting at `index` without bounds
624            /// checks.
625            ///
626            /// This function is intended for hot binary codec paths where the
627            /// caller has already validated the buffer length externally.
628            ///
629            /// # Parameters
630            ///
631            /// - `input`: Source byte buffer.
632            /// - `index`: Start byte index in `input`.
633            ///
634            /// # Returns
635            ///
636            /// Returns the decoded floating-point value and the non-zero number
637            /// of consumed bytes.
638            ///
639            /// # Safety
640            ///
641            /// The caller must guarantee that:
642            ///
643            /// - `index + Self::MIN_UNITS_PER_VALUE <= input.len()`
644            /// - `input[index..index + Self::MIN_UNITS_PER_VALUE]` is valid for
645            ///   reading.
646            #[must_use]
647            #[inline(always)]
648            pub unsafe fn decode_unchecked(
649                input: &[u8],
650                index: usize,
651            ) -> ($ty, core::num::NonZeroUsize) {
652                debug_assert!(index + Self::MIN_UNITS_PER_VALUE <= input.len());
653
654                // SAFETY:
655                // The caller guarantees that the readable range is fully
656                // in-bounds. `read_unaligned` permits unaligned memory
657                // access.
658                let pointer =
659                    unsafe { input.as_ptr().add(index).cast::<$bits>() };
660
661                // SAFETY:
662                // The pointer is valid for an unaligned integer load.
663                let raw = unsafe { ptr::read_unaligned(pointer) };
664
665                (
666                    <$ty>::from_bits(<$bits>::from_be(raw)),
667                    // SAFETY: `$ty` is a concrete primitive floating-point
668                    // type with non-zero size.
669                    unsafe {
670                        core::num::NonZeroUsize::new_unchecked(
671                            Self::MIN_UNITS_PER_VALUE,
672                        )
673                    },
674                )
675            }
676
677            /// Encodes `value` into `output` starting at `index`
678            /// without bounds checks.
679            ///
680            /// This function is intended for hot binary codec paths where the
681            /// caller has already validated the buffer length externally.
682            ///
683            /// # Parameters
684            ///
685            /// - `value`: Floating-point value to encode.
686            /// - `output`: Destination byte buffer.
687            /// - `index`: Start byte index in `output`.
688            ///
689            /// # Safety
690            ///
691            /// The caller must guarantee that:
692            ///
693            /// - `index + Self::MAX_UNITS_PER_VALUE <= output.len()`
694            /// - `output[index..index + Self::MAX_UNITS_PER_VALUE]` is valid
695            ///   for writing.
696            #[inline(always)]
697            pub unsafe fn encode_unchecked(
698                value: $ty,
699                output: &mut [u8],
700                index: usize,
701            ) -> usize {
702                debug_assert!(
703                    index + Self::MAX_UNITS_PER_VALUE <= output.len()
704                );
705
706                let raw = value.to_bits().to_be();
707
708                // SAFETY:
709                // The caller guarantees that the writable range is fully
710                // in-bounds. `write_unaligned` permits unaligned memory
711                // access.
712                let pointer =
713                    unsafe { output.as_mut_ptr().add(index).cast::<$bits>() };
714
715                // SAFETY:
716                // The pointer is valid for an unaligned integer store.
717                unsafe {
718                    ptr::write_unaligned(pointer, raw);
719                }
720                Self::MAX_UNITS_PER_VALUE
721            }
722        }
723
724        unsafe impl Codec for BinaryCodec<$ty, BigEndian> {
725            type Value = $ty;
726            type Unit = u8;
727            type DecodeError = Infallible;
728            type EncodeError = Infallible;
729
730            #[inline(always)]
731            fn min_units_per_value(&self) -> core::num::NonZeroUsize {
732                // SAFETY: `$ty` is a concrete primitive floating-point type
733                // with non-zero size.
734                unsafe {
735                    core::num::NonZeroUsize::new_unchecked(
736                        Self::MIN_UNITS_PER_VALUE,
737                    )
738                }
739            }
740
741            #[inline(always)]
742            fn max_units_per_value(&self) -> core::num::NonZeroUsize {
743                // SAFETY: `$ty` is a concrete primitive floating-point type
744                // with non-zero size.
745                unsafe {
746                    core::num::NonZeroUsize::new_unchecked(
747                        Self::MAX_UNITS_PER_VALUE,
748                    )
749                }
750            }
751
752            #[inline(always)]
753            unsafe fn decode_unchecked(
754                &self,
755                input: &[u8],
756                index: usize,
757            ) -> Result<($ty, core::num::NonZeroUsize), Self::DecodeError> {
758                // SAFETY: The caller upholds the `Codec::decode_unchecked`
759                // contract.
760                Ok(unsafe { Self::decode_unchecked(input, index) })
761            }
762
763            #[inline(always)]
764            unsafe fn encode_unchecked(
765                &self,
766                value: &$ty,
767                output: &mut [u8],
768                index: usize,
769            ) -> Result<usize, Self::EncodeError> {
770                // SAFETY: The caller upholds the `Codec::encode_unchecked`
771                // contract.
772                Ok(unsafe { Self::encode_unchecked(*value, output, index) })
773            }
774        }
775
776        impl BinaryCodec<$ty, LittleEndian> {
777            /// Minimum number of bytes required to encode or decode this type.
778            pub const MIN_UNITS_PER_VALUE: usize = $len;
779
780            /// Maximum number of bytes required to encode or decode this type.
781            pub const MAX_UNITS_PER_VALUE: usize = Self::MIN_UNITS_PER_VALUE;
782
783            /// Decodes a value from `input` starting at `index` without bounds
784            /// checks.
785            ///
786            /// This function is intended for hot binary codec paths where the
787            /// caller has already validated the buffer length externally.
788            ///
789            /// # Parameters
790            ///
791            /// - `input`: Source byte buffer.
792            /// - `index`: Start byte index in `input`.
793            ///
794            /// # Returns
795            ///
796            /// Returns the decoded floating-point value and the non-zero number
797            /// of consumed bytes.
798            ///
799            /// # Safety
800            ///
801            /// The caller must guarantee that:
802            ///
803            /// - `index + Self::MIN_UNITS_PER_VALUE <= input.len()`
804            /// - `input[index..index + Self::MIN_UNITS_PER_VALUE]` is valid for
805            ///   reading.
806            #[must_use]
807            #[inline(always)]
808            pub unsafe fn decode_unchecked(
809                input: &[u8],
810                index: usize,
811            ) -> ($ty, core::num::NonZeroUsize) {
812                debug_assert!(index + Self::MIN_UNITS_PER_VALUE <= input.len());
813
814                // SAFETY:
815                // The caller guarantees that the readable range is fully
816                // in-bounds. `read_unaligned` permits unaligned memory
817                // access.
818                let pointer =
819                    unsafe { input.as_ptr().add(index).cast::<$bits>() };
820
821                // SAFETY:
822                // The pointer is valid for an unaligned integer load.
823                let raw = unsafe { ptr::read_unaligned(pointer) };
824
825                (
826                    <$ty>::from_bits(<$bits>::from_le(raw)),
827                    // SAFETY: `$ty` is a concrete primitive floating-point
828                    // type with non-zero size.
829                    unsafe {
830                        core::num::NonZeroUsize::new_unchecked(
831                            Self::MIN_UNITS_PER_VALUE,
832                        )
833                    },
834                )
835            }
836
837            /// Encodes `value` into `output` starting at `index`
838            /// without bounds checks.
839            ///
840            /// This function is intended for hot binary codec paths where the
841            /// caller has already validated the buffer length externally.
842            ///
843            /// # Parameters
844            ///
845            /// - `value`: Floating-point value to encode.
846            /// - `output`: Destination byte buffer.
847            /// - `index`: Start byte index in `output`.
848            ///
849            /// # Safety
850            ///
851            /// The caller must guarantee that:
852            ///
853            /// - `index + Self::MAX_UNITS_PER_VALUE <= output.len()`
854            /// - `output[index..index + Self::MAX_UNITS_PER_VALUE]` is valid
855            ///   for writing.
856            #[inline(always)]
857            pub unsafe fn encode_unchecked(
858                value: $ty,
859                output: &mut [u8],
860                index: usize,
861            ) -> usize {
862                debug_assert!(
863                    index + Self::MAX_UNITS_PER_VALUE <= output.len()
864                );
865
866                let raw = value.to_bits().to_le();
867
868                // SAFETY:
869                // The caller guarantees that the writable range is fully
870                // in-bounds. `write_unaligned` permits unaligned memory
871                // access.
872                let pointer =
873                    unsafe { output.as_mut_ptr().add(index).cast::<$bits>() };
874
875                // SAFETY:
876                // The pointer is valid for an unaligned integer store.
877                unsafe {
878                    ptr::write_unaligned(pointer, raw);
879                }
880                Self::MAX_UNITS_PER_VALUE
881            }
882        }
883
884        unsafe impl Codec for BinaryCodec<$ty, LittleEndian> {
885            type Value = $ty;
886            type Unit = u8;
887            type DecodeError = Infallible;
888            type EncodeError = Infallible;
889
890            #[inline(always)]
891            fn min_units_per_value(&self) -> core::num::NonZeroUsize {
892                // SAFETY: `$ty` is a concrete primitive floating-point type
893                // with non-zero size.
894                unsafe {
895                    core::num::NonZeroUsize::new_unchecked(
896                        Self::MIN_UNITS_PER_VALUE,
897                    )
898                }
899            }
900
901            #[inline(always)]
902            fn max_units_per_value(&self) -> core::num::NonZeroUsize {
903                // SAFETY: `$ty` is a concrete primitive floating-point type
904                // with non-zero size.
905                unsafe {
906                    core::num::NonZeroUsize::new_unchecked(
907                        Self::MAX_UNITS_PER_VALUE,
908                    )
909                }
910            }
911
912            #[inline(always)]
913            unsafe fn decode_unchecked(
914                &self,
915                input: &[u8],
916                index: usize,
917            ) -> Result<($ty, core::num::NonZeroUsize), Self::DecodeError> {
918                // SAFETY: The caller upholds the `Codec::decode_unchecked`
919                // contract.
920                Ok(unsafe { Self::decode_unchecked(input, index) })
921            }
922
923            #[inline(always)]
924            unsafe fn encode_unchecked(
925                &self,
926                value: &$ty,
927                output: &mut [u8],
928                index: usize,
929            ) -> Result<usize, Self::EncodeError> {
930                // SAFETY: The caller upholds the `Codec::encode_unchecked`
931                // contract.
932                Ok(unsafe { Self::encode_unchecked(*value, output, index) })
933            }
934        }
935    };
936}
937
938impl_integer_binary_codec!(u16, 2);
939impl_integer_binary_codec!(u32, 4);
940impl_integer_binary_codec!(u64, 8);
941impl_integer_binary_codec!(u128, 16);
942impl_integer_binary_codec!(i16, 2);
943impl_integer_binary_codec!(i32, 4);
944impl_integer_binary_codec!(i64, 8);
945impl_integer_binary_codec!(i128, 16);
946impl_float_binary_codec!(f32, u32, 4);
947impl_float_binary_codec!(f64, u64, 8);