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 | |