From 5161fe15462e597e4c9bc3e7353fde1141ea7465 Mon Sep 17 00:00:00 2001 From: trivernis Date: Fri, 13 Mar 2020 14:26:10 +0100 Subject: [PATCH] Add C bindings --- .gitignore | 3 ++- Cargo.toml | 4 ++++ src/chunks.rs | 55 ++++++++++++++++++++++++++++++++++++--------------- src/io.rs | 31 +++++++++++++++++++---------- 4 files changed, 66 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index cc06e82..5c69189 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ **/*.rs.bk Cargo.lock *.bdf -.idea \ No newline at end of file +.idea +cbindgen.toml \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index fc0ede4..34b52b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,10 @@ version = "0.1.0" authors = ["trivernis "] edition = "2018" +[lib] +name = "bdf" +crate-type = ["staticlib", "cdylib", "lib"] + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/src/chunks.rs b/src/chunks.rs index c96f747..15ead13 100644 --- a/src/chunks.rs +++ b/src/chunks.rs @@ -14,6 +14,7 @@ pub const META_CHUNK_NAME: &str = "META"; pub const HTBL_CHUNK_NAME: &str = "HTBL"; pub const DTBL_CHUNK_NAME: &str = "DTBL"; +#[repr(C)] #[derive(Debug, Clone)] pub struct GenericChunk { pub length: u32, @@ -22,6 +23,7 @@ pub struct GenericChunk { pub crc: u32, } +#[repr(C)] #[derive(Debug, Clone)] pub struct MetaChunk { pub chunk_count: u32, @@ -30,11 +32,13 @@ pub struct MetaChunk { pub compression_method: Option, } +#[repr(C)] #[derive(Debug, Clone)] pub struct HashLookupTable { pub entries: HashMap, } +#[repr(C)] #[derive(Debug, Clone)] pub struct HashEntry { pub(crate) id: u32, @@ -42,6 +46,7 @@ pub struct HashEntry { name: String, } +#[repr(C)] #[derive(Debug, Clone)] pub struct DataEntry { pub plain: String, @@ -50,7 +55,8 @@ pub struct DataEntry { impl GenericChunk { /// Serializes the chunk to a vector of bytes - pub fn serialize(&mut self) -> Vec { + #[no_mangle] + pub extern fn serialize(&mut self) -> Vec { let mut serialized: Vec = Vec::new(); let mut length_raw = [0u8; 4]; BigEndian::write_u32(&mut length_raw, self.length); @@ -66,7 +72,8 @@ impl GenericChunk { } /// Returns the data entries of the chunk - pub fn data_entries( + #[no_mangle] + pub extern fn data_entries( &mut self, lookup_table: &HashLookupTable, ) -> Result, Error> { @@ -116,7 +123,8 @@ impl GenericChunk { } /// Constructs the chunk from a Vec of Data entries and a hash lookup table - pub fn from_data_entries( + #[no_mangle] + pub extern fn from_data_entries( entries: &Vec, lookup_table: &HashLookupTable, ) -> GenericChunk { @@ -134,7 +142,8 @@ impl GenericChunk { } } - pub fn compress(&mut self) -> Result<(), Error> { + #[no_mangle] + pub extern fn compress(&mut self) -> Result<(), Error> { let data = self.data.as_slice(); let mut compressor = XzEncoder::new(data, 6); let mut compressed: Vec = Vec::new(); @@ -145,7 +154,8 @@ impl GenericChunk { Ok(()) } - pub fn decompress(&mut self) -> Result<(), Error> { + #[no_mangle] + pub extern fn decompress(&mut self) -> Result<(), Error> { let data = self.data.as_slice(); let mut decompressor = XzDecoder::new(data); let mut decompressed: Vec = Vec::new(); @@ -194,7 +204,8 @@ impl From<&HashLookupTable> for GenericChunk { impl MetaChunk { /// Creates a new meta chunk - pub fn new(entry_count: u64, entries_per_chunk: u32, compress: bool) -> Self { + #[no_mangle] + pub extern fn new(entry_count: u64, entries_per_chunk: u32, compress: bool) -> Self { let compression_method = if compress { Some(LZMA.to_string()) } else { @@ -211,7 +222,8 @@ impl MetaChunk { } /// Serializes the chunk into bytes - pub fn serialize(&self) -> Vec { + #[no_mangle] + pub extern fn serialize(&self) -> Vec { let mut serialized_data: Vec = Vec::new(); let mut chunk_count_raw = [0u8; 4]; BigEndian::write_u32(&mut chunk_count_raw, self.chunk_count); @@ -236,6 +248,7 @@ impl MetaChunk { impl TryFrom for MetaChunk { type Error = Error; + #[no_mangle] fn try_from(chunk: GenericChunk) -> Result { if &chunk.name != META_CHUNK_NAME { return Err(Error::new( @@ -272,17 +285,20 @@ impl TryFrom for MetaChunk { } impl HashLookupTable { - pub fn new(entries: HashMap) -> Self { + #[no_mangle] + pub extern fn new(entries: HashMap) -> Self { Self { entries } } /// Returns an entry by the name of the hash function - pub fn get_entry(&self, name: &String) -> Option<(&u32, &HashEntry)> { + #[no_mangle] + pub extern fn get_entry(&self, name: &String) -> Option<(&u32, &HashEntry)> { self.entries.iter().find(|(_, entry)| entry.name == *name) } /// Serializes the lookup table into a vector of bytes - pub fn serialize(&self) -> Vec { + #[no_mangle] + pub extern fn serialize(&self) -> Vec { let mut serialized_full: Vec = Vec::new(); for (_, entry) in &self.entries { serialized_full.append(entry.serialize().as_mut()) @@ -295,6 +311,7 @@ impl HashLookupTable { impl TryFrom for HashLookupTable { type Error = Error; + #[no_mangle] fn try_from(chunk: GenericChunk) -> Result { if &chunk.name != HTBL_CHUNK_NAME { return Err(Error::new( @@ -333,7 +350,8 @@ impl TryFrom for HashLookupTable { } impl HashEntry { - pub fn new(name: String, output_length: u32) -> Self { + #[no_mangle] + pub extern fn new(name: String, output_length: u32) -> Self { Self { id: 0, name, @@ -342,7 +360,8 @@ impl HashEntry { } /// Serializes the entry to a vector of bytes - pub fn serialize(&self) -> Vec { + #[no_mangle] + pub extern fn serialize(&self) -> Vec { let mut serialized: Vec = Vec::new(); let mut id_raw = [0u8; 4]; BigEndian::write_u32(&mut id_raw, self.id); @@ -361,7 +380,8 @@ impl HashEntry { } impl DataEntry { - pub fn new(plain: String) -> Self { + #[no_mangle] + pub extern fn new(plain: String) -> Self { Self { hashes: HashMap::new(), plain, @@ -369,17 +389,20 @@ impl DataEntry { } /// Adds a hash to the hash values - pub fn add_hash_value(&mut self, name: String, value: Vec) { + #[no_mangle] + pub extern fn add_hash_value(&mut self, name: String, value: Vec) { self.hashes.insert(name, value); } /// Returns the hash value for a given name of a hash function - pub fn get_hash_value(&self, name: String) -> Option<&Vec> { + #[no_mangle] + pub extern fn get_hash_value(&self, name: String) -> Option<&Vec> { self.hashes.get(&name) } /// Serializes the entry to a vector of bytes - pub fn serialize(&self, lookup_table: &HashLookupTable) -> Vec { + #[no_mangle] + pub extern fn serialize(&self, lookup_table: &HashLookupTable) -> Vec { let mut pw_plain_raw = self.plain.clone().into_bytes(); let mut pw_length_raw = [0u8; 4]; BigEndian::write_u32(&mut pw_length_raw, pw_plain_raw.len() as u32); diff --git a/src/io.rs b/src/io.rs index aa29318..850b5b9 100644 --- a/src/io.rs +++ b/src/io.rs @@ -8,6 +8,7 @@ use std::convert::TryInto; const ENTRIES_PER_CHUNK: u32 = 100_000; +#[repr(C)] pub struct BDFReader { reader: BufReader, pub metadata: Option, @@ -15,6 +16,7 @@ pub struct BDFReader { compressed: bool, } +#[repr(C)] pub struct BDFWriter { writer: BufWriter, metadata: MetaChunk, @@ -25,7 +27,8 @@ pub struct BDFWriter { } impl BDFWriter { - pub fn new(writer: BufWriter, entry_count: u64, compress: bool) -> Self { + #[no_mangle] + pub extern fn new(writer: BufWriter, entry_count: u64, compress: bool) -> Self { Self { metadata: MetaChunk::new(entry_count, ENTRIES_PER_CHUNK, compress), lookup_table: HashLookupTable::new(HashMap::new()), @@ -38,7 +41,8 @@ impl BDFWriter { /// Adds an entry to the hash lookup table /// If the lookup table has already been written to the file, an error ris returned - pub fn add_lookup_entry(&mut self, mut entry: HashEntry) -> Result { + #[no_mangle] + pub extern fn add_lookup_entry(&mut self, mut entry: HashEntry) -> Result { if self.head_written { return Err(Error::new( ErrorKind::Other, @@ -55,7 +59,7 @@ impl BDFWriter { /// Adds a data entry to the file. /// If the number of entries per chunk is reached, /// the data will be written to the file - pub fn add_data_entry(&mut self, data_entry: DataEntry) -> Result<(), Error> { + pub extern fn add_data_entry(&mut self, data_entry: DataEntry) -> Result<(), Error> { self.data_entries.push(data_entry); if self.data_entries.len() >= ENTRIES_PER_CHUNK as usize { self.flush()?; @@ -65,7 +69,8 @@ impl BDFWriter { } /// Writes the data to the file - pub fn flush(&mut self) -> Result<(), Error> { + #[no_mangle] + pub extern fn flush(&mut self) -> Result<(), Error> { if !self.head_written { self.writer.write(BDF_HDR)?; let mut generic_meta = GenericChunk::from(&self.metadata); @@ -86,13 +91,15 @@ impl BDFWriter { Ok(()) } - pub fn flush_writer(&mut self) -> Result<(), Error> { + #[no_mangle] + pub extern fn flush_writer(&mut self) -> Result<(), Error> { self.writer.flush() } } impl BDFReader { - pub fn new(reader: BufReader) -> Self { + #[no_mangle] + pub extern fn new(reader: BufReader) -> Self { Self { metadata: None, lookup_table: None, @@ -102,7 +109,8 @@ impl BDFReader { } /// Verifies the header of the file and reads and stores the metadata - pub fn read_metadata(&mut self) -> Result<&MetaChunk, Error> { + #[no_mangle] + pub extern fn read_metadata(&mut self) -> Result<&MetaChunk, Error> { if !self.validate_header() { return Err(Error::new(ErrorKind::InvalidData, "invalid BDF Header")); } @@ -131,7 +139,8 @@ impl BDFReader { /// Reads the lookup table of the file. /// This function should be called after the read_metadata function was called - pub fn read_lookup_table(&mut self) -> Result<&HashLookupTable, Error> { + #[no_mangle] + pub extern fn read_lookup_table(&mut self) -> Result<&HashLookupTable, Error> { match &self.metadata { None => self.read_metadata()?, Some(t) => t, @@ -150,7 +159,8 @@ impl BDFReader { } /// Validates the header of the file - fn validate_header(&mut self) -> bool { + #[no_mangle] + extern fn validate_header(&mut self) -> bool { let mut header = [0u8; 11]; let _ = self.reader.read(&mut header); @@ -158,7 +168,8 @@ impl BDFReader { } /// Returns the next chunk if one is available. - pub fn next_chunk(&mut self) -> Result { + #[no_mangle] + pub extern fn next_chunk(&mut self) -> Result { let mut length_raw = [0u8; 4]; let _ = self.reader.read_exact(&mut length_raw)?; let length = BigEndian::read_u32(&mut length_raw);