Compare commits

..

1 Commits

Author SHA1 Message Date
8effb1ff7d 0.2.4: fix length defines 2025-08-17 15:52:13 +01:00
9 changed files with 159 additions and 221 deletions

3
.gitignore vendored
View File

@@ -1,5 +1,2 @@
# Cargo # Cargo
target/ target/
# Slop
.vscode/

36
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,36 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'bin2hpp'",
"cargo": {
"args": ["build", "--bin=bin2hpp", "--package=bin2hpp"],
"filter": {
"name": "bin2hpp",
"kind": "bin"
}
},
"args": ["-i", "/home/adam/my_library.dll"],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'bin2hpp'",
"cargo": {
"args": ["test", "--no-run", "--bin=bin2hpp", "--package=bin2hpp"],
"filter": {
"name": "bin2hpp",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
]
}

2
Cargo.lock generated
View File

@@ -4,7 +4,7 @@ version = 4
[[package]] [[package]]
name = "bin2hpp" name = "bin2hpp"
version = "0.3.0" version = "0.2.4"
dependencies = [ dependencies = [
"thiserror", "thiserror",
] ]

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "bin2hpp" name = "bin2hpp"
version = "0.3.0" version = "0.2.4"
authors = ["Adam Macdonald"] authors = ["Adam Macdonald"]
edition = "2024" edition = "2024"
license = "GPL-3.0-only" license = "GPL-3.0-only"

View File

@@ -6,55 +6,25 @@ CLI tool for converting files into header files which one can use to directly em
## Building ## Building
`cargo build`, or `cargo [build | run | install] --profile optimised` to build an optimised binary. 1. `cargo build`
The performance (but potentially not the executable size) can be further optimised by telling the compiler to use a more modern instruction set: `RUSTFLAGS="-C target-cpu=native"`.
## Usage ## Usage
### Basic usage ### Basic usage
`$ bin2hpp -i my_library.dll -o c_header.h` will generate a `c_header.h` file in your current working directory containing something similar to the following: `$ bin2hpp -i ~/my_library.dll --overwrite` will generate an `my_library_dll.h` file in your current working directory containing something similar to the following:
```c ```c
// Generated by bin2hpp 0.3.0 // Generated by bin2hpp 0.2.4
#ifdef MY_LIBRARY_DLL_INIT #ifndef MY_LIBRARY_DLL_H
#define MY_LIBRARY_DLL_H
#define MY_LIBRARY_DLL_LEN 9728 #define MY_LIBRARY_DLL_LEN 9728
const unsigned char MY_LIBRARY_DLL[MY_LIBRARY_DLL_LEN] = {77,90,144,0,3,0,0,0,4, ...}; const unsigned char MY_LIBRARY_DLL[MY_LIBRARY_DLL_LEN] = {0x4d,0x5a,0x90,0x0,0x3,0x0,0x0, ...};
#else #else
#define MY_LIBRARY_DLL_LEN 9728
extern const unsigned char MY_LIBRARY_DLL[MY_LIBRARY_DLL_LEN]; extern const unsigned char MY_LIBRARY_DLL[MY_LIBRARY_DLL_LEN];
#endif #endif
``` ```
Note how in **C mode** one must `#define MY_LIBRARY_DLL_INIT`, or whatever the corresponding implementation macro is for your generated header file, in exactly _one_ C source file (`*.c`, not in a `*.h` file).
Example:
`main.c`
```c
// In any other C source files other than the first: do not redefine
#define MY_LIBRARY_DLL_INIT
#include "c_header.h"
int main() {
return MY_LIBRARY_DLL_LEN;
}
```
Alternatively, in **C++ mode**: `bin2hpp -i my_library.dll -o cpp_header.hpp --cpp --constexpr --stdarray --i8`
```cpp
// Generated by bin2hpp 0.3.0
#if !defined(CPP_HEADER_HPP)
#define CPP_HEADER_HPP
#include <cstdint>
#include <array>
#define MY_LIBRARY_DLL_LEN 9728
inline constexpr std::array<std::int8_t,MY_LIBRARY_DLL_LEN> MY_LIBRARY_DLL = {77,90,-112,0,3,0,0,0,4, ...};
#endif
```
### Note about CLI arguments ### Note about CLI arguments
Command line arguments are not positional. The input file path argument is the Command line arguments are not positional. The input file path argument is the

View File

@@ -22,8 +22,7 @@ pub struct Config {
pub(crate) constness: Constness, pub(crate) constness: Constness,
pub(crate) symbol_name: String, pub(crate) symbol_name: String,
pub(crate) namespace: Option<String>, pub(crate) namespace: Option<String>,
pub(crate) impl_macro: String, pub(crate) guard_name: String,
pub(crate) header_guard: String,
pub(crate) container_type: ContainerType, pub(crate) container_type: ContainerType,
} }
@@ -57,54 +56,6 @@ pub fn parse_config_from_args(args: &CliArgs) -> Result<Config, ProgramConfigErr
} }
}; };
let symbol_name = match &args.symbol_name {
Some(s) => {
if s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
s.to_string()
} else {
return Err(ProgramConfigError::InvalidSymbolName(s.to_string()));
}
}
_ => {
let mut stem: String = match args.input_file_path.file_stem() {
Some(s) => s.to_string_lossy().to_string(),
None => {
return Err(ProgramConfigError::InvalidPath(
args.input_file_path.to_string_lossy().to_string(),
));
}
};
stem.retain(|c| c.is_ascii_alphanumeric() || c == '_');
let mut ext: String = match args.input_file_path.extension() {
Some(s) => format!("_{}", s.to_string_lossy()),
None => "".to_string(),
};
ext.retain(|c| c.is_ascii_alphanumeric() || c == '_');
format!("{}{}", stem.to_ascii_uppercase(), ext.to_ascii_uppercase())
}
};
let header_guard = match output_path.file_stem() {
Some(stem) => {
format!(
"{}_{}",
stem.to_string_lossy().to_uppercase(),
if let Some(ext) = output_path.extension() {
ext.to_string_lossy().to_uppercase()
} else {
"".into()
}
)
}
None => {
return Err(ProgramConfigError::InvalidPath(
output_path.to_string_lossy().to_string(),
));
}
};
Ok(Config { Ok(Config {
input_file_path: args.input_file_path.clone(), input_file_path: args.input_file_path.clone(),
output_file_path: output_path.clone(), output_file_path: output_path.clone(),
@@ -116,7 +67,34 @@ pub fn parse_config_from_args(args: &CliArgs) -> Result<Config, ProgramConfigErr
}, },
language: args.language, language: args.language,
constness: args.constness, constness: args.constness,
symbol_name: symbol_name.clone(), symbol_name: match &args.symbol_name {
Some(s) => {
if s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
s.to_string()
} else {
return Err(ProgramConfigError::InvalidSymbolName(s.to_string()));
}
}
_ => {
let mut stem: String = match args.input_file_path.file_stem() {
Some(s) => s.to_string_lossy().to_string(),
None => {
return Err(ProgramConfigError::InvalidPath(
args.input_file_path.to_string_lossy().to_string(),
));
}
};
stem.retain(|c| c.is_ascii_alphanumeric() || c == '_');
let mut ext: String = match args.input_file_path.extension() {
Some(s) => format!("_{}", s.to_string_lossy()),
None => "".to_string(),
};
ext.retain(|c| c.is_ascii_alphanumeric() || c == '_');
format!("{}{}", stem.to_ascii_uppercase(), ext.to_ascii_uppercase())
}
},
namespace: match &args.namespace { namespace: match &args.namespace {
Some(ns) => { Some(ns) => {
if ns.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { if ns.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
@@ -127,7 +105,7 @@ pub fn parse_config_from_args(args: &CliArgs) -> Result<Config, ProgramConfigErr
} }
_ => None, _ => None,
}, },
impl_macro: match &args.impl_macro_name { guard_name: match &args.guard_name {
Some(guard) => { Some(guard) => {
if guard.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { if guard.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
guard.clone() guard.clone()
@@ -136,10 +114,29 @@ pub fn parse_config_from_args(args: &CliArgs) -> Result<Config, ProgramConfigErr
} }
} }
_ => { _ => {
format!("{}_INIT", symbol_name.to_uppercase()) let mut stem: String = match output_path.file_stem() {
Some(s) => s.to_string_lossy().to_ascii_uppercase(),
None => {
return Err(ProgramConfigError::InvalidPath(
args.input_file_path.to_string_lossy().to_string(),
));
}
};
stem.retain(|c| c.is_ascii_alphanumeric() || c == '_');
let mut ext: String = match output_path.extension() {
Some(s) => s.to_string_lossy().to_ascii_uppercase(),
None => {
return Err(ProgramConfigError::InvalidPath(
args.input_file_path.to_string_lossy().to_string(),
));
}
};
ext.retain(|c| c.is_ascii_alphanumeric() || c == '_');
format!("{}_{}", stem, ext)
} }
}, },
header_guard: header_guard,
container_type: args.container_type, container_type: args.container_type,
}) })
} }

View File

@@ -20,26 +20,6 @@ pub enum ByteType {
I8, I8,
} }
impl ByteType {
pub fn to_c_type(&self) -> &'static str {
match self {
ByteType::UnsignedChar => "unsigned char",
ByteType::Char => "char",
ByteType::U8 => "uint8_t",
ByteType::I8 => "int8_t",
}
}
pub fn to_cpp_type(&self) -> &'static str {
match self {
ByteType::UnsignedChar => self.to_c_type(),
ByteType::Char => self.to_c_type(),
ByteType::U8 => "std::uint8_t",
ByteType::I8 => "std::int8_t",
}
}
}
#[derive(Debug, PartialEq, Clone, Copy)] #[derive(Debug, PartialEq, Clone, Copy)]
pub enum ContainerType { pub enum ContainerType {
CArray(ByteType), CArray(ByteType),
@@ -81,7 +61,7 @@ pub struct CliArgs {
pub(crate) constness: Constness, pub(crate) constness: Constness,
pub(crate) symbol_name: Option<String>, pub(crate) symbol_name: Option<String>,
pub(crate) namespace: Option<String>, pub(crate) namespace: Option<String>,
pub(crate) impl_macro_name: Option<String>, pub(crate) guard_name: Option<String>,
pub(crate) container_type: ContainerType, pub(crate) container_type: ContainerType,
} }
@@ -123,10 +103,10 @@ pub fn parse_cli_args(args: &[String]) -> Result<CliArgs, CliParseError> {
None => None, None => None,
}; };
let impl_macro_name = match args.iter().position(|s| *s == "--impl") { let guard_name = match args.iter().position(|s| *s == "--guard") {
Some(idx) => match args.get(idx + 1) { Some(idx) => match args.get(idx + 1) {
Some(s) => Some(s), Some(s) => Some(s),
None => return Err(CliParseError::MissingOperand("--impl")), None => return Err(CliParseError::MissingOperand("--guard")),
}, },
None => None, None => None,
}; };
@@ -237,7 +217,7 @@ pub fn parse_cli_args(args: &[String]) -> Result<CliArgs, CliParseError> {
constness: constness, constness: constness,
symbol_name: symbol_name.cloned(), symbol_name: symbol_name.cloned(),
namespace: namespace.cloned(), namespace: namespace.cloned(),
impl_macro_name: impl_macro_name.cloned(), guard_name: guard_name.cloned(),
container_type: container_type, container_type: container_type,
}); });
} }

View File

@@ -28,8 +28,8 @@ OUTPUT OPTIONS:
--symname NAME Symbol name --symname NAME Symbol name
--namespace NAME Namespace in which the symbol will exist (C++ mode) --namespace NAME Namespace in which the symbol will exist (C++ mode)
--impl NAME Preprocessor macro to use for the implementation guard --guard NAME Preprocessor macro to use for the header guard (#ifndef ...)
in C mode (default: derive from symbol name) (default: derive from output filename)
CONTAINER TYPES: CONTAINER TYPES:

View File

@@ -29,21 +29,20 @@ pub fn header(config: &Config, in_data: &[u8]) -> Result<String, HeaderGeneratio
// Header guard // Header guard
if config.language == Language::Cpp { if config.language == Language::Cpp {
// C++ style
write!( write!(
buf, buf,
"#if !defined({}){}", "#if !defined({}){}",
config.header_guard, config.line_ending config.guard_name, config.line_ending
) )
.unwrap(); .unwrap();
write!(buf, "#define {}{}", config.header_guard, config.line_ending).unwrap();
} else { } else {
// C style write!(buf, "#ifndef {}{}", config.guard_name, config.line_ending).unwrap();
write!(buf, "#ifdef {}{}", config.impl_macro, config.line_ending).unwrap();
} }
write!(buf, "#define {}{}", config.guard_name, config.line_ending).unwrap();
// Includes & main content // Includes & main content
with_includes(config, &mut buf);
with_contents(config, in_data, &mut buf)?; with_contents(config, in_data, &mut buf)?;
// End header guard // End header guard
@@ -58,21 +57,6 @@ pub fn with_contents(
in_data: &[u8], in_data: &[u8],
buf: &mut String, buf: &mut String,
) -> Result<(), HeaderGenerationError> { ) -> Result<(), HeaderGenerationError> {
with_includes(config, buf);
let length: usize = match config.container_type {
ContainerType::CString => in_data.len() + 1, // +1 for NUL terminator
_ => in_data.len(),
};
write!(
buf,
"{}{}",
length_define(config, length),
config.line_ending
)
.unwrap();
if config.language == Language::Cpp { if config.language == Language::Cpp {
// C++ mode // C++ mode
@@ -87,18 +71,9 @@ pub fn with_contents(
// C mode // C mode
with_definition(config, in_data, buf)?; with_definition(config, in_data, buf)?;
write!(buf, "#else{}", config.line_ending).unwrap(); write!(buf, "#else{}", config.line_ending).unwrap();
with_includes(config, buf); with_includes(config, buf);
write!( with_declaration(config, in_data, buf);
buf,
"{}{}",
length_define(config, length),
config.line_ending
)
.unwrap();
with_declaration(config, buf);
} }
Ok(()) Ok(())
@@ -177,16 +152,29 @@ pub fn with_definition(
in_data: &[u8], in_data: &[u8],
buf: &mut String, buf: &mut String,
) -> Result<(), HeaderGenerationError> { ) -> Result<(), HeaderGenerationError> {
let length: usize = match config.container_type {
ContainerType::CString => in_data.len() + 1, // +1 for NUL terminator
_ => in_data.len(),
};
let modifiers = definition_modifiers(config); let modifiers = definition_modifiers(config);
write!(
buf,
"{}{}",
length_define(config, length),
config.line_ending
)
.unwrap();
write!( write!(
buf, buf,
"{}{} = {};{}", "{}{} = {};{}",
modifiers, modifiers,
data_type_and_symbol(config), data_type_and_symbol(config, in_data),
match config.container_type { match config.container_type {
ContainerType::CArray(_) => with_array_initialiser(config, in_data), ContainerType::CArray(_) => with_array_initialiser(in_data),
ContainerType::StdArray(_) => with_array_initialiser(config, in_data), ContainerType::StdArray(_) => with_array_initialiser(in_data),
_ => with_string_initialiser(config, in_data)?, _ => with_string_initialiser(config, in_data)?,
}, },
config.line_ending config.line_ending
@@ -196,47 +184,25 @@ pub fn with_definition(
Ok(()) Ok(())
} }
pub fn with_declaration(config: &Config, buf: &mut String) { pub fn with_declaration(config: &Config, in_data: &[u8], buf: &mut String) {
let modifiers = declaration_modifiers(config); let modifiers = declaration_modifiers(config);
write!( write!(
buf, buf,
"{}{};{}", "{}{};{}",
modifiers, modifiers,
data_type_and_symbol(config), data_type_and_symbol(config, in_data),
config.line_ending config.line_ending
) )
.unwrap(); .unwrap();
} }
pub fn with_array_initialiser(config: &Config, in_data: &[u8]) -> String { pub fn with_array_initialiser(in_data: &[u8]) -> String {
let mut out_str: String = String::with_capacity(in_data.len() * 6 + 16); let mut out_str: String = String::with_capacity(in_data.len() * 6 + 16);
let signed_literals = match config.container_type {
ContainerType::CArray(byte_type) => match byte_type {
ByteType::Char => true,
ByteType::I8 => true,
_ => false,
},
ContainerType::StdArray(byte_type) => match byte_type {
ByteType::Char => true,
ByteType::I8 => true,
_ => false,
},
_ => false,
};
write!(out_str, "{{").unwrap(); write!(out_str, "{{").unwrap();
for b in in_data {
if signed_literals { write!(out_str, "{:#x},", b).unwrap();
for b in in_data {
let b = i8::from_ne_bytes([*b; 1]);
write!(out_str, "{},", b).unwrap();
}
} else {
for b in in_data {
write!(out_str, "{},", b).unwrap();
}
} }
let _ = out_str.pop(); let _ = out_str.pop();
write!(out_str, "}}").unwrap(); write!(out_str, "}}").unwrap();
@@ -280,49 +246,41 @@ pub fn length_symbol(config: &Config) -> String {
out_str out_str
} }
pub fn data_type_and_symbol(config: &Config) -> String { pub fn data_type_and_symbol(config: &Config, in_data: &[u8]) -> String {
let mut out_str = String::with_capacity(config.symbol_name.len() + 20); let u8_type = match config.language {
Language::C => "uint8_t",
Language::Cpp => "std::uint8_t",
};
let i8_type = match config.language {
Language::C => "int8_t",
Language::Cpp => "std::int8_t",
};
let length_sym = length_symbol(config); let length_sym = length_symbol(config);
match config.container_type { match config.container_type {
ContainerType::CArray(byte_type) => write!( ContainerType::CArray(byte_type) => match byte_type {
out_str, ByteType::UnsignedChar => {
"{} {}[{}]", format!("unsigned char {}[{}]", config.symbol_name, length_sym)
if config.language == Language::Cpp { }
byte_type.to_cpp_type() ByteType::Char => format!("char {}[{}]", config.symbol_name, length_sym),
} else { _ => format!("{} {}[{}]", u8_type, config.symbol_name, length_sym),
byte_type.to_c_type() },
}, ContainerType::StdArray(byte_type) => {
config.symbol_name, format!(
length_sym "std::array<{},{}> {}",
) match byte_type {
.unwrap(), ByteType::UnsignedChar => "unsigned char",
ContainerType::StdArray(byte_type) => write!( ByteType::Char => "char",
out_str, ByteType::U8 => u8_type,
"std::array<{},{}> {}", ByteType::I8 => i8_type,
if config.language == Language::Cpp { },
byte_type.to_cpp_type() in_data.len(),
} else { config.symbol_name
byte_type.to_c_type() )
},
length_sym,
config.symbol_name
)
.unwrap(),
ContainerType::CString => write!(
out_str,
"{} {}[{}]",
ByteType::Char.to_c_type(),
config.symbol_name,
length_sym
)
.unwrap(),
ContainerType::StdString => write!(out_str, "std::string {}", config.symbol_name).unwrap(),
ContainerType::StdStringView => {
write!(out_str, "std::string_view {}", config.symbol_name).unwrap()
} }
}; ContainerType::CString => format!("char {}[{}]", config.symbol_name, length_sym),
ContainerType::StdString => format!("std::string {}", config.symbol_name),
out_str ContainerType::StdStringView => format!("std::string_view {}", config.symbol_name),
}
} }