explicit_endian/
lib.rs

1/*
2 * Copyright 2023 Alberto Ruiz <aruiz@gnome.org>
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
7 */
8
9//! # Explicit endian conversion no_std crate
10//!
11//! A lightweight, no_std crate for convenient data conversion between different endianness formats. Simplifies managing binary data on systems with varying endianness.
12//!
13//! ## Example
14//!
15//! ```rust
16//! extern crate explicit_endian as ee;
17//!
18//! use ee::{LittleEndian, BigEndian, Swappable};
19//!
20//! fn main() {
21//!     let value = 42u32;
22//!
23//!     // Convert to little-endian
24//!     let le_value: LittleEndian<u32> = value.into();
25//!
26//!     // Convert to big-endian
27//!     let be_value: BigEndian<u32> = value.into();
28//!
29//!     // You can now work with le_value and be_value in their respective endianness formats.
30//! }
31//! ```
32//!
33//! ## Supported Data Types
34//!
35//! - `u16`, `u32`, `u64`, `u128`
36//! - `i16`, `i32`, `i64`, `i128`
37//! - `usize`, `isize`
38//! - `f32`, `f64`
39
40#![no_std]
41#![feature(core_intrinsics)]
42
43use core::intrinsics::transmute;
44
45/// A trait representing data types that can be swapped between endianness formats.
46pub trait Swappable {}
47
48impl Swappable for u128 {}
49impl Swappable for u64 {}
50impl Swappable for u32 {}
51impl Swappable for u16 {}
52
53impl Swappable for i128 {}
54impl Swappable for i64 {}
55impl Swappable for i32 {}
56impl Swappable for i16 {}
57
58impl Swappable for usize {}
59impl Swappable for isize {}
60
61impl Swappable for f32 {}
62impl Swappable for f64 {}
63
64#[cfg(feature = "serde")]
65use serde::{Serialize, Deserialize};
66
67/// A wrapper to store data in little endian format
68#[derive(Debug, Clone, Copy, Eq, PartialEq)]
69#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
70pub struct LittleEndian<T: Swappable>(T);
71
72/// A wrapper to store data in big endian format
73#[derive(Debug, Clone, Copy, Eq, PartialEq)]
74#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
75pub struct BigEndian<T: Swappable>(T);
76
77pub trait ReadAs<T: Swappable> {
78    fn read_as(&self) -> T;
79}
80
81/// A macro to implement Into<T: Swappable> and From<T: Swappable> for either LittleEndian<T> or BigEndian<T>
82macro_rules! into_from {
83    ($type: ident, $end: ident, $a: tt, $b: tt, $swap: tt) => {
84        impl Into<$type> for $end<$type> {
85            fn into(self) -> $type {
86                #[cfg(target_endian = $a)]
87                return self.0;
88                #[cfg(target_endian = $b)]
89                return unsafe { transmute(self.0.$swap()) };
90            }
91        }
92
93        impl From<$type> for $end<$type> {
94            fn from(value: $type) -> Self {
95                #[cfg(target_endian = $a)]
96                return Self(value);
97                #[cfg(target_endian = $b)]
98                return Self(unsafe { transmute(value.$swap()) });
99            }
100        }
101
102        impl ReadAs<$type> for $end<$type> {
103            fn read_as(&self) -> $type {
104                (*self).into()
105            }
106        }
107    };
108}
109
110/// Wrapper macro to implement From/Into<T: Swappable> for LittleEndian<T: Swappable>
111macro_rules! le_into_from {
112    ($type: ident) => {
113        into_from! {$type, LittleEndian, "little", "big", to_le_bytes}
114    };
115}
116
117/// Wrapper macro to implement From/Into<T: Swappable> for BigEndian<T: Swappable>
118macro_rules! be_into_from {
119    ($type: ident) => {
120        into_from! {$type, BigEndian, "big", "little", to_be_bytes}
121    };
122}
123
124le_into_from! {u16}
125le_into_from! {u32}
126le_into_from! {u64}
127le_into_from! {u128}
128le_into_from! {usize}
129
130le_into_from! {i16}
131le_into_from! {i32}
132le_into_from! {i64}
133le_into_from! {i128}
134le_into_from! {isize}
135
136le_into_from! {f32}
137le_into_from! {f64}
138
139be_into_from! {u16}
140be_into_from! {u32}
141be_into_from! {u64}
142be_into_from! {u128}
143be_into_from! {usize}
144
145be_into_from! {i16}
146be_into_from! {i32}
147be_into_from! {i64}
148be_into_from! {i128}
149be_into_from! {isize}
150
151be_into_from! {f32}
152be_into_from! {f64}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157
158    macro_rules! test_type {
159        ($fun: ident, $type: ident, $val: expr, $rev: expr) => {
160            #[test]
161            fn $fun() {
162                let l: LittleEndian<$type> = $val.into();
163                #[cfg(target_endian = "little")]
164                assert_eq!(l.0, $val);
165                #[cfg(target_endian = "big")]
166                assert_eq!(l.0, $rev);
167
168                let b: BigEndian<$type> = $val.into();
169                #[cfg(target_endian = "little")]
170                assert_eq!(b.0, $rev);
171                #[cfg(target_endian = "big")]
172                assert_eq!(b.0, $val);
173            }
174        };
175    }
176
177    #[test]
178    fn test_read_value() {
179        let foo: LittleEndian<u16> = 15.into();
180        foo.read_as() as u16;
181    }
182
183    test_type! {test_u128, u128, 0xaa000000000000000000000000000000, 0x000000000000000000000000000000aa}
184    test_type! {test_u64, u64, 0xaa00000000000000, 0x00000000000000aa}
185    test_type! {test_u32, u32, 0xaa000000, 0x000000aa}
186    test_type! {test_u16, u16, 0xaa00, 0x00aa}
187
188    #[cfg(target_pointer_width = "32")]
189    test_type! {test_usize, usize, 0xaa000000, 0x000000aa}
190    #[cfg(target_pointer_width = "64")]
191    test_type! {test_usize, usize, 0xaa00000000000000, 0x00000000000000aa}
192
193    #[cfg(target_pointer_width = "32")]
194    test_type! {test_isize, isize, -2isize, -16777217isize}
195    #[cfg(target_pointer_width = "64")]
196    test_type! {test_isize, isize, -2isize, -72057594037927937isize}
197
198    test_type! {test_i16, i16, -2i16, -257i16}
199    test_type! {test_i32, i32, -2i32, -16777217i32}
200    test_type! {test_i64, i64, -2i64, -72057594037927937i64}
201    test_type! {test_i128, i128, -2i128, -1329227995784915872903807060280344577i128}
202    test_type! {test_f32, f32, 1.3_f32, 272302750000000000000000_f32}
203    test_type! {test_f64, f64, 1.3_f64, -6065988000116450000000000000000000000000000000000000000000000000000_f64}
204}