Import lebe 0.5.2 upstream upstream/0.5.2
authorDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 6 Apr 2023 04:05:03 +0000 (13:05 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Thu, 6 Apr 2023 04:05:03 +0000 (13:05 +0900)
.cargo_vcs_info.json [new file with mode: 0644]
.github/workflows/rust.yml [new file with mode: 0644]
.gitignore [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
Cargo.toml.orig [new file with mode: 0644]
LICENSE-BSD-3-Clause [new file with mode: 0644]
README.md [new file with mode: 0644]
benches/benches.rs [new file with mode: 0644]
src/lib.rs [new file with mode: 0644]
tests/tests.rs [new file with mode: 0644]

diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644 (file)
index 0000000..599778c
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "git": {
+    "sha1": "65f45436514fadcdee8b1b1ddb59b402cbe293eb"
+  },
+  "path_in_vcs": ""
+}
\ No newline at end of file
diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml
new file mode 100644 (file)
index 0000000..1e6a7d5
--- /dev/null
@@ -0,0 +1,22 @@
+name: Rust\r
+\r
+on:\r
+  push:\r
+    branches: [ "master" ]\r
+  pull_request:\r
+    branches: [ "master" ]\r
+\r
+env:\r
+  CARGO_TERM_COLOR: always\r
+\r
+jobs:\r
+  build:\r
+\r
+    runs-on: ubuntu-latest\r
+\r
+    steps:\r
+    - uses: actions/checkout@v3\r
+    - name: Build\r
+      run: cargo build --verbose\r
+    - name: Run tests\r
+      run: cargo test --verbose\r
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d7ce9db
--- /dev/null
@@ -0,0 +1,5 @@
+/target\r
+**/*.rs.bk\r
+Cargo.lock\r
+.idea\r
+*.iml
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644 (file)
index 0000000..38a619e
--- /dev/null
@@ -0,0 +1,61 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "lebe"
+version = "0.5.2"
+authors = ["johannesvollmer <johannes596@t-online.de>"]
+description = "Tiny, dead simple, high performance endianness conversions with a generic API"
+documentation = "https://docs.rs/crate/lebe/"
+readme = "README.md"
+keywords = [
+    "endianness",
+    "binary",
+    "io",
+    "byteorder",
+    "endian",
+]
+categories = [
+    "encoding",
+    "filesystem",
+    "algorithms",
+]
+license = "BSD-3-Clause"
+repository = "https://github.com/johannesvollmer/lebe"
+
+[profile.bench]
+lto = true
+debug = true
+
+[lib]
+path = "src/lib.rs"
+test = true
+doctest = true
+bench = true
+doc = true
+plugin = false
+proc-macro = false
+
+[[bench]]
+name = "benches"
+harness = false
+
+[dev-dependencies.bencher]
+version = "0.1.5"
+
+[dev-dependencies.byteorder]
+version = "1.4.3"
+
+[features]
+
+[badges.maintenance]
+status = "actively-developed"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644 (file)
index 0000000..7cebd79
--- /dev/null
@@ -0,0 +1,42 @@
+[package]\r
+name = "lebe"\r
+version = "0.5.2"\r
+authors = ["johannesvollmer <johannes596@t-online.de>"]\r
+edition = "2018"\r
+\r
+description = "Tiny, dead simple, high performance endianness conversions with a generic API"\r
+repository = "https://github.com/johannesvollmer/lebe"\r
+documentation = "https://docs.rs/crate/lebe/"\r
+readme = "README.md"\r
+license = "BSD-3-Clause"\r
+keywords = ["endianness", "binary", "io", "byteorder", "endian"]\r
+categories = ["encoding", "filesystem", "algorithms"]\r
+\r
+[lib]\r
+path = "src/lib.rs"\r
+test = true\r
+doctest = true\r
+bench = true\r
+doc = true\r
+plugin = false\r
+proc-macro = false\r
+\r
+\r
+[badges]\r
+maintenance = { status = "actively-developed" }\r
+\r
+[features]\r
+# simd = []\r
+\r
+[dev-dependencies]\r
+bencher = "0.1.5"\r
+byteorder = "1.4.3"\r
+\r
+[[bench]]\r
+name = "benches"\r
+harness = false\r
+\r
+[profile.bench]\r
+lto = true\r
+debug = true\r
+\r
diff --git a/LICENSE-BSD-3-Clause b/LICENSE-BSD-3-Clause
new file mode 100644 (file)
index 0000000..f9f8c18
--- /dev/null
@@ -0,0 +1,26 @@
+Copyright (c) 2022 Contributors to the lebe Project. All rights reserved.\r
+\r
+Redistribution and use in source and binary forms, with or without modification,\r
+are permitted provided that the following conditions are met:\r
+\r
+1. Redistributions of source code must retain the above copyright notice,\r
+this list of conditions and the following disclaimer.\r
+\r
+2. Redistributions in binary form must reproduce the above copyright notice,\r
+this list of conditions and the following disclaimer in the documentation\r
+and/or other materials provided with the distribution.\r
+\r
+3. Neither the name of the copyright holder nor the names of its contributors\r
+may be used to endorse or promote products derived from this software without\r
+specific prior written permission.\r
+\r
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\r
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\r
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE\r
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..8bae314
--- /dev/null
+++ b/README.md
@@ -0,0 +1,90 @@
+[![Rust Docs](https://docs.rs/lebe/badge.svg)](https://docs.rs/lebe) \r
+[![Crate Crate](https://img.shields.io/crates/v/lebe.svg)](https://crates.io/crates/lebe) \r
+![Lines of Code](https://tokei.rs/b1/github/johannesvollmer/lebe?category=code)\r
+\r
+\r
+# LEBE\r
+Tiny, dead simple, high performance endianness conversions with a generic API.\r
+This crate purposefully does not have a different method, like `write_u16(my_value)`, for each primitive type. Instead, this uses generic type inference: `write(my_u16)`.  \r
+\r
+# Purpose\r
+This crate has exactly two purposes:\r
+  1. Simple conversion between slices of primitives and byte arrays without unsafe code\r
+  2. Simple and fast conversion from one endianness to the other one\r
+\r
+The [byteorder crate](https://github.com/BurntSushi/byteorder) uses ![Lines of Code](https://tokei.rs/b1/github/BurntSushi/byteorder?category=code) for this.\r
+\r
+This simplifies reading and writing binary data to files or network streams.\r
+\r
+\r
+# Usage\r
+\r
+Write values.\r
+```rust\r
+    use lebe::io::WriteEndian;\r
+    use std::io::Write;\r
+    \r
+    fn main(){\r
+        let mut output_bytes: Vec<u8> = Vec::new();\r
+\r
+        let numbers: &[i32] = &[ 32, 102, 420, 594 ];\r
+        output_bytes.write_as_little_endian(numbers.len()).unwrap();\r
+        output_bytes.write_as_little_endian(numbers).unwrap();\r
+    }\r
+```\r
+\r
+Read numbers.\r
+```rust\r
+    use lebe::io::ReadEndian;\r
+    use std::io::Read;\r
+    \r
+    fn main(){\r
+        let mut input_bytes: &[u8] = &[ 3, 244 ];\r
+        let number: u16 = input_bytes.read_from_little_endian().unwrap();\r
+    }\r
+```\r
+\r
+Read slices.\r
+```rust\r
+    use lebe::io::ReadEndian;\r
+    use std::io::Read;\r
+    \r
+    fn main(){\r
+        let mut input_bytes: &[u8] = &[ 0, 2, 0, 3, 244, 1, 0, 3, 244, 1 ];\r
+        \r
+        let len: u16 = input_bytes.read_from_little_endian().unwrap();\r
+        let mut numbers = vec![ 0.0; len as usize ];\r
+        \r
+        input_bytes.read_from_little_endian_into(numbers.as_mut_slice()).unwrap();\r
+    }\r
+```\r
+\r
+Convert slices in-place.\r
+```rust\r
+    use lebe::Endian;\r
+    \r
+    fn main(){\r
+        let mut numbers: &[i32] = &[ 32, 102, 420, 594 ];\r
+        numbers.convert_current_to_little_endian();\r
+    }\r
+```\r
+\r
+\r
+# Why not use [byteorder](https://crates.io/crates/byteorder)?\r
+This crate supports batch-writing slices with native speed \r
+where the os has the matching endianness. Writing slices in `byteorder` \r
+must be done manually, and may be slower than expected. \r
+This crate does provide u8 and i8 slice operations for completeness.\r
+Also, the API of this crate looks simpler.\r
+\r
+# Why not use [endianness](https://crates.io/crates/endianness)?\r
+This crate has no runtime costs, just as `byteorder`.\r
+\r
+# Why not use this crate?\r
+The other crates probably have better documentation.\r
+\r
+\r
+# Fun Facts\r
+LEBE is made up from 'le' for little endian and 'be' for big endian.\r
+If you say that word using english pronounciation, \r
+a german might think you said the german word for 'love'.\r
diff --git a/benches/benches.rs b/benches/benches.rs
new file mode 100644 (file)
index 0000000..7ddc59b
--- /dev/null
@@ -0,0 +1,137 @@
+#[macro_use]\r
+extern crate bencher;\r
+\r
+use bencher::Bencher;\r
+use lebe::prelude::*;\r
+use byteorder::{ReadBytesExt, LittleEndian, BigEndian, WriteBytesExt};\r
+use std::io::{Read, Write, Cursor};\r
+\r
+const COUNT_8:  usize = 2048;\r
+const COUNT_16: usize = COUNT_8 / 2;\r
+const COUNT_32: usize = COUNT_8 / 4;\r
+const COUNT_64: usize = COUNT_8 / 8;\r
+\r
+\r
+fn bytes(count: usize) -> Cursor<Vec<u8>> {\r
+    let vec: Vec<u8> = (0..count).map(|i| (i % 256) as u8).collect();\r
+    Cursor::new(vec)\r
+}\r
+\r
+fn floats(count: usize) -> Vec<f32> {\r
+    (0..count).map(|i| i as f32).collect()\r
+}\r
+\r
+fn read_slice_f32_le_crate(bench: &mut Bencher) {\r
+    bench.iter(move ||{\r
+        let mut target = vec![ 0_f32; COUNT_32 ];\r
+        bencher::black_box(bytes(COUNT_8).read_from_little_endian_into(target.as_mut_slice())).unwrap();\r
+        bencher::black_box(target);\r
+    })\r
+}\r
+\r
+fn read_slice_f32_le_byteorder(bench: &mut Bencher) {\r
+    bench.iter(move ||{\r
+        let mut target = vec![ 0_f32; COUNT_32 ];\r
+        bencher::black_box(bytes(COUNT_8).read_f32_into::<LittleEndian>(target.as_mut_slice())).unwrap();\r
+        bencher::black_box(target);\r
+    })\r
+}\r
+\r
+fn read_slice_f32_be_crate(bench: &mut Bencher) {\r
+    bench.iter(move ||{\r
+        let mut target = vec![ 0_f32; COUNT_32 ];\r
+        bencher::black_box(bytes(COUNT_8).read_from_big_endian_into(target.as_mut_slice())).unwrap();\r
+        bencher::black_box(target);\r
+    })\r
+}\r
+\r
+fn read_slice_f32_be_byteorder(bench: &mut Bencher) {\r
+    bench.iter(move ||{\r
+        let mut target = vec![ 0_f32; COUNT_32 ];\r
+        bencher::black_box(bytes(COUNT_8).read_f32_into::<BigEndian>(target.as_mut_slice())).unwrap();\r
+        bencher::black_box(target);\r
+    })\r
+}\r
+\r
+// FIXME faster than baseline?!?!!\r
+fn write_slice_f32_le_crate(bench: &mut Bencher) {\r
+    bench.iter(move ||{\r
+        let data = floats(COUNT_32);\r
+        let mut output = Vec::with_capacity(COUNT_8);\r
+\r
+        bencher::black_box(output.write_as_little_endian(data.as_slice())).unwrap();\r
+        assert_eq!(output.len(), COUNT_8);\r
+        bencher::black_box(output);\r
+    })\r
+}\r
+\r
+fn write_slice_f32_le_byteorder(bench: &mut Bencher) {\r
+    bench.iter(move ||{\r
+        let data = floats(COUNT_32);\r
+        let mut output = Vec::with_capacity(COUNT_8);\r
+\r
+        for number in data {\r
+            bencher::black_box(output.write_f32::<LittleEndian>(number)).unwrap();\r
+        }\r
+\r
+        assert_eq!(output.len(), COUNT_8);\r
+        bencher::black_box(output);\r
+    })\r
+}\r
+\r
+\r
+fn write_slice_f32_be_crate(bench: &mut Bencher) {\r
+    bench.iter(move ||{\r
+        let data = floats(COUNT_32);\r
+        let mut output = Vec::with_capacity(COUNT_8);\r
+\r
+        bencher::black_box(output.write_as_big_endian(data.as_slice())).unwrap();\r
+        assert_eq!(output.len(), COUNT_8);\r
+        bencher::black_box(output);\r
+    })\r
+}\r
+\r
+fn write_slice_f32_be_byteorder(bench: &mut Bencher) {\r
+    bench.iter(move ||{\r
+        let data = floats(COUNT_32);\r
+        let mut output = Vec::with_capacity(COUNT_8);\r
+\r
+        for number in data {\r
+            bencher::black_box(output.write_f32::<BigEndian>(number)).unwrap();\r
+        }\r
+\r
+        assert_eq!(output.len(), COUNT_8);\r
+        bencher::black_box(output);\r
+    })\r
+}\r
+\r
+\r
+\r
+fn read_slice_baseline(bench: &mut Bencher) {\r
+    bench.iter(move ||{\r
+        let mut target = vec![ 0_u8; COUNT_8 ];\r
+        bencher::black_box(bytes(COUNT_8).read_exact(target.as_mut_slice())).unwrap();\r
+        bencher::black_box(target);\r
+    })\r
+}\r
+\r
+\r
+fn write_slice_baseline(bench: &mut Bencher) {\r
+    bench.iter(move ||{\r
+        let data = bytes(COUNT_8).into_inner();\r
+        let mut output = Vec::with_capacity(COUNT_8);\r
+\r
+        bencher::black_box(output.write_all(data.as_slice())).unwrap();\r
+        bencher::black_box(output);\r
+    })\r
+}\r
+\r
+benchmark_group!(\r
+    benches,\r
+    read_slice_f32_be_byteorder, read_slice_f32_be_crate, read_slice_f32_le_byteorder,\r
+    read_slice_f32_le_crate, write_slice_f32_le_byteorder, write_slice_f32_le_crate,\r
+    write_slice_f32_be_byteorder, write_slice_f32_be_crate,\r
+    read_slice_baseline, write_slice_baseline\r
+);\r
+\r
+benchmark_main!(benches);
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644 (file)
index 0000000..4dab607
--- /dev/null
@@ -0,0 +1,578 @@
+#![warn(\r
+    missing_docs, unused,\r
+    trivial_numeric_casts,\r
+    future_incompatible,\r
+    rust_2018_compatibility,\r
+    rust_2018_idioms,\r
+    clippy::all\r
+)]\r
+\r
+#![doc(html_root_url = "https://docs.rs/lebe/0.5.0")]\r
+\r
+//! Dead simple endianness conversions.\r
+//! The following operations are implemented on\r
+//! `u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`, `u128`, `i128`, `f32`, `f64`:\r
+//!\r
+//!\r
+//! ### Read Numbers\r
+//! ```rust\r
+//! use lebe::prelude::*;\r
+//! let mut reader: &[u8] = &[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];\r
+//!\r
+//! let number : u64 = reader.read_from_little_endian()?;\r
+//! let number = u64::read_from_big_endian(&mut reader)?;\r
+//! # Ok::<(), std::io::Error>(())\r
+//! ```\r
+//!\r
+//! ### Read Slices\r
+//! ```rust\r
+//! use std::io::Read;\r
+//! use lebe::prelude::*;\r
+//! let mut reader: &[u8] = &[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];\r
+//!\r
+//! let mut numbers: &mut [u64] = &mut [0, 0];\r
+//! reader.read_from_little_endian_into(numbers)?;\r
+//! # Ok::<(), std::io::Error>(())\r
+//! ```\r
+//!\r
+//! ### Write Numbers\r
+//! ```rust\r
+//! use std::io::Read;\r
+//! use lebe::prelude::*;\r
+//! let mut writer: Vec<u8> = Vec::new();\r
+//!\r
+//! let number: u64 = 1237691;\r
+//! writer.write_as_big_endian(&number)?;\r
+//! # Ok::<(), std::io::Error>(())\r
+//! ```\r
+//!\r
+//! ### Write Slices\r
+//! ```rust\r
+//! use std::io::Write;\r
+//! use lebe::prelude::*;\r
+//! let mut writer: Vec<u8> = Vec::new();\r
+//!\r
+//! let numbers: &[u64] = &[1_u64, 234545_u64];\r
+//! writer.write_as_little_endian(numbers)?;\r
+//! # Ok::<(), std::io::Error>(())\r
+//! ```\r
+//!\r
+\r
+\r
+/// Exports some of the most common types.\r
+pub mod prelude {\r
+    pub use super::Endian;\r
+    pub use super::io::{ WriteEndian, ReadEndian, ReadPrimitive };\r
+}\r
+\r
+/// Represents values that can swap their bytes to reverse their endianness.\r
+///\r
+/// Supports converting values in-place using [`swap_bytes`] or [`convert_current_to_little_endian`]:\r
+/// Supports converting while transferring ownership using\r
+/// [`from_little_endian_into_current`] or [`from_current_into_little_endian`].\r
+///\r
+///\r
+/// For the types `u8`, `i8`, `&[u8]` and `&[i8]`, this trait will never transform any data,\r
+/// as they are just implemented for completeness.\r
+pub trait Endian {\r
+\r
+    /// Swaps all bytes in this value, inverting its endianness.\r
+    fn swap_bytes(&mut self);\r
+\r
+    /// On a little endian machine, this does nothing.\r
+    /// On a big endian machine, the bytes of this value are reversed.\r
+    #[inline] fn convert_current_to_little_endian(&mut self) {\r
+        #[cfg(target_endian = "big")] {\r
+            self.swap_bytes();\r
+        }\r
+    }\r
+\r
+    /// On a big endian machine, this does nothing.\r
+    /// On a little endian machine, the bytes of this value are reversed.\r
+    #[inline] fn convert_current_to_big_endian(&mut self) {\r
+        #[cfg(target_endian = "little")] {\r
+            self.swap_bytes();\r
+        }\r
+    }\r
+\r
+    /// On a little endian machine, this does nothing.\r
+    /// On a big endian machine, the bytes of this value are reversed.\r
+    #[inline] fn convert_little_endian_to_current(&mut self) {\r
+        #[cfg(target_endian = "big")] {\r
+            self.swap_bytes();\r
+        }\r
+    }\r
+\r
+    /// On a big endian machine, this does nothing.\r
+    /// On a little endian machine, the bytes of this value are reversed.\r
+    #[inline] fn convert_big_endian_to_current(&mut self) {\r
+        #[cfg(target_endian = "little")] {\r
+            self.swap_bytes();\r
+        }\r
+    }\r
+\r
+    /// On a little endian machine, this does nothing.\r
+    /// On a big endian machine, the bytes of this value are reversed.\r
+    #[inline] fn from_current_into_little_endian(mut self) -> Self where Self: Sized {\r
+        self.convert_current_to_little_endian();\r
+        self\r
+    }\r
+\r
+    /// On a big endian machine, this does nothing.\r
+    /// On a little endian machine, the bytes of this value are reversed.\r
+    #[inline] fn from_current_into_big_endian(mut self) -> Self where Self: Sized {\r
+        self.convert_current_to_big_endian();\r
+        self\r
+    }\r
+\r
+    /// On a little endian machine, this does nothing.\r
+    /// On a big endian machine, the bytes of this value are reversed.\r
+    #[inline] fn from_little_endian_into_current(mut self) -> Self where Self: Sized {\r
+        self.convert_little_endian_to_current();\r
+        self\r
+    }\r
+\r
+    /// On a big endian machine, this does nothing.\r
+    /// On a little endian machine, the bytes of this value are reversed.\r
+    #[inline] fn from_big_endian_into_current(mut self) -> Self where Self: Sized {\r
+        self.convert_big_endian_to_current();\r
+        self\r
+    }\r
+}\r
+\r
+\r
+// call a macro for each argument\r
+macro_rules! call_single_arg_macro_for_each {\r
+    ($macro: ident, $( $arguments: ident ),* ) => {\r
+        $( $macro! { $arguments }  )*\r
+    };\r
+}\r
+\r
+// implement this interface for primitive signed and unsigned integers\r
+macro_rules! implement_simple_primitive_endian {\r
+    ($type: ident) => {\r
+        impl Endian for $type {\r
+            fn swap_bytes(&mut self) {\r
+                *self = $type::swap_bytes(*self);\r
+            }\r
+        }\r
+    };\r
+}\r
+\r
+\r
+call_single_arg_macro_for_each! {\r
+    implement_simple_primitive_endian,\r
+    u16, u32, u64, u128, i16, i32, i64, i128\r
+}\r
+\r
+// no-op implementations\r
+impl Endian for u8 { fn swap_bytes(&mut self) {} }\r
+impl Endian for i8 { fn swap_bytes(&mut self) {} }\r
+impl Endian for [u8] { fn swap_bytes(&mut self) {} }\r
+impl Endian for [i8] { fn swap_bytes(&mut self) {} }\r
+\r
+// implement this interface for primitive floats, because they do not have a `swap_bytes()` in `std`\r
+macro_rules! implement_float_primitive_by_bits {\r
+    ($type: ident) => {\r
+        impl Endian for $type {\r
+            fn swap_bytes(&mut self) {\r
+                *self = Self::from_bits(self.to_bits().swap_bytes());\r
+            }\r
+        }\r
+    };\r
+}\r
+\r
+\r
+implement_float_primitive_by_bits!(f32);\r
+implement_float_primitive_by_bits!(f64);\r
+\r
+macro_rules! implement_slice_by_element {\r
+    ($type: ident) => {\r
+        impl Endian for [$type] {\r
+            fn swap_bytes(&mut self) {\r
+                for number in self.iter_mut() { // TODO SIMD?\r
+                    number.swap_bytes();\r
+                }\r
+            }\r
+        }\r
+    };\r
+}\r
+\r
+call_single_arg_macro_for_each! {\r
+    implement_slice_by_element,\r
+    u16, u32, u64, u128,\r
+    i16, i32, i64, i128,\r
+    f64, f32\r
+}\r
+\r
+/// Easily write primitives and slices of primitives to\r
+/// binary `std::io::Write` streams and easily read from binary `std::io::Read` streams.\r
+///\r
+/// Also contains the unsafe `bytes` module for reinterpreting values as byte slices and vice versa.\r
+pub mod io {\r
+    use super::Endian;\r
+    use std::io::{Read, Write, Result};\r
+\r
+    /// Reinterpret values as byte slices and byte slices as values unsafely.\r
+    pub mod bytes {\r
+        use std::io::{Read, Write, Result};\r
+\r
+        /// View this slice of values as a slice of bytes.\r
+        #[inline]\r
+        pub unsafe fn slice_as_bytes<T>(value: &[T]) -> &[u8] {\r
+            std::slice::from_raw_parts(\r
+                value.as_ptr() as *const u8,\r
+                value.len() * std::mem::size_of::<T>()\r
+            )\r
+        }\r
+\r
+        /// View this slice of values as a mutable slice of bytes.\r
+        #[inline]\r
+        pub unsafe fn slice_as_bytes_mut<T>(value: &mut [T]) -> &mut [u8] {\r
+            std::slice::from_raw_parts_mut(\r
+                value.as_mut_ptr() as *mut u8,\r
+                value.len() * std::mem::size_of::<T>()\r
+            )\r
+        }\r
+\r
+        /// View this reference as a slice of bytes.\r
+        #[inline]\r
+        pub unsafe fn value_as_bytes<T: Sized>(value: &T) -> &[u8] {\r
+            std::slice::from_raw_parts(\r
+                value as *const T as *const u8,\r
+                std::mem::size_of::<T>()\r
+            )\r
+        }\r
+\r
+        /// View this reference as a mutable slice of bytes.\r
+        #[inline]\r
+        pub unsafe fn value_as_bytes_mut<T: Sized>(value: &mut T) ->&mut [u8] {\r
+            std::slice::from_raw_parts_mut(\r
+                value as *mut T as *mut u8,\r
+                std::mem::size_of::<T>()\r
+            )\r
+        }\r
+\r
+        /// View this slice as a mutable slice of bytes and write it.\r
+        #[inline]\r
+        pub unsafe fn write_slice<T>(write: &mut impl Write, value: &[T]) -> Result<()> {\r
+            write.write_all(slice_as_bytes(value))\r
+        }\r
+\r
+        /// Read a slice of bytes into the specified slice.\r
+        #[inline]\r
+        pub unsafe fn read_slice<T>(read: &mut impl Read, value: &mut [T]) -> Result<()> {\r
+            read.read_exact(slice_as_bytes_mut(value))\r
+        }\r
+\r
+        /// View this reference as a mutable slice of bytes and write it.\r
+        #[inline]\r
+        pub unsafe fn write_value<T: Sized>(write: &mut impl Write, value: &T) -> Result<()> {\r
+            write.write_all(value_as_bytes(value))\r
+        }\r
+\r
+        /// Read a slice of bytes into the specified reference.\r
+        #[inline]\r
+        pub unsafe fn read_value<T: Sized>(read: &mut impl Read, value: &mut T) -> Result<()> {\r
+            read.read_exact(value_as_bytes_mut(value))\r
+        }\r
+    }\r
+\r
+    /// A `std::io::Write` output stream which supports writing any primitive values as bytes.\r
+    /// Will encode the values to be either little endian or big endian, as desired.\r
+    ///\r
+    /// This extension trait is implemented for all `Write` types.\r
+    /// Add `use lebe::io::WriteEndian;` to your code\r
+    /// to automatically unlock this functionality for all types that implement `Write`.\r
+    pub trait WriteEndian<T: ?Sized> {\r
+\r
+        /// Write the byte value of the specified reference, converting it to little endianness\r
+        fn write_as_little_endian(&mut self, value: &T) -> Result<()>;\r
+\r
+        /// Write the byte value of the specified reference, converting it to big endianness\r
+        fn write_as_big_endian(&mut self, value: &T) -> Result<()>;\r
+\r
+        /// Write the byte value of the specified reference, not converting it\r
+        fn write_as_native_endian(&mut self, value: &T) -> Result<()> {\r
+            #[cfg(target_endian = "little")] { self.write_as_little_endian(value) }\r
+            #[cfg(target_endian = "big")] { self.write_as_big_endian(value) }\r
+        }\r
+    }\r
+\r
+    /// A `std::io::Read` input stream which supports reading any primitive values from bytes.\r
+    /// Will decode the values from either little endian or big endian, as desired.\r
+    ///\r
+    /// This extension trait is implemented for all `Read` types.\r
+    /// Add `use lebe::io::ReadEndian;` to your code\r
+    /// to automatically unlock this functionality for all types that implement `Read`.\r
+    pub trait ReadEndian<T: ?Sized> {\r
+\r
+        /// Read into the supplied reference. Acts the same as `std::io::Read::read_exact`.\r
+        fn read_from_little_endian_into(&mut self, value: &mut T) -> Result<()>;\r
+\r
+        /// Read into the supplied reference. Acts the same as `std::io::Read::read_exact`.\r
+        fn read_from_big_endian_into(&mut self, value: &mut T) -> Result<()>;\r
+\r
+        /// Read into the supplied reference. Acts the same as `std::io::Read::read_exact`.\r
+        fn read_from_native_endian_into(&mut self, value: &mut T) -> Result<()> {\r
+            #[cfg(target_endian = "little")] { self.read_from_little_endian_into(value) }\r
+            #[cfg(target_endian = "big")] { self.read_from_big_endian_into(value) }\r
+        }\r
+\r
+        /// Read the byte value of the inferred type\r
+        #[inline]\r
+        fn read_from_little_endian(&mut self) -> Result<T> where T: Sized + Default {\r
+            let mut value = T::default();\r
+            self.read_from_little_endian_into(&mut value)?;\r
+            Ok(value)\r
+        }\r
+\r
+        /// Read the byte value of the inferred type\r
+        #[inline]\r
+        fn read_from_big_endian(&mut self) -> Result<T> where T: Sized + Default {\r
+            let mut value = T::default();\r
+            self.read_from_big_endian_into(&mut value)?;\r
+            Ok(value)\r
+        }\r
+\r
+        /// Read the byte value of the inferred type\r
+        #[inline]\r
+        fn read_from_native_endian(&mut self) -> Result<T> where T: Sized + Default {\r
+            #[cfg(target_endian = "little")] { self.read_from_little_endian() }\r
+            #[cfg(target_endian = "big")] { self.read_from_big_endian() }\r
+        }\r
+    }\r
+\r
+    // implement primitive for all types that are implemented by `Read`\r
+    impl<R: Read + ReadEndian<P>, P: Default> ReadPrimitive<R> for P {}\r
+\r
+\r
+    /// Offers a prettier versions of reading a primitive number.\r
+    ///\r
+    /// The default way of reading a value is:\r
+    /// ```rust\r
+    /// # use std::io::Read;\r
+    /// # use lebe::prelude::*;\r
+    /// # let mut reader : &[u8] = &[2, 1];\r
+    ///\r
+    /// let number: u16 = reader.read_from_little_endian()?;\r
+    /// println!("{}", number);\r
+    /// # Ok::<(), std::io::Error>(())\r
+    ///\r
+    /// ```\r
+    ///\r
+    /// This trait enables you to use expressions:\r
+    /// ```rust\r
+    /// # use std::io::Read;\r
+    /// # use lebe::prelude::*;\r
+    /// # let mut reader : &[u8] = &[2, 1];\r
+    ///\r
+    /// println!("{}", u16::read_from_little_endian(&mut reader)?);\r
+    /// # Ok::<(), std::io::Error>(())\r
+    /// ```\r
+    /// .\r
+    ///\r
+    pub trait ReadPrimitive<R: Read + ReadEndian<Self>> : Sized + Default {\r
+        /// Read this value from the supplied reader. Same as `ReadEndian::read_from_little_endian()`.\r
+        fn read_from_little_endian(read: &mut R) -> Result<Self> {\r
+            read.read_from_little_endian()\r
+        }\r
+\r
+        /// Read this value from the supplied reader. Same as `ReadEndian::read_from_big_endian()`.\r
+        fn read_from_big_endian(read: &mut R) -> Result<Self> {\r
+            read.read_from_big_endian()\r
+        }\r
+\r
+        /// Read this value from the supplied reader. Same as `ReadEndian::read_from_native_endian()`.\r
+        fn read_from_native_endian(read: &mut R) -> Result<Self> {\r
+            read.read_from_native_endian()\r
+        }\r
+    }\r
+\r
+    macro_rules! implement_simple_primitive_write {\r
+        ($type: ident) => {\r
+            impl<W: Write> WriteEndian<$type> for W {\r
+                fn write_as_little_endian(&mut self, value: &$type) -> Result<()> {\r
+                    unsafe { bytes::write_value(self, &value.from_current_into_little_endian()) }\r
+                }\r
+\r
+                fn write_as_big_endian(&mut self, value: &$type) -> Result<()> {\r
+                    unsafe { bytes::write_value(self, &value.from_current_into_big_endian()) }\r
+                }\r
+            }\r
+\r
+            impl<R: Read> ReadEndian<$type> for R {\r
+                #[inline]\r
+                fn read_from_little_endian_into(&mut self, value: &mut $type) -> Result<()> {\r
+                    unsafe { bytes::read_value(self, value)?; }\r
+                    value.convert_little_endian_to_current();\r
+                    Ok(())\r
+                }\r
+\r
+                #[inline]\r
+                fn read_from_big_endian_into(&mut self, value: &mut $type) -> Result<()> {\r
+                    unsafe { bytes::read_value(self, value)?; }\r
+                    value.convert_big_endian_to_current();\r
+                    Ok(())\r
+                }\r
+            }\r
+        };\r
+    }\r
+\r
+    call_single_arg_macro_for_each! {\r
+        implement_simple_primitive_write,\r
+        u8, u16, u32, u64, u128,\r
+        i8, i16, i32, i64, i128,\r
+        f32, f64\r
+    }\r
+\r
+\r
+    macro_rules! implement_slice_io {\r
+        ($type: ident) => {\r
+            impl<W: Write> WriteEndian<[$type]> for W {\r
+                fn write_as_little_endian(&mut self, value: &[$type]) -> Result<()> {\r
+                    #[cfg(target_endian = "big")] {\r
+                        for number in value { // TODO SIMD!\r
+                            self.write_as_little_endian(number)?;\r
+                        }\r
+                    }\r
+\r
+                    // else write whole slice\r
+                    #[cfg(target_endian = "little")]\r
+                    unsafe { bytes::write_slice(self, value)?; }\r
+\r
+                    Ok(())\r
+                }\r
+\r
+                fn write_as_big_endian(&mut self, value: &[$type]) -> Result<()> {\r
+                    #[cfg(target_endian = "little")] {\r
+                        for number in value { // TODO SIMD!\r
+                            self.write_as_big_endian(number)?;\r
+                        }\r
+                    }\r
+\r
+                    // else write whole slice\r
+                    #[cfg(target_endian = "big")]\r
+                    unsafe { bytes::write_slice(self, value)?; }\r
+\r
+                    Ok(())\r
+                }\r
+            }\r
+\r
+            impl<R: Read> ReadEndian<[$type]> for R {\r
+                fn read_from_little_endian_into(&mut self, value: &mut [$type]) -> Result<()> {\r
+                    unsafe { bytes::read_slice(self, value)? };\r
+                    value.convert_little_endian_to_current();\r
+                    Ok(())\r
+                }\r
+\r
+                fn read_from_big_endian_into(&mut self, value: &mut [$type]) -> Result<()> {\r
+                    unsafe { bytes::read_slice(self, value)? };\r
+                    value.convert_big_endian_to_current();\r
+                    Ok(())\r
+                }\r
+            }\r
+        };\r
+    }\r
+\r
+    call_single_arg_macro_for_each! {\r
+        implement_slice_io,\r
+        u8, u16, u32, u64, u128,\r
+        i8, i16, i32, i64, i128,\r
+        f64, f32\r
+    }\r
+\r
+\r
+\r
+    // TODO: SIMD\r
+    /*impl<R: Read> ReadEndian<[f32]> for R {\r
+        fn read_from_little_endian_into(&mut self, value: &mut [f32]) -> Result<()> {\r
+            unsafe { bytes::read_slice(self, value)? };\r
+            value.convert_little_endian_to_current();\r
+            Ok(())\r
+        }\r
+\r
+        fn read_from_big_endian_into(&mut self, value: &mut [f32]) -> Result<()> {\r
+            unsafe { bytes::read_slice(self, value)? };\r
+            value.convert_big_endian_to_current();\r
+            Ok(())\r
+        }\r
+    }\r
+\r
+    impl<W: Write> WriteEndian<[f32]> for W {\r
+        fn write_as_big_endian(&mut self, value: &[f32]) -> Result<()> {\r
+            if cfg!(target_endian = "little") {\r
+\r
+                // FIX ME this SIMD optimization makes no difference ... why? like, ZERO difference, not even worse\r
+//                #[cfg(feature = "simd")]\r
+                #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]\r
+                unsafe {\r
+                    if is_x86_feature_detected!("avx2") {\r
+                        write_bytes_avx(self, value);\r
+                        return Ok(());\r
+                    }\r
+                }\r
+\r
+                // otherwise (no avx2 available)\r
+//                for number in value {\r
+//                    self.write_as_little_endian(number);\r
+//                }\r
+//\r
+//                return Ok(());\r
+                unimplemented!();\r
+\r
+                #[target_feature(enable = "avx2")]\r
+                #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]\r
+                unsafe fn write_bytes_avx(write: &mut impl Write, slice: &[f32]) -> Result<()> {\r
+                    #[cfg(target_arch = "x86")] use std::arch::x86 as mm;\r
+                    #[cfg(target_arch = "x86_64")] use std::arch::x86_64 as mm;\r
+\r
+                    let bytes: &[u8] = crate::io::bytes::slice_as_bytes(slice);\r
+                    let mut chunks = bytes.chunks_exact(32);\r
+\r
+                    let indices = mm::_mm256_set_epi8(\r
+                        0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,\r
+                        0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15\r
+//                        3,2,1,0, 7,6,5,4, 11,10,9,8, 15,14,13,12,\r
+//                        3,2,1,0, 7,6,5,4, 11,10,9,8, 15,14,13,12\r
+                    );\r
+\r
+                    for chunk in &mut chunks {\r
+                        let data = mm::_mm256_loadu_si256(chunk.as_ptr() as _);\r
+                        let result = mm::_mm256_shuffle_epi8(data, indices);\r
+                        let mut out = [0_u8; 32];\r
+                        mm::_mm256_storeu_si256(out.as_mut_ptr() as _, result);\r
+                        write.write_all(&out)?;\r
+                    }\r
+\r
+                    let remainder = chunks.remainder();\r
+\r
+                    { // copy remainder into larger slice, with zeroes at the end\r
+                        let mut last_chunk = [0_u8; 32];\r
+                        last_chunk[0..remainder.len()].copy_from_slice(remainder);\r
+                        let data = mm::_mm256_loadu_si256(last_chunk.as_ptr() as _);\r
+                        let result = mm::_mm256_shuffle_epi8(data, indices);\r
+                        mm::_mm256_storeu_si256(last_chunk.as_mut_ptr() as _, result);\r
+                        write.write_all(&last_chunk[0..remainder.len()])?;\r
+                    }\r
+\r
+                    Ok(())\r
+                }\r
+            }\r
+\r
+            else {\r
+                unsafe { bytes::write_slice(self, value)?; }\r
+                Ok(())\r
+            }\r
+        }\r
+\r
+        fn write_as_little_endian(&mut self, value: &[f32]) -> Result<()> {\r
+            for number in value {\r
+                self.write_as_little_endian(number)?;\r
+            }\r
+\r
+            Ok(())\r
+        }\r
+    }*/\r
+}\r
+\r
diff --git a/tests/tests.rs b/tests/tests.rs
new file mode 100644 (file)
index 0000000..811454a
--- /dev/null
@@ -0,0 +1,213 @@
+extern crate lebe;\r
+\r
+use lebe::prelude::*;\r
+use std::mem;\r
+\r
+use byteorder::{WriteBytesExt, LittleEndian, BigEndian, ReadBytesExt};\r
+\r
+#[test]\r
+fn make_le_u32_slice() {\r
+    // as seen on https://doc.rust-lang.org/std/primitive.u32.html#method.to_le\r
+    let n = 0x1Au32;\r
+\r
+    let mut n_le = [n];\r
+    n_le.convert_current_to_little_endian();\r
+\r
+    if cfg!(target_endian = "little") {\r
+        assert_eq!(n_le, [n])\r
+    }\r
+    else {\r
+        assert_eq!(n_le, [u32::swap_bytes(n)])\r
+    }\r
+\r
+//    assert_eq!(n_le, byteorder::LittleEndian::from_)\r
+}\r
+\r
+#[test]\r
+fn make_be_u32_slice() {\r
+    // as seen on https://doc.rust-lang.org/std/primitive.u32.html#method.to_be\r
+    let n = 0x1Au32;\r
+\r
+    let mut n_be = [n];\r
+    n_be.convert_current_to_big_endian();\r
+\r
+    if cfg!(target_endian = "big") {\r
+        assert_eq!(n_be, [n])\r
+    }\r
+    else {\r
+        assert_eq!(n_be, [n.swap_bytes()])\r
+    }\r
+}\r
+\r
+#[test]\r
+fn make_le_u16_slice() {\r
+    // as seen on https://doc.rust-lang.org/std/primitive.u16.html#method.to_le\r
+    let n = 0x1Au16;\r
+\r
+    let mut n_le = [n];\r
+    n_le.convert_current_to_little_endian();\r
+\r
+    if cfg!(target_endian = "little") {\r
+        assert_eq!(n_le, [n])\r
+    }\r
+    else {\r
+        assert_eq!(n_le, [n.swap_bytes()])\r
+    }\r
+}\r
+\r
+#[test]\r
+fn make_le_i64_slice() {\r
+    // as seen on https://doc.rust-lang.org/std/primitive.u64.html#method.to_be\r
+    let n1 = 0x14F3EEBCCD93895A_i64;\r
+    let n2 = 0x114F3EF99B81CC5A_i64;\r
+\r
+    let mut n_be = [n1, n2];\r
+    n_be.convert_current_to_big_endian();\r
+\r
+    if cfg!(target_endian = "big") {\r
+        assert_eq!(n_be, [n1, n2])\r
+    }\r
+    else {\r
+        assert_eq!(n_be, [n1.swap_bytes(), n2.swap_bytes()])\r
+    }\r
+}\r
+\r
+#[test]\r
+fn make_be_f64() {\r
+    let i = 0x14F3EEBCCD93895A_u64;\r
+\r
+    let mut f: f64 = unsafe { mem::transmute(i) };\r
+    f.convert_current_to_big_endian();\r
+\r
+    assert_eq!(f, unsafe { mem::transmute(i.to_be()) })\r
+}\r
+\r
+#[test]\r
+fn into_be_f64() {\r
+    let i = 0x14F3EEBCCD93895A_u64;\r
+\r
+    let f: f64 = unsafe { mem::transmute(i) };\r
+    let f = f.from_current_into_big_endian();\r
+\r
+    assert_eq!(f, unsafe { mem::transmute(i.to_be()) })\r
+}\r
+\r
+#[test]\r
+fn into_be_i16() {\r
+    let i = 0x195A_i16;\r
+    let be = i.from_current_into_big_endian();\r
+\r
+    if cfg!(target_endian = "big") {\r
+        assert_eq!(be, i)\r
+    }\r
+    else {\r
+        assert_eq!(be, i.swap_bytes())\r
+    }\r
+}\r
+\r
+#[test]\r
+fn into_be_u32() {\r
+    let i = 0x1220943_u32;\r
+    let be = i.from_current_into_big_endian();\r
+\r
+    if cfg!(target_endian = "big") {\r
+        assert_eq!(be, i)\r
+    }\r
+    else {\r
+        assert_eq!(be, i.swap_bytes())\r
+    }\r
+}\r
+\r
+#[test]\r
+fn cmp_read_be_u16() {\r
+    let read: &[u8] = &[0x33, 0xbb];\r
+    let a = u16::read_from_big_endian(&mut read.clone()).unwrap();\r
+    let b: u16 = read.clone().read_from_big_endian().unwrap();\r
+    let c = read.clone().read_u16::<BigEndian>().unwrap();\r
+\r
+    assert_eq!(a, b);\r
+    assert_eq!(a, c);\r
+}\r
+\r
+#[test]\r
+fn cmp_read_le_u16() {\r
+    let read: &[u8] = &[0x33, 0xbb];\r
+    let a = u16::read_from_little_endian(&mut read.clone()).unwrap();\r
+    let b: u16 = read.clone().read_from_little_endian().unwrap();\r
+    let c = read.clone().read_u16::<LittleEndian>().unwrap();\r
+\r
+    assert_eq!(a, b);\r
+    assert_eq!(a, c);\r
+}\r
+\r
+#[test]\r
+fn cmp_read_le_f32() {\r
+    let read: &[u8] = &[0x33, 0xBB, 0x44, 0xCC];\r
+    let a = f32::read_from_little_endian(&mut read.clone()).unwrap();\r
+    let b: f32 = read.clone().read_from_little_endian().unwrap();\r
+    let c = read.clone().read_f32::<LittleEndian>().unwrap();\r
+\r
+    assert_eq!(a, b);\r
+    assert_eq!(a, c);\r
+}\r
+\r
+#[test]\r
+fn cmp_read_be_slice()  {\r
+    let mut write_expected = Vec::new();\r
+    let mut write_actual = Vec::new();\r
+\r
+    let data: Vec<f32> = (0..31*31).map(|i| i as f32).collect();\r
+\r
+    for number in &data {\r
+        write_expected.write_f32::<BigEndian>(*number).unwrap();\r
+    }\r
+\r
+    write_actual.write_as_big_endian(data.as_slice()).unwrap();\r
+    assert_eq!(write_actual, write_expected);\r
+}\r
+\r
+#[test]\r
+fn cmp_write_le_slice() {\r
+    let mut write_expected = Vec::new();\r
+    let mut write_actual = Vec::new();\r
+\r
+    let data: Vec<f32> = (0..31*31).map(|i| i as f32).collect();\r
+\r
+    for number in &data {\r
+        write_expected.write_f32::<LittleEndian>(*number).unwrap();\r
+    }\r
+\r
+    write_actual.write_as_little_endian(data.as_slice()).unwrap();\r
+\r
+    assert_eq!(write_actual, write_expected);\r
+}\r
+\r
+#[test]\r
+fn cmp_write_le_u32() {\r
+    let mut write_expected = Vec::new();\r
+    let mut write_actual = Vec::new();\r
+\r
+    let data = 0x23573688_u32;\r
+    write_expected.write_u32::<LittleEndian>(data).unwrap();\r
+    write_actual.write_as_little_endian(&data).unwrap();\r
+\r
+    assert_eq!(write_actual, write_expected);\r
+}\r
+\r
+\r
+\r
+#[test]\r
+fn cmp_write_le_slice_u64() {\r
+    let mut write_expected = Vec::new();\r
+    let mut write_actual = Vec::new();\r
+\r
+    let data: Vec<u64> = (1000..1000+310*31).map(|i| i as u64).collect();\r
+\r
+    for number in &data {\r
+        write_expected.write_u64::<LittleEndian>(*number).unwrap();\r
+    }\r
+\r
+    write_actual.write_as_little_endian(data.as_slice()).unwrap();\r
+\r
+    assert_eq!(write_actual, write_expected);\r
+}
\ No newline at end of file