Merge pull request #4 from cccb/additional-grid-features

Additional grid features
This commit is contained in:
Vinzenz Schroeter 2024-06-05 20:31:47 +02:00 committed by GitHub
commit 15a3c4d596
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 619 additions and 473 deletions

40
Cargo.lock generated
View file

@ -95,6 +95,18 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 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]] [[package]]
name = "bzip2" name = "bzip2"
version = "0.4.4" version = "0.4.4"
@ -267,6 +279,12 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "funty"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.15" version = "0.2.15"
@ -419,6 +437,12 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "radium"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.8.5" version = "0.8.5"
@ -542,6 +566,7 @@ dependencies = [
name = "servicepoint" name = "servicepoint"
version = "0.5.1" version = "0.5.1"
dependencies = [ dependencies = [
"bitvec",
"bzip2", "bzip2",
"clap 4.5.4", "clap 4.5.4",
"flate2", "flate2",
@ -602,6 +627,12 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "tap"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
[[package]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.10.1" version = "3.10.1"
@ -766,6 +797,15 @@ version = "0.52.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
[[package]]
name = "wyz"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
dependencies = [
"tap",
]
[[package]] [[package]]
name = "zstd" name = "zstd"
version = "0.13.1" version = "0.13.1"

View file

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

View 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");
}

View file

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

View file

@ -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:?}");
}
}

View file

@ -1,3 +1,5 @@
use std::slice::{Iter, IterMut};
use crate::{DataRef, Grid}; use crate::{DataRef, Grid};
/// A 2D grid of bytes /// A 2D grid of bytes
@ -9,6 +11,22 @@ pub struct ByteGrid {
} }
impl 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. /// Loads a `ByteGrid` with the specified dimensions from the provided data.
/// ///
/// returns: `ByteGrid` that contains a copy of 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) { /// Iterate over all cells in `ByteGrid`.
assert!( ///
x < self.width, /// Order is equivalent to the following loop:
"cannot access byte {x}-{y} because x is outside of bounds 0..{}", /// ```
self.width /// # use servicepoint::{ByteGrid, Grid};
); /// # let grid = ByteGrid::new(2,2);
assert!( /// for y in 0..grid.height() {
y < self.height, /// for x in 0..grid.width() {
"cannot access byte {x}-{y} because y is outside of bounds 0..{}", /// grid.get(x, y);
self.height /// }
); /// }
/// ```
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 { 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. /// Sets the value of the cell at the specified position in the `ByteGrid.
/// ///
/// # Arguments /// # Arguments
@ -59,17 +117,12 @@ impl Grid<u8> for ByteGrid {
/// * `x` and `y`: position of the cell /// * `x` and `y`: position of the cell
/// * `value`: the value to write to the cell /// * `value`: the value to write to the cell
/// ///
/// returns: old value of the cell.
///
/// # Panics /// # Panics
/// ///
/// When accessing `x` or `y` out of bounds. /// 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); self.assert_in_bounds(x, y);
let pos = &mut self.data[x + y * self.width]; self.data[x + y * self.width] = value;
let old_val = *pos;
*pos = value;
old_val
} }
/// Gets the current value at the specified position. /// 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. /// When accessing `x` or `y` out of bounds.
fn get(&self, x: usize, y: usize) -> u8 { 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] self.data[x + y * self.width]
} }
@ -97,17 +150,6 @@ impl Grid<u8> for ByteGrid {
fn height(&self) -> usize { fn height(&self) -> usize {
self.height 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 { 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)] #[cfg(test)]
mod tests { mod tests {
use crate::{ByteGrid, DataRef, Grid}; use crate::{ByteGrid, DataRef, Grid};
@ -184,4 +247,73 @@ mod tests {
assert_eq!(vec.data, [1, 2, 3, 4]); assert_eq!(vec.data, [1, 2, 3, 4]);
assert_eq!(vec.get(1, 0), 2) 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);
}
} }

View file

@ -1,7 +1,9 @@
use bitvec::prelude::BitVec;
use crate::command_code::CommandCode; use crate::command_code::CommandCode;
use crate::compression::{into_compressed, into_decompressed}; use crate::compression::{into_compressed, into_decompressed};
use crate::{ use crate::{
BitVec, ByteGrid, CompressionCode, Grid, Header, Packet, PixelGrid, ByteGrid, CompressionCode, Grid, Header, Packet, PixelGrid, SpBitVec,
TILE_SIZE, TILE_SIZE,
}; };
@ -43,16 +45,16 @@ pub enum Command {
BitmapLegacy, BitmapLegacy,
/// Set pixel data starting at the offset. /// Set pixel data starting at the offset.
/// The contained `BitVec` is always uncompressed. /// 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. /// Set pixel data according to an and-mask starting at the offset.
/// The contained `BitVec` is always uncompressed. /// 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. /// Set pixel data according to an or-mask starting at the offset.
/// The contained `BitVec` is always uncompressed. /// 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. /// Set pixel data according to a xor-mask starting at the offset.
/// The contained `BitVec` is always uncompressed. /// 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. /// Show text on the screen. Note that the byte data has to be CP437 encoded.
Cp437Data(Origin, ByteGrid), Cp437Data(Origin, ByteGrid),
/// Sets a window of pixels to the specified values /// Sets a window of pixels to the specified values
@ -366,7 +368,7 @@ impl Command {
/// Helper method for Packets into `BitMapLinear*`-Commands /// Helper method for Packets into `BitMapLinear*`-Commands
fn packet_into_linear_bitmap( fn packet_into_linear_bitmap(
packet: Packet, packet: Packet,
) -> Result<(BitVec, CompressionCode), TryFromPacketError> { ) -> Result<(SpBitVec, CompressionCode), TryFromPacketError> {
let Packet(Header(_, _, length, sub, reserved), payload) = packet; let Packet(Header(_, _, length, sub, reserved), payload) = packet;
if reserved != 0 { if reserved != 0 {
return Err(TryFromPacketError::ExtraneousHeaderValues); return Err(TryFromPacketError::ExtraneousHeaderValues);
@ -387,17 +389,18 @@ impl Command {
payload.len(), payload.len(),
)); ));
} }
Ok((BitVec::from(&*payload), sub)) Ok((BitVec::from_vec(payload), sub))
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bitvec::prelude::BitVec;
use crate::command::TryFromPacketError; use crate::command::TryFromPacketError;
use crate::command_code::CommandCode; use crate::command_code::CommandCode;
use crate::{ use crate::{
BitVec, ByteGrid, Command, CompressionCode, Grid, Header, Origin, ByteGrid, Command, CompressionCode, Header, Origin, Packet, PixelGrid,
Packet, PixelGrid,
}; };
fn round_trip(original: Command) { fn round_trip(original: Command) {
@ -462,20 +465,24 @@ mod tests {
#[test] #[test]
fn round_trip_bitmap_linear() { fn round_trip_bitmap_linear() {
for compression in all_compressions().to_owned() { 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( round_trip(Command::BitmapLinearAnd(
23, 23,
BitVec::new(40), BitVec::repeat(false, 40),
compression, compression,
)); ));
round_trip(Command::BitmapLinearOr( round_trip(Command::BitmapLinearOr(
23, 23,
BitVec::new(40), BitVec::repeat(false, 40),
compression, compression,
)); ));
round_trip(Command::BitmapLinearXor( round_trip(Command::BitmapLinearXor(
23, 23,
BitVec::new(40), BitVec::repeat(false, 40),
compression, compression,
)); ));
round_trip(Command::BitmapLinearWin( round_trip(Command::BitmapLinearWin(
@ -590,8 +597,12 @@ mod tests {
#[test] #[test]
fn error_decompression_failed_and() { fn error_decompression_failed_and() {
for compression in all_compressions().to_owned() { for compression in all_compressions().to_owned() {
let p: Packet = let p: Packet = Command::BitmapLinearAnd(
Command::BitmapLinearAnd(0, BitVec::new(8), compression).into(); 0,
BitVec::repeat(false, 8),
compression,
)
.into();
let Packet(header, mut payload) = p; let Packet(header, mut payload) = p;
// mangle it // mangle it
@ -633,7 +644,7 @@ mod tests {
fn error_reserved_used() { fn error_reserved_used() {
let Packet(header, payload) = Command::BitmapLinear( let Packet(header, payload) = Command::BitmapLinear(
0, 0,
BitVec::new(8), BitVec::repeat(false, 8),
CompressionCode::Uncompressed, CompressionCode::Uncompressed,
) )
.into(); .into();
@ -649,7 +660,7 @@ mod tests {
fn error_invalid_compression() { fn error_invalid_compression() {
let Packet(header, payload) = Command::BitmapLinear( let Packet(header, payload) = Command::BitmapLinear(
0, 0,
BitVec::new(8), BitVec::repeat(false, 8),
CompressionCode::Uncompressed, CompressionCode::Uncompressed,
) )
.into(); .into();
@ -665,7 +676,7 @@ mod tests {
fn error_unexpected_size() { fn error_unexpected_size() {
let Packet(header, payload) = Command::BitmapLinear( let Packet(header, payload) = Command::BitmapLinear(
0, 0,
BitVec::new(8), BitVec::repeat(false, 8),
CompressionCode::Uncompressed, CompressionCode::Uncompressed,
) )
.into(); .into();

View file

@ -1,24 +1,58 @@
/// A two-dimensional grid of `T` /// A two-dimensional grid of `T`
pub trait Grid<T> { pub trait Grid<T> {
#[must_use] /// Sets the value at the specified position
/// Creates a new Grid with the specified dimensions.
/// ///
/// # Arguments /// # Arguments
/// ///
/// - width: size in x-direction /// * `x` and `y`: position of the cell to read
/// - height: size in y-direction
/// ///
/// returns: Grid with all cells initialized to default state. /// # Panics
fn new(width: usize, height: usize) -> Self;
/// Sets the value at the specified position
/// ///
/// returns: the old value /// 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 /// 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; 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 /// Sets all cells in the grid to the specified value
fn fill(&mut self, value: T); fn fill(&mut self, value: T);
@ -28,35 +62,29 @@ pub trait Grid<T> {
/// the height in y-direction /// the height in y-direction
fn height(&self) -> usize; 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 /// When the specified position is out of bounds for this grid.
/// fn assert_in_bounds(&self, x: usize, y: usize) {
/// * `x`: column of the top left cell assert!(
/// * `y`: row of the top left cell x < self.width(),
/// * `w`: size of window in x-direction "cannot access index [{x}, {y}] because x is outside of bounds 0..{}",
/// * `h`: size of window in y-direction self.width()
/// );
/// returns: Self assert!(
/// y < self.height(),
/// # Examples "cannot access byte [{x}, {y}] because y is outside of bounds 0..{}",
/// To avoid boxing, this example is using the concrete type `ByteGrid`. self.height()
/// ``` );
/// 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;
} }

View file

@ -2,7 +2,9 @@
use std::time::Duration; 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::byte_grid::ByteGrid;
pub use crate::command::{Brightness, Command, Offset, Origin}; pub use crate::command::{Brightness, Command, Offset, Origin};
pub use crate::compression_code::CompressionCode; 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::packet::{Header, Packet, Payload};
pub use crate::pixel_grid::PixelGrid; pub use crate::pixel_grid::PixelGrid;
mod bit_vec; type SpBitVec = BitVec<u8, Msb0>;
mod byte_grid; mod byte_grid;
mod command; mod command;
mod command_code; mod command_code;

View file

@ -69,4 +69,10 @@ mod tests {
let p = Packet::try_from(&*data).unwrap(); let p = Packet::try_from(&*data).unwrap();
assert_eq!(p, Packet(Header(0, 1, 2, 3, 4), vec![42u8; 23])); 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(()))
}
} }

View file

@ -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. /// A grid of pixels stored in packed bytes.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct PixelGrid { pub struct PixelGrid {
width: usize, width: usize,
height: usize, height: usize,
bit_vec: BitVec, bit_vec: SpBitVec,
} }
impl PixelGrid { 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. /// Creates a new pixel grid with the size of the whole screen.
#[must_use] #[must_use]
pub fn max_sized() -> Self { pub fn max_sized() -> Self {
@ -35,46 +60,63 @@ impl PixelGrid {
Self { Self {
width, width,
height, height,
bit_vec: BitVec::from(data), bit_vec: BitVec::from_slice(data),
} }
} }
fn check_indexes(&self, x: usize, y: usize) { /// Iterate over all cells in `PixelGrid`.
assert!( ///
x < self.width, /// Order is equivalent to the following loop:
"cannot access pixel {x}-{y} because x is outside of bounds 0..{}", /// ```
self.width /// # use servicepoint::{PixelGrid, Grid};
); /// # let grid = PixelGrid::new(8,2);
assert!( /// for y in 0..grid.height() {
y < self.height, /// for x in 0..grid.width() {
"cannot access pixel {x}-{y} because y is outside of bounds 0..{}", /// grid.get(x, y);
self.height /// }
); /// }
/// ```
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 { 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`. /// Sets the value of the specified position in the `PixelGrid`.
/// ///
/// # Arguments /// # Arguments
@ -87,22 +129,14 @@ impl Grid<bool> for PixelGrid {
/// # Panics /// # Panics
/// ///
/// When accessing `x` or `y` out of bounds. /// 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.assert_in_bounds(x, y);
self.bit_vec.set(x + y * self.width, value) 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 { 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`. /// Sets the state of all pixels in the `PixelGrid`.
@ -122,28 +156,15 @@ impl Grid<bool> for PixelGrid {
fn height(&self) -> usize { fn height(&self) -> usize {
self.height 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 { impl DataRef for PixelGrid {
fn data_ref_mut(&mut self) -> &mut [u8] { 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] { 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)] #[cfg(test)]
mod tests { mod tests {
use crate::{DataRef, Grid, PixelGrid}; use crate::{DataRef, Grid, PixelGrid};
@ -201,4 +242,51 @@ mod tests {
let grid = PixelGrid::load(8, 3, &data); let grid = PixelGrid::load(8, 3, &data);
assert_eq!(grid.data_ref(), [0xAA, 0x55, 0xAA]); 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));
}
} }

View file

@ -17,19 +17,19 @@ This crate contains C bindings for the `servicepoint` library, enabling users to
#include "servicepoint.h" #include "servicepoint.h"
int main(void) { int main(void) {
sp2_Connection *connection = sp2_connection_open("localhost:2342"); sp_Connection *connection = sp_connection_open("localhost:2342");
if (connection == NULL) if (connection == NULL)
return 1; return 1;
sp2_PixelGrid *pixels = sp2_pixel_grid_new(sp2_PIXEL_WIDTH, sp2_PIXEL_HEIGHT); sp_PixelGrid *pixels = sp_pixel_grid_new(sp_PIXEL_WIDTH, sp_PIXEL_HEIGHT);
sp2_pixel_grid_fill(pixels, true); sp_pixel_grid_fill(pixels, true);
sp2_Command *command = sp2_command_bitmap_linear_win(0, 0, pixels, Uncompressed); sp_Command *command = sp_command_bitmap_linear_win(0, 0, pixels, Uncompressed);
sp2_Packet *packet = sp2_packet_from_command(command); sp_Packet *packet = sp_packet_from_command(command);
if (!sp2_connection_send(connection, packet)) if (!sp_connection_send(connection, packet))
return 1; return 1;
sp2_connection_dealloc(connection); sp_connection_dealloc(connection);
return 0; return 0;
} }
``` ```
@ -52,12 +52,12 @@ You have the choice of linking statically (recommended) or dynamically.
## Notes on differences to rust library ## Notes on differences to rust library
- function names are: `sp2_` \<struct_name\> \<rust name\>. - function names are: `sp_` \<struct_name\> \<rust name\>.
- Use the rust documentation.
- Instances get consumed in the same way they do when writing rust / C# code. Do not use an instance after an (implicit!) free. - 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! - 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. - 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. - 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 ## Everything else

View file

@ -1,9 +1,9 @@
const SP_INCLUDE: &str = "DEP_SERVICEPOINT_INCLUDE"; const SP_INCLUDE: &str = "DEP_SERVICEPOINT_INCLUDE";
fn main() { fn main() {
println!("cargo:rerun-if-changed=src/main.c"); println!("cargo::rerun-if-changed=src/main.c");
println!("cargo:rerun-if-changed=build.rs"); println!("cargo::rerun-if-changed=build.rs");
println!("cargo:rerun-if-env-changed={SP_INCLUDE}"); println!("cargo::rerun-if-env-changed={SP_INCLUDE}");
let sp_include = let sp_include =
std::env::var_os(SP_INCLUDE).unwrap().into_string().unwrap(); std::env::var_os(SP_INCLUDE).unwrap().into_string().unwrap();

View file

@ -2,9 +2,29 @@
//! //!
//! prefix `sp_bit_vec_` //! prefix `sp_bit_vec_`
use servicepoint::{BitVec, DataRef};
use crate::c_slice::CByteSlice; 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. /// 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 /// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_bit_vec_dealloc`. /// by explicitly calling `sp_bit_vec_dealloc`.
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut BitVec { pub unsafe extern "C" fn sp_bit_vec_new(size: usize) -> *mut CBitVec {
Box::into_raw(Box::new(BitVec::new(size))) 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. /// 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( pub unsafe extern "C" fn sp_bit_vec_load(
data: *const u8, data: *const u8,
data_length: usize, data_length: usize,
) -> *mut BitVec { ) -> *mut CBitVec {
let data = std::slice::from_raw_parts(data, data_length); 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`. /// 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 /// - the returned instance is freed in some way, either by using a consuming function or
/// by explicitly calling `sp_bit_vec_dealloc`. /// by explicitly calling `sp_bit_vec_dealloc`.
#[no_mangle] #[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())) 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` is not used concurrently or after this call
/// - `this` was not passed to another consuming function, e.g. to create a `Command` /// - `this` was not passed to another consuming function, e.g. to create a `Command`
#[no_mangle] #[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); _ = 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 /// - `this` is not written to concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_get( pub unsafe extern "C" fn sp_bit_vec_get(
this: *const BitVec, this: *const CBitVec,
index: usize, index: usize,
) -> bool { ) -> bool {
(*this).get(index) *(*this).actual.get(index).unwrap()
} }
/// Sets the value of a bit in the `BitVec`. /// 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 /// - `this` is not written to or read from concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_set( pub unsafe extern "C" fn sp_bit_vec_set(
this: *mut BitVec, this: *mut CBitVec,
index: usize, index: usize,
value: bool, value: bool,
) -> bool { ) {
(*this).set(index, value) (*this).actual.set(index, value)
} }
/// Sets the value of all bits in the `BitVec`. /// 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` points to a valid `BitVec`
/// - `this` is not written to or read from concurrently /// - `this` is not written to or read from concurrently
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_fill(this: *mut BitVec, value: bool) { pub unsafe extern "C" fn sp_bit_vec_fill(this: *mut CBitVec, value: bool) {
(*this).fill(value) (*this).actual.fill(value)
} }
/// Gets the length of the `BitVec` in bits. /// 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` /// - `this` points to a valid `BitVec`
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_len(this: *const BitVec) -> usize { pub unsafe extern "C" fn sp_bit_vec_len(this: *const CBitVec) -> usize {
(*this).len() (*this).actual.len()
} }
/// Returns true if length is 0. /// 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` /// - `this` points to a valid `BitVec`
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_is_empty(this: *const BitVec) -> bool { pub unsafe extern "C" fn sp_bit_vec_is_empty(this: *const CBitVec) -> bool {
(*this).is_empty() (*this).actual.is_empty()
} }
/// Gets an unsafe reference to the data of the `BitVec` instance. /// 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 /// - the returned memory range is never accessed concurrently, either via the `BitVec` or directly
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_bit_vec_unsafe_data_ref( pub unsafe extern "C" fn sp_bit_vec_unsafe_data_ref(
this: *mut BitVec, this: *mut CBitVec,
) -> CByteSlice { ) -> CByteSlice {
let data = (*this).data_ref_mut(); let data = (*this).actual.as_raw_mut_slice();
CByteSlice { CByteSlice {
start: data.as_mut_ptr_range().start, start: data.as_mut_ptr_range().start,
length: data.len(), length: data.len(),

View file

@ -1,3 +1,5 @@
//! FFI slice helper
#[repr(C)] #[repr(C)]
/// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code. /// Represents a span of memory (`&mut [u8]` ) as a struct usable by C code.
/// ///

View file

@ -5,10 +5,12 @@
use std::ptr::null_mut; use std::ptr::null_mut;
use servicepoint::{ use servicepoint::{
BitVec, Brightness, ByteGrid, Command, CompressionCode, Offset, Origin, Brightness, ByteGrid, Command, CompressionCode, Offset, Origin, Packet,
Packet, PixelGrid, PixelGrid,
}; };
use crate::bit_vec::CBitVec;
/// Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process. /// Tries to turn a `Packet` into a `Command`. The packet is deallocated in the process.
/// ///
/// Returns: pointer to new `Command` instance or NULL /// Returns: pointer to new `Command` instance or NULL
@ -140,13 +142,13 @@ pub unsafe extern "C" fn sp_command_char_brightness(
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear( pub unsafe extern "C" fn sp_command_bitmap_linear(
offset: Offset, offset: Offset,
bit_vec: *mut BitVec, bit_vec: *mut CBitVec,
compression: CompressionCode, compression: CompressionCode,
) -> *mut Command { ) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec); let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinear( Box::into_raw(Box::new(Command::BitmapLinear(
offset, offset,
bit_vec, bit_vec.into(),
compression, compression,
))) )))
} }
@ -166,13 +168,13 @@ pub unsafe extern "C" fn sp_command_bitmap_linear(
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_and( pub unsafe extern "C" fn sp_command_bitmap_linear_and(
offset: Offset, offset: Offset,
bit_vec: *mut BitVec, bit_vec: *mut CBitVec,
compression: CompressionCode, compression: CompressionCode,
) -> *mut Command { ) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec); let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinearAnd( Box::into_raw(Box::new(Command::BitmapLinearAnd(
offset, offset,
bit_vec, bit_vec.into(),
compression, compression,
))) )))
} }
@ -192,13 +194,13 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_and(
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_or( pub unsafe extern "C" fn sp_command_bitmap_linear_or(
offset: Offset, offset: Offset,
bit_vec: *mut BitVec, bit_vec: *mut CBitVec,
compression: CompressionCode, compression: CompressionCode,
) -> *mut Command { ) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec); let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinearOr( Box::into_raw(Box::new(Command::BitmapLinearOr(
offset, offset,
bit_vec, bit_vec.into(),
compression, compression,
))) )))
} }
@ -218,13 +220,13 @@ pub unsafe extern "C" fn sp_command_bitmap_linear_or(
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn sp_command_bitmap_linear_xor( pub unsafe extern "C" fn sp_command_bitmap_linear_xor(
offset: Offset, offset: Offset,
bit_vec: *mut BitVec, bit_vec: *mut CBitVec,
compression: CompressionCode, compression: CompressionCode,
) -> *mut Command { ) -> *mut Command {
let bit_vec = *Box::from_raw(bit_vec); let bit_vec = *Box::from_raw(bit_vec);
Box::into_raw(Box::new(Command::BitmapLinearXor( Box::into_raw(Box::new(Command::BitmapLinearXor(
offset, offset,
bit_vec, bit_vec.into(),
compression, compression,
))) )))
} }

View file

@ -22,4 +22,4 @@ pub mod pixel_grid;
/// The minimum time needed for the display to refresh the screen in ms. /// 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; pub const FRAME_PACING_MS: u32 = servicepoint::FRAME_PACING.as_millis() as u32;
mod c_slice; pub mod c_slice;

View file

@ -4,6 +4,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServicePoint", "ServicePoin
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "lang-cs", "examples/lang_cs/lang_cs.csproj", "{DA3B8B6E-993A-47DA-844B-F92AF520FF59}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "lang-cs", "examples/lang_cs/lang_cs.csproj", "{DA3B8B6E-993A-47DA-844B-F92AF520FF59}"
EndProject 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU

View file

@ -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> /// <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)] [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> /// <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)] [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> /// <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)] [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> /// <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)] [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> /// <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)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_get", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.U1)] [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> /// <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)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_set", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.U1)] public static extern void sp_bit_vec_set(CBitVec* @this, nuint index, [MarshalAs(UnmanagedType.U1)] bool value);
public static extern bool sp_bit_vec_set(BitVec* @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> /// <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)] [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> /// <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)] [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> /// <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)] [DllImport(__DllName, EntryPoint = "sp_bit_vec_is_empty", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
[return: MarshalAs(UnmanagedType.U1)] [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> /// <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)] [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> /// <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)] [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> /// <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)] [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> /// <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)] [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> /// <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)] [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> /// <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)] [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> /// <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)] [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)] [StructLayout(LayoutKind.Sequential)]
public unsafe partial struct CByteSlice public unsafe partial struct CByteSlice
{ {
@ -234,11 +238,6 @@ namespace ServicePoint.BindGen
public nuint length; public nuint length;
} }
[StructLayout(LayoutKind.Sequential)]
public unsafe partial struct BitVec
{
}
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public unsafe partial struct ByteGrid public unsafe partial struct ByteGrid
{ {

View file

@ -2,7 +2,7 @@ using ServicePoint.BindGen;
namespace ServicePoint; namespace ServicePoint;
public sealed class BitVec : SpNativeInstance<BindGen.BitVec> public sealed class BitVec : SpNativeInstance<BindGen.CBitVec>
{ {
public static BitVec New(int size) 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)
{ {
} }

View file

@ -27,21 +27,15 @@
</PropertyGroup> </PropertyGroup>
<!-- generate C# bindings --> <!-- 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 --release"/>
<Exec Command="cargo build --manifest-path ../../../crates/servicepoint_binding_c/Cargo.toml --release"/>
</Target> </Target>
<Target Name="BuildBindings" Condition="'$(Configuration)'=='Debug'" BeforeTargets="Build"> <Target Name="BuildBindings" Condition="'$(Configuration)'=='Debug'" BeforeTargets="PrepareForBuild">
<Exec Command="cargo build"/> <Exec Command="cargo build"/>
<Exec Command="cargo build --manifest-path ../../../crates/servicepoint_binding_c/Cargo.toml"/>
</Target> </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"/>
</Target>
<!-- include native binary in output --> <!-- include native binary in output -->
<ItemGroup Condition="'$(Configuration)'=='Debug'"> <ItemGroup Condition="'$(Configuration)'=='Debug'">
<Content Include="../../../target/debug/libservicepoint_binding_c.so" CopyToOutputDirectory="Always"> <Content Include="../../../target/debug/libservicepoint_binding_c.so" CopyToOutputDirectory="Always">

View file

@ -1,8 +1,8 @@
//! Build script generating the C# code needed to call methods from the `servicepoint` C library. //! Build script generating the C# code needed to call methods from the `servicepoint` C library.
fn main() { fn main() {
println!("cargo:rerun-if-changed=../servicepoint_binding_c/src"); println!("cargo::rerun-if-changed=../servicepoint_binding_c/src");
println!("cargo:rerun-if-changed=build.rs"); println!("cargo::rerun-if-changed=build.rs");
csbindgen::Builder::default() csbindgen::Builder::default()
.input_extern_file("../servicepoint_binding_c/src/bit_vec.rs") .input_extern_file("../servicepoint_binding_c/src/bit_vec.rs")
.input_extern_file("../servicepoint_binding_c/src/byte_grid.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/lib.rs")
.input_extern_file("../servicepoint_binding_c/src/c_slice.rs") .input_extern_file("../servicepoint_binding_c/src/c_slice.rs")
.input_extern_file("../servicepoint_binding_c/src/packet.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/byte_grid.rs")
.input_extern_file("../servicepoint/src/command.rs") .input_extern_file("../servicepoint/src/command.rs")
.input_extern_file("../servicepoint/src/connection.rs") .input_extern_file("../servicepoint/src/connection.rs")