simple_endian/
io.rs

1// top-level helper removed; modules import size_of where needed
2
3// Core-only helpers: operate on slices/Vec<u8> and don't require std IO traits.
4// Enabled under the `io-core` feature.
5
6#[cfg(feature = "io-core")]
7pub mod core_io {
8    use crate::{BigEndian, LittleEndian, SpecificEndian};
9    use core::mem::size_of;
10
11    // Canonical representation: use u128 as a universal integer representation
12    // for all supported types. This simplifies generic conversion without
13    // associated type gymnastics.
14    pub trait EndianRepr: Copy {
15        fn from_u128(v: u128) -> Self;
16        fn to_u128(self) -> u128;
17    }
18
19    impl EndianRepr for u8 {
20        fn from_u128(v: u128) -> Self {
21            v as u8
22        }
23        fn to_u128(self) -> u128 {
24            self as u128
25        }
26    }
27    impl EndianRepr for u16 {
28        fn from_u128(v: u128) -> Self {
29            v as u16
30        }
31        fn to_u128(self) -> u128 {
32            self as u128
33        }
34    }
35    impl EndianRepr for u32 {
36        fn from_u128(v: u128) -> Self {
37            v as u32
38        }
39        fn to_u128(self) -> u128 {
40            self as u128
41        }
42    }
43    impl EndianRepr for u64 {
44        fn from_u128(v: u128) -> Self {
45            v as u64
46        }
47        fn to_u128(self) -> u128 {
48            self as u128
49        }
50    }
51    impl EndianRepr for u128 {
52        fn from_u128(v: u128) -> Self {
53            v
54        }
55        fn to_u128(self) -> u128 {
56            self
57        }
58    }
59    impl EndianRepr for f32 {
60        fn from_u128(v: u128) -> Self {
61            f32::from_bits(v as u32)
62        }
63        fn to_u128(self) -> u128 {
64            self.to_bits() as u128
65        }
66    }
67    impl EndianRepr for f64 {
68        fn from_u128(v: u128) -> Self {
69            f64::from_bits(v as u64)
70        }
71        fn to_u128(self) -> u128 {
72            self.to_bits() as u128
73        }
74    }
75
76    // --- Tuple support ------------------------------------------------------
77    //
78    // Tuples are encoded as a *concatenation* of each element's bytes.
79    //
80    // This crate's core IO machinery for `BigEndian<T>` / `LittleEndian<T>` uses a
81    // u128-based intermediate representation, so for tuples we interpret that u128 as
82    // the raw bytes (big-endian or little-endian depending on the wrapper) packed
83    // into a u128.
84    //
85    // Encoding contract for a tuple `(A, B, ...)`:
86    // - `to_u128()` returns a u128 whose *big-endian byte representation* is the
87    //   concatenation of each element's big-endian bytes.
88    // - `from_u128()` reverses that process.
89    //
90    // With this convention, the existing `FromSlice for BigEndian<T>/LittleEndian<T>`
91    // implementations work for tuples too.
92
93    macro_rules! impl_endianrepr_for_tuple {
94        ( $( ($idx:tt, $T:ident) ),+ $(,)? ) => {
95            impl<$( $T ),+> EndianRepr for ( $( $T ),+ )
96            where
97                $( $T: EndianRepr + Copy ),+
98            {
99                #[allow(unused_assignments)]
100                fn from_u128(v: u128) -> Self {
101                    let bytes = v.to_be_bytes();
102                    let total = 0usize $( + size_of::<$T>() )+;
103                    if total > 16 {
104                        panic!("tuple EndianRepr total size exceeds 16 bytes");
105                    }
106                    // The compact value is right-aligned within the u128.
107                    let mut offset = 16usize - total;
108                    (
109                        $(
110                            {
111                                let n = size_of::<$T>();
112                                let chunk = &bytes[offset..offset + n];
113                                let part = match n {
114                                    1 => chunk[0] as u128,
115                                    2 => u16::from_be_bytes([chunk[0], chunk[1]]) as u128,
116                                    4 => u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]) as u128,
117                                    8 => u64::from_be_bytes([
118                                        chunk[0], chunk[1], chunk[2], chunk[3],
119                                        chunk[4], chunk[5], chunk[6], chunk[7],
120                                    ]) as u128,
121                                    16 => u128::from_be_bytes(chunk.try_into().unwrap()),
122                                    _ => panic!("unsupported size in tuple EndianRepr"),
123                                };
124                                offset += n;
125                                <$T as EndianRepr>::from_u128(part)
126                            }
127                        ),+
128                    )
129                }
130
131                #[allow(unused_assignments)]
132                fn to_u128(self) -> u128 {
133                    let mut bytes = [0u8; 16];
134                    let total = 0usize $( + size_of::<$T>() )+;
135                    if total > 16 {
136                        panic!("tuple EndianRepr total size exceeds 16 bytes");
137                    }
138                    // Right-align data within the u128 so `BigEndian<T>` reads/writes
139                    // can slice the last `size_of::<T>()` bytes.
140                    let mut offset = 16usize - total;
141                    $(
142                        {
143                            let n = size_of::<$T>();
144                            let part = self.$idx.to_u128();
145                            match n {
146                                1 => bytes[offset] = part as u8,
147                                2 => bytes[offset..offset + 2].copy_from_slice(&(part as u16).to_be_bytes()),
148                                4 => bytes[offset..offset + 4].copy_from_slice(&(part as u32).to_be_bytes()),
149                                8 => bytes[offset..offset + 8].copy_from_slice(&(part as u64).to_be_bytes()),
150                                16 => bytes[offset..offset + 16].copy_from_slice(&(part as u128).to_be_bytes()),
151                                _ => panic!("unsupported size in tuple EndianRepr"),
152                            }
153                            offset += n;
154                        }
155                    )+
156                    u128::from_be_bytes(bytes)
157                }
158            }
159        };
160    }
161
162    impl_endianrepr_for_tuple!((0, A), (1, B));
163    impl_endianrepr_for_tuple!((0, A), (1, B), (2, C));
164    impl_endianrepr_for_tuple!((0, A), (1, B), (2, C), (3, D));
165    impl_endianrepr_for_tuple!((0, A), (1, B), (2, C), (3, D), (4, E));
166    impl_endianrepr_for_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F));
167    impl_endianrepr_for_tuple!((0, A), (1, B), (2, C), (3, D), (4, E), (5, F), (6, G));
168    impl_endianrepr_for_tuple!(
169        (0, A),
170        (1, B),
171        (2, C),
172        (3, D),
173        (4, E),
174        (5, F),
175        (6, G),
176        (7, H)
177    );
178    impl_endianrepr_for_tuple!(
179        (0, A),
180        (1, B),
181        (2, C),
182        (3, D),
183        (4, E),
184        (5, F),
185        (6, G),
186        (7, H),
187        (8, I)
188    );
189    impl_endianrepr_for_tuple!(
190        (0, A),
191        (1, B),
192        (2, C),
193        (3, D),
194        (4, E),
195        (5, F),
196        (6, G),
197        (7, H),
198        (8, I),
199        (9, J)
200    );
201    impl_endianrepr_for_tuple!(
202        (0, A),
203        (1, B),
204        (2, C),
205        (3, D),
206        (4, E),
207        (5, F),
208        (6, G),
209        (7, H),
210        (8, I),
211        (9, J),
212        (10, K)
213    );
214    impl_endianrepr_for_tuple!(
215        (0, A),
216        (1, B),
217        (2, C),
218        (3, D),
219        (4, E),
220        (5, F),
221        (6, G),
222        (7, H),
223        (8, I),
224        (9, J),
225        (10, K),
226        (11, L)
227    );
228
229    // read_be_from_slice/read_le_from_slice removed: use `FromSlice` impls
230    // and the convenience `read_from_slice` function below instead.
231
232    // (private write helpers removed; use `write_to_extend` on the FromSlice impl)
233
234    /// Read raw bytes into a `BigEndian<T>`.
235    ///
236    /// Short helper; avoids `_be`/`_le` in the public API name.
237    ///
238    /// Example:
239    ///
240    /// ```ignore
241    /// use simple_endian::io::core_io;
242    /// let data: [u8; 2] = [0x12, 0x34];
243    /// let be: simple_endian::BigEndian<u16> = core_io::from_bytes(&data).unwrap();
244    /// assert_eq!(be.to_native(), 0x1234u16);
245    /// ```
246    /// Zero-allocation enum that holds either a `BigEndian<T>` or
247    /// `LittleEndian<T>` value. This replaces an earlier boxed trait-based
248    /// approach to avoid heap allocation.
249    pub enum EndianValue<T>
250    where
251        T: crate::SpecificEndian<T>,
252    {
253        Big(BigEndian<T>),
254        Little(LittleEndian<T>),
255    }
256
257    impl<T> EndianValue<T>
258    where
259        T: crate::SpecificEndian<T> + Copy,
260    {
261        /// Convert the stored endian wrapper into the host-native `T`.
262        pub fn to_native(&self) -> T {
263            match self {
264                EndianValue::Big(b) => b.to_native(),
265                EndianValue::Little(l) => l.to_native(),
266            }
267        }
268    }
269
270    /// Read raw bytes and return an `EndianValue<T>` chosen by
271    /// `T::default().endian()`.
272    pub fn from_bytes<T>(data: &[u8]) -> Result<EndianValue<T>, &'static str>
273    where
274        T: crate::SpecificEndian<T> + Default + Copy + EndianRepr,
275    {
276        let default = T::default();
277        match default.endian() {
278            crate::Endian::Big => read_from_slice::<BigEndian<T>>(data).map(EndianValue::Big),
279            crate::Endian::Little => {
280                read_from_slice::<LittleEndian<T>>(data).map(EndianValue::Little)
281            }
282        }
283    }
284
285    /// Convenience: read bytes and return the host-native `T` directly.
286    pub fn from_bytes_to_native<T>(data: &[u8]) -> Result<T, &'static str>
287    where
288        T: crate::SpecificEndian<T> + Default + Copy + EndianRepr,
289    {
290        Ok(from_bytes::<T>(data)?.to_native())
291    }
292
293    /// Trait describing types that can be read from / written to a byte slice
294    /// representation. Implemented for `BigEndian<T>` and `LittleEndian<T>`.
295    pub trait FromSlice: Sized {
296        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str>;
297        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str>;
298    }
299
300    /// Convenience generic helpers.
301    pub fn read_from_slice<E: FromSlice>(data: &[u8]) -> Result<E, &'static str> {
302        E::read_from_slice(data)
303    }
304
305    pub fn write_to_extend<E: FromSlice>(
306        v: &E,
307        out: &mut impl Extend<u8>,
308    ) -> Result<(), &'static str> {
309        v.write_to_extend(out)
310    }
311
312    impl<T> FromSlice for BigEndian<T>
313    where
314        T: SpecificEndian<T> + Copy + EndianRepr,
315    {
316        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
317            if data.len() < size_of::<T>() {
318                return Err("insufficient data");
319            }
320            let buf = &data[..size_of::<T>()];
321            // For composite types (like tuples), `EndianRepr` treats the u128 as a
322            // compact byte container and expects the bytes to be right-aligned.
323            let mut a = [0u8; 16];
324            a[16 - buf.len()..].copy_from_slice(buf);
325            let v = T::from_u128(u128::from_be_bytes(a));
326            Ok(BigEndian::from(v))
327        }
328
329        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
330            // Write bytes in big-endian order for the logical value.
331            //
332            // For tuples, `to_u128()` acts as a byte container (right-aligned).
333            let repr = self.to_native().to_u128();
334            let bytes = repr.to_be_bytes();
335            let n = size_of::<T>();
336            if !(n == 1 || n == 2 || n == 4 || n == 8 || n == 16) {
337                return Err("unsupported size");
338            }
339            // Emit the last `n` bytes (compact encoding).
340            out.extend(core::iter::IntoIterator::into_iter(
341                bytes[16 - n..].iter().copied(),
342            ));
343            Ok(())
344        }
345    }
346
347    impl<T> FromSlice for LittleEndian<T>
348    where
349        T: SpecificEndian<T> + Copy + EndianRepr,
350    {
351        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
352            if data.len() < size_of::<T>() {
353                return Err("insufficient data");
354            }
355            let buf = &data[..size_of::<T>()];
356            // Composite types treat the u128 as a compact byte container.
357            let mut a = [0u8; 16];
358            a[..buf.len()].copy_from_slice(buf);
359            let v = T::from_u128(u128::from_le_bytes(a));
360            Ok(LittleEndian::from(v))
361        }
362
363        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
364            let repr = self.to_native().to_u128();
365            let bytes = repr.to_le_bytes();
366            let n = size_of::<T>();
367            if !(n == 1 || n == 2 || n == 4 || n == 8 || n == 16) {
368                return Err("unsupported size");
369            }
370            out.extend(core::iter::IntoIterator::into_iter(
371                bytes[..n].iter().copied(),
372            ));
373            Ok(())
374        }
375    }
376
377    // --- Fixed UTF helpers (feature-gated) ---
378
379    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
380    impl<const N: usize> FromSlice for crate::FixedUtf16BeCodeUnits<N> {
381        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
382            if data.len() < 2 * N {
383                return Err("insufficient data");
384            }
385
386            let mut out = [crate::BigEndian::<u16>::from_bits(0); N];
387            let mut i = 0;
388            while i < N {
389                let base = 2 * i;
390                // Parse standard UTF-16BE wire bytes into a native scalar.
391                let native = u16::from_be_bytes([data[base], data[base + 1]]);
392                // Store as correctly endian-tagged bits.
393                #[cfg(target_endian = "big")]
394                {
395                    out[i] = crate::BigEndian::<u16>::from_bits(native);
396                }
397                #[cfg(target_endian = "little")]
398                {
399                    out[i] = crate::BigEndian::<u16>::from_bits(native.swap_bytes());
400                }
401                i += 1;
402            }
403
404            Ok(crate::FixedUtf16BeCodeUnits::from(out))
405        }
406
407        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
408            for cu in self.as_units() {
409                // Serialize the *native scalar* as UTF-16BE on the wire.
410                out.extend(core::iter::IntoIterator::into_iter(
411                    cu.to_native().to_be_bytes(),
412                ));
413            }
414            Ok(())
415        }
416    }
417
418    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
419    impl<const N: usize> FromSlice for crate::FixedUtf16LeCodeUnits<N> {
420        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
421            if data.len() < 2 * N {
422                return Err("insufficient data");
423            }
424
425            let mut out = [crate::LittleEndian::<u16>::from_bits(0); N];
426            let mut i = 0;
427            while i < N {
428                let base = 2 * i;
429                // Parse standard UTF-16LE wire bytes into a native scalar.
430                let native = u16::from_le_bytes([data[base], data[base + 1]]);
431                // Store as correctly endian-tagged bits.
432                #[cfg(target_endian = "little")]
433                {
434                    out[i] = crate::LittleEndian::<u16>::from_bits(native);
435                }
436                #[cfg(target_endian = "big")]
437                {
438                    out[i] = crate::LittleEndian::<u16>::from_bits(native.swap_bytes());
439                }
440                i += 1;
441            }
442
443            Ok(crate::FixedUtf16LeCodeUnits::from(out))
444        }
445
446        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
447            for cu in self.as_units() {
448                // Serialize the *native scalar* as UTF-16LE on the wire.
449                out.extend(core::iter::IntoIterator::into_iter(
450                    cu.to_native().to_le_bytes(),
451                ));
452            }
453            Ok(())
454        }
455    }
456
457    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
458    impl<const N: usize> FromSlice for crate::FixedUtf32BeCodeUnits<N> {
459        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
460            if data.len() < 4 * N {
461                return Err("insufficient data");
462            }
463
464            let mut out = [crate::BigEndian::<u32>::from_bits(0); N];
465            let mut i = 0;
466            while i < N {
467                let base = 4 * i;
468                // Parse standard UTF-32BE wire bytes into a native scalar.
469                let native = u32::from_be_bytes([
470                    data[base],
471                    data[base + 1],
472                    data[base + 2],
473                    data[base + 3],
474                ]);
475                // Store as correctly endian-tagged bits.
476                #[cfg(target_endian = "big")]
477                {
478                    out[i] = crate::BigEndian::<u32>::from_bits(native);
479                }
480                #[cfg(target_endian = "little")]
481                {
482                    out[i] = crate::BigEndian::<u32>::from_bits(native.swap_bytes());
483                }
484                i += 1;
485            }
486
487            Ok(crate::FixedUtf32BeCodeUnits::from(out))
488        }
489
490        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
491            for cu in self.as_units() {
492                // Serialize the *native scalar* as UTF-32BE on the wire.
493                out.extend(core::iter::IntoIterator::into_iter(
494                    cu.to_native().to_be_bytes(),
495                ));
496            }
497            Ok(())
498        }
499    }
500
501    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
502    impl<const N: usize> FromSlice for crate::FixedUtf32LeCodeUnits<N> {
503        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
504            if data.len() < 4 * N {
505                return Err("insufficient data");
506            }
507
508            let mut out = [crate::LittleEndian::<u32>::from_bits(0); N];
509            let mut i = 0;
510            while i < N {
511                let base = 4 * i;
512                // Parse standard UTF-32LE wire bytes into a native scalar.
513                let native = u32::from_le_bytes([
514                    data[base],
515                    data[base + 1],
516                    data[base + 2],
517                    data[base + 3],
518                ]);
519                // Store as correctly endian-tagged bits.
520                #[cfg(target_endian = "little")]
521                {
522                    out[i] = crate::LittleEndian::<u32>::from_bits(native);
523                }
524                #[cfg(target_endian = "big")]
525                {
526                    out[i] = crate::LittleEndian::<u32>::from_bits(native.swap_bytes());
527                }
528                i += 1;
529            }
530
531            Ok(crate::FixedUtf32LeCodeUnits::from(out))
532        }
533
534        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
535            for cu in self.as_units() {
536                // Serialize the *native scalar* as UTF-32LE on the wire.
537                out.extend(core::iter::IntoIterator::into_iter(
538                    cu.to_native().to_le_bytes(),
539                ));
540            }
541            Ok(())
542        }
543    }
544
545    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
546    impl<const N: usize> FromSlice for crate::FixedUtf16BeNullPadded<N> {
547        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
548            Ok(crate::FixedUtf16BeNullPadded::from(
549                <crate::FixedUtf16BeCodeUnits<N> as FromSlice>::read_from_slice(data)?,
550            ))
551        }
552
553        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
554            <crate::FixedUtf16BeCodeUnits<N> as FromSlice>::write_to_extend(&self.0, out)
555        }
556    }
557
558    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
559    impl<const N: usize> FromSlice for crate::FixedUtf16BeSpacePadded<N> {
560        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
561            Ok(crate::FixedUtf16BeSpacePadded::from(
562                <crate::FixedUtf16BeCodeUnits<N> as FromSlice>::read_from_slice(data)?,
563            ))
564        }
565
566        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
567            <crate::FixedUtf16BeCodeUnits<N> as FromSlice>::write_to_extend(&self.0, out)
568        }
569    }
570
571    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
572    impl<const N: usize> FromSlice for crate::FixedUtf16LeNullPadded<N> {
573        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
574            Ok(crate::FixedUtf16LeNullPadded::from(
575                <crate::FixedUtf16LeCodeUnits<N> as FromSlice>::read_from_slice(data)?,
576            ))
577        }
578
579        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
580            <crate::FixedUtf16LeCodeUnits<N> as FromSlice>::write_to_extend(&self.0, out)
581        }
582    }
583
584    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
585    impl<const N: usize> FromSlice for crate::FixedUtf16LeSpacePadded<N> {
586        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
587            Ok(crate::FixedUtf16LeSpacePadded::from(
588                <crate::FixedUtf16LeCodeUnits<N> as FromSlice>::read_from_slice(data)?,
589            ))
590        }
591
592        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
593            <crate::FixedUtf16LeCodeUnits<N> as FromSlice>::write_to_extend(&self.0, out)
594        }
595    }
596
597    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
598    impl<const N: usize> FromSlice for crate::FixedUtf32BeNullPadded<N> {
599        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
600            Ok(crate::FixedUtf32BeNullPadded::from(
601                <crate::FixedUtf32BeCodeUnits<N> as FromSlice>::read_from_slice(data)?,
602            ))
603        }
604
605        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
606            <crate::FixedUtf32BeCodeUnits<N> as FromSlice>::write_to_extend(&self.0, out)
607        }
608    }
609
610    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
611    impl<const N: usize> FromSlice for crate::FixedUtf32BeSpacePadded<N> {
612        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
613            Ok(crate::FixedUtf32BeSpacePadded::from(
614                <crate::FixedUtf32BeCodeUnits<N> as FromSlice>::read_from_slice(data)?,
615            ))
616        }
617
618        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
619            <crate::FixedUtf32BeCodeUnits<N> as FromSlice>::write_to_extend(&self.0, out)
620        }
621    }
622
623    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
624    impl<const N: usize> FromSlice for crate::FixedUtf32LeNullPadded<N> {
625        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
626            Ok(crate::FixedUtf32LeNullPadded::from(
627                <crate::FixedUtf32LeCodeUnits<N> as FromSlice>::read_from_slice(data)?,
628            ))
629        }
630
631        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
632            <crate::FixedUtf32LeCodeUnits<N> as FromSlice>::write_to_extend(&self.0, out)
633        }
634    }
635
636    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
637    impl<const N: usize> FromSlice for crate::FixedUtf32LeSpacePadded<N> {
638        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
639            Ok(crate::FixedUtf32LeSpacePadded::from(
640                <crate::FixedUtf32LeCodeUnits<N> as FromSlice>::read_from_slice(data)?,
641            ))
642        }
643
644        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
645            <crate::FixedUtf32LeCodeUnits<N> as FromSlice>::write_to_extend(&self.0, out)
646        }
647    }
648
649    #[cfg(all(feature = "text_fixed", feature = "text_utf8"))]
650    impl<const N: usize> FromSlice for crate::FixedUtf8NullPadded<N> {
651        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
652            if data.len() < N {
653                return Err("insufficient data");
654            }
655            let mut out = [0u8; N];
656            out.copy_from_slice(&data[..N]);
657            Ok(crate::FixedUtf8NullPadded::from(
658                crate::FixedUtf8Bytes::from(out),
659            ))
660        }
661
662        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
663            out.extend(self.0.as_bytes().iter().copied());
664            Ok(())
665        }
666    }
667
668    #[cfg(all(feature = "text_fixed", feature = "text_utf8"))]
669    impl<const N: usize> FromSlice for crate::FixedUtf8SpacePadded<N> {
670        fn read_from_slice(data: &[u8]) -> Result<Self, &'static str> {
671            if data.len() < N {
672                return Err("insufficient data");
673            }
674            let mut out = [0u8; N];
675            out.copy_from_slice(&data[..N]);
676            Ok(crate::FixedUtf8SpacePadded::from(
677                crate::FixedUtf8Bytes::from(out),
678            ))
679        }
680
681        fn write_to_extend(&self, out: &mut impl Extend<u8>) -> Result<(), &'static str> {
682            out.extend(self.0.as_bytes().iter().copied());
683            Ok(())
684        }
685    }
686}
687
688// Std-backed Read/Write wrappers: enabled under `io-std` which depends on `io-core`.
689#[cfg(feature = "io-std")]
690pub mod std_io {
691    use super::core_io;
692    use crate::{BigEndian, LittleEndian};
693    use core::any::TypeId;
694    use core::mem::size_of;
695    use std::io::{self, Read, Write};
696
697    fn read_be<R, T>(reader: &mut R) -> io::Result<BigEndian<T>>
698    where
699        R: Read + ?Sized,
700        T: crate::SpecificEndian<T> + Default + Copy + core_io::EndianRepr + 'static,
701    {
702        // Fast paths for common primitives to avoid heap allocation.
703        if TypeId::of::<T>() == TypeId::of::<u16>() {
704            let mut buf = [0u8; 2];
705            reader.read_exact(&mut buf)?;
706            let v = u16::from_be_bytes(buf);
707            // SAFETY: We just proved T == u16.
708            let v: T = unsafe { core::mem::transmute_copy(&v) };
709            return Ok(BigEndian::from(v));
710        }
711        if TypeId::of::<T>() == TypeId::of::<u32>() {
712            let mut buf = [0u8; 4];
713            reader.read_exact(&mut buf)?;
714            let v = u32::from_be_bytes(buf);
715            let v: T = unsafe { core::mem::transmute_copy(&v) };
716            return Ok(BigEndian::from(v));
717        }
718        if TypeId::of::<T>() == TypeId::of::<u64>() {
719            let mut buf = [0u8; 8];
720            reader.read_exact(&mut buf)?;
721            let v = u64::from_be_bytes(buf);
722            let v: T = unsafe { core::mem::transmute_copy(&v) };
723            return Ok(BigEndian::from(v));
724        }
725
726        let mut buf = vec![0u8; size_of::<T>()];
727        reader.read_exact(&mut buf)?;
728        core_io::read_from_slice::<BigEndian<T>>(&buf)
729            .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
730    }
731
732    fn read_le<R, T>(reader: &mut R) -> io::Result<LittleEndian<T>>
733    where
734        R: Read + ?Sized,
735        T: crate::SpecificEndian<T> + Default + Copy + core_io::EndianRepr + 'static,
736    {
737        if TypeId::of::<T>() == TypeId::of::<u16>() {
738            let mut buf = [0u8; 2];
739            reader.read_exact(&mut buf)?;
740            let v = u16::from_le_bytes(buf);
741            let v: T = unsafe { core::mem::transmute_copy(&v) };
742            return Ok(LittleEndian::from(v));
743        }
744        if TypeId::of::<T>() == TypeId::of::<u32>() {
745            let mut buf = [0u8; 4];
746            reader.read_exact(&mut buf)?;
747            let v = u32::from_le_bytes(buf);
748            let v: T = unsafe { core::mem::transmute_copy(&v) };
749            return Ok(LittleEndian::from(v));
750        }
751        if TypeId::of::<T>() == TypeId::of::<u64>() {
752            let mut buf = [0u8; 8];
753            reader.read_exact(&mut buf)?;
754            let v = u64::from_le_bytes(buf);
755            let v: T = unsafe { core::mem::transmute_copy(&v) };
756            return Ok(LittleEndian::from(v));
757        }
758
759        let mut buf = vec![0u8; size_of::<T>()];
760        reader.read_exact(&mut buf)?;
761        core_io::read_from_slice::<LittleEndian<T>>(&buf)
762            .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
763    }
764
765    fn write_be<W, T>(writer: &mut W, v: &BigEndian<T>) -> io::Result<()>
766    where
767        W: Write + ?Sized,
768        T: crate::SpecificEndian<T> + Copy + core_io::EndianRepr + 'static,
769    {
770        if TypeId::of::<T>() == TypeId::of::<u16>() {
771            let n: u16 = unsafe { core::mem::transmute_copy(&v.to_native()) };
772            return writer.write_all(&n.to_be_bytes());
773        }
774        if TypeId::of::<T>() == TypeId::of::<u32>() {
775            let n: u32 = unsafe { core::mem::transmute_copy(&v.to_native()) };
776            return writer.write_all(&n.to_be_bytes());
777        }
778        if TypeId::of::<T>() == TypeId::of::<u64>() {
779            let n: u64 = unsafe { core::mem::transmute_copy(&v.to_native()) };
780            return writer.write_all(&n.to_be_bytes());
781        }
782
783        let mut out = Vec::new();
784        core_io::write_to_extend(v, &mut out)
785            .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
786        writer.write_all(&out)
787    }
788
789    fn write_le<W, T>(writer: &mut W, v: &LittleEndian<T>) -> io::Result<()>
790    where
791        W: Write + ?Sized,
792        T: crate::SpecificEndian<T> + Copy + core_io::EndianRepr + 'static,
793    {
794        if TypeId::of::<T>() == TypeId::of::<u16>() {
795            let n: u16 = unsafe { core::mem::transmute_copy(&v.to_native()) };
796            return writer.write_all(&n.to_le_bytes());
797        }
798        if TypeId::of::<T>() == TypeId::of::<u32>() {
799            let n: u32 = unsafe { core::mem::transmute_copy(&v.to_native()) };
800            return writer.write_all(&n.to_le_bytes());
801        }
802        if TypeId::of::<T>() == TypeId::of::<u64>() {
803            let n: u64 = unsafe { core::mem::transmute_copy(&v.to_native()) };
804            return writer.write_all(&n.to_le_bytes());
805        }
806
807        let mut out = Vec::new();
808        core_io::write_to_extend(v, &mut out)
809            .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
810        writer.write_all(&out)
811    }
812
813    pub trait EndianRead: Sized {
814        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self>;
815    }
816
817    pub trait EndianWrite {
818        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()>;
819    }
820
821    impl<T> EndianRead for BigEndian<T>
822    where
823        T: crate::SpecificEndian<T> + Default + Copy + core_io::EndianRepr + 'static,
824    {
825        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
826            read_be::<R, T>(reader)
827        }
828    }
829
830    impl<T> EndianRead for LittleEndian<T>
831    where
832        T: crate::SpecificEndian<T> + Default + Copy + core_io::EndianRepr + 'static,
833    {
834        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
835            read_le::<R, T>(reader)
836        }
837    }
838
839    impl<T> EndianWrite for BigEndian<T>
840    where
841        T: crate::SpecificEndian<T> + Copy + core_io::EndianRepr + 'static,
842    {
843        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
844            write_be::<W, T>(writer, self)
845        }
846    }
847
848    impl<T> EndianWrite for LittleEndian<T>
849    where
850        T: crate::SpecificEndian<T> + Copy + core_io::EndianRepr + 'static,
851    {
852        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
853            write_le::<W, T>(writer, self)
854        }
855    }
856
857    // Tuple support lives at the `SpecificEndian` layer.
858    //
859    // Note: We intentionally do *not* provide specialized std-IO impls for
860    // `BigEndian<(..)>` / `LittleEndian<(..)>` here because the blanket impls
861    // above (`impl<T> EndianRead/EndianWrite for BigEndian<T>`) already cover
862    // tuples once they implement `core_io::EndianRepr`. Adding explicit tuple
863    // impls causes trait coherence conflicts (E0119).
864
865    impl<const N: usize> EndianRead for [u8; N] {
866        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
867            let mut buf = [0u8; N];
868            reader.read_exact(&mut buf)?;
869            Ok(buf)
870        }
871    }
872
873    impl<const N: usize> EndianWrite for [u8; N] {
874        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
875            writer.write_all(self)
876        }
877    }
878
879    impl<E, const N: usize> EndianRead for [E; N]
880    where
881        E: EndianRead + Copy,
882    {
883        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
884            let mut out = [E::read_from(reader)?; N];
885            for i in 1..N {
886                out[i] = E::read_from(reader)?;
887            }
888            Ok(out)
889        }
890    }
891
892    impl<E, const N: usize> EndianWrite for [E; N]
893    where
894        E: EndianWrite,
895    {
896        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
897            for v in self {
898                v.write_to(writer)?;
899            }
900            Ok(())
901        }
902    }
903
904    /// Read an endian-aware value of type `E` from a reader.
905    ///
906    /// This helper works with both sized readers (e.g. `std::io::Cursor<Vec<u8>>`) and
907    /// unsized trait objects like `&mut dyn std::io::Read`.
908    ///
909    /// In particular, this is designed to support the common “extension trait” pattern:
910    ///
911    /// ```rust
912    /// use std::io::{self, Read};
913    ///
914    /// pub trait ReadBytesExt: Read {
915    ///     fn read_u32_be(&mut self) -> io::Result<u32>;
916    /// }
917    ///
918    /// impl<R: Read + ?Sized> ReadBytesExt for R {
919    ///     fn read_u32_be(&mut self) -> io::Result<u32> {
920    ///         let be: simple_endian::BigEndian<u32> = simple_endian::read_specific(self)?;
921    ///         Ok(be.to_native())
922    ///     }
923    /// }
924    ///
925    /// fn read_from_dyn(r: &mut dyn Read) -> io::Result<u32> {
926    ///     r.read_u32_be()
927    /// }
928    /// ```
929    pub fn read_specific<R, E>(reader: &mut R) -> io::Result<E>
930    where
931        R: Read + ?Sized,
932        E: EndianRead,
933    {
934        E::read_from(reader)
935    }
936
937    /// Write an endian-aware value of type `E` to a writer.
938    ///
939    /// Like [`read_specific`], this supports both sized writers and `&mut dyn std::io::Write`.
940    pub fn write_specific<W, E>(writer: &mut W, v: &E) -> io::Result<()>
941    where
942        W: Write + ?Sized,
943        E: EndianWrite,
944    {
945        v.write_to(writer)
946    }
947
948    /// Dyn-friendly adapter for `read_specific`.
949    ///
950    /// This is purely ergonomic: it lets consumers call the helper from
951    /// `&mut dyn Read` contexts without having to name (or be generic over) the
952    /// reader type.
953    pub fn read_specific_dyn<E>(reader: &mut dyn Read) -> io::Result<E>
954    where
955        E: EndianRead,
956    {
957        read_specific::<dyn Read, E>(reader)
958    }
959
960    /// Dyn-friendly adapter for `write_specific`.
961    ///
962    /// This is purely ergonomic: it lets consumers call the helper from
963    /// `&mut dyn Write` contexts without having to name (or be generic over) the
964    /// writer type.
965    pub fn write_specific_dyn<E>(writer: &mut dyn Write, v: &E) -> io::Result<()>
966    where
967        E: EndianWrite,
968    {
969        write_specific::<dyn Write, E>(writer, v)
970    }
971
972    /// Read a value in its *wire* representation and convert it into a native type.
973    ///
974    /// This is the recommended ergonomic pattern for `#[derive(Endianize)]` types:
975    ///
976    /// * the generated `*Wire` type is the IO/layout type (it implements [`EndianRead`])
977    /// * your “real” type is the native type used throughout your program
978    ///
979    /// Conceptually:
980    ///
981    /// 1. read `W` from the stream (endian-correct)
982    /// 2. convert into `T` using `From<W>`
983    ///
984    /// Under the hood, this is equivalent to:
985    ///
986    /// ```ignore
987    /// let wire: W = read_specific(reader)?;
988    /// let native: T = wire.into();
989    /// ```
990    ///
991    /// ### Example
992    ///
993    /// ```ignore
994    /// use simple_endian::Endianize;
995    /// use simple_endian::read_native;
996    ///
997    /// #[derive(Endianize)]
998    /// #[endian(le)]
999    /// #[repr(C)]
1000    /// struct Header {
1001    ///     magic: u32,
1002    ///     version: u16,
1003    /// }
1004    ///
1005    /// // Reads `HeaderWire` and converts to `Header`.
1006    /// let header: Header = read_native::<_, HeaderWire, Header>(&mut reader)?;
1007    /// # Ok::<(), std::io::Error>(())
1008    /// ```
1009    ///
1010    /// ### Notes
1011    ///
1012    /// * This composes naturally for nested `Endianize` types (a `PacketWire` contains a `HeaderWire`).
1013    /// * For enums, the wire representation is `tag + union payload`, so you typically want to keep
1014    ///   trait derives on the native enum and only convert at the boundary.
1015    pub fn read_native<R, W, T>(reader: &mut R) -> io::Result<T>
1016    where
1017        R: Read + ?Sized,
1018        W: EndianRead,
1019        T: From<W>,
1020    {
1021        let wire: W = read_specific(reader)?;
1022        Ok(wire.into())
1023    }
1024
1025    /// Like [`read_native`], but uses `TryFrom<W>` for fallible conversion.
1026    ///
1027    /// Conversion errors are mapped to `io::ErrorKind::InvalidData`.
1028    pub fn try_read_native<R, W, T>(reader: &mut R) -> io::Result<T>
1029    where
1030        R: Read + ?Sized,
1031        W: EndianRead,
1032        T: TryFrom<W>,
1033        <T as TryFrom<W>>::Error: core::fmt::Display,
1034    {
1035        let wire: W = read_specific(reader)?;
1036        T::try_from(wire).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e.to_string()))
1037    }
1038
1039    /// Convert a native value into its *wire* representation and write it.
1040    ///
1041    /// This is the “mirror” of [`read_native`]: convert first, then write.
1042    ///
1043    /// Under the hood this is equivalent to:
1044    ///
1045    /// ```ignore
1046    /// let wire: W = value.into();
1047    /// write_specific(writer, &wire)
1048    /// ```
1049    pub fn write_native<Wrt, W, T>(writer: &mut Wrt, v: T) -> io::Result<()>
1050    where
1051        Wrt: Write + ?Sized,
1052        W: EndianWrite,
1053        W: From<T>,
1054    {
1055        let wire: W = v.into();
1056        write_specific(writer, &wire)
1057    }
1058
1059    /// Convenience wrapper over [`write_native`] when you only have a reference.
1060    ///
1061    /// This clones the value and forwards to [`write_native`].
1062    pub fn write_native_ref<Wrt, W, T>(writer: &mut Wrt, v: &T) -> io::Result<()>
1063    where
1064        Wrt: Write + ?Sized,
1065        W: EndianWrite,
1066        T: Clone,
1067        W: From<T>,
1068    {
1069        write_native::<Wrt, W, T>(writer, v.clone())
1070    }
1071
1072    /// Like [`write_native`], but uses `TryFrom<T>` for fallible conversion.
1073    ///
1074    /// Conversion errors are mapped to `io::ErrorKind::InvalidInput`.
1075    pub fn try_write_native<Wrt, W, T>(writer: &mut Wrt, v: T) -> io::Result<()>
1076    where
1077        Wrt: Write + ?Sized,
1078        W: EndianWrite,
1079        W: TryFrom<T>,
1080        <W as TryFrom<T>>::Error: core::fmt::Display,
1081    {
1082        let wire: W = W::try_from(v)
1083            .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e.to_string()))?;
1084        write_specific(writer, &wire)
1085    }
1086
1087    /// Convenience wrapper over [`try_write_native`] when you only have a reference.
1088    ///
1089    /// This clones the value and forwards to [`try_write_native`].
1090    pub fn try_write_native_ref<Wrt, W, T>(writer: &mut Wrt, v: &T) -> io::Result<()>
1091    where
1092        Wrt: Write + ?Sized,
1093        W: EndianWrite,
1094        T: Clone,
1095        W: TryFrom<T>,
1096        <W as TryFrom<T>>::Error: core::fmt::Display,
1097    {
1098        try_write_native::<Wrt, W, T>(writer, v.clone())
1099    }
1100
1101    // --- Fixed UTF helpers (feature-gated) ---
1102
1103    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
1104    impl<const N: usize> EndianRead for crate::FixedUtf16BeCodeUnits<N> {
1105        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1106            let mut buf = vec![0u8; 2 * N];
1107            reader.read_exact(&mut buf)?;
1108            core_io::read_from_slice::<Self>(&buf)
1109                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1110        }
1111    }
1112
1113    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
1114    impl<const N: usize> EndianRead for crate::FixedUtf16BeNullPadded<N> {
1115        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1116            let mut buf = vec![0u8; 2 * N];
1117            reader.read_exact(&mut buf)?;
1118            core_io::read_from_slice::<Self>(&buf)
1119                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1120        }
1121    }
1122
1123    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
1124    impl<const N: usize> EndianWrite for crate::FixedUtf16BeNullPadded<N> {
1125        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1126            let mut out = Vec::new();
1127            core_io::write_to_extend(self, &mut out)
1128                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1129            writer.write_all(&out)
1130        }
1131    }
1132
1133    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
1134    impl<const N: usize> EndianRead for crate::FixedUtf16BeSpacePadded<N> {
1135        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1136            let mut buf = vec![0u8; 2 * N];
1137            reader.read_exact(&mut buf)?;
1138            core_io::read_from_slice::<Self>(&buf)
1139                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1140        }
1141    }
1142
1143    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
1144    impl<const N: usize> EndianWrite for crate::FixedUtf16BeSpacePadded<N> {
1145        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1146            let mut out = Vec::new();
1147            core_io::write_to_extend(self, &mut out)
1148                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1149            writer.write_all(&out)
1150        }
1151    }
1152
1153    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
1154    impl<const N: usize> EndianWrite for crate::FixedUtf16BeCodeUnits<N> {
1155        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1156            let mut out = Vec::new();
1157            core_io::write_to_extend(self, &mut out)
1158                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1159            writer.write_all(&out)
1160        }
1161    }
1162
1163    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
1164    impl<const N: usize> EndianRead for crate::FixedUtf16LeCodeUnits<N> {
1165        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1166            let mut buf = vec![0u8; 2 * N];
1167            reader.read_exact(&mut buf)?;
1168            core_io::read_from_slice::<Self>(&buf)
1169                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1170        }
1171    }
1172
1173    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
1174    impl<const N: usize> EndianRead for crate::FixedUtf16LeNullPadded<N> {
1175        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1176            let mut buf = vec![0u8; 2 * N];
1177            reader.read_exact(&mut buf)?;
1178            core_io::read_from_slice::<Self>(&buf)
1179                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1180        }
1181    }
1182
1183    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
1184    impl<const N: usize> EndianWrite for crate::FixedUtf16LeNullPadded<N> {
1185        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1186            let mut out = Vec::new();
1187            core_io::write_to_extend(self, &mut out)
1188                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1189            writer.write_all(&out)
1190        }
1191    }
1192
1193    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
1194    impl<const N: usize> EndianRead for crate::FixedUtf16LeSpacePadded<N> {
1195        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1196            let mut buf = vec![0u8; 2 * N];
1197            reader.read_exact(&mut buf)?;
1198            core_io::read_from_slice::<Self>(&buf)
1199                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1200        }
1201    }
1202
1203    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
1204    impl<const N: usize> EndianWrite for crate::FixedUtf16LeSpacePadded<N> {
1205        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1206            let mut out = Vec::new();
1207            core_io::write_to_extend(self, &mut out)
1208                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1209            writer.write_all(&out)
1210        }
1211    }
1212
1213    #[cfg(all(feature = "text_fixed", feature = "text_utf16"))]
1214    impl<const N: usize> EndianWrite for crate::FixedUtf16LeCodeUnits<N> {
1215        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1216            let mut out = Vec::new();
1217            core_io::write_to_extend(self, &mut out)
1218                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1219            writer.write_all(&out)
1220        }
1221    }
1222
1223    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
1224    impl<const N: usize> EndianRead for crate::FixedUtf32BeCodeUnits<N> {
1225        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1226            let mut buf = vec![0u8; 4 * N];
1227            reader.read_exact(&mut buf)?;
1228            core_io::read_from_slice::<Self>(&buf)
1229                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1230        }
1231    }
1232
1233    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
1234    impl<const N: usize> EndianRead for crate::FixedUtf32BeNullPadded<N> {
1235        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1236            let mut buf = vec![0u8; 4 * N];
1237            reader.read_exact(&mut buf)?;
1238            core_io::read_from_slice::<Self>(&buf)
1239                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1240        }
1241    }
1242
1243    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
1244    impl<const N: usize> EndianWrite for crate::FixedUtf32BeNullPadded<N> {
1245        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1246            let mut out = Vec::new();
1247            core_io::write_to_extend(self, &mut out)
1248                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1249            writer.write_all(&out)
1250        }
1251    }
1252
1253    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
1254    impl<const N: usize> EndianRead for crate::FixedUtf32BeSpacePadded<N> {
1255        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1256            let mut buf = vec![0u8; 4 * N];
1257            reader.read_exact(&mut buf)?;
1258            core_io::read_from_slice::<Self>(&buf)
1259                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1260        }
1261    }
1262
1263    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
1264    impl<const N: usize> EndianWrite for crate::FixedUtf32BeSpacePadded<N> {
1265        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1266            let mut out = Vec::new();
1267            core_io::write_to_extend(self, &mut out)
1268                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1269            writer.write_all(&out)
1270        }
1271    }
1272
1273    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
1274    impl<const N: usize> EndianWrite for crate::FixedUtf32BeCodeUnits<N> {
1275        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1276            let mut out = Vec::new();
1277            core_io::write_to_extend(self, &mut out)
1278                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1279            writer.write_all(&out)
1280        }
1281    }
1282
1283    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
1284    impl<const N: usize> EndianRead for crate::FixedUtf32LeCodeUnits<N> {
1285        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1286            let mut buf = vec![0u8; 4 * N];
1287            reader.read_exact(&mut buf)?;
1288            core_io::read_from_slice::<Self>(&buf)
1289                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1290        }
1291    }
1292
1293    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
1294    impl<const N: usize> EndianRead for crate::FixedUtf32LeNullPadded<N> {
1295        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1296            let mut buf = vec![0u8; 4 * N];
1297            reader.read_exact(&mut buf)?;
1298            core_io::read_from_slice::<Self>(&buf)
1299                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1300        }
1301    }
1302
1303    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
1304    impl<const N: usize> EndianWrite for crate::FixedUtf32LeNullPadded<N> {
1305        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1306            let mut out = Vec::new();
1307            core_io::write_to_extend(self, &mut out)
1308                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1309            writer.write_all(&out)
1310        }
1311    }
1312
1313    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
1314    impl<const N: usize> EndianRead for crate::FixedUtf32LeSpacePadded<N> {
1315        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1316            let mut buf = vec![0u8; 4 * N];
1317            reader.read_exact(&mut buf)?;
1318            core_io::read_from_slice::<Self>(&buf)
1319                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1320        }
1321    }
1322
1323    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
1324    impl<const N: usize> EndianWrite for crate::FixedUtf32LeSpacePadded<N> {
1325        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1326            let mut out = Vec::new();
1327            core_io::write_to_extend(self, &mut out)
1328                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1329            writer.write_all(&out)
1330        }
1331    }
1332
1333    #[cfg(all(feature = "text_fixed", feature = "text_utf32"))]
1334    impl<const N: usize> EndianWrite for crate::FixedUtf32LeCodeUnits<N> {
1335        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1336            let mut out = Vec::new();
1337            core_io::write_to_extend(self, &mut out)
1338                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1339            writer.write_all(&out)
1340        }
1341    }
1342
1343    // --- Fixed UTF-8 helpers (feature-gated) ---
1344
1345    #[cfg(all(feature = "text_fixed", feature = "text_utf8"))]
1346    impl<const N: usize> EndianRead for crate::FixedUtf8NullPadded<N> {
1347        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1348            let mut buf = vec![0u8; N];
1349            reader.read_exact(&mut buf)?;
1350            core_io::read_from_slice::<Self>(&buf)
1351                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1352        }
1353    }
1354
1355    #[cfg(all(feature = "text_fixed", feature = "text_utf8"))]
1356    impl<const N: usize> EndianWrite for crate::FixedUtf8NullPadded<N> {
1357        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1358            let mut out = Vec::new();
1359            core_io::write_to_extend(self, &mut out)
1360                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1361            writer.write_all(&out)
1362        }
1363    }
1364
1365    #[cfg(all(feature = "text_fixed", feature = "text_utf8"))]
1366    impl<const N: usize> EndianRead for crate::FixedUtf8SpacePadded<N> {
1367        fn read_from<R: Read + ?Sized>(reader: &mut R) -> io::Result<Self> {
1368            let mut buf = vec![0u8; N];
1369            reader.read_exact(&mut buf)?;
1370            core_io::read_from_slice::<Self>(&buf)
1371                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
1372        }
1373    }
1374
1375    #[cfg(all(feature = "text_fixed", feature = "text_utf8"))]
1376    impl<const N: usize> EndianWrite for crate::FixedUtf8SpacePadded<N> {
1377        fn write_to<W: Write + ?Sized>(&self, writer: &mut W) -> io::Result<()> {
1378            let mut out = Vec::new();
1379            core_io::write_to_extend(self, &mut out)
1380                .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
1381            writer.write_all(&out)
1382        }
1383    }
1384}
1385
1386#[cfg(all(test, feature = "io-std"))]
1387mod tests {
1388    use super::std_io::*;
1389    use crate::{BigEndian, LittleEndian, SpecificEndian};
1390    use std::io::Cursor;
1391
1392    fn round_trip_be<T>(val: T)
1393    where
1394        T: SpecificEndian<T> + Copy + PartialEq + core::fmt::Debug,
1395        BigEndian<T>: EndianWrite + EndianRead + From<T> + Into<T>,
1396    {
1397        let be: BigEndian<T> = BigEndian::from(val);
1398        let mut buf = Vec::new();
1399        write_specific(&mut buf, &be).unwrap();
1400
1401        let mut cur = Cursor::new(buf);
1402        let out: BigEndian<T> = read_specific(&mut cur).unwrap();
1403        assert_eq!(out.to_native(), be.to_native());
1404    }
1405
1406    fn round_trip_le<T>(val: T)
1407    where
1408        T: SpecificEndian<T> + Copy + PartialEq + core::fmt::Debug,
1409        LittleEndian<T>: EndianWrite + EndianRead + From<T> + Into<T>,
1410    {
1411        let le: LittleEndian<T> = LittleEndian::from(val);
1412        let mut buf = Vec::new();
1413        write_specific(&mut buf, &le).unwrap();
1414
1415        let mut cur = Cursor::new(buf);
1416        let out: LittleEndian<T> = read_specific(&mut cur).unwrap();
1417        assert_eq!(out.to_native(), le.to_native());
1418    }
1419
1420    #[test]
1421    fn be_u16_round_trip() {
1422        round_trip_be::<u16>(0x1234);
1423    }
1424
1425    #[test]
1426    fn le_u16_round_trip() {
1427        round_trip_le::<u16>(0x1234);
1428    }
1429
1430    #[test]
1431    fn be_u32_round_trip() {
1432        round_trip_be::<u32>(0x12345678);
1433    }
1434
1435    #[test]
1436    fn le_u32_round_trip() {
1437        round_trip_le::<u32>(0x12345678);
1438    }
1439
1440    #[test]
1441    fn be_u64_round_trip() {
1442        round_trip_be::<u64>(0x1234567890abcdef);
1443    }
1444
1445    #[test]
1446    fn le_u64_round_trip() {
1447        round_trip_le::<u64>(0x1234567890abcdef);
1448    }
1449
1450    #[test]
1451    fn be_f32_round_trip() {
1452        round_trip_be::<f32>(1234.5);
1453    }
1454
1455    #[test]
1456    fn le_f32_round_trip() {
1457        round_trip_le::<f32>(1234.5);
1458    }
1459
1460    #[test]
1461    fn be_f64_round_trip() {
1462        round_trip_be::<f64>(1234567.89);
1463    }
1464
1465    #[test]
1466    fn le_f64_round_trip() {
1467        round_trip_le::<f64>(1234567.89);
1468    }
1469
1470    #[test]
1471    fn multiple_sequence_read() {
1472        // Write sequence: u16be, u32le, u8be
1473        let a: BigEndian<u16> = 0xfaceu16.into();
1474        let b: LittleEndian<u32> = 0xdeadbeefu32.into();
1475        let c: BigEndian<u8> = 0x7fu8.into();
1476
1477        let mut buf = Vec::new();
1478        write_specific(&mut buf, &a).unwrap();
1479        write_specific(&mut buf, &b).unwrap();
1480        write_specific(&mut buf, &c).unwrap();
1481
1482        let mut cur = Cursor::new(buf);
1483        let ra: BigEndian<u16> = read_specific(&mut cur).unwrap();
1484        let rb: LittleEndian<u32> = read_specific(&mut cur).unwrap();
1485        let rc: BigEndian<u8> = read_specific(&mut cur).unwrap();
1486
1487        assert_eq!(ra.to_native(), a.to_native());
1488        assert_eq!(rb.to_native(), b.to_native());
1489        assert_eq!(rc.to_native(), c.to_native());
1490    }
1491
1492    #[test]
1493    fn insufficient_bytes_error() {
1494        // Create a buffer too small for u64
1495        let mut cur = Cursor::new(vec![0u8; 3]);
1496        let res: std::io::Result<BigEndian<u64>> = read_specific(&mut cur);
1497        assert!(res.is_err());
1498    }
1499}