Skip to content

Commit

Permalink
Merge a6afa1a into fb8e338
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanq committed Jan 4, 2019
2 parents fb8e338 + a6afa1a commit 1c8cd28
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ All notable changes (and upcoming changes) to this crate will be documented in t
- CHANGELOG.md
- Travis CI configuration.
- Badges for documentation and build status on Travis.
- `Signs` trait which provides methods for checking the sign bit of a primitive and for sign-extending primitive values.

## 1.0.0 - 2018-12-23
### Added
Expand Down
47 changes: 32 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ Types for manipulating numeric primitives at the bit level.
[![Coverage Status](https://coveralls.io/repos/github/ryanq/quark/badge.svg?branch=master)](https://coveralls.io/github/ryanq/quark?branch=master)
[![Docs.rs](https://docs.rs/quark/badge.svg)](https://docs.rs/quark)

The `quark` crate provides traits for accessing parts of numeric primitives and adds new types
to represent numbers using bit counts that aren't included in the standard library.
The `quark` crate provides traits for accessing parts of numeric primitives and
adds new types to represent numbers using bit counts that aren't included in
the standard library.

## Bit Indexing

Accessing a bit or range of bits in a numeric primitive can be awkward and less than readable
using shifts and masks:
Accessing a bit or range of bits in a numeric primitive can be awkward and less
than readable using shifts and masks:

```rust
let big: u16 = 0x35;
Expand All @@ -22,7 +23,8 @@ assert_eq!(small, 0xd);

At a glance, it's not easy to parse things like:

- How many bits are contributing to the resulting value and which ones are definitely zero?
- How many bits are contributing to the resulting value and which ones are
definitely zero?
- Which bits in the original value are in the result?

Using the `BitIndex` trait, the above example can be written as:
Expand All @@ -37,8 +39,8 @@ assert_eq!(small, 0xd);

## Bit Masks

The `BitMask` trait allows for easily generating a bit mask using just the length and apply
masks:
The `BitMask` trait allows for easily generating a bit mask using just the
length and apply masks:

```rust
use quark::BitMask;
Expand All @@ -52,15 +54,30 @@ assert_eq!(value.mask_to(16), 0x5678);

## Bit Sizes

When implementing a trait on numeric types, sometimes the number of bits of a type will be
required. One way around this is adding a `bit_size()` or `bit_length()` method to the trait in
order to return a constant for each type. The `BitSize` trait adds a `BIT_SIZE` constant to the
numeric types that can be used in implementing traits without needing another method.
When implementing a trait on numeric types, sometimes the number of bits of a
type will be required. One way around this is adding a `bit_size()` or
`bit_length()` method to the trait in order to return a constant for each type.
The `BitSize` trait adds a `BIT_SIZE` constant to the numeric types that can be
used in implementing traits without needing another method.

## Sign Extension

The `Signs` trait adds methods for checking the sign bit on unsigned primitives
(and signed ones) and for sign-extending values an arbitrary number of bits:

```rust
use quark::Signs;

let unsigned = 0x00ff_ffffu32;
let signed = unsigned.sign_extend(8);
assert_eq!(signed, 0xffff_ffff);
```

## Why `quark`?

Because our programs are primitives at the very lowest level, types like `i32`, `u8`, and
`usize` are like atomic pieces of data. The `quark` crate goes to the next level down, and
quarks are at that next level w.r.t. atoms.
Because our programs are primitives at the very lowest level, types like `i32`,
`u8`, and `usize` are like atomic pieces of data. The `quark` crate goes to the
next level down, and quarks are at that next level w.r.t. atoms.

Also, I have an affinity for names with a 'Q' because my last name starts with one.
Also, I have an affinity for names with a 'Q' because my last name starts with
one.
13 changes: 13 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@
//! order to return a constant for each type. The `BitSize` trait adds a `BIT_SIZE` constant to the
//! numeric types that can be used in implementing traits without needing another method.
//!
//! # Sign Extension
//!
//! The `Signs` trait adds methods for checking the sign bit on unsigned primitives (and signed ones) and for sign-extending values an arbitrary number of bits:
//!
//! ```
//! # use quark::Signs;
//! # let unsigned = 0x00ff_ffffu32;
//! let signed = unsigned.sign_extend(8);
//! # assert_eq!(signed, 0xffff_ffff);
//! ```
//!
//! # Why `quark`?
//!
//! Because our programs are primitives at the very lowest level, types like `i32`, `u8`, and
Expand All @@ -62,7 +73,9 @@
mod bit_index;
mod bit_mask;
mod bit_size;
mod signs;

pub use self::bit_index::*;
pub use self::bit_mask::*;
pub use self::bit_size::*;
pub use self::signs::*;
107 changes: 107 additions & 0 deletions src/signs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
use crate::BitSize;

/// Provides operations based on signs
///
/// This trait defines functions for querying the sign bit of values and for sign extending values
/// from arbitrary values.
///
/// # Examples
///
/// ```
/// use quark::Signs;
///
/// let value: u32 = 0xffff_fff0;
/// assert_eq!(value.sign_bit(), true);
///
/// let value: u32 = 0x0000_8000;
/// assert_eq!(value.sign_extend(16), 0xffff_8000);
/// ```
pub trait Signs: BitSize {
/// Returns whether the sign bit is set.
fn sign_bit(&self) -> bool;

/// Fills the upper N bits of a value with the next bit down.
fn sign_extend(&self, bits: usize) -> Self;
}

macro_rules! signs_impl {
($s_type:ty) => {
impl Signs for $s_type {
fn sign_bit(&self) -> bool {
*self < 0
}

fn sign_extend(&self, bits: usize) -> Self {
if bits >= Self::BIT_SIZE {
0
} else {
self << bits >> bits
}
}
}
};
($u_type:ty, $s_type:ty) => {
impl Signs for $u_type {
fn sign_bit(&self) -> bool {
(*self as $s_type) < 0
}

fn sign_extend(&self, bits: usize) -> Self {
if bits >= Self::BIT_SIZE {
0
} else {
(((self << bits) as $s_type) >> bits) as $u_type
}
}
}
};
}

signs_impl!(i8);
signs_impl!(i16);
signs_impl!(i32);
signs_impl!(i64);
signs_impl!(i128);
signs_impl!(isize);
signs_impl!(u8, i8);
signs_impl!(u16, i16);
signs_impl!(u32, i32);
signs_impl!(u64, i64);
signs_impl!(u128, i128);
signs_impl!(usize, isize);

#[cfg(test)]
mod test {
use super::*;

#[test]
fn unsigned() {
let value: u32 = 0x0000_8000;
assert_eq!(value.sign_bit(), false);
assert_eq!(value.sign_extend(15), 0x0000_8000);
assert_eq!(value.sign_extend(16), 0xffff_8000);
assert_eq!(value.sign_extend(17), 0);

let value: u32 = 0x8000_8000;
assert_eq!(value.sign_bit(), true);
assert_eq!(value.sign_extend(15), 0x0000_8000);
assert_eq!(value.sign_extend(16), 0xffff_8000);
assert_eq!(value.sign_extend(17), 0);
}

#[test]
fn signed() {
let value: i32 = -65536; // 0xffff_0000
assert_eq!(value.sign_bit(), true);
assert_eq!(value.sign_extend(15), -65536);
assert_eq!(value.sign_extend(16), 0);
assert_eq!(value.sign_extend(17), 0);

let value: i32 = 0x7fff_0000;
assert_eq!(value.sign_bit(), false);
assert_eq!(value.sign_extend(1), -65536);
assert_eq!(value.sign_extend(15), -65536);
assert_eq!(value.sign_extend(16), 0);
assert_eq!(value.sign_extend(17), 0);
}
}

0 comments on commit 1c8cd28

Please sign in to comment.