solana/sdk/gen-headers/src/main.rs

169 lines
5.3 KiB
Rust

#![allow(clippy::integer_arithmetic)]
use {
log::info,
regex::{Captures, Regex},
std::{
ffi::OsStr,
fs,
io::{prelude::*, BufWriter},
path::PathBuf,
str,
},
};
/**
* 1. process every inc file in syscalls header file
*
* 2. in every such file replace the syscall declaration by a new
* declaration with a new extended name, and a static function
* definition that computes a hash of the original name and uses the
* hash to initialize a function pointer, the function pointer then is
* used the call the syscall function.
*/
fn main() {
let syscalls_inc_path = PathBuf::from("sdk/sbf/c/inc/sol/inc");
if syscalls_inc_path.is_dir() {
for entry in fs::read_dir(syscalls_inc_path).expect("Can't open headers dir") {
let entry = entry.expect("Can't open header file");
let path = entry.path();
if !path.is_dir() {
let extension = path.extension();
if extension == Some(OsStr::new("inc")) {
transform(&path);
}
}
}
}
}
/**
* Transform input inc file to a valid C header file replacing
* declaration templates with valid C code.
*/
fn transform(inc: &PathBuf) {
let inc_path = PathBuf::from(inc);
let Some(filename) = inc_path.file_name() else {
return;
};
let Some(parent) = inc_path.parent() else {
return;
};
let Some(parent) = parent.parent() else {
return;
};
let mut header_path = PathBuf::from(parent);
let mut filename = PathBuf::from(filename);
filename.set_extension("h");
header_path.push(filename);
info!(
"Transforming file {} -> {}",
inc.display(),
header_path.display()
);
let mut input = match fs::File::open(inc) {
Ok(x) => x,
Err(err) => panic!("Failed to open {}: {}", inc.display(), err),
};
let mut input_content = vec![];
input.read_to_end(&mut input_content).unwrap();
let input_content = str::from_utf8(&input_content).unwrap();
let output = match fs::File::create(&header_path) {
Ok(x) => x,
Err(err) => panic!("Failed to create {}: {}", header_path.display(), err),
};
let mut output_writer = BufWriter::new(output);
let decl_re =
Regex::new(r"@SYSCALL ([0-9A-Za-z_*]+)[[:space:]]+(sol_[0-9A-Za-z_]+)\(([^);]*)\);")
.unwrap();
let comm_re = Regex::new(r",").unwrap();
let output_content = decl_re.replace_all(input_content, |caps: &Captures| {
let ty = &caps[1].to_string();
let func = &caps[2].to_string();
let args = &caps[3].to_string();
let warn = format!("/* DO NOT MODIFY THIS GENERATED FILE. INSTEAD CHANGE {} AND RUN `cargo run --bin gen-headers` */", inc.display());
let ifndef = format!("#ifndef SOL_SBFV2\n{ty} {func}({args});");
let hash = sys_hash(func);
let typedef_statement = format!("typedef {ty}(*{func}_pointer_type)({args});");
let mut arg = 0;
let mut arg_list = "".to_string();
if !args.is_empty() {
arg_list = comm_re
.replace_all(args, |_caps: &Captures| {
arg += 1;
format!(" arg{arg},")
})
.to_string();
arg += 1;
arg_list = format!("{arg_list} arg{arg}");
}
let function_signature = format!("static {ty} {func}({arg_list})");
let pointer_assignment = format!(
"{func}_pointer_type {func}_pointer = ({func}_pointer_type) {hash};"
);
if !args.is_empty() {
arg_list = "arg1".to_string();
for a in 2..arg + 1 {
arg_list = format!("{arg_list}, arg{a}");
}
}
let return_statement = if ty == "void" {
format!("{func}_pointer({arg_list});")
} else {
format!("return {func}_pointer({arg_list});")
};
format!(
"{warn}\n{ifndef}\n#else\n{typedef_statement}\n{function_signature} {{\n {pointer_assignment}\n {return_statement}\n}}\n#endif",
)
});
write!(output_writer, "{output_content}").unwrap();
}
const fn sys_hash(name: &str) -> usize {
murmur3_32(name.as_bytes(), 0) as usize
}
#[inline(always)]
const fn murmur3_32(buf: &[u8], seed: u32) -> u32 {
let mut hash = seed;
let mut i = 0;
while i < buf.len() / 4 {
let buf = [buf[i * 4], buf[i * 4 + 1], buf[i * 4 + 2], buf[i * 4 + 3]];
hash ^= pre_mix(buf);
hash = hash.rotate_left(13);
hash = hash.wrapping_mul(5).wrapping_add(0xe6546b64);
i += 1;
}
match buf.len() % 4 {
0 => {}
1 => {
hash ^= pre_mix([buf[i * 4], 0, 0, 0]);
}
2 => {
hash ^= pre_mix([buf[i * 4], buf[i * 4 + 1], 0, 0]);
}
3 => {
hash ^= pre_mix([buf[i * 4], buf[i * 4 + 1], buf[i * 4 + 2], 0]);
}
_ => { /* unreachable!() */ }
}
hash ^= buf.len() as u32;
hash ^= hash.wrapping_shr(16);
hash = hash.wrapping_mul(0x85ebca6b);
hash ^= hash.wrapping_shr(13);
hash = hash.wrapping_mul(0xc2b2ae35);
hash ^= hash.wrapping_shr(16);
hash
}
const fn pre_mix(buf: [u8; 4]) -> u32 {
u32::from_le_bytes(buf)
.wrapping_mul(0xcc9e2d51)
.rotate_left(15)
.wrapping_mul(0x1b873593)
}