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