mirror of
https://github.com/cccb/servicepoint.git
synced 2025-01-18 10:00:14 +01:00
Merge pull request #4 from cccb/additional-grid-features
Additional grid features
This commit is contained in:
commit
15a3c4d596
40
Cargo.lock
generated
40
Cargo.lock
generated
|
@ -95,6 +95,18 @@ version = "2.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bzip2"
|
||||
version = "0.4.4"
|
||||
|
@ -267,6 +279,12 @@ dependencies = [
|
|||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
|
@ -419,6 +437,12 @@ dependencies = [
|
|||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
|
@ -542,6 +566,7 @@ dependencies = [
|
|||
name = "servicepoint"
|
||||
version = "0.5.1"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"bzip2",
|
||||
"clap 4.5.4",
|
||||
"flate2",
|
||||
|
@ -602,6 +627,12 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.10.1"
|
||||
|
@ -766,6 +797,15 @@ version = "0.52.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.1"
|
||||
|
|
|
@ -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 }
|
||||
|
|
37
crates/servicepoint/examples/brightness_tester.rs
Normal file
37
crates/servicepoint/examples/brightness_tester.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
//! Show a brightness level test pattern on screen
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
use servicepoint::Command::BitmapLinearWin;
|
||||
use servicepoint::*;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
struct Cli {
|
||||
#[arg(short, long, default_value = "localhost:2342")]
|
||||
destination: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
let connection = Connection::open(cli.destination).unwrap();
|
||||
|
||||
let mut pixels = PixelGrid::max_sized();
|
||||
pixels.fill(true);
|
||||
|
||||
connection
|
||||
.send(BitmapLinearWin(
|
||||
Origin(0, 0),
|
||||
pixels,
|
||||
CompressionCode::Uncompressed,
|
||||
))
|
||||
.expect("send failed");
|
||||
|
||||
let mut brightnesses = ByteGrid::new(TILE_WIDTH, TILE_HEIGHT);
|
||||
for (index, byte) in brightnesses.data_ref_mut().iter_mut().enumerate() {
|
||||
*byte = (index % u8::MAX as usize) as u8;
|
||||
}
|
||||
|
||||
connection
|
||||
.send(Command::CharBrightness(Origin(0, 0), brightnesses))
|
||||
.expect("send failed");
|
||||
}
|
|
@ -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))
|
||||
|
|
|
@ -1,228 +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()
|
||||
}
|
||||
|
||||
/// 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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:?}");
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
use std::slice::{Iter, IterMut};
|
||||
|
||||
use crate::{DataRef, Grid};
|
||||
|
||||
/// A 2D grid of bytes
|
||||
|
@ -9,6 +11,22 @@ pub struct ByteGrid {
|
|||
}
|
||||
|
||||
impl ByteGrid {
|
||||
/// Creates a new `ByteGrid` with the specified dimensions.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - width: size in x-direction
|
||||
/// - height: size in y-direction
|
||||
///
|
||||
/// returns: `ByteGrid` initialized to 0.
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
Self {
|
||||
data: vec![0; width * height],
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads a `ByteGrid` with the specified dimensions from the provided data.
|
||||
///
|
||||
/// returns: `ByteGrid` that contains a copy of the provided data
|
||||
|
@ -26,32 +44,72 @@ impl ByteGrid {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_indexes(&self, x: usize, y: usize) {
|
||||
assert!(
|
||||
x < self.width,
|
||||
"cannot access byte {x}-{y} because x is outside of bounds 0..{}",
|
||||
self.width
|
||||
);
|
||||
assert!(
|
||||
y < self.height,
|
||||
"cannot access byte {x}-{y} because y is outside of bounds 0..{}",
|
||||
self.height
|
||||
);
|
||||
/// Iterate over all cells in `ByteGrid`.
|
||||
///
|
||||
/// Order is equivalent to the following loop:
|
||||
/// ```
|
||||
/// # use servicepoint::{ByteGrid, Grid};
|
||||
/// # let grid = ByteGrid::new(2,2);
|
||||
/// for y in 0..grid.height() {
|
||||
/// for x in 0..grid.width() {
|
||||
/// grid.get(x, y);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn iter(&self) -> Iter<u8> {
|
||||
self.data.iter()
|
||||
}
|
||||
|
||||
/// Iterate over all rows in `ByteGrid` top to bottom.
|
||||
pub fn iter_rows(&self) -> IterRows {
|
||||
IterRows {
|
||||
byte_grid: self,
|
||||
row: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator that allows modifying each value.
|
||||
///
|
||||
/// The iterator yields all cells from top left to bottom right.
|
||||
pub fn iter_mut(&mut self) -> IterMut<u8> {
|
||||
self.data.iter_mut()
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the current value at the specified position.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x` and `y`: position of the cell
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When accessing `x` or `y` out of bounds.
|
||||
pub fn get_ref_mut(&mut self, x: usize, y: usize) -> &mut u8 {
|
||||
self.assert_in_bounds(x, y);
|
||||
&mut self.data[x + y * self.width]
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the current value at the specified position if position is in bounds.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x` and `y`: position of the cell
|
||||
///
|
||||
/// returns: Reference to cell or None
|
||||
pub fn get_ref_mut_optional(
|
||||
&mut self,
|
||||
x: isize,
|
||||
y: isize,
|
||||
) -> Option<&mut u8> {
|
||||
if self.is_in_bounds(x, y) {
|
||||
Some(&mut self.data[x as usize + y as usize * self.width])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Grid<u8> for ByteGrid {
|
||||
/// Creates a new `ByteGrid` with the specified dimensions.
|
||||
///
|
||||
/// returns: `ByteGrid` initialized to 0.
|
||||
fn new(width: usize, height: usize) -> Self {
|
||||
Self {
|
||||
data: vec![0; width * height],
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the value of the cell at the specified position in the `ByteGrid.
|
||||
///
|
||||
/// # Arguments
|
||||
|
@ -59,17 +117,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 {
|
||||
self.check_indexes(x, y);
|
||||
let pos = &mut self.data[x + y * self.width];
|
||||
let old_val = *pos;
|
||||
*pos = value;
|
||||
old_val
|
||||
fn set(&mut self, x: usize, y: usize, value: u8) {
|
||||
self.assert_in_bounds(x, y);
|
||||
self.data[x + y * self.width] = value;
|
||||
}
|
||||
|
||||
/// Gets the current value at the specified position.
|
||||
|
@ -82,7 +135,7 @@ impl Grid<u8> for ByteGrid {
|
|||
///
|
||||
/// When accessing `x` or `y` out of bounds.
|
||||
fn get(&self, x: usize, y: usize) -> u8 {
|
||||
self.check_indexes(x, y);
|
||||
self.assert_in_bounds(x, y);
|
||||
self.data[x + y * self.width]
|
||||
}
|
||||
|
||||
|
@ -97,17 +150,6 @@ impl Grid<u8> for ByteGrid {
|
|||
fn height(&self) -> usize {
|
||||
self.height
|
||||
}
|
||||
|
||||
fn window(&self, x: usize, y: usize, w: usize, h: usize) -> Self {
|
||||
let mut win = Self::new(w, h);
|
||||
for win_x in 0..w {
|
||||
for win_y in 0..h {
|
||||
let value = self.get(x + win_x, y + win_y);
|
||||
win.set(win_x, win_y, value);
|
||||
}
|
||||
}
|
||||
win
|
||||
}
|
||||
}
|
||||
|
||||
impl DataRef for ByteGrid {
|
||||
|
@ -129,6 +171,27 @@ impl From<ByteGrid> for Vec<u8> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct IterRows<'t> {
|
||||
byte_grid: &'t ByteGrid,
|
||||
row: usize,
|
||||
}
|
||||
|
||||
impl<'t> Iterator for IterRows<'t> {
|
||||
type Item = Iter<'t, u8>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.row >= self.byte_grid.height {
|
||||
return None;
|
||||
}
|
||||
|
||||
let start = self.row * self.byte_grid.width;
|
||||
let end = start + self.byte_grid.width;
|
||||
let result = self.byte_grid.data[start..end].iter();
|
||||
self.row += 1;
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{ByteGrid, DataRef, Grid};
|
||||
|
@ -184,4 +247,73 @@ mod tests {
|
|||
assert_eq!(vec.data, [1, 2, 3, 4]);
|
||||
assert_eq!(vec.get(1, 0), 2)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter() {
|
||||
let mut vec = ByteGrid::new(2, 2);
|
||||
vec.set(1, 1, 5);
|
||||
|
||||
let mut iter = vec.iter();
|
||||
assert_eq!(*iter.next().unwrap(), 0);
|
||||
assert_eq!(*iter.next().unwrap(), 0);
|
||||
assert_eq!(*iter.next().unwrap(), 0);
|
||||
assert_eq!(*iter.next().unwrap(), 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter_mut() {
|
||||
let mut vec = ByteGrid::new(2, 3);
|
||||
for (index, cell) in vec.iter_mut().enumerate() {
|
||||
*cell = index as u8;
|
||||
}
|
||||
|
||||
assert_eq!(vec.data_ref(), [0, 1, 2, 3, 4, 5]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter_rows() {
|
||||
let vec = ByteGrid::load(2, 3, &[0, 1, 1, 2, 2, 3]);
|
||||
for (y, row) in vec.iter_rows().enumerate() {
|
||||
for (x, val) in row.enumerate() {
|
||||
assert_eq!(*val, (x + y) as u8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn out_of_bounds_x() {
|
||||
let mut vec = ByteGrid::load(2, 2, &[0, 1, 2, 3]);
|
||||
vec.set(2, 1, 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn out_of_bounds_y() {
|
||||
let vec = ByteGrid::load(2, 2, &[0, 1, 2, 3]);
|
||||
vec.get(1, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ref_mut() {
|
||||
let mut vec = ByteGrid::load(2, 2, &[0, 1, 2, 3]);
|
||||
|
||||
let top_left = vec.get_ref_mut(0, 0);
|
||||
*top_left += 5;
|
||||
|
||||
assert_eq!(None, vec.get_ref_mut_optional(2, 2));
|
||||
assert_eq!(Some(&mut 5), vec.get_ref_mut_optional(0, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn optional() {
|
||||
let mut grid = ByteGrid::load(2, 2, &[0, 1, 2, 3]);
|
||||
grid.set_optional(0, 0, 5);
|
||||
grid.set_optional(-1, 0, 8);
|
||||
grid.set_optional(0, 8, 42);
|
||||
assert_eq!(grid.data, [5, 1, 2, 3]);
|
||||
|
||||
assert_eq!(grid.get_optional(0, 0), Some(5));
|
||||
assert_eq!(grid.get_optional(0, 8), None);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use bitvec::prelude::BitVec;
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
|
@ -43,16 +45,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 +368,7 @@ impl Command {
|
|||
/// Helper method for Packets into `BitMapLinear*`-Commands
|
||||
fn packet_into_linear_bitmap(
|
||||
packet: Packet,
|
||||
) -> Result<(BitVec, CompressionCode), TryFromPacketError> {
|
||||
) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> {
|
||||
let Packet(Header(_, _, length, sub, reserved), payload) = packet;
|
||||
if reserved != 0 {
|
||||
return Err(TryFromPacketError::ExtraneousHeaderValues);
|
||||
|
@ -387,17 +389,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 +465,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 +597,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 +644,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 +660,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 +676,7 @@ mod tests {
|
|||
fn error_unexpected_size() {
|
||||
let Packet(header, payload) = Command::BitmapLinear(
|
||||
0,
|
||||
BitVec::new(8),
|
||||
BitVec::repeat(false, 8),
|
||||
CompressionCode::Uncompressed,
|
||||
)
|
||||
.into();
|
||||
|
|
|
@ -1,24 +1,58 @@
|
|||
/// A two-dimensional grid of `T`
|
||||
pub trait Grid<T> {
|
||||
#[must_use]
|
||||
/// Creates a new Grid with the specified dimensions.
|
||||
/// Sets the value at the specified position
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - width: size in x-direction
|
||||
/// - height: size in y-direction
|
||||
/// * `x` and `y`: position of the cell to read
|
||||
///
|
||||
/// returns: Grid with all cells initialized to default state.
|
||||
fn new(width: usize, height: usize) -> Self;
|
||||
|
||||
/// Sets the value at the specified position
|
||||
/// # Panics
|
||||
///
|
||||
/// returns: the old value
|
||||
fn set(&mut self, x: usize, y: usize, value: T) -> T;
|
||||
/// When accessing `x` or `y` out of bounds.
|
||||
fn set(&mut self, x: usize, y: usize, value: T);
|
||||
|
||||
/// Get the current value at the specified position
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x` and `y`: position of the cell to read
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When accessing `x` or `y` out of bounds.
|
||||
fn get(&self, x: usize, y: usize) -> T;
|
||||
|
||||
/// Get the current value at the specified position if the position is inside of bounds
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x` and `y`: position of the cell to read
|
||||
///
|
||||
/// returns: Value at position or None
|
||||
fn get_optional(&self, x: isize, y: isize) -> Option<T> {
|
||||
if self.is_in_bounds(x, y) {
|
||||
Some(self.get(x as usize, y as usize))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the value at the specified position if the position is inside of bounds
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `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) -> bool {
|
||||
if self.is_in_bounds(x, y) {
|
||||
self.set(x as usize, y as usize, value);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets all cells in the grid to the specified value
|
||||
fn fill(&mut self, value: T);
|
||||
|
||||
|
@ -28,35 +62,29 @@ pub trait Grid<T> {
|
|||
/// the height in y-direction
|
||||
fn height(&self) -> usize;
|
||||
|
||||
/// Creates a new instance containing the specified window.
|
||||
/// Checks whether the specified signed position is in grid bounds
|
||||
fn is_in_bounds(&self, x: isize, y: isize) -> bool {
|
||||
x >= 0
|
||||
&& x < self.width() as isize
|
||||
&& y >= 0
|
||||
&& y < self.height() as isize
|
||||
}
|
||||
|
||||
/// Asserts that the specified unsigned position is in grid bounds.
|
||||
///
|
||||
/// Use concrete types to avoid boxing.
|
||||
/// # Panics
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x`: column of the top left cell
|
||||
/// * `y`: row of the top left cell
|
||||
/// * `w`: size of window in x-direction
|
||||
/// * `h`: size of window in y-direction
|
||||
///
|
||||
/// returns: Self
|
||||
///
|
||||
/// # Examples
|
||||
/// To avoid boxing, this example is using the concrete type `ByteGrid`.
|
||||
/// ```
|
||||
/// use servicepoint::{ByteGrid, Grid};
|
||||
/// fn split(grid: ByteGrid) -> (ByteGrid, ByteGrid) {
|
||||
/// assert!(grid.width() >= 2);
|
||||
/// let split_x = grid.width() / 2;
|
||||
/// let right_w = grid.width() - split_x;
|
||||
///
|
||||
/// let left = grid.window(0, 0, split_x, grid.height());
|
||||
/// let right = grid.window(split_x, 0, right_w, grid.height());
|
||||
/// (left, right)
|
||||
/// }
|
||||
///
|
||||
/// let (l, r) = split(ByteGrid::new(9, 5));
|
||||
/// ```
|
||||
#[must_use]
|
||||
fn window(&self, x: usize, y: usize, w: usize, h: usize) -> Self;
|
||||
/// When the specified position is out of bounds for this grid.
|
||||
fn assert_in_bounds(&self, x: usize, y: usize) {
|
||||
assert!(
|
||||
x < self.width(),
|
||||
"cannot access index [{x}, {y}] because x is outside of bounds 0..{}",
|
||||
self.width()
|
||||
);
|
||||
assert!(
|
||||
y < self.height(),
|
||||
"cannot access byte [{x}, {y}] because y is outside of bounds 0..{}",
|
||||
self.height()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
use std::time::Duration;
|
||||
|
||||
pub use crate::bit_vec::BitVec;
|
||||
pub use bitvec;
|
||||
use bitvec::prelude::{BitVec, Msb0};
|
||||
|
||||
pub use crate::byte_grid::ByteGrid;
|
||||
pub use crate::command::{Brightness, Command, Offset, Origin};
|
||||
pub use crate::compression_code::CompressionCode;
|
||||
|
@ -12,7 +14,8 @@ pub use crate::grid::Grid;
|
|||
pub use crate::packet::{Header, Packet, Payload};
|
||||
pub use crate::pixel_grid::PixelGrid;
|
||||
|
||||
mod bit_vec;
|
||||
type SpBitVec = BitVec<u8, Msb0>;
|
||||
|
||||
mod byte_grid;
|
||||
mod command;
|
||||
mod command_code;
|
||||
|
|
|
@ -69,4 +69,10 @@ mod tests {
|
|||
let p = Packet::try_from(&*data).unwrap();
|
||||
assert_eq!(p, Packet(Header(0, 1, 2, 3, 4), vec![42u8; 23]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn too_small() {
|
||||
let data = vec![0u8; 4];
|
||||
assert_eq!(Packet::try_from(data.as_slice()), Err(()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,39 @@
|
|||
use crate::{BitVec, DataRef, Grid, PIXEL_HEIGHT, PIXEL_WIDTH};
|
||||
use bitvec::order::Msb0;
|
||||
use bitvec::prelude::BitSlice;
|
||||
use bitvec::slice::IterMut;
|
||||
|
||||
use crate::{BitVec, DataRef, Grid, SpBitVec, PIXEL_HEIGHT, PIXEL_WIDTH};
|
||||
|
||||
/// 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 {
|
||||
/// Creates a new `PixelGrid` with the specified dimensions.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `width`: size in pixels in x-direction
|
||||
/// * `height`: size in pixels in y-direction
|
||||
///
|
||||
/// returns: `PixelGrid` initialized to all pixels off
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - when the width is not dividable by 8
|
||||
pub fn new(width: usize, height: usize) -> Self {
|
||||
assert_eq!(width % 8, 0);
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
bit_vec: BitVec::repeat(false, width * height),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new pixel grid with the size of the whole screen.
|
||||
#[must_use]
|
||||
pub fn max_sized() -> Self {
|
||||
|
@ -35,46 +60,63 @@ impl PixelGrid {
|
|||
Self {
|
||||
width,
|
||||
height,
|
||||
bit_vec: BitVec::from(data),
|
||||
bit_vec: BitVec::from_slice(data),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_indexes(&self, x: usize, y: usize) {
|
||||
assert!(
|
||||
x < self.width,
|
||||
"cannot access pixel {x}-{y} because x is outside of bounds 0..{}",
|
||||
self.width
|
||||
);
|
||||
assert!(
|
||||
y < self.height,
|
||||
"cannot access pixel {x}-{y} because y is outside of bounds 0..{}",
|
||||
self.height
|
||||
);
|
||||
/// Iterate over all cells in `PixelGrid`.
|
||||
///
|
||||
/// Order is equivalent to the following loop:
|
||||
/// ```
|
||||
/// # use servicepoint::{PixelGrid, Grid};
|
||||
/// # let grid = PixelGrid::new(8,2);
|
||||
/// for y in 0..grid.height() {
|
||||
/// for x in 0..grid.width() {
|
||||
/// grid.get(x, y);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn iter(&self) -> impl Iterator<Item = &bool> {
|
||||
self.bit_vec.iter().by_refs()
|
||||
}
|
||||
|
||||
/// Iterate over all cells in `PixelGrid` mutably.
|
||||
///
|
||||
/// Order is equivalent to the following loop:
|
||||
/// ```
|
||||
/// # use servicepoint::{PixelGrid, Grid};
|
||||
/// # let mut grid = PixelGrid::new(8,2);
|
||||
/// # let value = false;
|
||||
/// for y in 0..grid.height() {
|
||||
/// for x in 0..grid.width() {
|
||||
/// grid.set(x, y, value);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use servicepoint::{PixelGrid, Grid};
|
||||
/// # let mut grid = PixelGrid::new(8,2);
|
||||
/// # let value = false;
|
||||
/// for (index, mut pixel) in grid.iter_mut().enumerate() {
|
||||
/// pixel.set(index % 2 == 0)
|
||||
/// }
|
||||
/// ```
|
||||
pub fn iter_mut(&mut self) -> IterMut<u8, Msb0> {
|
||||
self.bit_vec.iter_mut()
|
||||
}
|
||||
|
||||
/// Iterate over all rows in `PixelGrid` top to bottom.
|
||||
pub fn iter_rows(&self) -> IterRows {
|
||||
IterRows {
|
||||
pixel_grid: self,
|
||||
row: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Grid<bool> for PixelGrid {
|
||||
/// Creates a new `PixelGrid` with the specified dimensions.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `width`: size in pixels in x-direction
|
||||
/// * `height`: size in pixels in y-direction
|
||||
///
|
||||
/// returns: `PixelGrid` initialized to all pixels off
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// - when the width is not dividable by 8
|
||||
fn new(width: usize, height: usize) -> Self {
|
||||
assert_eq!(width % 8, 0);
|
||||
Self {
|
||||
width,
|
||||
height,
|
||||
bit_vec: BitVec::new(width * height),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the value of the specified position in the `PixelGrid`.
|
||||
///
|
||||
/// # Arguments
|
||||
|
@ -87,22 +129,14 @@ 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 {
|
||||
self.check_indexes(x, y);
|
||||
fn set(&mut self, x: usize, y: usize, value: bool) {
|
||||
self.assert_in_bounds(x, y);
|
||||
self.bit_vec.set(x + y * self.width, value)
|
||||
}
|
||||
|
||||
/// Gets the current value at the specified position.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `x` and `y`: position of the cell to read
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// When accessing `x` or `y` out of bounds.
|
||||
fn get(&self, x: usize, y: usize) -> bool {
|
||||
self.bit_vec.get(x + y * self.width)
|
||||
self.assert_in_bounds(x, y);
|
||||
self.bit_vec[x + y * self.width]
|
||||
}
|
||||
|
||||
/// Sets the state of all pixels in the `PixelGrid`.
|
||||
|
@ -122,28 +156,15 @@ impl Grid<bool> for PixelGrid {
|
|||
fn height(&self) -> usize {
|
||||
self.height
|
||||
}
|
||||
|
||||
fn window(&self, x: usize, y: usize, w: usize, h: usize) -> Self {
|
||||
// TODO: how to deduplicate?
|
||||
// this cannot be moved into the trait because there, Self is not Sized
|
||||
let mut win = Self::new(w, h);
|
||||
for win_x in 0..w {
|
||||
for win_y in 0..h {
|
||||
let value = self.get(x + win_x, y + win_y);
|
||||
win.set(win_x, win_y, value);
|
||||
}
|
||||
}
|
||||
win
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,6 +175,26 @@ impl From<PixelGrid> for Vec<u8> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct IterRows<'t> {
|
||||
pixel_grid: &'t PixelGrid,
|
||||
row: usize,
|
||||
}
|
||||
|
||||
impl<'t> Iterator for IterRows<'t> {
|
||||
type Item = &'t BitSlice<u8, Msb0>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.row >= self.pixel_grid.height {
|
||||
return None;
|
||||
}
|
||||
|
||||
let start = self.row * self.pixel_grid.width;
|
||||
let end = start + self.pixel_grid.width;
|
||||
self.row += 1;
|
||||
Some(&self.pixel_grid.bit_vec[start..end])
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{DataRef, Grid, PixelGrid};
|
||||
|
@ -201,4 +242,51 @@ mod tests {
|
|||
let grid = PixelGrid::load(8, 3, &data);
|
||||
assert_eq!(grid.data_ref(), [0xAA, 0x55, 0xAA]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn out_of_bounds_x() {
|
||||
let vec = PixelGrid::new(8, 2);
|
||||
vec.get(8, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn out_of_bounds_y() {
|
||||
let mut vec = PixelGrid::new(8, 2);
|
||||
vec.set(1, 2, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter() {
|
||||
let grid = PixelGrid::new(8, 2);
|
||||
assert_eq!(16, grid.iter().count())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter_rows() {
|
||||
let grid = PixelGrid::load(8, 2, &[0x04, 0x40]);
|
||||
let mut iter = grid.iter_rows();
|
||||
|
||||
assert_eq!(iter.next().unwrap().count_ones(), 1);
|
||||
assert_eq!(iter.next().unwrap().count_ones(), 1);
|
||||
assert_eq!(None, iter.next());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter_mut() {
|
||||
let mut grid = PixelGrid::new(8, 2);
|
||||
for (index, mut pixel) in grid.iter_mut().enumerate() {
|
||||
pixel.set(index % 2 == 0);
|
||||
}
|
||||
assert_eq!(grid.data_ref(), [0xAA, 0xAA]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn data_ref_mut() {
|
||||
let mut grid = PixelGrid::new(8, 2);
|
||||
let data = grid.data_ref_mut();
|
||||
data[1] = 0x0F;
|
||||
assert!(grid.get(7, 1));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,19 +17,19 @@ This crate contains C bindings for the `servicepoint` library, enabling users to
|
|||
#include "servicepoint.h"
|
||||
|
||||
int main(void) {
|
||||
sp2_Connection *connection = sp2_connection_open("localhost:2342");
|
||||
sp_Connection *connection = sp_connection_open("localhost:2342");
|
||||
if (connection == NULL)
|
||||
return 1;
|
||||
|
||||
sp2_PixelGrid *pixels = sp2_pixel_grid_new(sp2_PIXEL_WIDTH, sp2_PIXEL_HEIGHT);
|
||||
sp2_pixel_grid_fill(pixels, true);
|
||||
sp_PixelGrid *pixels = sp_pixel_grid_new(sp_PIXEL_WIDTH, sp_PIXEL_HEIGHT);
|
||||
sp_pixel_grid_fill(pixels, true);
|
||||
|
||||
sp2_Command *command = sp2_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
|
||||
sp2_Packet *packet = sp2_packet_from_command(command);
|
||||
if (!sp2_connection_send(connection, packet))
|
||||
sp_Command *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
|
||||
sp_Packet *packet = sp_packet_from_command(command);
|
||||
if (!sp_connection_send(connection, packet))
|
||||
return 1;
|
||||
|
||||
sp2_connection_dealloc(connection);
|
||||
sp_connection_dealloc(connection);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
@ -52,12 +52,12 @@ You have the choice of linking statically (recommended) or dynamically.
|
|||
|
||||
## Notes on differences to rust library
|
||||
|
||||
- function names are: `sp2_` \<struct_name\> \<rust name\>.
|
||||
- Use the rust documentation.
|
||||
- function names are: `sp_` \<struct_name\> \<rust name\>.
|
||||
- Instances get consumed in the same way they do when writing rust / C# code. Do not use an instance after an (implicit!) free.
|
||||
- Option<T> or Result<T, E> turn into nullable return values - check for NULL!
|
||||
- There are no specifics for C++ here yet. You might get a nicer header when generating directly for C++, but it should be usable.
|
||||
- Reading and writing to instances concurrently is not safe. Only reading concurrently is safe.
|
||||
- documentation is included in the header and available [online](https://docs.rs/servicepoint_binding_c/latest/servicepoint_binding_c/)
|
||||
|
||||
## Everything else
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
const SP_INCLUDE: &str = "DEP_SERVICEPOINT_INCLUDE";
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=src/main.c");
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-env-changed={SP_INCLUDE}");
|
||||
println!("cargo::rerun-if-changed=src/main.c");
|
||||
println!("cargo::rerun-if-changed=build.rs");
|
||||
println!("cargo::rerun-if-env-changed={SP_INCLUDE}");
|
||||
|
||||
let sp_include =
|
||||
std::env::var_os(SP_INCLUDE).unwrap().into_string().unwrap();
|
||||
|
|
|
@ -2,9 +2,29 @@
|
|||
//!
|
||||
//! prefix `sp_bit_vec_`
|
||||
|
||||
use servicepoint::{BitVec, DataRef};
|
||||
|
||||
use crate::c_slice::CByteSlice;
|
||||
use servicepoint::bitvec::prelude::{BitVec, Msb0};
|
||||
|
||||
/// cbindgen:no-export
|
||||
type SpBitVec = BitVec<u8, Msb0>;
|
||||
|
||||
/// Opaque struct needed for correct code generation.
|
||||
#[derive(Clone)]
|
||||
pub struct CBitVec {
|
||||
actual: SpBitVec,
|
||||
}
|
||||
|
||||
impl From<SpBitVec> for CBitVec {
|
||||
fn from(actual: SpBitVec) -> Self {
|
||||
Self { actual }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CBitVec> for SpBitVec {
|
||||
fn from(value: CBitVec) -> Self {
|
||||
value.actual
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `BitVec` instance.
|
||||
///
|
||||
|
@ -25,8 +45,10 @@ use crate::c_slice::CByteSlice;
|
|||
/// - the returned instance is freed in some way, either by using a consuming function or
|
||||
/// by explicitly calling `sp_bit_vec_dealloc`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut BitVec {
|
||||
Box::into_raw(Box::new(BitVec::new(size)))
|
||||
pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut CBitVec {
|
||||
Box::into_raw(Box::new(CBitVec {
|
||||
actual: SpBitVec::repeat(false, size),
|
||||
}))
|
||||
}
|
||||
|
||||
/// Interpret the data as a series of bits and load then into a new `BitVec` instance.
|
||||
|
@ -43,9 +65,11 @@ pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut BitVec {
|
|||
pub unsafe extern "C" fn sp_bit_vec_load(
|
||||
data: *const u8,
|
||||
data_length: usize,
|
||||
) -> *mut BitVec {
|
||||
) -> *mut CBitVec {
|
||||
let data = std::slice::from_raw_parts(data, data_length);
|
||||
Box::into_raw(Box::new(BitVec::from(data)))
|
||||
Box::into_raw(Box::new(CBitVec {
|
||||
actual: SpBitVec::from_slice(data),
|
||||
}))
|
||||
}
|
||||
|
||||
/// Clones a `BitVec`.
|
||||
|
@ -59,7 +83,9 @@ pub unsafe extern "C" fn sp_bit_vec_load(
|
|||
/// - the returned instance is freed in some way, either by using a consuming function or
|
||||
/// by explicitly calling `sp_bit_vec_dealloc`.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_bit_vec_clone(this: *const BitVec) -> *mut BitVec {
|
||||
pub unsafe extern "C" fn sp_bit_vec_clone(
|
||||
this: *const CBitVec,
|
||||
) -> *mut CBitVec {
|
||||
Box::into_raw(Box::new((*this).clone()))
|
||||
}
|
||||
|
||||
|
@ -73,7 +99,7 @@ pub unsafe extern "C" fn sp_bit_vec_clone(this: *const BitVec) -> *mut BitVec {
|
|||
/// - `this` is not used concurrently or after this call
|
||||
/// - `this` was not passed to another consuming function, e.g. to create a `Command`
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_bit_vec_dealloc(this: *mut BitVec) {
|
||||
pub unsafe extern "C" fn sp_bit_vec_dealloc(this: *mut CBitVec) {
|
||||
_ = Box::from_raw(this);
|
||||
}
|
||||
|
||||
|
@ -98,10 +124,10 @@ pub unsafe extern "C" fn sp_bit_vec_dealloc(this: *mut BitVec) {
|
|||
/// - `this` is not written to concurrently
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_bit_vec_get(
|
||||
this: *const BitVec,
|
||||
this: *const CBitVec,
|
||||
index: usize,
|
||||
) -> bool {
|
||||
(*this).get(index)
|
||||
*(*this).actual.get(index).unwrap()
|
||||
}
|
||||
|
||||
/// Sets the value of a bit in the `BitVec`.
|
||||
|
@ -126,11 +152,11 @@ pub unsafe extern "C" fn sp_bit_vec_get(
|
|||
/// - `this` is not written to or read from concurrently
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_bit_vec_set(
|
||||
this: *mut BitVec,
|
||||
this: *mut CBitVec,
|
||||
index: usize,
|
||||
value: bool,
|
||||
) -> bool {
|
||||
(*this).set(index, value)
|
||||
) {
|
||||
(*this).actual.set(index, value)
|
||||
}
|
||||
|
||||
/// Sets the value of all bits in the `BitVec`.
|
||||
|
@ -146,8 +172,8 @@ pub unsafe extern "C" fn sp_bit_vec_set(
|
|||
/// - `this` points to a valid `BitVec`
|
||||
/// - `this` is not written to or read from concurrently
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_bit_vec_fill(this: *mut BitVec, value: bool) {
|
||||
(*this).fill(value)
|
||||
pub unsafe extern "C" fn sp_bit_vec_fill(this: *mut CBitVec, value: bool) {
|
||||
(*this).actual.fill(value)
|
||||
}
|
||||
|
||||
/// Gets the length of the `BitVec` in bits.
|
||||
|
@ -158,8 +184,8 @@ pub unsafe extern "C" fn sp_bit_vec_fill(this: *mut BitVec, value: bool) {
|
|||
///
|
||||
/// - `this` points to a valid `BitVec`
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_bit_vec_len(this: *const BitVec) -> usize {
|
||||
(*this).len()
|
||||
pub unsafe extern "C" fn sp_bit_vec_len(this: *const CBitVec) -> usize {
|
||||
(*this).actual.len()
|
||||
}
|
||||
|
||||
/// Returns true if length is 0.
|
||||
|
@ -170,8 +196,8 @@ pub unsafe extern "C" fn sp_bit_vec_len(this: *const BitVec) -> usize {
|
|||
///
|
||||
/// - `this` points to a valid `BitVec`
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_bit_vec_is_empty(this: *const BitVec) -> bool {
|
||||
(*this).is_empty()
|
||||
pub unsafe extern "C" fn sp_bit_vec_is_empty(this: *const CBitVec) -> bool {
|
||||
(*this).actual.is_empty()
|
||||
}
|
||||
|
||||
/// Gets an unsafe reference to the data of the `BitVec` instance.
|
||||
|
@ -185,9 +211,9 @@ pub unsafe extern "C" fn sp_bit_vec_is_empty(this: *const BitVec) -> bool {
|
|||
/// - the returned memory range is never accessed concurrently, either via the `BitVec` or directly
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_bit_vec_unsafe_data_ref(
|
||||
this: *mut BitVec,
|
||||
this: *mut CBitVec,
|
||||
) -> CByteSlice {
|
||||
let data = (*this).data_ref_mut();
|
||||
let data = (*this).actual.as_raw_mut_slice();
|
||||
CByteSlice {
|
||||
start: data.as_mut_ptr_range().start,
|
||||
length: data.len(),
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! FFI slice helper
|
||||
|
||||
#[repr(C)]
|
||||
/// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code.
|
||||
///
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
use std::ptr::null_mut;
|
||||
|
||||
use servicepoint::{
|
||||
BitVec, Brightness, ByteGrid, Command, CompressionCode, Offset, Origin,
|
||||
Packet, PixelGrid,
|
||||
Brightness, ByteGrid, Command, CompressionCode, Offset, Origin, Packet,
|
||||
PixelGrid,
|
||||
};
|
||||
|
||||
use crate::bit_vec::CBitVec;
|
||||
|
||||
/// Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process.
|
||||
///
|
||||
/// Returns: pointer to new `Command` instance or NULL
|
||||
|
@ -140,13 +142,13 @@ pub unsafe extern "C" fn sp_command_char_brightness(
|
|||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_command_bitmap_linear(
|
||||
offset: Offset,
|
||||
bit_vec: *mut BitVec,
|
||||
bit_vec: *mut CBitVec,
|
||||
compression: CompressionCode,
|
||||
) -> *mut Command {
|
||||
let bit_vec = *Box::from_raw(bit_vec);
|
||||
Box::into_raw(Box::new(Command::BitmapLinear(
|
||||
offset,
|
||||
bit_vec,
|
||||
bit_vec.into(),
|
||||
compression,
|
||||
)))
|
||||
}
|
||||
|
@ -166,13 +168,13 @@ pub unsafe extern "C" fn sp_command_bitmap_linear(
|
|||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_command_bitmap_linear_and(
|
||||
offset: Offset,
|
||||
bit_vec: *mut BitVec,
|
||||
bit_vec: *mut CBitVec,
|
||||
compression: CompressionCode,
|
||||
) -> *mut Command {
|
||||
let bit_vec = *Box::from_raw(bit_vec);
|
||||
Box::into_raw(Box::new(Command::BitmapLinearAnd(
|
||||
offset,
|
||||
bit_vec,
|
||||
bit_vec.into(),
|
||||
compression,
|
||||
)))
|
||||
}
|
||||
|
@ -192,13 +194,13 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_and(
|
|||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_command_bitmap_linear_or(
|
||||
offset: Offset,
|
||||
bit_vec: *mut BitVec,
|
||||
bit_vec: *mut CBitVec,
|
||||
compression: CompressionCode,
|
||||
) -> *mut Command {
|
||||
let bit_vec = *Box::from_raw(bit_vec);
|
||||
Box::into_raw(Box::new(Command::BitmapLinearOr(
|
||||
offset,
|
||||
bit_vec,
|
||||
bit_vec.into(),
|
||||
compression,
|
||||
)))
|
||||
}
|
||||
|
@ -218,13 +220,13 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_or(
|
|||
#[no_mangle]
|
||||
pub unsafe extern "C" fn sp_command_bitmap_linear_xor(
|
||||
offset: Offset,
|
||||
bit_vec: *mut BitVec,
|
||||
bit_vec: *mut CBitVec,
|
||||
compression: CompressionCode,
|
||||
) -> *mut Command {
|
||||
let bit_vec = *Box::from_raw(bit_vec);
|
||||
Box::into_raw(Box::new(Command::BitmapLinearXor(
|
||||
offset,
|
||||
bit_vec,
|
||||
bit_vec.into(),
|
||||
compression,
|
||||
)))
|
||||
}
|
||||
|
|
|
@ -22,4 +22,4 @@ pub mod pixel_grid;
|
|||
/// The minimum time needed for the display to refresh the screen in ms.
|
||||
pub const FRAME_PACING_MS: u32 = servicepoint::FRAME_PACING.as_millis() as u32;
|
||||
|
||||
mod c_slice;
|
||||
pub mod c_slice;
|
||||
|
|
|
@ -4,6 +4,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint", "ServicePoin
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "lang-cs", "examples/lang_cs/lang_cs.csproj", "{DA3B8B6E-993A-47DA-844B-F92AF520FF59}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{C2F8EC4A-2426-4DC3-990F-C43810B183F5}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
..\..\shell.nix = ..\..\shell.nix
|
||||
..\..\.envrc = ..\..\.envrc
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
|
@ -21,46 +21,45 @@ namespace ServicePoint.BindGen
|
|||
|
||||
/// <summary>Creates a new `BitVec` instance. # Arguments * `size`: size in bits. returns: `BitVec` with all bits set to false. # Panics When `size` is not divisible by 8. # Safety The caller has to make sure that: - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_bit_vec_dealloc`.</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_bit_vec_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern BitVec* sp_bit_vec_new(nuint size);
|
||||
public static extern CBitVec* sp_bit_vec_new(nuint size);
|
||||
|
||||
/// <summary>Interpret the data as a series of bits and load then into a new `BitVec` instance. # Safety The caller has to make sure that: - `data` points to a valid memory location of at least `data_length` bytes in size. - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_bit_vec_dealloc`.</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_bit_vec_load", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern BitVec* sp_bit_vec_load(byte* data, nuint data_length);
|
||||
public static extern CBitVec* sp_bit_vec_load(byte* data, nuint data_length);
|
||||
|
||||
/// <summary>Clones a `BitVec`. # Safety The caller has to make sure that: - `this` points to a valid `BitVec` - `this` is not written to concurrently - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_bit_vec_dealloc`.</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_bit_vec_clone", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern BitVec* sp_bit_vec_clone(BitVec* @this);
|
||||
public static extern CBitVec* sp_bit_vec_clone(CBitVec* @this);
|
||||
|
||||
/// <summary>Deallocates a `BitVec`. # Safety The caller has to make sure that: - `this` points to a valid `BitVec` - `this` is not used concurrently or after this call - `this` was not passed to another consuming function, e.g. to create a `Command`</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_bit_vec_dealloc", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void sp_bit_vec_dealloc(BitVec* @this);
|
||||
public static extern void sp_bit_vec_dealloc(CBitVec* @this);
|
||||
|
||||
/// <summary>Gets the value of a bit from the `BitVec`. # Arguments * `this`: instance to read from * `index`: the bit index to read returns: value of the bit # Panics When accessing `index` out of bounds. # Safety The caller has to make sure that: - `this` points to a valid `BitVec` - `this` is not written to concurrently</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_bit_vec_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static extern bool sp_bit_vec_get(BitVec* @this, nuint index);
|
||||
public static extern bool sp_bit_vec_get(CBitVec* @this, nuint index);
|
||||
|
||||
/// <summary>Sets the value of a bit in the `BitVec`. # Arguments * `this`: instance to write to * `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. # Safety The caller has to make sure that: - `this` points to a valid `BitVec` - `this` is not written to or read from concurrently</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_bit_vec_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static extern bool sp_bit_vec_set(BitVec* @this, nuint index, [MarshalAs(UnmanagedType.U1)] bool value);
|
||||
public static extern void sp_bit_vec_set(CBitVec* @this, nuint index, [MarshalAs(UnmanagedType.U1)] bool value);
|
||||
|
||||
/// <summary>Sets the value of all bits in the `BitVec`. # Arguments * `value`: the value to set all bits to # Safety The caller has to make sure that: - `this` points to a valid `BitVec` - `this` is not written to or read from concurrently</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_bit_vec_fill", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern void sp_bit_vec_fill(BitVec* @this, [MarshalAs(UnmanagedType.U1)] bool value);
|
||||
public static extern void sp_bit_vec_fill(CBitVec* @this, [MarshalAs(UnmanagedType.U1)] bool value);
|
||||
|
||||
/// <summary>Gets the length of the `BitVec` in bits. # Safety The caller has to make sure that: - `this` points to a valid `BitVec`</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_bit_vec_len", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern nuint sp_bit_vec_len(BitVec* @this);
|
||||
public static extern nuint sp_bit_vec_len(CBitVec* @this);
|
||||
|
||||
/// <summary>Returns true if length is 0. # Safety The caller has to make sure that: - `this` points to a valid `BitVec`</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_bit_vec_is_empty", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
public static extern bool sp_bit_vec_is_empty(BitVec* @this);
|
||||
public static extern bool sp_bit_vec_is_empty(CBitVec* @this);
|
||||
|
||||
/// <summary>Gets an unsafe reference to the data of the `BitVec` instance. ## Safety The caller has to make sure that: - `this` points to a valid `BitVec` - the returned memory range is never accessed after the passed `BitVec` has been freed - the returned memory range is never accessed concurrently, either via the `BitVec` or directly</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_bit_vec_unsafe_data_ref", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern CByteSlice sp_bit_vec_unsafe_data_ref(BitVec* @this);
|
||||
public static extern CByteSlice sp_bit_vec_unsafe_data_ref(CBitVec* @this);
|
||||
|
||||
/// <summary>Creates a new `ByteGrid` with the specified dimensions. returns: `ByteGrid` initialized to 0. # Safety The caller has to make sure that: - the returned instance is freed in some way, either by using a consuming function or by explicitly calling `sp_byte_grid_dealloc`.</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_byte_grid_new", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
|
@ -132,19 +131,19 @@ namespace ServicePoint.BindGen
|
|||
|
||||
/// <summary>Allocates a new `Command::BitmapLinear` instance. The passed `BitVec` gets consumed. # Safety The caller has to make sure that: - `bit_vec` points to a valid instance of `BitVec` - `bit_vec` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern Command* sp_command_bitmap_linear(nuint offset, BitVec* bit_vec, CompressionCode compression);
|
||||
public static extern Command* sp_command_bitmap_linear(nuint offset, CBitVec* bit_vec, CompressionCode compression);
|
||||
|
||||
/// <summary>Allocates a new `Command::BitmapLinearAnd` instance. The passed `BitVec` gets consumed. # Safety The caller has to make sure that: - `bit_vec` points to a valid instance of `BitVec` - `bit_vec` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear_and", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern Command* sp_command_bitmap_linear_and(nuint offset, BitVec* bit_vec, CompressionCode compression);
|
||||
public static extern Command* sp_command_bitmap_linear_and(nuint offset, CBitVec* bit_vec, CompressionCode compression);
|
||||
|
||||
/// <summary>Allocates a new `Command::BitmapLinearOr` instance. The passed `BitVec` gets consumed. # Safety The caller has to make sure that: - `bit_vec` points to a valid instance of `BitVec` - `bit_vec` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear_or", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern Command* sp_command_bitmap_linear_or(nuint offset, BitVec* bit_vec, CompressionCode compression);
|
||||
public static extern Command* sp_command_bitmap_linear_or(nuint offset, CBitVec* bit_vec, CompressionCode compression);
|
||||
|
||||
/// <summary>Allocates a new `Command::BitmapLinearXor` instance. The passed `BitVec` gets consumed. # Safety The caller has to make sure that: - `bit_vec` points to a valid instance of `BitVec` - `bit_vec` is not used concurrently or after this call - `compression` matches one of the allowed enum values - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_command_bitmap_linear_xor", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
public static extern Command* sp_command_bitmap_linear_xor(nuint offset, BitVec* bit_vec, CompressionCode compression);
|
||||
public static extern Command* sp_command_bitmap_linear_xor(nuint offset, CBitVec* bit_vec, CompressionCode compression);
|
||||
|
||||
/// <summary>Allocates a new `Command::Cp437Data` instance. The passed `ByteGrid` gets consumed. # Safety The caller has to make sure that: - `byte_grid` points to a valid instance of `ByteGrid` - `byte_grid` is not used concurrently or after this call - the returned `Command` instance is freed in some way, either by using a consuming function or by explicitly calling `sp_command_dealloc`.</summary>
|
||||
[DllImport(__DllName, EntryPoint = "sp_command_cp437_data", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
|
||||
|
@ -227,6 +226,11 @@ namespace ServicePoint.BindGen
|
|||
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe partial struct CBitVec
|
||||
{
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe partial struct CByteSlice
|
||||
{
|
||||
|
@ -234,11 +238,6 @@ namespace ServicePoint.BindGen
|
|||
public nuint length;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe partial struct BitVec
|
||||
{
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe partial struct ByteGrid
|
||||
{
|
||||
|
|
|
@ -2,7 +2,7 @@ using ServicePoint.BindGen;
|
|||
|
||||
namespace ServicePoint;
|
||||
|
||||
public sealed class BitVec : SpNativeInstance<BindGen.BitVec>
|
||||
public sealed class BitVec : SpNativeInstance<BindGen.CBitVec>
|
||||
{
|
||||
public static BitVec New(int size)
|
||||
{
|
||||
|
@ -80,7 +80,7 @@ public sealed class BitVec : SpNativeInstance<BindGen.BitVec>
|
|||
}
|
||||
}
|
||||
|
||||
private unsafe BitVec(BindGen.BitVec* instance) : base(instance)
|
||||
private unsafe BitVec(BindGen.CBitVec* instance) : base(instance)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -27,19 +27,13 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<!-- generate C# bindings -->
|
||||
<Target Name="BuildBindings" Condition="'$(Configuration)'=='Release'" BeforeTargets="Build">
|
||||
<Target Name="BuildBindings" Condition="'$(Configuration)'=='Release'" BeforeTargets="PrepareForBuild">
|
||||
<Exec Command="cargo build --release"/>
|
||||
<Exec Command="cargo build --manifest-path ../../../crates/servicepoint_binding_c/Cargo.toml --release"/>
|
||||
</Target>
|
||||
<Target Name="BuildBindings" Condition="'$(Configuration)'=='Debug'" BeforeTargets="Build">
|
||||
<Target Name="BuildBindings" Condition="'$(Configuration)'=='Debug'" BeforeTargets="PrepareForBuild">
|
||||
<Exec Command="cargo build"/>
|
||||
</Target>
|
||||
|
||||
<!-- build native library to include in output -->
|
||||
<Target Name="BuildLibrary" Condition="'$(Configuration)'=='Release'" BeforeTargets="Build">
|
||||
<Exec Command="cargo build --manifest-path ../../../crates/servicepoint/Cargo.toml --release"/>
|
||||
</Target>
|
||||
<Target Name="BuildLibrary" Condition="'$(Configuration)'=='Debug'" BeforeTargets="Build">
|
||||
<Exec Command="cargo build --manifest-path ../../../crates/servicepoint/Cargo.toml"/>
|
||||
<Exec Command="cargo build --manifest-path ../../../crates/servicepoint_binding_c/Cargo.toml"/>
|
||||
</Target>
|
||||
|
||||
<!-- include native binary in output -->
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
//! Build script generating the C# code needed to call methods from the `servicepoint` C library.
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=../servicepoint_binding_c/src");
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo::rerun-if-changed=../servicepoint_binding_c/src");
|
||||
println!("cargo::rerun-if-changed=build.rs");
|
||||
csbindgen::Builder::default()
|
||||
.input_extern_file("../servicepoint_binding_c/src/bit_vec.rs")
|
||||
.input_extern_file("../servicepoint_binding_c/src/byte_grid.rs")
|
||||
|
@ -12,7 +12,6 @@ fn main() {
|
|||
.input_extern_file("../servicepoint_binding_c/src/lib.rs")
|
||||
.input_extern_file("../servicepoint_binding_c/src/c_slice.rs")
|
||||
.input_extern_file("../servicepoint_binding_c/src/packet.rs")
|
||||
.input_extern_file("../servicepoint/src/bit_vec.rs")
|
||||
.input_extern_file("../servicepoint/src/byte_grid.rs")
|
||||
.input_extern_file("../servicepoint/src/command.rs")
|
||||
.input_extern_file("../servicepoint/src/connection.rs")
|
||||
|
|
Loading…
Reference in a new issue