Skip to main content

ai_lebe/
lib.rs

1#![no_std]
2#![warn(
3    missing_docs, unused,
4    trivial_numeric_casts,
5    future_incompatible,
6    rust_2018_compatibility,
7    rust_2018_idioms,
8    clippy::all
9)]
10
11#![doc(html_root_url = "https://docs.rs/ai-lebe/0.5.0")]
12
13//! Dead simple endianness conversions.
14//! The following operations are implemented on
15//! `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`, `u128`, `i128`, `f32`, `f64`:
16//!
17//!
18//! ### Read Numbers
19//! ```rust
20//! use ai_lebe::prelude::*;
21//! let mut reader: &[u8] = &[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
22//!
23//! let number : u64 = reader.read_from_little_endian()?;
24//! let number = u64::read_from_big_endian(&mut reader)?;
25//! # Ok::<(), std::io::Error>(())
26//! ```
27//!
28//! ### Read Slices
29//! ```rust
30//! use std::io::Read;
31//! use ai_lebe::prelude::*;
32//! let mut reader: &[u8] = &[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
33//!
34//! let mut numbers: &mut [u64] = &mut [0, 0];
35//! reader.read_from_little_endian_into(numbers)?;
36//! # Ok::<(), std::io::Error>(())
37//! ```
38//!
39//! ### Write Numbers
40//! ```rust
41//! use std::io::Read;
42//! use ai_lebe::prelude::*;
43//! let mut writer: Vec<u8> = Vec::new();
44//!
45//! let number: u64 = 1237691;
46//! writer.write_as_big_endian(&number)?;
47//! # Ok::<(), std::io::Error>(())
48//! ```
49//!
50//! ### Write Slices
51//! ```rust
52//! use std::io::Write;
53//! use ai_lebe::prelude::*;
54//! let mut writer: Vec<u8> = Vec::new();
55//!
56//! let numbers: &[u64] = &[1_u64, 234545_u64];
57//! writer.write_as_little_endian(numbers)?;
58//! # Ok::<(), std::io::Error>(())
59//! ```
60//!
61
62
63/// Exports some of the most common types.
64pub mod prelude {
65    pub use super::Endian;
66    pub use super::io::{ WriteEndian, ReadEndian, ReadPrimitive };
67}
68
69/// Represents values that can swap their bytes to reverse their endianness.
70///
71/// Supports converting values in-place using [`swap_bytes`] or [`convert_current_to_little_endian`]:
72/// Supports converting while transferring ownership using
73/// [`from_little_endian_into_current`] or [`from_current_into_little_endian`].
74///
75///
76/// For the types `u8`, `i8`, `&[u8]` and `&[i8]`, this trait will never transform any data,
77/// as they are just implemented for completeness.
78pub trait Endian {
79
80    /// Swaps all bytes in this value, inverting its endianness.
81    fn swap_bytes(&mut self);
82
83    /// On a little endian machine, this does nothing.
84    /// On a big endian machine, the bytes of this value are reversed.
85    #[inline] fn convert_current_to_little_endian(&mut self) {
86        #[cfg(target_endian = "big")] {
87            self.swap_bytes();
88        }
89    }
90
91    /// On a big endian machine, this does nothing.
92    /// On a little endian machine, the bytes of this value are reversed.
93    #[inline] fn convert_current_to_big_endian(&mut self) {
94        #[cfg(target_endian = "little")] {
95            self.swap_bytes();
96        }
97    }
98
99    /// On a little endian machine, this does nothing.
100    /// On a big endian machine, the bytes of this value are reversed.
101    #[inline] fn convert_little_endian_to_current(&mut self) {
102        #[cfg(target_endian = "big")] {
103            self.swap_bytes();
104        }
105    }
106
107    /// On a big endian machine, this does nothing.
108    /// On a little endian machine, the bytes of this value are reversed.
109    #[inline] fn convert_big_endian_to_current(&mut self) {
110        #[cfg(target_endian = "little")] {
111            self.swap_bytes();
112        }
113    }
114
115    /// On a little endian machine, this does nothing.
116    /// On a big endian machine, the bytes of this value are reversed.
117    #[inline] fn from_current_into_little_endian(mut self) -> Self where Self: Sized {
118        self.convert_current_to_little_endian();
119        self
120    }
121
122    /// On a big endian machine, this does nothing.
123    /// On a little endian machine, the bytes of this value are reversed.
124    #[inline] fn from_current_into_big_endian(mut self) -> Self where Self: Sized {
125        self.convert_current_to_big_endian();
126        self
127    }
128
129    /// On a little endian machine, this does nothing.
130    /// On a big endian machine, the bytes of this value are reversed.
131    #[inline] fn from_little_endian_into_current(mut self) -> Self where Self: Sized {
132        self.convert_little_endian_to_current();
133        self
134    }
135
136    /// On a big endian machine, this does nothing.
137    /// On a little endian machine, the bytes of this value are reversed.
138    #[inline] fn from_big_endian_into_current(mut self) -> Self where Self: Sized {
139        self.convert_big_endian_to_current();
140        self
141    }
142}
143
144
145// call a macro for each argument
146macro_rules! call_single_arg_macro_for_each {
147    ($macro: ident, $( $arguments: ident ),* ) => {
148        $( $macro! { $arguments }  )*
149    };
150}
151
152// implement this interface for primitive signed and unsigned integers
153macro_rules! implement_simple_primitive_endian {
154    ($type: ident) => {
155        impl Endian for $type {
156            fn swap_bytes(&mut self) {
157                *self = $type::swap_bytes(*self);
158            }
159        }
160    };
161}
162
163
164call_single_arg_macro_for_each! {
165    implement_simple_primitive_endian,
166    u16, u32, u64, u128, i16, i32, i64, i128
167}
168
169// no-op implementations
170impl Endian for u8 { fn swap_bytes(&mut self) {} }
171impl Endian for i8 { fn swap_bytes(&mut self) {} }
172impl Endian for [u8] { fn swap_bytes(&mut self) {} }
173impl Endian for [i8] { fn swap_bytes(&mut self) {} }
174
175// implement this interface for primitive floats, because they do not have a `swap_bytes()` in `std`
176macro_rules! implement_float_primitive_by_bits {
177    ($type: ident) => {
178        impl Endian for $type {
179            fn swap_bytes(&mut self) {
180                *self = Self::from_bits(self.to_bits().swap_bytes());
181            }
182        }
183    };
184}
185
186
187implement_float_primitive_by_bits!(f32);
188implement_float_primitive_by_bits!(f64);
189
190macro_rules! implement_slice_by_element {
191    ($type: ident) => {
192        impl Endian for [$type] {
193            fn swap_bytes(&mut self) {
194                for number in self.iter_mut() { // TODO SIMD?
195                    number.swap_bytes();
196                }
197            }
198        }
199    };
200}
201
202call_single_arg_macro_for_each! {
203    implement_slice_by_element,
204    u16, u32, u64, u128,
205    i16, i32, i64, i128,
206    f64, f32
207}
208
209/// Easily write primitives and slices of primitives to
210/// binary `std::io::Write` streams and easily read from binary `std::io::Read` streams.
211///
212/// Also contains the unsafe `bytes` module for reinterpreting values as byte slices and vice versa.
213pub mod io {
214    use super::Endian;
215    use no_std_io::io::{Read, Write, Result};
216
217    /// Reinterpret values as byte slices and byte slices as values unsafely.
218    pub mod bytes {
219        use no_std_io::io::{Read, Write, Result};
220
221        /// View this slice of values as a slice of bytes.
222        #[inline]
223        pub unsafe fn slice_as_bytes<T>(value: &[T]) -> &[u8] {
224            core::slice::from_raw_parts(
225                value.as_ptr() as *const u8,
226                value.len() * core::mem::size_of::<T>()
227            )
228        }
229
230        /// View this slice of values as a mutable slice of bytes.
231        #[inline]
232        pub unsafe fn slice_as_bytes_mut<T>(value: &mut [T]) -> &mut [u8] {
233            core::slice::from_raw_parts_mut(
234                value.as_mut_ptr() as *mut u8,
235                value.len() * core::mem::size_of::<T>()
236            )
237        }
238
239        /// View this reference as a slice of bytes.
240        #[inline]
241        pub unsafe fn value_as_bytes<T: Sized>(value: &T) -> &[u8] {
242            core::slice::from_raw_parts(
243                value as *const T as *const u8,
244                core::mem::size_of::<T>()
245            )
246        }
247
248        /// View this reference as a mutable slice of bytes.
249        #[inline]
250        pub unsafe fn value_as_bytes_mut<T: Sized>(value: &mut T) ->&mut [u8] {
251            core::slice::from_raw_parts_mut(
252                value as *mut T as *mut u8,
253                core::mem::size_of::<T>()
254            )
255        }
256
257        /// View this slice as a mutable slice of bytes and write it.
258        #[inline]
259        pub unsafe fn write_slice<T>(write: &mut impl Write, value: &[T]) -> Result<()> {
260            write.write_all(slice_as_bytes(value))
261        }
262
263        /// Read a slice of bytes into the specified slice.
264        #[inline]
265        pub unsafe fn read_slice<T>(read: &mut impl Read, value: &mut [T]) -> Result<()> {
266            read.read_exact(slice_as_bytes_mut(value))
267        }
268
269        /// View this reference as a mutable slice of bytes and write it.
270        #[inline]
271        pub unsafe fn write_value<T: Sized>(write: &mut impl Write, value: &T) -> Result<()> {
272            write.write_all(value_as_bytes(value))
273        }
274
275        /// Read a slice of bytes into the specified reference.
276        #[inline]
277        pub unsafe fn read_value<T: Sized>(read: &mut impl Read, value: &mut T) -> Result<()> {
278            read.read_exact(value_as_bytes_mut(value))
279        }
280    }
281
282    /// A `std::io::Write` output stream which supports writing any primitive values as bytes.
283    /// Will encode the values to be either little endian or big endian, as desired.
284    ///
285    /// This extension trait is implemented for all `Write` types.
286    /// Add `use ai_lebe::io::WriteEndian;` to your code
287    /// to automatically unlock this functionality for all types that implement `Write`.
288    pub trait WriteEndian<T: ?Sized> {
289
290        /// Write the byte value of the specified reference, converting it to little endianness
291        fn write_as_little_endian(&mut self, value: &T) -> Result<()>;
292
293        /// Write the byte value of the specified reference, converting it to big endianness
294        fn write_as_big_endian(&mut self, value: &T) -> Result<()>;
295
296        /// Write the byte value of the specified reference, not converting it
297        fn write_as_native_endian(&mut self, value: &T) -> Result<()> {
298            #[cfg(target_endian = "little")] { self.write_as_little_endian(value) }
299            #[cfg(target_endian = "big")] { self.write_as_big_endian(value) }
300        }
301    }
302
303    /// A `std::io::Read` input stream which supports reading any primitive values from bytes.
304    /// Will decode the values from either little endian or big endian, as desired.
305    ///
306    /// This extension trait is implemented for all `Read` types.
307    /// Add `use ai_lebe::io::ReadEndian;` to your code
308    /// to automatically unlock this functionality for all types that implement `Read`.
309    pub trait ReadEndian<T: ?Sized> {
310
311        /// Read into the supplied reference. Acts the same as `std::io::Read::read_exact`.
312        fn read_from_little_endian_into(&mut self, value: &mut T) -> Result<()>;
313
314        /// Read into the supplied reference. Acts the same as `std::io::Read::read_exact`.
315        fn read_from_big_endian_into(&mut self, value: &mut T) -> Result<()>;
316
317        /// Read into the supplied reference. Acts the same as `std::io::Read::read_exact`.
318        fn read_from_native_endian_into(&mut self, value: &mut T) -> Result<()> {
319            #[cfg(target_endian = "little")] { self.read_from_little_endian_into(value) }
320            #[cfg(target_endian = "big")] { self.read_from_big_endian_into(value) }
321        }
322
323        /// Read the byte value of the inferred type
324        #[inline]
325        fn read_from_little_endian(&mut self) -> Result<T> where T: Sized + Default {
326            let mut value = T::default();
327            self.read_from_little_endian_into(&mut value)?;
328            Ok(value)
329        }
330
331        /// Read the byte value of the inferred type
332        #[inline]
333        fn read_from_big_endian(&mut self) -> Result<T> where T: Sized + Default {
334            let mut value = T::default();
335            self.read_from_big_endian_into(&mut value)?;
336            Ok(value)
337        }
338
339        /// Read the byte value of the inferred type
340        #[inline]
341        fn read_from_native_endian(&mut self) -> Result<T> where T: Sized + Default {
342            #[cfg(target_endian = "little")] { self.read_from_little_endian() }
343            #[cfg(target_endian = "big")] { self.read_from_big_endian() }
344        }
345    }
346
347    // implement primitive for all types that are implemented by `Read`
348    impl<R: Read + ReadEndian<P>, P: Default> ReadPrimitive<R> for P {}
349
350
351    /// Offers a prettier versions of reading a primitive number.
352    ///
353    /// The default way of reading a value is:
354    /// ```rust
355    /// # use std::io::Read;
356    /// # use ai_lebe::prelude::*;
357    /// # let mut reader : &[u8] = &[2, 1];
358    ///
359    /// let number: u16 = reader.read_from_little_endian()?;
360    /// println!("{}", number);
361    /// # Ok::<(), std::io::Error>(())
362    ///
363    /// ```
364    ///
365    /// This trait enables you to use expressions:
366    /// ```rust
367    /// # use std::io::Read;
368    /// # use ai_lebe::prelude::*;
369    /// # let mut reader : &[u8] = &[2, 1];
370    ///
371    /// println!("{}", u16::read_from_little_endian(&mut reader)?);
372    /// # Ok::<(), std::io::Error>(())
373    /// ```
374    /// .
375    ///
376    pub trait ReadPrimitive<R: Read + ReadEndian<Self>> : Sized + Default {
377        /// Read this value from the supplied reader. Same as `ReadEndian::read_from_little_endian()`.
378        fn read_from_little_endian(read: &mut R) -> Result<Self> {
379            read.read_from_little_endian()
380        }
381
382        /// Read this value from the supplied reader. Same as `ReadEndian::read_from_big_endian()`.
383        fn read_from_big_endian(read: &mut R) -> Result<Self> {
384            read.read_from_big_endian()
385        }
386
387        /// Read this value from the supplied reader. Same as `ReadEndian::read_from_native_endian()`.
388        fn read_from_native_endian(read: &mut R) -> Result<Self> {
389            read.read_from_native_endian()
390        }
391    }
392
393    macro_rules! implement_simple_primitive_write {
394        ($type: ident) => {
395            impl<W: Write> WriteEndian<$type> for W {
396                fn write_as_little_endian(&mut self, value: &$type) -> Result<()> {
397                    unsafe { bytes::write_value(self, &value.from_current_into_little_endian()) }
398                }
399
400                fn write_as_big_endian(&mut self, value: &$type) -> Result<()> {
401                    unsafe { bytes::write_value(self, &value.from_current_into_big_endian()) }
402                }
403            }
404
405            impl<R: Read> ReadEndian<$type> for R {
406                #[inline]
407                fn read_from_little_endian_into(&mut self, value: &mut $type) -> Result<()> {
408                    unsafe { bytes::read_value(self, value)?; }
409                    value.convert_little_endian_to_current();
410                    Ok(())
411                }
412
413                #[inline]
414                fn read_from_big_endian_into(&mut self, value: &mut $type) -> Result<()> {
415                    unsafe { bytes::read_value(self, value)?; }
416                    value.convert_big_endian_to_current();
417                    Ok(())
418                }
419            }
420        };
421    }
422
423    call_single_arg_macro_for_each! {
424        implement_simple_primitive_write,
425        u8, u16, u32, u64, u128,
426        i8, i16, i32, i64, i128,
427        f32, f64
428    }
429
430
431    macro_rules! implement_slice_io {
432        ($type: ident) => {
433            impl<W: Write> WriteEndian<[$type]> for W {
434                fn write_as_little_endian(&mut self, value: &[$type]) -> Result<()> {
435                    #[cfg(target_endian = "big")] {
436                        for number in value { // TODO SIMD!
437                            self.write_as_little_endian(number)?;
438                        }
439                    }
440
441                    // else write whole slice
442                    #[cfg(target_endian = "little")]
443                    unsafe { bytes::write_slice(self, value)?; }
444
445                    Ok(())
446                }
447
448                fn write_as_big_endian(&mut self, value: &[$type]) -> Result<()> {
449                    #[cfg(target_endian = "little")] {
450                        for number in value { // TODO SIMD!
451                            self.write_as_big_endian(number)?;
452                        }
453                    }
454
455                    // else write whole slice
456                    #[cfg(target_endian = "big")]
457                    unsafe { bytes::write_slice(self, value)?; }
458
459                    Ok(())
460                }
461            }
462
463            impl<R: Read> ReadEndian<[$type]> for R {
464                fn read_from_little_endian_into(&mut self, value: &mut [$type]) -> Result<()> {
465                    unsafe { bytes::read_slice(self, value)? };
466                    value.convert_little_endian_to_current();
467                    Ok(())
468                }
469
470                fn read_from_big_endian_into(&mut self, value: &mut [$type]) -> Result<()> {
471                    unsafe { bytes::read_slice(self, value)? };
472                    value.convert_big_endian_to_current();
473                    Ok(())
474                }
475            }
476        };
477    }
478
479    call_single_arg_macro_for_each! {
480        implement_slice_io,
481        u8, u16, u32, u64, u128,
482        i8, i16, i32, i64, i128,
483        f64, f32
484    }
485
486
487
488    // TODO: SIMD
489    /*impl<R: Read> ReadEndian<[f32]> for R {
490        fn read_from_little_endian_into(&mut self, value: &mut [f32]) -> Result<()> {
491            unsafe { bytes::read_slice(self, value)? };
492            value.convert_little_endian_to_current();
493            Ok(())
494        }
495
496        fn read_from_big_endian_into(&mut self, value: &mut [f32]) -> Result<()> {
497            unsafe { bytes::read_slice(self, value)? };
498            value.convert_big_endian_to_current();
499            Ok(())
500        }
501    }
502
503    impl<W: Write> WriteEndian<[f32]> for W {
504        fn write_as_big_endian(&mut self, value: &[f32]) -> Result<()> {
505            if cfg!(target_endian = "little") {
506
507                // FIX ME this SIMD optimization makes no difference ... why? like, ZERO difference, not even worse
508//                #[cfg(feature = "simd")]
509                #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
510                unsafe {
511                    if is_x86_feature_detected!("avx2") {
512                        write_bytes_avx(self, value);
513                        return Ok(());
514                    }
515                }
516
517                // otherwise (no avx2 available)
518//                for number in value {
519//                    self.write_as_little_endian(number);
520//                }
521//
522//                return Ok(());
523                unimplemented!();
524
525                #[target_feature(enable = "avx2")]
526                #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
527                unsafe fn write_bytes_avx(write: &mut impl Write, slice: &[f32]) -> Result<()> {
528                    #[cfg(target_arch = "x86")] use std::arch::x86 as mm;
529                    #[cfg(target_arch = "x86_64")] use std::arch::x86_64 as mm;
530
531                    let bytes: &[u8] = crate::io::bytes::slice_as_bytes(slice);
532                    let mut chunks = bytes.chunks_exact(32);
533
534                    let indices = mm::_mm256_set_epi8(
535                        0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
536                        0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
537//                        3,2,1,0, 7,6,5,4, 11,10,9,8, 15,14,13,12,
538//                        3,2,1,0, 7,6,5,4, 11,10,9,8, 15,14,13,12
539                    );
540
541                    for chunk in &mut chunks {
542                        let data = mm::_mm256_loadu_si256(chunk.as_ptr() as _);
543                        let result = mm::_mm256_shuffle_epi8(data, indices);
544                        let mut out = [0_u8; 32];
545                        mm::_mm256_storeu_si256(out.as_mut_ptr() as _, result);
546                        write.write_all(&out)?;
547                    }
548
549                    let remainder = chunks.remainder();
550
551                    { // copy remainder into larger slice, with zeroes at the end
552                        let mut last_chunk = [0_u8; 32];
553                        last_chunk[0..remainder.len()].copy_from_slice(remainder);
554                        let data = mm::_mm256_loadu_si256(last_chunk.as_ptr() as _);
555                        let result = mm::_mm256_shuffle_epi8(data, indices);
556                        mm::_mm256_storeu_si256(last_chunk.as_mut_ptr() as _, result);
557                        write.write_all(&last_chunk[0..remainder.len()])?;
558                    }
559
560                    Ok(())
561                }
562            }
563
564            else {
565                unsafe { bytes::write_slice(self, value)?; }
566                Ok(())
567            }
568        }
569
570        fn write_as_little_endian(&mut self, value: &[f32]) -> Result<()> {
571            for number in value {
572                self.write_as_little_endian(number)?;
573            }
574
575            Ok(())
576        }
577    }*/
578}
579