Skip to content
This repository has been archived by the owner on Sep 21, 2024. It is now read-only.

fix: FFI header generation #270

Merged
merged 1 commit into from
Mar 10, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 34 additions & 14 deletions rust/noosphere/src/ffi/header_transformer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::BTreeMap;
use std::io::Write;

#[derive(Debug)]
Expand All @@ -12,15 +12,15 @@ use std::io::Write;
pub struct HeaderTransformer<W: Write> {
inner: W,
buffer: String,
rust_classes: HashMap<String, String>,
rust_classes: BTreeMap<String, String>,
}

impl<W: Write> HeaderTransformer<W> {
pub fn new(inner: W) -> std::io::Result<Self> {
Ok(HeaderTransformer {
inner,
buffer: String::with_capacity(200),
rust_classes: HashMap::<String, String>::new(),
rust_classes: BTreeMap::<String, String>::new(),
})
}

Expand All @@ -32,17 +32,24 @@ impl<W: Write> HeaderTransformer<W> {
/// the underlying `inner` buffer.
fn transform_statement(&mut self) -> std::io::Result<usize> {
let typedef_sigil = "typedef struct";
let typedef_sigil_len = typedef_sigil.len();

// Register new typedefs found.
if let Some(index) = self.buffer.find(typedef_sigil) {
let start_index = index + typedef_sigil.len() + 1;
if let Some(index) = self.buffer.find("typedef struct") {
let start_index = index + typedef_sigil_len + 1;
let mut rust_name = String::with_capacity(16);
for c in self.buffer[start_index..self.buffer.len()].chars() {
if c == ' ' {
let c_name = rust_case_to_c_case(&rust_name);
self.rust_classes.insert(rust_name, c_name);
break;
} else {
if !c.is_alphanumeric() {
// A non-alphanumeric character was parsed probably for
// a non-opaque struct, looks like:
// `typedef struct { size_t s } foo_t`
break;
}
rust_name.push(c);
}
}
Expand All @@ -51,12 +58,15 @@ impl<W: Write> HeaderTransformer<W> {
// Replace rust class names with snake_case names.
let mut line = String::with_capacity(self.buffer.len());
std::mem::swap(&mut line, &mut self.buffer);
for (rust_name, c_name) in self.rust_classes.iter() {

// Reverse the iterator over the BTreeMap, which orders
// by key, ensuring that class names that are subsets of
// other class names are replaced appropriately.
for (rust_name, c_name) in self.rust_classes.iter().rev() {
line = line.replace(rust_name, c_name);
}

let res = self.inner.write(line.as_bytes());
res
self.inner.write(line.as_bytes())
}

#[cfg(test)]
Expand Down Expand Up @@ -137,14 +147,23 @@ mod tests {
transformer
.write("#define __RUST_NOOSPHERE__\n".as_bytes())
.unwrap();

// `NsSphere` defined before `NsSphereFile` tests that
// token replacement works as expected when a token subset
// exists as a class e.g. ensure that `NsSphere` does not
// replace `NsSphereFile` as `ns_sphereFile`.
transformer
.write("typedef struct NsHeaders NsHeaders_t;\n".as_bytes())
.write("typedef struct NsSphere NsSphere_t;\n".as_bytes())
.unwrap();
transformer
.write("typedef struct NsSphereFile NsSphereFile_t;\n".as_bytes())
.unwrap();

transformer
.write(
r#"
void ns_headers_add (
NsHeaders_t * headers,
void ns_sphere_file_foo (
NsSphereFile_t * file,
char const * name,
char const * value);"#
.as_bytes(),
Expand All @@ -163,10 +182,11 @@ char const * value);"#
let expected = r#"
#ifndef __RUST_NOOSPHERE__
#define __RUST_NOOSPHERE__
typedef struct ns_headers ns_headers_t;
typedef struct ns_sphere ns_sphere_t;
typedef struct ns_sphere_file ns_sphere_file_t;

void ns_headers_add (
ns_headers_t * headers,
void ns_sphere_file_foo (
ns_sphere_file_t * file,
char const * name,
char const * value);
#endif /* __RUST_NOOSPHERE__ */
Expand Down