replace bit_vec module with bitvec library

This commit is contained in:
Vinzenz Schroeter 2024-06-03 21:08:26 +02:00
parent c600761f29
commit 947a3fe60e
16 changed files with 187 additions and 369 deletions

View file

@ -14,6 +14,7 @@ crate-type = ["rlib"]
[dependencies]
log = "0.4"
bitvec = "1.0"
flate2 = { version = "1.0", optional = true }
bzip2 = { version = "0.4", optional = true }
zstd = { version = "0.13", optional = true }

View file

@ -4,7 +4,7 @@ use std::time::Duration;
use clap::Parser;
use servicepoint::*;
use servicepoint::{bitvec::prelude::BitVec, *};
#[derive(Parser, Debug)]
struct Cli {
@ -34,7 +34,7 @@ fn main() {
// this works because the pixel grid has max size
let pixel_data: Vec<u8> = enabled_pixels.clone().into();
let bit_vec = BitVec::from(&*pixel_data);
let bit_vec = BitVec::from_vec(pixel_data);
connection
.send(Command::BitmapLinearAnd(0, bit_vec, CompressionCode::Lzma))

View file

@ -1,257 +0,0 @@
use crate::DataRef;
/// A fixed-size vector of bits
#[derive(Debug, Clone, PartialEq)]
pub struct BitVec {
size: usize,
data: Vec<u8>,
}
impl BitVec {
/// Create a new `BitVec`.
///
/// # Arguments
///
/// * `size`: size in bits.
///
/// returns: `BitVec` with all bits set to false.
///
/// # Panics
///
/// When `size` is not divisible by 8.
#[must_use]
pub fn new(size: usize) -> BitVec {
assert_eq!(size % 8, 0);
Self {
size,
data: vec![0; size / 8],
}
}
/// Sets the value of a bit.
///
/// # Arguments
///
/// * `index`: the bit index to edit
/// * `value`: the value to set the bit to
///
/// returns: old value of the bit
///
/// # Panics
///
/// When accessing `index` out of bounds.
pub fn set(&mut self, index: usize, value: bool) -> bool {
let (byte_index, bit_mask) = self.get_indexes(index);
let byte = self.data[byte_index];
let old_value = byte & bit_mask != 0;
self.data[byte_index] = if value {
byte | bit_mask
} else {
byte & (u8::MAX ^ bit_mask)
};
old_value
}
/// Gets the value of a bit.
///
/// # Arguments
///
/// * `index`: the bit index to read
///
/// returns: value of the bit
///
/// # Panics
///
/// When accessing `index` out of bounds.
#[must_use]
pub fn get(&self, index: usize) -> bool {
let (byte_index, bit_mask) = self.get_indexes(index);
self.data[byte_index] & bit_mask != 0
}
/// Sets all bits to the specified value
///
/// # Arguments
///
/// * `value`: the value to set all bits to
///
/// # Examples
/// ```
/// use servicepoint::BitVec;
/// let mut vec = BitVec::new(8);
/// vec.fill(true);
/// ```
pub fn fill(&mut self, value: bool) {
let byte: u8 = if value { 0xFF } else { 0x00 };
self.data.fill(byte);
}
/// Gets the length in bits
#[must_use]
pub fn len(&self) -> usize {
self.size
}
/// returns true if length is 0.
#[must_use]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
/// Get an iterator over every bit in the vector
pub fn iter(&self) -> Iter {
Iter {
bit_vec: self,
index: 0,
end: self.size,
}
}
/// Calculates the byte index and bitmask for a specific bit in the vector
fn get_indexes(&self, bit_index: usize) -> (usize, u8) {
assert!(
bit_index < self.size,
"bit index {bit_index} is outside of range 0..<{}",
self.size
);
let byte_index = bit_index / 8;
let bit_in_byte_index = 7 - bit_index % 8;
let bit_mask: u8 = 1 << bit_in_byte_index;
(byte_index, bit_mask)
}
}
impl DataRef for BitVec {
fn data_ref_mut(&mut self) -> &mut [u8] {
self.data.as_mut_slice()
}
fn data_ref(&self) -> &[u8] {
self.data.as_slice()
}
}
impl From<BitVec> for Vec<u8> {
/// Turns the `BitVec` into the underlying `Vec<u8>`
fn from(value: BitVec) -> Self {
value.data
}
}
impl From<&[u8]> for BitVec {
/// Interpret the data as a series of bits and load then into a new `BitVec` instance.
fn from(value: &[u8]) -> Self {
Self {
size: value.len() * 8,
data: Vec::from(value),
}
}
}
pub struct Iter<'t> {
pub(crate) bit_vec: &'t BitVec,
pub(crate) index: usize,
pub(crate) end: usize,
}
impl<'t> Iterator for Iter<'t> {
type Item = bool;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.end {
return None;
}
let result = Some(self.bit_vec.get(self.index));
self.index += 1;
result
}
}
#[cfg(test)]
mod tests {
use crate::{BitVec, DataRef};
#[test]
fn fill() {
let mut vec = BitVec::new(8 * 3);
assert_eq!(vec.data, [0x00, 0x00, 0x00]);
vec.fill(true);
assert_eq!(vec.data, [0xFF, 0xFF, 0xFF]);
vec.fill(false);
assert_eq!(vec.data, [0x00, 0x00, 0x00]);
}
#[test]
fn get_set() {
let mut vec = BitVec::new(8 * 3);
assert!(!vec.get(1));
assert!(!vec.get(11));
vec.set(1, true);
vec.set(11, true);
assert_eq!(vec.data, [0x40, 0x10, 0x00]);
assert!(!vec.get(0));
assert!(vec.get(1));
assert!(vec.get(11));
}
#[test]
fn load() {
let mut vec = BitVec::new(8 * 3);
vec.set(6, true);
vec.set(7, true);
vec.set(8, true);
vec.set(9, true);
vec.set(10, true);
vec.set(vec.len() - 1, true);
assert_eq!(vec.data, [0x03, 0xE0, 0x01]);
let data: Vec<u8> = vec.into();
let vec = BitVec::from(&*data);
assert_eq!(vec.data, [0x03, 0xE0, 0x01]);
}
#[test]
fn mut_data_ref() {
let mut vec = BitVec::new(8 * 3);
let data_ref = vec.data_ref_mut();
data_ref.copy_from_slice(&[0x40, 0x10, 0x00]);
assert_eq!(vec.data, [0x40, 0x10, 0x00]);
assert!(vec.get(1));
}
#[test]
fn is_empty() {
let vec = BitVec::new(8 * 3);
assert!(!vec.is_empty());
let vec = BitVec::new(0);
assert!(vec.is_empty());
}
#[test]
fn get_returns_old() {
let mut vec = BitVec::new(8);
assert!(!vec.set(1, true));
assert!(vec.set(1, true));
assert!(vec.set(1, false));
assert!(!vec.set(1, false));
}
#[test]
fn debug_print() {
let vec = BitVec::new(8 * 3);
format!("{vec:?}");
}
}

View file

@ -85,17 +85,12 @@ impl Grid<u8> for ByteGrid {
/// * `x` and `y`: position of the cell
/// * `value`: the value to write to the cell
///
/// returns: old value of the cell.
///
/// # Panics
///
/// When accessing `x` or `y` out of bounds.
fn set(&mut self, x: usize, y: usize, value: u8) -> u8 {
fn set(&mut self, x: usize, y: usize, value: u8) {
self.check_indexes(x, y);
let pos = &mut self.data[x + y * self.width];
let old_val = *pos;
*pos = value;
old_val
self.data[x + y * self.width] = value;
}
/// Gets the current value at the specified position.

View file

@ -1,7 +1,9 @@
use bitvec::prelude::{BitVec, Msb0};
use crate::command_code::CommandCode;
use crate::compression::{into_compressed, into_decompressed};
use crate::{
BitVec, ByteGrid, CompressionCode, Grid, Header, Packet, PixelGrid,
ByteGrid, CompressionCode, Grid, Header, Packet, PixelGrid, SpBitVec,
TILE_SIZE,
};
@ -25,6 +27,7 @@ pub type Offset = usize;
/// Type alias for documenting the meaning of the u16 in enum values
pub type Brightness = u8;
// TODO: check order
/// A command to send to the display.
#[derive(Debug, Clone, PartialEq)]
pub enum Command {
@ -43,16 +46,16 @@ pub enum Command {
BitmapLegacy,
/// Set pixel data starting at the offset.
/// The contained `BitVec` is always uncompressed.
BitmapLinear(Offset, BitVec, CompressionCode),
BitmapLinear(Offset, SpBitVec, CompressionCode),
/// Set pixel data according to an and-mask starting at the offset.
/// The contained `BitVec` is always uncompressed.
BitmapLinearAnd(Offset, BitVec, CompressionCode),
BitmapLinearAnd(Offset, SpBitVec, CompressionCode),
/// Set pixel data according to an or-mask starting at the offset.
/// The contained `BitVec` is always uncompressed.
BitmapLinearOr(Offset, BitVec, CompressionCode),
BitmapLinearOr(Offset, SpBitVec, CompressionCode),
/// Set pixel data according to a xor-mask starting at the offset.
/// The contained `BitVec` is always uncompressed.
BitmapLinearXor(Offset, BitVec, CompressionCode),
BitmapLinearXor(Offset, SpBitVec, CompressionCode),
/// Show text on the screen. Note that the byte data has to be CP437 encoded.
Cp437Data(Origin, ByteGrid),
/// Sets a window of pixels to the specified values
@ -366,7 +369,7 @@ impl Command {
/// Helper method for Packets into `BitMapLinear*`-Commands
fn packet_into_linear_bitmap(
packet: Packet,
) -> Result<(BitVec, CompressionCode), TryFromPacketError> {
) -> Result<(BitVec<u8, Msb0>, CompressionCode), TryFromPacketError> {
let Packet(Header(_, _, length, sub, reserved), payload) = packet;
if reserved != 0 {
return Err(TryFromPacketError::ExtraneousHeaderValues);
@ -387,17 +390,18 @@ impl Command {
payload.len(),
));
}
Ok((BitVec::from(&*payload), sub))
Ok((BitVec::from_vec(payload), sub))
}
}
#[cfg(test)]
mod tests {
use bitvec::prelude::BitVec;
use crate::command::TryFromPacketError;
use crate::command_code::CommandCode;
use crate::{
BitVec, ByteGrid, Command, CompressionCode, Grid, Header, Origin,
Packet, PixelGrid,
ByteGrid, Command, CompressionCode, Header, Origin, Packet, PixelGrid,
};
fn round_trip(original: Command) {
@ -462,20 +466,24 @@ mod tests {
#[test]
fn round_trip_bitmap_linear() {
for compression in all_compressions().to_owned() {
round_trip(Command::BitmapLinear(23, BitVec::new(40), compression));
round_trip(Command::BitmapLinear(
23,
BitVec::repeat(false, 40),
compression,
));
round_trip(Command::BitmapLinearAnd(
23,
BitVec::new(40),
BitVec::repeat(false, 40),
compression,
));
round_trip(Command::BitmapLinearOr(
23,
BitVec::new(40),
BitVec::repeat(false, 40),
compression,
));
round_trip(Command::BitmapLinearXor(
23,
BitVec::new(40),
BitVec::repeat(false, 40),
compression,
));
round_trip(Command::BitmapLinearWin(
@ -590,8 +598,12 @@ mod tests {
#[test]
fn error_decompression_failed_and() {
for compression in all_compressions().to_owned() {
let p: Packet =
Command::BitmapLinearAnd(0, BitVec::new(8), compression).into();
let p: Packet = Command::BitmapLinearAnd(
0,
BitVec::repeat(false, 8),
compression,
)
.into();
let Packet(header, mut payload) = p;
// mangle it
@ -633,7 +645,7 @@ mod tests {
fn error_reserved_used() {
let Packet(header, payload) = Command::BitmapLinear(
0,
BitVec::new(8),
BitVec::repeat(false, 8),
CompressionCode::Uncompressed,
)
.into();
@ -649,7 +661,7 @@ mod tests {
fn error_invalid_compression() {
let Packet(header, payload) = Command::BitmapLinear(
0,
BitVec::new(8),
BitVec::repeat(false, 8),
CompressionCode::Uncompressed,
)
.into();
@ -665,7 +677,7 @@ mod tests {
fn error_unexpected_size() {
let Packet(header, payload) = Command::BitmapLinear(
0,
BitVec::new(8),
BitVec::repeat(false, 8),
CompressionCode::Uncompressed,
)
.into();

View file

@ -6,12 +6,10 @@ pub trait Grid<T> {
///
/// * `x` and `y`: position of the cell to read
///
/// returns: the old value
///
/// # Panics
///
/// When accessing `x` or `y` out of bounds.
fn set(&mut self, x: usize, y: usize, value: T) -> T;
fn set(&mut self, x: usize, y: usize, value: T);
/// Get the current value at the specified position
///
@ -46,11 +44,12 @@ pub trait Grid<T> {
/// * `x` and `y`: position of the cell to read
///
/// returns: the old value or None
fn set_optional(&mut self, x: isize, y: isize, value: T) -> Option<T> {
fn set_optional(&mut self, x: isize, y: isize, value: T) -> bool {
if self.is_in_bounds(x, y) {
Some(self.set(x as usize, y as usize, value))
self.set(x as usize, y as usize, value);
true
} else {
None
false
}
}

View file

@ -2,7 +2,6 @@
use std::time::Duration;
pub use crate::bit_vec::BitVec;
pub use crate::byte_grid::ByteGrid;
pub use crate::command::{Brightness, Command, Offset, Origin};
pub use crate::compression_code::CompressionCode;
@ -11,8 +10,11 @@ pub use crate::data_ref::DataRef;
pub use crate::grid::{Grid, RefGrid};
pub use crate::packet::{Header, Packet, Payload};
pub use crate::pixel_grid::PixelGrid;
pub use bitvec;
use bitvec::prelude::{BitVec, Msb0};
type SpBitVec = BitVec<u8, Msb0>;
mod bit_vec;
mod byte_grid;
mod command;
mod command_code;

View file

@ -1,11 +1,14 @@
use crate::{BitVec, DataRef, Grid, PIXEL_HEIGHT, PIXEL_WIDTH};
use crate::{BitVec, DataRef, Grid, SpBitVec, PIXEL_HEIGHT, PIXEL_WIDTH};
use bitvec::order::Msb0;
use bitvec::prelude::BitSlice;
use bitvec::slice::Iter;
/// A grid of pixels stored in packed bytes.
#[derive(Debug, Clone, PartialEq)]
pub struct PixelGrid {
width: usize,
height: usize,
bit_vec: BitVec,
bit_vec: SpBitVec,
}
impl PixelGrid {
@ -26,7 +29,7 @@ impl PixelGrid {
Self {
width,
height,
bit_vec: BitVec::new(width * height),
bit_vec: BitVec::repeat(false, width * height),
}
}
@ -56,11 +59,11 @@ impl PixelGrid {
Self {
width,
height,
bit_vec: BitVec::from(data),
bit_vec: BitVec::from_slice(data),
}
}
pub fn iter(&self) -> crate::bit_vec::Iter {
pub fn iter(&self) -> Iter<'_, u8, Msb0> {
self.bit_vec.iter()
}
@ -98,13 +101,13 @@ impl Grid<bool> for PixelGrid {
/// # Panics
///
/// When accessing `x` or `y` out of bounds.
fn set(&mut self, x: usize, y: usize, value: bool) -> bool {
fn set(&mut self, x: usize, y: usize, value: bool) {
self.check_indexes(x, y);
self.bit_vec.set(x + y * self.width, value)
}
fn get(&self, x: usize, y: usize) -> bool {
self.bit_vec.get(x + y * self.width)
self.bit_vec[x + y * self.width]
}
/// Sets the state of all pixels in the `PixelGrid`.
@ -128,11 +131,11 @@ impl Grid<bool> for PixelGrid {
impl DataRef for PixelGrid {
fn data_ref_mut(&mut self) -> &mut [u8] {
self.bit_vec.data_ref_mut()
self.bit_vec.as_raw_mut_slice()
}
fn data_ref(&self) -> &[u8] {
self.bit_vec.data_ref()
self.bit_vec.as_raw_slice()
}
}
@ -149,19 +152,17 @@ pub struct IterRows<'t> {
}
impl<'t> Iterator for IterRows<'t> {
type Item = crate::bit_vec::Iter<'t>;
type Item = &'t BitSlice<u8, Msb0>;
fn next(&mut self) -> Option<Self::Item> {
if self.row >= self.pixel_grid.height {
return None;
}
let result = Some(crate::bit_vec::Iter {
bit_vec: &self.pixel_grid.bit_vec,
index: self.row * self.pixel_grid.width,
end: (self.row + 1) * self.pixel_grid.width,
});
let start = self.row * self.pixel_grid.width;
let end = start + self.pixel_grid.width;
self.row += 1;
result
Some(&self.pixel_grid.bit_vec[start..end])
}
}