One thing I'm going to do first is create an enum with only two values. This represents a binary value and is more memory-efficient than passing around a bunch of u8s. This way 16 Bits can be represented as 16 actual bits in memory (though this isn't guaranteed).
/// a single bit
#[derive(Clone, Copy, Debug)]
enum Bit {
/// 1, high
H1 = 1,
/// 0, low
L0 = 0,
}
use Bit::*; // allow us to just use `L0` and `H1` without the `Bit::` prefix
It is likely much faster to split a number into bits using numerical operators. There are a couple ways of doing it.
- Iterating a mask
With this we increase our mask each time, building an array from it. The 15 - i is there because we want the MSB at index 0.
/// convert a number to 16 bits by sliding a mask across it
fn into_bits_mask(num: u16) -> [Bit; 16] {
let mut out = [L0; 16];
for i in 0..16 {
out[15 - i] = if num & (1u16 << i) > 0 {
H1
} else {
L0
};
}
out
}
- Shifting the number with a static mask
This is essentially the same thing, but we shift the number instead of the mask.
/// convert a number to 16 bits by right-shifting it
fn into_bits_shift(num: u16) -> [Bit; 16] {
let mut out = [L0; 16];
for i in 0..16 {
out[15 - i] = if (num >> i) & 1u16 > 0 {
H1
} else {
L0
};
}
out
}
We can then modify these to output an array of 4 nibbles.
/// convert a number to 4 nibbles by sliding a mask across it
fn into_nibbles_mask(num: u16) -> [[Bit; 4]; 4] {
let mut out = [[L0; 4]; 4];
for i in 0..16 {
let mask = 1u16 << (15 - i);
out[i / 4][i % 4] = if num & mask > 0 {
H1
} else {
L0
};
}
out
}
/// convert a number to 4 nibbles by right-shifting it
fn into_nibbles_shift(num: u16) -> [[Bit; 4]; 4] {
let mut out = [[L0; 4]; 4];
for i in 0..16 {
out[i /4][i % 4] = if (num >> (15 - i)) & 1u16 > 0 {
H1
} else {
L0
};
}
out
}
There is room for more optimization here, of course.
Here's a working example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=b010e34728d554e995e0de4ddb4b1eed
EDIT: I was asked about using iterators, so here's my most iterator-function styled method
/// convert a number to 4 nibbles using iterator methods
fn into_nibbles_iter(num: u16) -> Vec<Vec<Bit>> {
// split into nibbles
[num >> 12, num >> 8, num >> 4, num]
.iter()
.map(|nibble| {
// mask off each bit
[nibble & 8, nibble & 4, nibble & 2, nibble & 1]
.iter()
// convert to Bits
.map(|b| if b > &0 { H1 } else { L0 })
.collect()
})
.collect()
}
And here's a new playground link demonstrating it: https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=5ecda81379c3cab749709f551109adfb