Skip to content

Commit

Permalink
Rollup of PR #41
Browse files Browse the repository at this point in the history
  • Loading branch information
ImmemorConsultrixContrarie authored and myrrlyn committed Jan 23, 2020
1 parent 997b359 commit 5ad5cdf
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 191 deletions.
81 changes: 51 additions & 30 deletions src/fields.rs
Expand Up @@ -48,7 +48,11 @@ use crate::{
store::BitStore,
};

use core::mem;
use core::{
cmp,
mem,
ptr,
};

use either::Either;

Expand Down Expand Up @@ -900,36 +904,53 @@ zero-extends; where `U` is narrower, it truncates.
**/
fn resize<T, U>(value: T) -> U
where T: BitStore, U: BitStore {
let zero = 0usize;
let mut slab = zero.to_ne_bytes();
let start = 0;

/* Copy the source value into the correct region of the intermediate slab.
The `BitStore::as_bytes` method returns the value as native-endian-order
bytes. These bytes are then written into the correct location of the slab
(low addresses on little-endian, high addresses on big-endian) to be
interpreted as `usize`.
*/
match mem::size_of::<T>() {
n @ 1 | n @ 2 | n @ 4 | n @ 8 => {
#[cfg(target_endian = "big")]
let start = mem::size_of::<usize>() - n;

slab[start ..][.. n].copy_from_slice(value.as_bytes());
},
_ => unreachable!("BitStore is not implemented on types of this size"),
}
let mid = usize::from_ne_bytes(slab);
// Truncate to the correct size, then wrap in `U` through the trait method.
match mem::size_of::<U>() {
1 => U::from_bytes(&(mid as u8).to_ne_bytes()[..]),
2 => U::from_bytes(&(mid as u16).to_ne_bytes()[..]),
4 => U::from_bytes(&(mid as u32).to_ne_bytes()[..]),
#[cfg(target_pointer_width = "64")]
8 => U::from_bytes(&mid.to_ne_bytes()[..]),
_ => unreachable!("BitStore is not implemented on types of this size"),
let mut out = U::FALSE;
let bytes_t = mem::size_of::<T>();
let bytes_u = mem::size_of::<U>();

unsafe {
/* On big-endian targets, the significant bytes of a value are in the
high portion of its memory slot. Truncation reads only from the high
bytes; extension writes only into the high bytes.
Note: attributes are not currently supported on `if`-expressions, so
this must use the form `if cfg!` instead. `cfg!` is a compile-time macro
that expands to a constant `true` or `false` depending on the flag, so
this has the net effect of becoming either `if true {} else {}` or
`if false {} else {}`, eliminating the branch from actual codegen.
*/
if cfg!(target_endian = "big") {
// Truncate by reading the high bytes of `value` into `out`.
if bytes_t > bytes_u {
ptr::copy_nonoverlapping(
(&value as *const T as *const u8).add(bytes_t - bytes_u),
&mut out as *mut U as *mut u8,
bytes_u,
);
}
// Extend by writing `value` into the high bytes of `out`.
else {
ptr::copy_nonoverlapping(
&value as *const T as *const u8,
(&mut out as *mut U as *mut u8).add(bytes_u - bytes_t),
bytes_t,
);
}
}
/* On little-endian targets, the significant bytes of a value are in the
low portion of its memory slot. Truncation and extension are both plain
copies into the start of a zero-buffer, for the smaller width.
*/
else {
ptr::copy_nonoverlapping(
&value as *const T as *const u8,
&mut out as *mut U as *mut u8,
cmp::min(bytes_t, bytes_u),
);
}
}

out
}

#[allow(clippy::inconsistent_digit_grouping)]
Expand Down
196 changes: 35 additions & 161 deletions src/store.rs
Expand Up @@ -36,7 +36,6 @@ use core::{
Shr,
ShrAssign,
},
slice,
};

use radium::marker::BitOps;
Expand Down Expand Up @@ -249,33 +248,6 @@ pub trait BitStore:
// invert (0 becomes 1, 1 becomes 0), zero-extend, count ones
<Self as BitStore>::count_ones(!self)
}

/// Interprets a value as a sequence of bytes.
///
/// # Parameters
///
/// - `&self`
///
/// # Returns
///
/// A slice covering `*self` as a sequence of individual bytes.
fn as_bytes(&self) -> &[u8];

/// Interprets a sequence of bytes as `Self`.
///
/// # Parameters
///
/// - `bytes`: The bytes to interpret as `Self`. This must be exactly
/// `mem::size_of::<Self>` bytes long.
///
/// # Returns
///
/// An instance of `Self` constructed by reinterpreting `bytes`.
///
/// # Panics
///
/// This panics if `bytes.len()` is not `mem::size_of::<Self>()`.
fn from_bytes(bytes: &[u8]) -> Self;
}

/** Compute the number of elements required to store a number of bits.
Expand All @@ -297,144 +269,44 @@ pub const fn elts<T>(bits: usize) -> usize {
bits / width + (bits % width != 0) as usize
}

impl BitStore for u8 {
const TYPENAME: &'static str = "u8";
/// Batch implementation of `BitStore` for the appropriate fundamental integers.
macro_rules! bitstore {
($($t:ty => $bits:literal , $atom:ty ;)*) => { $(
impl BitStore for $t {
const TYPENAME: &'static str = stringify!($t);

const FALSE: Self = 0;
const TRUE: Self = !0;
const FALSE: Self = 0;
const TRUE: Self = !0;

#[cfg(feature = "atomic")]
type Access = atomic::AtomicU8;
#[cfg(feature = "atomic")]
type Access = $atom;

#[cfg(not(feature = "atomic"))]
type Access = Cell<Self>;
#[cfg(not(feature = "atomic"))]
type Access = Cell<Self>;

#[inline]
fn as_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Self as *const u8, 1) }
}

#[inline]
fn from_bytes(bytes: &[u8]) -> Self {
bytes
.try_into()
.map(Self::from_ne_bytes)
.expect("<u8 as BitStore>::from_bytes requires a slice of length 1")
}
#[inline(always)]
fn count_ones(self) -> usize {
Self::count_ones(self) as usize
}
}
)* };
}

impl BitStore for u16 {
const TYPENAME: &'static str = "u16";

const FALSE: Self = 0;
const TRUE: Self = !0;

#[cfg(feature = "atomic")]
type Access = atomic::AtomicU16;

#[cfg(not(feature = "atomic"))]
type Access = Cell<Self>;

#[inline]
fn as_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Self as *const u8, 2) }
}

#[inline]
fn from_bytes(bytes: &[u8]) -> Self {
bytes
.try_into()
.map(Self::from_ne_bytes)
.expect("<u16 as BitStore>::from_bytes requires a slice of length 2")
}
bitstore! {
u8 => 1, atomic::AtomicU8;
u16 => 2, atomic::AtomicU16;
u32 => 4, atomic::AtomicU32;
}

impl BitStore for u32 {
const TYPENAME: &'static str = "u32";

const FALSE: Self = 0;
const TRUE: Self = !0;

#[cfg(feature = "atomic")]
type Access = atomic::AtomicU32;

#[cfg(not(feature = "atomic"))]
type Access = Cell<Self>;

#[inline]
fn as_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Self as *const u8, 4) }
}

#[inline]
fn from_bytes(bytes: &[u8]) -> Self {
bytes
.try_into()
.map(Self::from_ne_bytes)
.expect("<u32 as BitStore>::from_bytes requires a slice of length 4")
}
#[cfg(target_pointer_width = "32")]
bitstore! {
usize => 4, atomic::AtomicUsize;
}

#[cfg(target_pointer_width = "64")]
impl BitStore for u64 {
const TYPENAME: &'static str = "u64";

const FALSE: Self = 0;
const TRUE: Self = !0;

#[cfg(feature = "atomic")]
type Access = atomic::AtomicU64;

#[cfg(not(feature = "atomic"))]
type Access = Cell<Self>;

#[inline]
fn as_bytes(&self) -> &[u8] {
unsafe { slice::from_raw_parts(self as *const Self as *const u8, 8) }
}

#[inline]
fn from_bytes(bytes: &[u8]) -> Self {
bytes
.try_into()
.map(Self::from_ne_bytes)
.expect("<u64 as BitStore>::from_bytes requires a slice of length 8")
}
}

impl BitStore for usize {
#[cfg(target_pointer_width = "32")]
const TYPENAME: &'static str = "u32";

#[cfg(target_pointer_width = "64")]
const TYPENAME: &'static str = "u64";

const FALSE: Self = 0;
const TRUE: Self = !0;

#[cfg(feature = "atomic")]
type Access = atomic::AtomicUsize;

#[cfg(not(feature = "atomic"))]
type Access = Cell<Self>;

#[inline]
fn as_bytes(&self) -> &[u8] {
unsafe {
slice::from_raw_parts(
self as *const Self as *const u8,
size_of::<Self>(),
)
}
}

#[inline]
fn from_bytes(bytes: &[u8]) -> Self {
bytes
.try_into()
.map(Self::from_ne_bytes)
.expect("<usize as BitStore>::from_bytes requires a slice of its exact width in bytes")
}
bitstore! {
u64 => 8, atomic::AtomicU64;
usize => 8, atomic::AtomicUsize;
}

#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
Expand All @@ -450,11 +322,13 @@ private, this trait effectively forbids downstream implementation of the
#[doc(hidden)]
pub trait Sealed {}

impl Sealed for u8 {}
impl Sealed for u16 {}
impl Sealed for u32 {}
macro_rules! seal {
($($t:ty),*) => { $(
impl Sealed for $t {}
)* };
}

#[cfg(target_pointer_width = "64")]
impl Sealed for u64 {}
seal!(u8, u16, u32, usize);

impl Sealed for usize {}
#[cfg(target_pointer_width = "64")]
seal!(u64);

0 comments on commit 5ad5cdf

Please sign in to comment.