mirror of
https://github.com/Sarsoo/csbindgen.git
synced 2025-01-09 13:27:47 +00:00
refactoring one
This commit is contained in:
parent
4ac772ad22
commit
4189fd72fd
@ -3,8 +3,13 @@ name = "csbindgen-tests"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# [[bin]]
|
||||
# name = "csbindgen-test-app"
|
||||
# path = "src/main.rs"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
csbindgen = { path = "../csbindgen" }
|
||||
|
@ -1,10 +1,9 @@
|
||||
#![feature(core_intrinsics)]
|
||||
fn main() {
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header("c/lz4/lz4.h")
|
||||
.header("c/lz4/lz4hc.h")
|
||||
.header("c/lz4/lz4frame.h")
|
||||
.header("c/lz4/xxhash.h")
|
||||
//.header("c/lz4/lz4hc.h")
|
||||
//.header("c/lz4/lz4frame.h")
|
||||
//.header("c/lz4/xxhash.h")
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
@ -12,12 +11,11 @@ fn main() {
|
||||
.write_to_file("src/lz4/mod.rs")
|
||||
.expect("Couldn't write bindings!");
|
||||
|
||||
cc::Build::new().file("c/lz4/lz4.c").compile("lz4");
|
||||
cc::Build::new().file("c/lz4/lz4.c").compile("csharp_lz4");
|
||||
|
||||
// TODO:write test
|
||||
csbindgen::run(
|
||||
"src/lz4/mod.rs",
|
||||
"src/ffi.rs",
|
||||
"../dotnet-sandbox/bindgen.cs",
|
||||
)
|
||||
csbindgen::Builder::new()
|
||||
.input_bindgen_file("src/lz4/mod.cs")
|
||||
.rust_method_prefix("csbindgen_")
|
||||
.generate_to_file("src/ffi.rs", "../dotnet-sandbox/bindgen.cs")
|
||||
.unwrap();
|
||||
}
|
||||
|
23
csbindgen-tests/src/__main.rs
Normal file
23
csbindgen-tests/src/__main.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use std::env;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(non_snake_case)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(non_upper_case_globals)]
|
||||
mod lz4;
|
||||
|
||||
fn main() {
|
||||
let path = env::current_dir().unwrap();
|
||||
println!("starting dir: {}", path.display());
|
||||
|
||||
unsafe {
|
||||
let num = lz4::LZ4_versionNumber();
|
||||
println!("lz4 num: {}", num);
|
||||
}
|
||||
|
||||
csbindgen::run(
|
||||
"csbindgen-tests/src/lz4/mod.rs",
|
||||
"csbindgen-tests/src/ffi.rs",
|
||||
"dotnet-sandbox/bindgen.cs",
|
||||
);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,13 +0,0 @@
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
|
||||
let path = env::current_dir().unwrap();
|
||||
println!("starting dir: {}", path.display());
|
||||
|
||||
csbindgen::run(
|
||||
"csbindgen-tests/src/lz4/mod.rs",
|
||||
"csbindgen-tests/src/ffi.rs",
|
||||
"dotnet-sandbox/bindgen.cs",
|
||||
);
|
||||
}
|
128
csbindgen/src/builder.rs
Normal file
128
csbindgen/src/builder.rs
Normal file
@ -0,0 +1,128 @@
|
||||
use std::{
|
||||
error::Error,
|
||||
fs::{File, OpenOptions},
|
||||
io::{self, Write},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use crate::generate;
|
||||
|
||||
pub struct Builder {
|
||||
options: BindgenOptions,
|
||||
}
|
||||
|
||||
pub struct BindgenOptions {
|
||||
pub input_bindgen_file: String,
|
||||
|
||||
/// add original extern call type prefix to rust wrapper,
|
||||
/// `return {rust_method_type_path}::foo()`
|
||||
pub rust_method_type_path: String,
|
||||
|
||||
/// add method prefix to rust wrapper,
|
||||
/// `pub extern "C" fn {rust_method_prefix}foo()`
|
||||
pub rust_method_prefix: String,
|
||||
|
||||
/// add file header string to rust wrapper,
|
||||
/// `mod lz4;`, `use super::lz4;`
|
||||
pub rust_file_header: String,
|
||||
|
||||
/// configure C# file namespace(default is `CsBindgen`),
|
||||
/// "namespace {csharp_namespace}"
|
||||
pub csharp_namespace: String,
|
||||
|
||||
/// configure C# class name(default is `NativeMethods`),
|
||||
/// `public static unsafe partial class {csharp_class_name}`
|
||||
pub csharp_class_name: String,
|
||||
|
||||
/// configure C# load dll name,
|
||||
/// `[DllImport({csharp_dll_name})]`
|
||||
pub csharp_dll_name: String,
|
||||
|
||||
/// configure C# calling method name prefix,
|
||||
/// `public static extern void {csharp_method_prefix}foo()`
|
||||
pub csharp_method_prefix: String,
|
||||
|
||||
/// configure c_long to {csharp_c_long_convert} type,
|
||||
/// default is `Int32`.
|
||||
pub csharp_c_long_convert: String,
|
||||
|
||||
/// configure c_long to {csharp_c_long_convert} type,
|
||||
/// default is `UInt32`.
|
||||
pub csharp_c_ulong_convert: String,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
options: BindgenOptions {
|
||||
input_bindgen_file: "".to_string(),
|
||||
rust_method_type_path: "".to_string(),
|
||||
rust_method_prefix: "".to_string(),
|
||||
rust_file_header: "".to_string(),
|
||||
csharp_namespace: "CsBindgen".to_string(),
|
||||
csharp_class_name: "NativeMethods".to_string(),
|
||||
csharp_dll_name: "".to_string(),
|
||||
csharp_method_prefix: "".to_string(),
|
||||
csharp_c_long_convert: "Int32".to_string(),
|
||||
csharp_c_ulong_convert: "UInt32".to_string(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Change an input .rs file(such as generated from bindgen) to generate binding.
|
||||
pub fn input_bindgen_file<T: Into<String>>(mut self, input_bindgen_file: T) -> Builder {
|
||||
self.options.input_bindgen_file = input_bindgen_file.into();
|
||||
self
|
||||
}
|
||||
|
||||
// TODO:method chain methods...
|
||||
|
||||
/// add method prefix to rust wrapper,
|
||||
/// `pub extern "C" fn {rust_method_prefix}foo()`
|
||||
pub fn rust_method_prefix<T: Into<String>>(mut self, rust_method_prefix: T) -> Builder {
|
||||
self.options.rust_method_prefix = rust_method_prefix.into();
|
||||
self
|
||||
}
|
||||
|
||||
// pub fn generate_csharp_file<T: AsRef<Path>>(&self, csharp_output_path: T) -> io::Result<()> {
|
||||
// let mut file = OpenOptions::new()
|
||||
// .write(true)
|
||||
// .truncate(true)
|
||||
// .create(true)
|
||||
// .open(csharp_output_path.as_ref())?;
|
||||
|
||||
// let code = self.generate();
|
||||
// file.write_all(code.as_bytes())?;
|
||||
// file.flush()?;
|
||||
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
pub fn generate_to_file<P: AsRef<Path>>(
|
||||
&self,
|
||||
rust_output_path: P,
|
||||
csharp_output_path: P,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut rust_file = make_file(rust_output_path)?;
|
||||
let mut csharp_file = make_file(csharp_output_path)?;
|
||||
|
||||
let (rust, csharp) = generate(&self.options)?;
|
||||
|
||||
rust_file.write_all(rust.as_bytes())?;
|
||||
rust_file.flush()?;
|
||||
|
||||
csharp_file.write_all(csharp.as_bytes())?;
|
||||
csharp_file.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn make_file<P: AsRef<Path>>(path: P) -> io::Result<File> {
|
||||
let file = OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.create(true)
|
||||
.open(path)?;
|
||||
Ok(file)
|
||||
}
|
186
csbindgen/src/emitter.rs
Normal file
186
csbindgen/src/emitter.rs
Normal file
@ -0,0 +1,186 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::builder::BindgenOptions;
|
||||
use crate::type_meta::*;
|
||||
use crate::util::*;
|
||||
|
||||
pub fn emit_rust_method(list: &Vec<ExternMethod>, options: &BindgenOptions) -> String {
|
||||
// configure
|
||||
let method_type_path = &options.rust_method_type_path;
|
||||
let method_prefix = &options.rust_method_prefix;
|
||||
let file_header = &options.rust_file_header;
|
||||
|
||||
let mut methods_string = String::new();
|
||||
|
||||
for item in list {
|
||||
let method_name = item.method_name.as_str();
|
||||
let parameters = item
|
||||
.parameters
|
||||
.iter()
|
||||
.map(|p| {
|
||||
format!(
|
||||
" {}: {}",
|
||||
p.name,
|
||||
p.rust_type.to_string(method_type_path)
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(",\n");
|
||||
|
||||
let return_line = match &item.return_type {
|
||||
None => "".to_string(),
|
||||
Some(v) => format!(" -> {}", v.to_string(method_type_path)),
|
||||
};
|
||||
|
||||
let parameter_only_names = item
|
||||
.parameters
|
||||
.iter()
|
||||
.map(|p| format!(" {}", p.name))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",\n");
|
||||
|
||||
let template = format!(
|
||||
"
|
||||
#[no_mangle]
|
||||
pub extern \"C\" fn {method_prefix}{method_name}(
|
||||
{parameters}
|
||||
){return_line}
|
||||
{{
|
||||
unsafe {{
|
||||
return {method_type_path}{method_name}(
|
||||
{parameter_only_names}
|
||||
)
|
||||
}}
|
||||
}}
|
||||
"
|
||||
);
|
||||
|
||||
methods_string.push_str(template.as_str());
|
||||
}
|
||||
|
||||
let result = format!(
|
||||
"// auto-generated via csbindgen
|
||||
|
||||
#[allow(unused)]
|
||||
use ::std::os::raw::*;
|
||||
|
||||
{file_header}
|
||||
|
||||
{methods_string}
|
||||
"
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn emit_csharp(
|
||||
methods: &Vec<ExternMethod>,
|
||||
aliases: &HashMap<String, RustType>,
|
||||
structs: &Vec<RustStruct>,
|
||||
options: &BindgenOptions
|
||||
) -> String {
|
||||
// configure
|
||||
let namespace = &options.csharp_namespace;
|
||||
let class_name = &options.csharp_class_name;
|
||||
let dll_name = &options.csharp_dll_name;
|
||||
let method_prefix = &options.csharp_method_prefix;
|
||||
|
||||
let mut method_list_string = String::new();
|
||||
for item in methods {
|
||||
let method_name = &item.method_name;
|
||||
let return_type = match &item.return_type {
|
||||
Some(x) => x.to_csharp_string(&options),
|
||||
None => "void".to_string(),
|
||||
};
|
||||
|
||||
let parameters = item
|
||||
.parameters
|
||||
.iter()
|
||||
.map(|p| format!("{} {}", p.rust_type.to_csharp_string(&options), p.name))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
method_list_string.push_str_ln(
|
||||
" [DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]",
|
||||
);
|
||||
method_list_string.push_str_ln(
|
||||
format!(" public static extern {return_type} {method_prefix}{method_name}({parameters});").as_str(),
|
||||
);
|
||||
method_list_string.push_str("\n");
|
||||
}
|
||||
|
||||
let mut alias_string = String::new();
|
||||
let mut aliases: Vec<_> = aliases.iter().collect();
|
||||
aliases.sort_by_key(|x| x.0);
|
||||
for (name, rust_type) in aliases {
|
||||
alias_string.push_str_ln(
|
||||
format!(" using {} = {};", name, rust_type.to_csharp_string(&options)).as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut structs_string = String::new();
|
||||
for item in structs {
|
||||
let name = &item.struct_name;
|
||||
let layout_kind = if item.is_union {
|
||||
"Explicit"
|
||||
} else {
|
||||
"Sequential"
|
||||
};
|
||||
|
||||
structs_string
|
||||
.push_str_ln(format!(" [StructLayout(LayoutKind.{layout_kind})]").as_str());
|
||||
structs_string.push_str_ln(format!(" public unsafe struct {name}").as_str());
|
||||
structs_string.push_str_ln(" {");
|
||||
for field in &item.fields {
|
||||
if item.is_union {
|
||||
structs_string.push_str_ln(" [FieldOffset(0)]");
|
||||
}
|
||||
structs_string.push_str(
|
||||
format!(
|
||||
" public {} {}",
|
||||
field.rust_type.to_csharp_string(&options),
|
||||
field.name
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
if field.rust_type.is_fixed_array {
|
||||
let mut digits = field.rust_type.fixed_array_digits.clone();
|
||||
if digits == "0" {
|
||||
digits = "1".to_string(); // 0 fixed array is not allowed in C#
|
||||
};
|
||||
|
||||
structs_string.push_str(format!("[{}]", digits).as_str());
|
||||
}
|
||||
structs_string.push_str_ln(";");
|
||||
}
|
||||
structs_string.push_str_ln(" }");
|
||||
structs_string.push_str("\n");
|
||||
}
|
||||
|
||||
// TODO: for Unity, `__Intern`.
|
||||
let result = format!(
|
||||
"// <auto-generated>
|
||||
// This code is generated via csbindgen.
|
||||
// DON'T CHANGE THIS DIRECTLY.
|
||||
// </auto-generated>
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace {namespace}
|
||||
{{
|
||||
{alias_string}
|
||||
|
||||
public static unsafe partial class {class_name}
|
||||
{{
|
||||
const string __DllName = \"{dll_name}\";
|
||||
|
||||
{method_list_string}
|
||||
}}
|
||||
|
||||
{structs_string}
|
||||
}}
|
||||
"
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
@ -1,17 +1,20 @@
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
fmt::Display,
|
||||
fs::{self, File},
|
||||
io::Write,
|
||||
};
|
||||
mod builder;
|
||||
mod emitter;
|
||||
mod parser;
|
||||
mod type_meta;
|
||||
mod util;
|
||||
|
||||
use syn::{ForeignItem, Item, Pat, ReturnType};
|
||||
pub use builder::Builder;
|
||||
|
||||
// mod lz4;
|
||||
use builder::BindgenOptions;
|
||||
use emitter::*;
|
||||
use parser::*;
|
||||
use std::{collections::HashSet, error::Error};
|
||||
|
||||
pub fn run(rs_path: &str, output_path: &str, csharp_output_path: &str) {
|
||||
let file_content = fs::read_to_string(rs_path).unwrap();
|
||||
let file_ast = syn::parse_file(file_content.as_str()).unwrap();
|
||||
pub(crate) fn generate(options: &BindgenOptions) -> Result<(String, String), Box<dyn Error>> {
|
||||
let path = &options.input_bindgen_file;
|
||||
let file_content = std::fs::read_to_string(path)?;
|
||||
let file_ast = syn::parse_file(file_content.as_str())?;
|
||||
|
||||
let methods = collect_method(&file_ast);
|
||||
let aliases = collect_type_alias(&file_ast);
|
||||
@ -39,551 +42,11 @@ pub fn run(rs_path: &str, output_path: &str, csharp_output_path: &str) {
|
||||
|
||||
let structs = reduce_struct(&structs, &using_types);
|
||||
|
||||
let vecs = emit_rust_method(&methods);
|
||||
|
||||
// TODO: filter methods
|
||||
//if !(t.method_name.starts_with("_") || t.method_name == "") {
|
||||
|
||||
let mut file = File::create(output_path).unwrap();
|
||||
{
|
||||
// TODO:modify here
|
||||
// TODO:modify here
|
||||
file.write_all("#[allow(unused)]\n".as_bytes()).unwrap();
|
||||
file.write_all("use ::std::os::raw::*;\n".as_bytes())
|
||||
.unwrap();
|
||||
file.write_all("use super::lz4;\n\n".as_bytes()).unwrap();
|
||||
for str in vecs {
|
||||
file.write_all(str.as_bytes()).unwrap();
|
||||
}
|
||||
let rust = emit_rust_method(&methods, &options);
|
||||
let csharp = emit_csharp(&methods, &aliases, &structs, &options);
|
||||
|
||||
file.flush().unwrap();
|
||||
}
|
||||
|
||||
let csharp = emit_csharp(&methods, &aliases, &structs);
|
||||
let mut file = File::create(csharp_output_path).unwrap();
|
||||
file.write_all(csharp.as_bytes()).unwrap();
|
||||
file.flush().unwrap();
|
||||
}
|
||||
|
||||
fn collect_method(ast: &syn::File) -> Vec<ExternMethod> {
|
||||
let mut list: Vec<ExternMethod> = Vec::new();
|
||||
|
||||
for item in ast.items.iter() {
|
||||
if let Item::ForeignMod(m) = item {
|
||||
for item in m.items.iter() {
|
||||
if let ForeignItem::Fn(m) = item {
|
||||
let method_name = m.sig.ident.to_string();
|
||||
|
||||
let mut parameters: Vec<Parameter> = Vec::new();
|
||||
let mut retrun_type: Option<RustType> = None;
|
||||
|
||||
// argument
|
||||
for arg in m.sig.inputs.iter() {
|
||||
if let syn::FnArg::Typed(t) = arg {
|
||||
let mut parameter_name: String = "".to_string();
|
||||
|
||||
if let Pat::Ident(ident) = &*t.pat {
|
||||
parameter_name = ident.ident.to_string();
|
||||
}
|
||||
|
||||
let rust_type = parse_type(&t.ty);
|
||||
|
||||
parameters.push(Parameter {
|
||||
name: parameter_name,
|
||||
rust_type: rust_type,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// return
|
||||
if let ReturnType::Type(_, b) = &m.sig.output {
|
||||
let rust_type = parse_type(&b);
|
||||
if rust_type.type_name == "" {
|
||||
continue;
|
||||
}
|
||||
|
||||
retrun_type = Some(rust_type);
|
||||
}
|
||||
|
||||
let t = ExternMethod {
|
||||
method_name: method_name.clone(),
|
||||
parameters: parameters,
|
||||
return_type: retrun_type,
|
||||
};
|
||||
|
||||
list.push(t.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
fn collect_type_alias(ast: &syn::File) -> Vec<(String, RustType)> {
|
||||
let mut result = Vec::new();
|
||||
for item in ast.items.iter() {
|
||||
if let Item::Type(t) = item {
|
||||
let name = t.ident.to_string();
|
||||
let alias = parse_type(&t.ty);
|
||||
|
||||
// pointer can not use alias.
|
||||
if !alias.is_pointer || !alias.is_pointer_pointer {
|
||||
result.push((name, alias));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
fn collect_struct(ast: &syn::File) -> Vec<RustStruct> {
|
||||
// collect union or struct
|
||||
|
||||
let mut result = Vec::new();
|
||||
|
||||
for item in ast.items.iter() {
|
||||
if let Item::Union(t) = item {
|
||||
let name = t.ident.to_string();
|
||||
let fields = collect_fields(&t.fields);
|
||||
|
||||
result.push(RustStruct {
|
||||
struct_name: name,
|
||||
fields: fields,
|
||||
is_union: true,
|
||||
});
|
||||
} else if let Item::Struct(t) = item {
|
||||
if let syn::Fields::Named(f) = &t.fields {
|
||||
let name = t.ident.to_string();
|
||||
let fields = collect_fields(&f);
|
||||
result.push(RustStruct {
|
||||
struct_name: name,
|
||||
fields: fields,
|
||||
is_union: false,
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn collect_fields(fields: &syn::FieldsNamed) -> Vec<FieldMember> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
for field in &fields.named {
|
||||
if let Some(x) = &field.ident {
|
||||
let t = parse_type(&field.ty);
|
||||
result.push(FieldMember {
|
||||
name: x.to_string(),
|
||||
rust_type: t,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn reduce_type_alias(
|
||||
aliases: &Vec<(String, RustType)>,
|
||||
using_types: &HashSet<String>,
|
||||
) -> HashMap<String, RustType> {
|
||||
let mut map = HashMap::new();
|
||||
for (name, rust_type) in aliases {
|
||||
if using_types.contains(name) {
|
||||
map.insert(name.clone(), rust_type.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for (name, rust_type) in aliases {
|
||||
let pointed = map.get(rust_type.type_name.as_str());
|
||||
if let Some(x) = pointed {
|
||||
map.insert(name.to_string(), x.clone());
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
fn reduce_struct(structs: &Vec<RustStruct>, using_types: &HashSet<String>) -> Vec<RustStruct> {
|
||||
let mut result = Vec::new();
|
||||
for item in structs {
|
||||
if using_types.contains(&item.struct_name) {
|
||||
result.push(item.clone());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn parse_type(t: &syn::Type) -> RustType {
|
||||
let mut has_star = false;
|
||||
let mut has_star_star = false;
|
||||
let mut has_const = false;
|
||||
let mut has_mut = false;
|
||||
let mut digits: String = "".to_string();
|
||||
|
||||
let name = match t {
|
||||
syn::Type::Ptr(t) => {
|
||||
has_star = true;
|
||||
has_const = t.const_token.is_some();
|
||||
has_mut = t.mutability.is_some();
|
||||
|
||||
if let syn::Type::Path(path) = &*t.elem {
|
||||
path.path.segments.last().unwrap().ident.to_string()
|
||||
} else if let syn::Type::Ptr(t) = &*t.elem {
|
||||
has_star = false;
|
||||
has_star_star = true;
|
||||
if let syn::Type::Path(path) = &*t.elem {
|
||||
path.path.segments.last().unwrap().ident.to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
syn::Type::Path(t) => t.path.segments.last().unwrap().ident.to_string(),
|
||||
syn::Type::Array(t) => {
|
||||
if let syn::Expr::Lit(x) = &t.len {
|
||||
if let syn::Lit::Int(x) = &x.lit {
|
||||
digits = x.base10_digits().to_string();
|
||||
}
|
||||
};
|
||||
|
||||
parse_type(&t.elem).type_name // maybe ok, only retrieve type_name
|
||||
}
|
||||
_ => "".to_string(),
|
||||
};
|
||||
|
||||
return RustType {
|
||||
is_const: has_const,
|
||||
is_mut: has_mut,
|
||||
is_pointer: has_star,
|
||||
is_pointer_pointer: has_star_star,
|
||||
is_fixed_array: (digits != ""),
|
||||
type_name: name,
|
||||
fixed_array_digits: digits,
|
||||
};
|
||||
}
|
||||
|
||||
// TODO:rewrite
|
||||
fn emit_rust_method(list: &Vec<ExternMethod>) -> Vec<String> {
|
||||
let mod_name = "lz4";
|
||||
|
||||
let mut result = Vec::new();
|
||||
|
||||
for item in list {
|
||||
let method_name = item.method_name.as_str();
|
||||
let parameters = item.parameters.iter().map(|p| {
|
||||
let mut sb = p.name.to_string();
|
||||
sb.push_str(": ");
|
||||
sb.push_str(p.rust_type.to_string(mod_name).as_str());
|
||||
sb
|
||||
});
|
||||
|
||||
let parameter_only_names = item.parameters.iter().map(|p| p.name.as_str());
|
||||
|
||||
let return_line = match &item.return_type {
|
||||
None => "".to_string(),
|
||||
Some(v) => {
|
||||
let mut sb = " -> ".to_string();
|
||||
sb.push_str(v.to_string(mod_name).as_str());
|
||||
sb
|
||||
}
|
||||
};
|
||||
|
||||
let mut sb = String::new();
|
||||
sb.push_str("#[no_mangle]\n");
|
||||
sb.push_str(format!("pub extern \"C\" fn csbindgen_{method_name}(\n").as_str());
|
||||
for parameter in parameters {
|
||||
sb.push_str(" ");
|
||||
sb.push_str(parameter.as_str());
|
||||
sb.push_str(",\n");
|
||||
}
|
||||
sb.push_str(")");
|
||||
if return_line != "" {
|
||||
sb.push_str(return_line.as_str());
|
||||
}
|
||||
sb.push_str("\n");
|
||||
sb.push_str("{\n");
|
||||
sb.push_str(" unsafe {\n");
|
||||
sb.push_str(format!(" return {mod_name}::{method_name}(\n").as_str());
|
||||
for p in parameter_only_names {
|
||||
sb.push_str(" ");
|
||||
sb.push_str(p);
|
||||
sb.push_str(",\n");
|
||||
}
|
||||
sb.push_str(" )\n");
|
||||
sb.push_str(" }\n");
|
||||
sb.push_str_ln("}\n");
|
||||
|
||||
result.push(sb);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn emit_csharp(
|
||||
methods: &Vec<ExternMethod>,
|
||||
aliases: &HashMap<String, RustType>,
|
||||
structs: &Vec<RustStruct>,
|
||||
) -> String {
|
||||
// TODO: options
|
||||
let namespace = "Csbindgen";
|
||||
let class_name = "NativeMethods";
|
||||
let dll_name = "csbindgen_tests";
|
||||
let method_prefix = "csbindgen_";
|
||||
|
||||
let mut method_list_string = String::new();
|
||||
for item in methods {
|
||||
let method_name = &item.method_name;
|
||||
let return_type = match &item.return_type {
|
||||
Some(x) => x.to_csharp_string(),
|
||||
None => "void".to_string(),
|
||||
};
|
||||
|
||||
let parameters = item
|
||||
.parameters
|
||||
.iter()
|
||||
.map(|p| format!("{} {}", p.rust_type.to_csharp_string(), p.name))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
|
||||
method_list_string.push_str_ln(
|
||||
" [DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]",
|
||||
);
|
||||
method_list_string.push_str_ln(
|
||||
format!(" public static extern {return_type} {method_prefix}{method_name}({parameters});").as_str(),
|
||||
);
|
||||
method_list_string.push_str("\n");
|
||||
}
|
||||
|
||||
let mut alias_string = String::new();
|
||||
let mut aliases: Vec<_> = aliases.iter().collect();
|
||||
aliases.sort_by_key(|x| x.0);
|
||||
for (name, rust_type) in aliases {
|
||||
alias_string.push_str_ln(
|
||||
format!(" using {} = {};", name, rust_type.to_csharp_string()).as_str(),
|
||||
);
|
||||
}
|
||||
|
||||
let mut structs_string = String::new();
|
||||
for item in structs {
|
||||
let name = &item.struct_name;
|
||||
let layout_kind = if item.is_union {
|
||||
"Explicit"
|
||||
} else {
|
||||
"Sequential"
|
||||
};
|
||||
|
||||
structs_string
|
||||
.push_str_ln(format!(" [StructLayout(LayoutKind.{layout_kind})]").as_str());
|
||||
structs_string.push_str_ln(format!(" public unsafe struct {name}").as_str());
|
||||
structs_string.push_str_ln(" {");
|
||||
for field in &item.fields {
|
||||
if item.is_union {
|
||||
structs_string.push_str_ln(" [FieldOffset(0)]");
|
||||
}
|
||||
structs_string.push_str(
|
||||
format!(
|
||||
" public {} {}",
|
||||
field.rust_type.to_csharp_string(),
|
||||
field.name
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
if field.rust_type.is_fixed_array {
|
||||
let mut digits = field.rust_type.fixed_array_digits.clone();
|
||||
if digits == "0" {
|
||||
digits = "1".to_string(); // 0 fixed array is not allowed in C#
|
||||
};
|
||||
|
||||
structs_string.push_str(format!("[{}]", digits).as_str());
|
||||
}
|
||||
structs_string.push_str_ln(";");
|
||||
}
|
||||
structs_string.push_str_ln(" }");
|
||||
structs_string.push_str("\n");
|
||||
}
|
||||
|
||||
let result = format!(
|
||||
"// <auto-generated>
|
||||
// This code is generated via csbindgen.
|
||||
// DON'T CHANGE THIS DIRECTLY.
|
||||
// </auto-generated>
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace {namespace}
|
||||
{{
|
||||
{alias_string}
|
||||
|
||||
public static unsafe partial class {class_name}
|
||||
{{
|
||||
const string __DllName = \"{dll_name}\";
|
||||
|
||||
{method_list_string}
|
||||
}}
|
||||
|
||||
{structs_string}
|
||||
}}
|
||||
"
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
trait PushStrLn {
|
||||
fn push_str_ln(&mut self, string: &str);
|
||||
}
|
||||
|
||||
impl PushStrLn for String {
|
||||
fn push_str_ln(&mut self, string: &str) {
|
||||
self.push_str(string);
|
||||
self.push('\n');
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Parameter {
|
||||
pub name: String,
|
||||
pub rust_type: RustType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FieldMember {
|
||||
pub name: String,
|
||||
pub rust_type: RustType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExternMethod {
|
||||
pub method_name: String,
|
||||
pub parameters: Vec<Parameter>,
|
||||
pub return_type: Option<RustType>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct RustType {
|
||||
pub type_name: String,
|
||||
pub is_pointer: bool,
|
||||
pub is_pointer_pointer: bool,
|
||||
pub is_const: bool,
|
||||
pub is_mut: bool,
|
||||
pub is_fixed_array: bool,
|
||||
pub fixed_array_digits: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RustStruct {
|
||||
pub struct_name: String,
|
||||
pub fields: Vec<FieldMember>,
|
||||
pub is_union: bool,
|
||||
}
|
||||
|
||||
impl RustType {
|
||||
pub fn to_string(&self, mod_prefix: &str) -> String {
|
||||
let mut sb = String::new();
|
||||
|
||||
if self.is_pointer || self.is_pointer_pointer {
|
||||
sb.push_str("*");
|
||||
}
|
||||
if self.is_const {
|
||||
sb.push_str("const");
|
||||
}
|
||||
if self.is_mut {
|
||||
sb.push_str("mut");
|
||||
}
|
||||
if self.is_pointer_pointer {
|
||||
if self.is_const {
|
||||
sb.push_str(" *const");
|
||||
} else {
|
||||
sb.push_str(" *mut");
|
||||
}
|
||||
}
|
||||
|
||||
sb.push_str(" ");
|
||||
|
||||
if self.is_fixed_array {
|
||||
sb.push_str("[");
|
||||
sb.push_str(self.type_name.as_str());
|
||||
sb.push_str("; ");
|
||||
sb.push_str(self.fixed_array_digits.as_str());
|
||||
sb.push_str("]");
|
||||
} else {
|
||||
if !self.type_name.starts_with("c_")
|
||||
&& !(self.type_name == "usize" || self.type_name == "isize")
|
||||
{
|
||||
sb.push_str(mod_prefix);
|
||||
sb.push_str("::");
|
||||
}
|
||||
sb.push_str(self.type_name.as_str());
|
||||
}
|
||||
|
||||
sb
|
||||
}
|
||||
|
||||
pub fn to_csharp_string(&self) -> String {
|
||||
fn convert_type_name(type_name: &str) -> &str {
|
||||
let name = match type_name {
|
||||
// std::os::raw https://doc.rust-lang.org/std/os/raw/index.html
|
||||
"c_char" => "Byte",
|
||||
"c_schar" => "SByte",
|
||||
"c_uchar" => "Byte",
|
||||
"c_short" => "Int16",
|
||||
"c_ushort" => "UInt16",
|
||||
"c_int" => "Int32",
|
||||
"c_uint" => "UInt32",
|
||||
"c_long" => "Int32", // int? long? nint? TODO:configure
|
||||
"c_ulong" => "UInt32", // uint? ulong? nuint? TODO:configure
|
||||
"c_longlong" => "Int64",
|
||||
"c_ulonglong" => "UInt64",
|
||||
"c_float" => "Float",
|
||||
"c_double" => "Double",
|
||||
"c_void" => "void",
|
||||
// rust primitives
|
||||
"i8" => "SByte",
|
||||
"i16" => "Int16",
|
||||
"i32" => "Int32",
|
||||
"i64" => "Int64",
|
||||
"i128" => "Int128", // .NET 7
|
||||
"isize" => "IntPtr",
|
||||
"u8" => "Byte",
|
||||
"u16" => "UInt16",
|
||||
"u32" => "UInt32",
|
||||
"u64" => "UInt64",
|
||||
"u128" => "UInt128", // .NET 7
|
||||
"f32" => "Float",
|
||||
"f64" => "Double",
|
||||
"bool" => "Boolean",
|
||||
"usize" => "UIntPtr",
|
||||
"()" => "void", // if type is parameter, can't use in C#
|
||||
_ => type_name, // as is
|
||||
};
|
||||
name
|
||||
}
|
||||
|
||||
let mut sb = String::new();
|
||||
|
||||
if self.is_fixed_array {
|
||||
sb.push_str("fixed ");
|
||||
sb.push_str(convert_type_name(self.type_name.as_str()));
|
||||
} else {
|
||||
sb.push_str(convert_type_name(self.type_name.as_str()));
|
||||
if self.is_pointer {
|
||||
sb.push_str("*");
|
||||
}
|
||||
if self.is_pointer_pointer {
|
||||
sb.push_str("**");
|
||||
}
|
||||
}
|
||||
|
||||
sb
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RustType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.to_string(""))
|
||||
}
|
||||
return Ok((rust, csharp));
|
||||
}
|
||||
|
203
csbindgen/src/parser.rs
Normal file
203
csbindgen/src/parser.rs
Normal file
@ -0,0 +1,203 @@
|
||||
use std::collections::{HashSet, HashMap};
|
||||
use crate::type_meta::*;
|
||||
use syn::{ForeignItem, Item, Pat, ReturnType};
|
||||
|
||||
pub fn collect_method(ast: &syn::File) -> Vec<ExternMethod> {
|
||||
let mut list: Vec<ExternMethod> = Vec::new();
|
||||
|
||||
for item in ast.items.iter() {
|
||||
if let Item::ForeignMod(m) = item {
|
||||
for item in m.items.iter() {
|
||||
if let ForeignItem::Fn(m) = item {
|
||||
let method_name = m.sig.ident.to_string();
|
||||
|
||||
let mut parameters: Vec<Parameter> = Vec::new();
|
||||
let mut retrun_type: Option<RustType> = None;
|
||||
|
||||
// argument
|
||||
for arg in m.sig.inputs.iter() {
|
||||
if let syn::FnArg::Typed(t) = arg {
|
||||
let mut parameter_name: String = "".to_string();
|
||||
|
||||
if let Pat::Ident(ident) = &*t.pat {
|
||||
parameter_name = ident.ident.to_string();
|
||||
}
|
||||
|
||||
let rust_type = parse_type(&t.ty);
|
||||
|
||||
parameters.push(Parameter {
|
||||
name: parameter_name,
|
||||
rust_type: rust_type,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// return
|
||||
if let ReturnType::Type(_, b) = &m.sig.output {
|
||||
let rust_type = parse_type(&b);
|
||||
if rust_type.type_name == "" {
|
||||
continue;
|
||||
}
|
||||
|
||||
retrun_type = Some(rust_type);
|
||||
}
|
||||
|
||||
let t = ExternMethod {
|
||||
method_name: method_name.clone(),
|
||||
parameters: parameters,
|
||||
return_type: retrun_type,
|
||||
};
|
||||
|
||||
list.push(t.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
pub fn collect_type_alias(ast: &syn::File) -> Vec<(String, RustType)> {
|
||||
let mut result = Vec::new();
|
||||
for item in ast.items.iter() {
|
||||
if let Item::Type(t) = item {
|
||||
let name = t.ident.to_string();
|
||||
let alias = parse_type(&t.ty);
|
||||
|
||||
// pointer can not use alias.
|
||||
if !alias.is_pointer || !alias.is_pointer_pointer {
|
||||
result.push((name, alias));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn collect_struct(ast: &syn::File) -> Vec<RustStruct> {
|
||||
// collect union or struct
|
||||
let mut result = Vec::new();
|
||||
|
||||
for item in ast.items.iter() {
|
||||
if let Item::Union(t) = item {
|
||||
let name = t.ident.to_string();
|
||||
let fields = collect_fields(&t.fields);
|
||||
|
||||
result.push(RustStruct {
|
||||
struct_name: name,
|
||||
fields: fields,
|
||||
is_union: true,
|
||||
});
|
||||
} else if let Item::Struct(t) = item {
|
||||
if let syn::Fields::Named(f) = &t.fields {
|
||||
let name = t.ident.to_string();
|
||||
let fields = collect_fields(&f);
|
||||
result.push(RustStruct {
|
||||
struct_name: name,
|
||||
fields: fields,
|
||||
is_union: false,
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn collect_fields(fields: &syn::FieldsNamed) -> Vec<FieldMember> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
for field in &fields.named {
|
||||
if let Some(x) = &field.ident {
|
||||
let t = parse_type(&field.ty);
|
||||
result.push(FieldMember {
|
||||
name: x.to_string(),
|
||||
rust_type: t,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn reduce_type_alias(
|
||||
aliases: &Vec<(String, RustType)>,
|
||||
using_types: &HashSet<String>,
|
||||
) -> HashMap<String, RustType> {
|
||||
let mut map = HashMap::new();
|
||||
for (name, rust_type) in aliases {
|
||||
if using_types.contains(name) {
|
||||
map.insert(name.clone(), rust_type.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for (name, rust_type) in aliases {
|
||||
let pointed = map.get(rust_type.type_name.as_str());
|
||||
if let Some(x) = pointed {
|
||||
map.insert(name.to_string(), x.clone());
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
pub fn reduce_struct(structs: &Vec<RustStruct>, using_types: &HashSet<String>) -> Vec<RustStruct> {
|
||||
let mut result = Vec::new();
|
||||
for item in structs {
|
||||
if using_types.contains(&item.struct_name) {
|
||||
result.push(item.clone());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
fn parse_type(t: &syn::Type) -> RustType {
|
||||
let mut has_star = false;
|
||||
let mut has_star_star = false;
|
||||
let mut has_const = false;
|
||||
let mut has_mut = false;
|
||||
let mut digits: String = "".to_string();
|
||||
|
||||
let name = match t {
|
||||
syn::Type::Ptr(t) => {
|
||||
has_star = true;
|
||||
has_const = t.const_token.is_some();
|
||||
has_mut = t.mutability.is_some();
|
||||
|
||||
if let syn::Type::Path(path) = &*t.elem {
|
||||
path.path.segments.last().unwrap().ident.to_string()
|
||||
} else if let syn::Type::Ptr(t) = &*t.elem {
|
||||
has_star = false;
|
||||
has_star_star = true;
|
||||
if let syn::Type::Path(path) = &*t.elem {
|
||||
path.path.segments.last().unwrap().ident.to_string()
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
} else {
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
syn::Type::Path(t) => t.path.segments.last().unwrap().ident.to_string(),
|
||||
syn::Type::Array(t) => {
|
||||
if let syn::Expr::Lit(x) = &t.len {
|
||||
if let syn::Lit::Int(x) = &x.lit {
|
||||
digits = x.base10_digits().to_string();
|
||||
}
|
||||
};
|
||||
|
||||
parse_type(&t.elem).type_name // maybe ok, only retrieve type_name
|
||||
}
|
||||
_ => "".to_string(),
|
||||
};
|
||||
|
||||
return RustType {
|
||||
is_const: has_const,
|
||||
is_mut: has_mut,
|
||||
is_pointer: has_star,
|
||||
is_pointer_pointer: has_star_star,
|
||||
is_fixed_array: (digits != ""),
|
||||
type_name: name,
|
||||
fixed_array_digits: digits,
|
||||
};
|
||||
}
|
146
csbindgen/src/type_meta.rs
Normal file
146
csbindgen/src/type_meta.rs
Normal file
@ -0,0 +1,146 @@
|
||||
use crate::builder::BindgenOptions;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Parameter {
|
||||
pub name: String,
|
||||
pub rust_type: RustType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FieldMember {
|
||||
pub name: String,
|
||||
pub rust_type: RustType,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ExternMethod {
|
||||
pub method_name: String,
|
||||
pub parameters: Vec<Parameter>,
|
||||
pub return_type: Option<RustType>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct RustType {
|
||||
pub type_name: String,
|
||||
pub is_pointer: bool,
|
||||
pub is_pointer_pointer: bool,
|
||||
pub is_const: bool,
|
||||
pub is_mut: bool,
|
||||
pub is_fixed_array: bool,
|
||||
pub fixed_array_digits: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RustStruct {
|
||||
pub struct_name: String,
|
||||
pub fields: Vec<FieldMember>,
|
||||
pub is_union: bool,
|
||||
}
|
||||
|
||||
impl RustType {
|
||||
pub fn to_string(&self, type_path: &str) -> String {
|
||||
let mut sb = String::new();
|
||||
|
||||
if self.is_pointer || self.is_pointer_pointer {
|
||||
sb.push_str("*");
|
||||
}
|
||||
if self.is_const {
|
||||
sb.push_str("const");
|
||||
}
|
||||
if self.is_mut {
|
||||
sb.push_str("mut");
|
||||
}
|
||||
if self.is_pointer_pointer {
|
||||
if self.is_const {
|
||||
sb.push_str(" *const");
|
||||
} else {
|
||||
sb.push_str(" *mut");
|
||||
}
|
||||
}
|
||||
|
||||
sb.push_str(" ");
|
||||
|
||||
if self.is_fixed_array {
|
||||
sb.push_str("[");
|
||||
sb.push_str(self.type_name.as_str());
|
||||
sb.push_str("; ");
|
||||
sb.push_str(self.fixed_array_digits.as_str());
|
||||
sb.push_str("]");
|
||||
} else {
|
||||
if !self.type_name.starts_with("c_")
|
||||
&& !(self.type_name == "usize" || self.type_name == "isize")
|
||||
&& !(type_path == "")
|
||||
{
|
||||
sb.push_str(type_path);
|
||||
sb.push_str("::");
|
||||
}
|
||||
sb.push_str(self.type_name.as_str());
|
||||
}
|
||||
|
||||
sb
|
||||
}
|
||||
|
||||
pub fn to_csharp_string(&self, options: &BindgenOptions) -> String {
|
||||
fn convert_type_name(type_name: &str, options: &BindgenOptions) -> String {
|
||||
let name = match type_name {
|
||||
// std::os::raw https://doc.rust-lang.org/std/os/raw/index.html
|
||||
"c_char" => "Byte",
|
||||
"c_schar" => "SByte",
|
||||
"c_uchar" => "Byte",
|
||||
"c_short" => "Int16",
|
||||
"c_ushort" => "UInt16",
|
||||
"c_int" => "Int32",
|
||||
"c_uint" => "UInt32",
|
||||
"c_long" => &options.csharp_c_long_convert,
|
||||
"c_ulong" => &options.csharp_c_ulong_convert,
|
||||
"c_longlong" => "Int64",
|
||||
"c_ulonglong" => "UInt64",
|
||||
"c_float" => "Float",
|
||||
"c_double" => "Double",
|
||||
"c_void" => "void",
|
||||
// rust primitives
|
||||
"i8" => "SByte",
|
||||
"i16" => "Int16",
|
||||
"i32" => "Int32",
|
||||
"i64" => "Int64",
|
||||
"i128" => "Int128", // .NET 7
|
||||
"isize" => "IntPtr",
|
||||
"u8" => "Byte",
|
||||
"u16" => "UInt16",
|
||||
"u32" => "UInt32",
|
||||
"u64" => "UInt64",
|
||||
"u128" => "UInt128", // .NET 7
|
||||
"f32" => "Float",
|
||||
"f64" => "Double",
|
||||
"bool" => "Boolean",
|
||||
"usize" => "UIntPtr",
|
||||
"()" => "void", // if type is parameter, can't use in C#
|
||||
_ => type_name, // as is
|
||||
};
|
||||
name.to_string()
|
||||
}
|
||||
|
||||
let mut sb = String::new();
|
||||
|
||||
if self.is_fixed_array {
|
||||
sb.push_str("fixed ");
|
||||
sb.push_str(convert_type_name(self.type_name.as_str(), &options).as_str());
|
||||
} else {
|
||||
sb.push_str(convert_type_name(self.type_name.as_str(), &options).as_str());
|
||||
if self.is_pointer {
|
||||
sb.push_str("*");
|
||||
}
|
||||
if self.is_pointer_pointer {
|
||||
sb.push_str("**");
|
||||
}
|
||||
}
|
||||
|
||||
sb
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RustType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.to_string(""))
|
||||
}
|
||||
}
|
10
csbindgen/src/util.rs
Normal file
10
csbindgen/src/util.rs
Normal file
@ -0,0 +1,10 @@
|
||||
pub trait PushStrLn {
|
||||
fn push_str_ln(&mut self, string: &str);
|
||||
}
|
||||
|
||||
impl PushStrLn for String {
|
||||
fn push_str_ln(&mut self, string: &str) {
|
||||
self.push_str(string);
|
||||
self.push('\n');
|
||||
}
|
||||
}
|
@ -7,28 +7,10 @@ using System.Runtime.InteropServices;
|
||||
|
||||
namespace Csbindgen
|
||||
{
|
||||
using LZ4F_blockChecksum_t = Int32;
|
||||
using LZ4F_blockMode_t = Int32;
|
||||
using LZ4F_blockSizeID_t = Int32;
|
||||
using LZ4F_cctx = LZ4F_cctx_s;
|
||||
using LZ4F_compressionContext_t = LZ4F_cctx_s;
|
||||
using LZ4F_contentChecksum_t = Int32;
|
||||
using LZ4F_dctx = LZ4F_dctx_s;
|
||||
using LZ4F_decompressionContext_t = LZ4F_dctx_s;
|
||||
using LZ4F_errorCode_t = UIntPtr;
|
||||
using LZ4F_frameType_t = Int32;
|
||||
using LZ4_byte = Byte;
|
||||
using LZ4_i8 = SByte;
|
||||
using LZ4_streamDecode_t = LZ4_streamDecode_u;
|
||||
using LZ4_streamHC_t = LZ4_streamHC_u;
|
||||
using LZ4_stream_t = LZ4_stream_u;
|
||||
using LZ4_u16 = UInt16;
|
||||
using LZ4_u32 = UInt32;
|
||||
using XXH32_hash_t = UInt32;
|
||||
using XXH32_state_t = XXH32_state_s;
|
||||
using XXH64_hash_t = UInt64;
|
||||
using XXH64_state_t = XXH64_state_s;
|
||||
using XXH_errorcode = Int32;
|
||||
|
||||
|
||||
public static unsafe partial class NativeMethods
|
||||
@ -170,210 +152,6 @@ namespace Csbindgen
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void csbindgen_LZ4_resetStream(LZ4_stream_t* streamPtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compress_HC(Byte* src, Byte* dst, Int32 srcSize, Int32 dstCapacity, Int32 compressionLevel);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_sizeofStateHC();
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compress_HC_extStateHC(void* stateHC, Byte* src, Byte* dst, Int32 srcSize, Int32 maxDstSize, Int32 compressionLevel);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compress_HC_destSize(void* stateHC, Byte* src, Byte* dst, Int32* srcSizePtr, Int32 targetDstSize, Int32 compressionLevel);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern LZ4_streamHC_t* csbindgen_LZ4_createStreamHC();
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_freeStreamHC(LZ4_streamHC_t* streamHCPtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void csbindgen_LZ4_resetStreamHC_fast(LZ4_streamHC_t* streamHCPtr, Int32 compressionLevel);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_loadDictHC(LZ4_streamHC_t* streamHCPtr, Byte* dictionary, Int32 dictSize);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compress_HC_continue(LZ4_streamHC_t* streamHCPtr, Byte* src, Byte* dst, Int32 srcSize, Int32 maxDstSize);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compress_HC_continue_destSize(LZ4_streamHC_t* LZ4_streamHCPtr, Byte* src, Byte* dst, Int32* srcSizePtr, Int32 targetDstSize);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_saveDictHC(LZ4_streamHC_t* streamHCPtr, Byte* safeBuffer, Int32 maxDictSize);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern LZ4_streamHC_t* csbindgen_LZ4_initStreamHC(void* buffer, UIntPtr size);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compressHC(Byte* source, Byte* dest, Int32 inputSize);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compressHC_limitedOutput(Byte* source, Byte* dest, Int32 inputSize, Int32 maxOutputSize);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compressHC2(Byte* source, Byte* dest, Int32 inputSize, Int32 compressionLevel);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compressHC2_limitedOutput(Byte* source, Byte* dest, Int32 inputSize, Int32 maxOutputSize, Int32 compressionLevel);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compressHC_withStateHC(void* state, Byte* source, Byte* dest, Int32 inputSize);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compressHC_limitedOutput_withStateHC(void* state, Byte* source, Byte* dest, Int32 inputSize, Int32 maxOutputSize);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compressHC2_withStateHC(void* state, Byte* source, Byte* dest, Int32 inputSize, Int32 compressionLevel);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compressHC2_limitedOutput_withStateHC(void* state, Byte* source, Byte* dest, Int32 inputSize, Int32 maxOutputSize, Int32 compressionLevel);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compressHC_continue(LZ4_streamHC_t* LZ4_streamHCPtr, Byte* source, Byte* dest, Int32 inputSize);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compressHC_limitedOutput_continue(LZ4_streamHC_t* LZ4_streamHCPtr, Byte* source, Byte* dest, Int32 inputSize, Int32 maxOutputSize);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void* csbindgen_LZ4_createHC(Byte* inputBuffer);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_freeHC(void* LZ4HC_Data);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Byte* csbindgen_LZ4_slideInputBufferHC(void* LZ4HC_Data);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compressHC2_continue(void* LZ4HC_Data, Byte* source, Byte* dest, Int32 inputSize, Int32 compressionLevel);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_compressHC2_limitedOutput_continue(void* LZ4HC_Data, Byte* source, Byte* dest, Int32 inputSize, Int32 maxOutputSize, Int32 compressionLevel);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_sizeofStreamStateHC();
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4_resetStreamStateHC(void* state, Byte* inputBuffer);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void csbindgen_LZ4_resetStreamHC(LZ4_streamHC_t* streamHCPtr, Int32 compressionLevel);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UInt32 csbindgen_LZ4F_isError(LZ4F_errorCode_t code);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Byte* csbindgen_LZ4F_getErrorName(LZ4F_errorCode_t code);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern Int32 csbindgen_LZ4F_compressionLevel_max();
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UIntPtr csbindgen_LZ4F_compressFrameBound(UIntPtr srcSize, LZ4F_preferences_t* preferencesPtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UIntPtr csbindgen_LZ4F_compressFrame(void* dstBuffer, UIntPtr dstCapacity, void* srcBuffer, UIntPtr srcSize, LZ4F_preferences_t* preferencesPtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UInt32 csbindgen_LZ4F_getVersion();
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern LZ4F_errorCode_t csbindgen_LZ4F_createCompressionContext(LZ4F_cctx** cctxPtr, UInt32 version);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern LZ4F_errorCode_t csbindgen_LZ4F_freeCompressionContext(LZ4F_cctx* cctx);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UIntPtr csbindgen_LZ4F_compressBegin(LZ4F_cctx* cctx, void* dstBuffer, UIntPtr dstCapacity, LZ4F_preferences_t* prefsPtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UIntPtr csbindgen_LZ4F_compressBound(UIntPtr srcSize, LZ4F_preferences_t* prefsPtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UIntPtr csbindgen_LZ4F_compressUpdate(LZ4F_cctx* cctx, void* dstBuffer, UIntPtr dstCapacity, void* srcBuffer, UIntPtr srcSize, LZ4F_compressOptions_t* cOptPtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UIntPtr csbindgen_LZ4F_flush(LZ4F_cctx* cctx, void* dstBuffer, UIntPtr dstCapacity, LZ4F_compressOptions_t* cOptPtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UIntPtr csbindgen_LZ4F_compressEnd(LZ4F_cctx* cctx, void* dstBuffer, UIntPtr dstCapacity, LZ4F_compressOptions_t* cOptPtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern LZ4F_errorCode_t csbindgen_LZ4F_createDecompressionContext(LZ4F_dctx** dctxPtr, UInt32 version);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern LZ4F_errorCode_t csbindgen_LZ4F_freeDecompressionContext(LZ4F_dctx* dctx);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UIntPtr csbindgen_LZ4F_headerSize(void* src, UIntPtr srcSize);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UIntPtr csbindgen_LZ4F_getFrameInfo(LZ4F_dctx* dctx, LZ4F_frameInfo_t* frameInfoPtr, void* srcBuffer, UIntPtr* srcSizePtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UIntPtr csbindgen_LZ4F_decompress(LZ4F_dctx* dctx, void* dstBuffer, UIntPtr* dstSizePtr, void* srcBuffer, UIntPtr* srcSizePtr, LZ4F_decompressOptions_t* dOptPtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void csbindgen_LZ4F_resetDecompressionContext(LZ4F_dctx* dctx);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern UInt32 csbindgen_XXH_versionNumber();
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH32_hash_t csbindgen_XXH32(void* input, UIntPtr length, UInt32 seed);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH32_state_t* csbindgen_XXH32_createState();
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH_errorcode csbindgen_XXH32_freeState(XXH32_state_t* statePtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void csbindgen_XXH32_copyState(XXH32_state_t* dst_state, XXH32_state_t* src_state);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH_errorcode csbindgen_XXH32_reset(XXH32_state_t* statePtr, UInt32 seed);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH_errorcode csbindgen_XXH32_update(XXH32_state_t* statePtr, void* input, UIntPtr length);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH32_hash_t csbindgen_XXH32_digest(XXH32_state_t* statePtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void csbindgen_XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH32_hash_t csbindgen_XXH32_hashFromCanonical(XXH32_canonical_t* src);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH64_hash_t csbindgen_XXH64(void* input, UIntPtr length, UInt64 seed);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH64_state_t* csbindgen_XXH64_createState();
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH_errorcode csbindgen_XXH64_freeState(XXH64_state_t* statePtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void csbindgen_XXH64_copyState(XXH64_state_t* dst_state, XXH64_state_t* src_state);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH_errorcode csbindgen_XXH64_reset(XXH64_state_t* statePtr, UInt64 seed);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH_errorcode csbindgen_XXH64_update(XXH64_state_t* statePtr, void* input, UIntPtr length);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH64_hash_t csbindgen_XXH64_digest(XXH64_state_t* statePtr);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern void csbindgen_XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash);
|
||||
|
||||
[DllImport(__DllName, CallingConvention = CallingConvention.Cdecl)]
|
||||
public static extern XXH64_hash_t csbindgen_XXH64_hashFromCanonical(XXH64_canonical_t* src);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -415,106 +193,6 @@ namespace Csbindgen
|
||||
public LZ4_streamDecode_t_internal internal_donotuse;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct LZ4HC_CCtx_internal
|
||||
{
|
||||
public fixed LZ4_u32 hashTable[32768];
|
||||
public fixed LZ4_u16 chainTable[65536];
|
||||
public LZ4_byte* end;
|
||||
public LZ4_byte* prefixStart;
|
||||
public LZ4_byte* dictStart;
|
||||
public LZ4_u32 dictLimit;
|
||||
public LZ4_u32 lowLimit;
|
||||
public LZ4_u32 nextToUpdate;
|
||||
public Int16 compressionLevel;
|
||||
public LZ4_i8 favorDecSpeed;
|
||||
public LZ4_i8 dirty;
|
||||
public LZ4HC_CCtx_internal* dictCtx;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public unsafe struct LZ4_streamHC_u
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public fixed Byte minStateSize[262200];
|
||||
[FieldOffset(0)]
|
||||
public LZ4HC_CCtx_internal internal_donotuse;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct LZ4F_frameInfo_t
|
||||
{
|
||||
public LZ4F_blockSizeID_t blockSizeID;
|
||||
public LZ4F_blockMode_t blockMode;
|
||||
public LZ4F_contentChecksum_t contentChecksumFlag;
|
||||
public LZ4F_frameType_t frameType;
|
||||
public UInt64 contentSize;
|
||||
public UInt32 dictID;
|
||||
public LZ4F_blockChecksum_t blockChecksumFlag;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct LZ4F_preferences_t
|
||||
{
|
||||
public LZ4F_frameInfo_t frameInfo;
|
||||
public Int32 compressionLevel;
|
||||
public UInt32 autoFlush;
|
||||
public UInt32 favorDecSpeed;
|
||||
public fixed UInt32 reserved[3];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct LZ4F_cctx_s
|
||||
{
|
||||
public fixed Byte _unused[1];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct LZ4F_compressOptions_t
|
||||
{
|
||||
public UInt32 stableSrc;
|
||||
public fixed UInt32 reserved[3];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct LZ4F_dctx_s
|
||||
{
|
||||
public fixed Byte _unused[1];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct LZ4F_decompressOptions_t
|
||||
{
|
||||
public UInt32 stableDst;
|
||||
public UInt32 skipChecksums;
|
||||
public UInt32 reserved1;
|
||||
public UInt32 reserved0;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct XXH32_state_s
|
||||
{
|
||||
public fixed Byte _unused[1];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct XXH32_canonical_t
|
||||
{
|
||||
public fixed Byte digest[4];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct XXH64_state_s
|
||||
{
|
||||
public fixed Byte _unused[1];
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public unsafe struct XXH64_canonical_t
|
||||
{
|
||||
public fixed Byte digest[8];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user