Compare commits
7 Commits
8effb1ff7d
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 987152f14f | |||
| 5657325d97 | |||
| 8054e10c7b | |||
| 077861f13c | |||
| 0b418a728f | |||
| 3ba2eadf3e | |||
| 184fb9eda3 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,5 @@
|
|||||||
# Cargo
|
# Cargo
|
||||||
target/
|
target/
|
||||||
|
|
||||||
|
# Slop
|
||||||
|
.vscode/
|
||||||
|
|||||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -4,7 +4,7 @@ version = 4
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bin2hpp"
|
name = "bin2hpp"
|
||||||
version = "0.2.3"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bin2hpp"
|
name = "bin2hpp"
|
||||||
version = "0.2.3"
|
version = "0.3.0"
|
||||||
authors = ["Adam Macdonald"]
|
authors = ["Adam Macdonald"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
|
|||||||
48
README.md
48
README.md
@@ -6,24 +6,52 @@ CLI tool for converting files into header files which one can use to directly em
|
|||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
1. `cargo build`
|
`cargo build`, or `cargo [build | run | install] --profile optimised` to build an optimised binary.
|
||||||
|
|
||||||
|
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_image.bmp` will generate an `my_image_bmp.h` file in your current working directory containing something similar to the following:
|
`$ 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:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// Generated by bin2hpp 0.2.0
|
// Generated by bin2hpp 0.3.0
|
||||||
#ifndef MY_IMAGE_BMP_H
|
#ifdef MY_LIBRARY_DLL_INIT
|
||||||
#define MY_IMAGE_BMP_H
|
#define MY_LIBRARY_DLL_LEN 9728
|
||||||
#include <stddef.h>
|
const unsigned char MY_LIBRARY_DLL[MY_LIBRARY_DLL_LEN] = {77,90,144,0,3,0,0,0,4, ...};
|
||||||
const size_t MYIMAGE_BMP_LEN = 36000054;
|
|
||||||
const unsigned char MYIMAGE_BMP[MYIMAGE_BMP_LEN] = {0x42,0x4d,0x36,0x51,0x25, ...}
|
|
||||||
#else
|
#else
|
||||||
extern const size_t MYIMAGE_BMP_LEN;
|
#define MY_LIBRARY_DLL_LEN 9728
|
||||||
extern const unsigned char MYIMAGE_BMP[MYIMAGE_BMP_LEN];
|
extern const unsigned char MY_LIBRARY_DLL[MY_LIBRARY_DLL_LEN];
|
||||||
|
#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
|
#endif
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
77
src/cfg.rs
77
src/cfg.rs
@@ -22,7 +22,8 @@ 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) guard_name: String,
|
pub(crate) impl_macro: String,
|
||||||
|
pub(crate) header_guard: String,
|
||||||
pub(crate) container_type: ContainerType,
|
pub(crate) container_type: ContainerType,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,18 +57,7 @@ pub fn parse_config_from_args(args: &CliArgs) -> Result<Config, ProgramConfigErr
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Config {
|
let symbol_name = match &args.symbol_name {
|
||||||
input_file_path: args.input_file_path.clone(),
|
|
||||||
output_file_path: output_path.clone(),
|
|
||||||
overwrite: args.overwrite,
|
|
||||||
line_ending: if args.line_ending == LineEnding::Unix {
|
|
||||||
LF_LINE_ENDING.into()
|
|
||||||
} else {
|
|
||||||
CRLF_LINE_ENDING.into()
|
|
||||||
},
|
|
||||||
language: args.language,
|
|
||||||
constness: args.constness,
|
|
||||||
symbol_name: match &args.symbol_name {
|
|
||||||
Some(s) => {
|
Some(s) => {
|
||||||
if s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
|
if s.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
|
||||||
s.to_string()
|
s.to_string()
|
||||||
@@ -84,17 +74,49 @@ pub fn parse_config_from_args(args: &CliArgs) -> Result<Config, ProgramConfigErr
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
stem.retain(|c| c.is_ascii_alphanumeric());
|
stem.retain(|c| c.is_ascii_alphanumeric() || c == '_');
|
||||||
|
|
||||||
let mut ext: String = match args.input_file_path.extension() {
|
let mut ext: String = match args.input_file_path.extension() {
|
||||||
Some(s) => format!("_{}", s.to_string_lossy()),
|
Some(s) => format!("_{}", s.to_string_lossy()),
|
||||||
None => "".to_string(),
|
None => "".to_string(),
|
||||||
};
|
};
|
||||||
ext.retain(|c| c.is_ascii_alphanumeric());
|
ext.retain(|c| c.is_ascii_alphanumeric() || c == '_');
|
||||||
|
|
||||||
format!("{}{}", stem.to_ascii_uppercase(), ext.to_ascii_uppercase())
|
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 {
|
||||||
|
input_file_path: args.input_file_path.clone(),
|
||||||
|
output_file_path: output_path.clone(),
|
||||||
|
overwrite: args.overwrite,
|
||||||
|
line_ending: if args.line_ending == LineEnding::Unix {
|
||||||
|
LF_LINE_ENDING.into()
|
||||||
|
} else {
|
||||||
|
CRLF_LINE_ENDING.into()
|
||||||
},
|
},
|
||||||
|
language: args.language,
|
||||||
|
constness: args.constness,
|
||||||
|
symbol_name: symbol_name.clone(),
|
||||||
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 == '_') {
|
||||||
@@ -105,7 +127,7 @@ pub fn parse_config_from_args(args: &CliArgs) -> Result<Config, ProgramConfigErr
|
|||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
guard_name: match &args.guard_name {
|
impl_macro: match &args.impl_macro_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()
|
||||||
@@ -114,29 +136,10 @@ pub fn parse_config_from_args(args: &CliArgs) -> Result<Config, ProgramConfigErr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let mut stem: String = match output_path.file_stem() {
|
format!("{}_INIT", symbol_name.to_uppercase())
|
||||||
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());
|
|
||||||
|
|
||||||
format!("{}_{}", stem, ext)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
header_guard: header_guard,
|
||||||
container_type: args.container_type,
|
container_type: args.container_type,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/cli.rs
28
src/cli.rs
@@ -20,6 +20,26 @@ 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),
|
||||||
@@ -61,7 +81,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) guard_name: Option<String>,
|
pub(crate) impl_macro_name: Option<String>,
|
||||||
pub(crate) container_type: ContainerType,
|
pub(crate) container_type: ContainerType,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,10 +123,10 @@ pub fn parse_cli_args(args: &[String]) -> Result<CliArgs, CliParseError> {
|
|||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let guard_name = match args.iter().position(|s| *s == "--guard") {
|
let impl_macro_name = match args.iter().position(|s| *s == "--impl") {
|
||||||
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("--guard")),
|
None => return Err(CliParseError::MissingOperand("--impl")),
|
||||||
},
|
},
|
||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
@@ -217,7 +237,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(),
|
||||||
guard_name: guard_name.cloned(),
|
impl_macro_name: impl_macro_name.cloned(),
|
||||||
container_type: container_type,
|
container_type: container_type,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
--guard NAME Preprocessor macro to use for the header guard (#ifndef ...)
|
--impl NAME Preprocessor macro to use for the implementation guard
|
||||||
(default: derive from output filename)
|
in C mode (default: derive from symbol name)
|
||||||
|
|
||||||
CONTAINER TYPES:
|
CONTAINER TYPES:
|
||||||
|
|
||||||
|
|||||||
251
src/generate.rs
251
src/generate.rs
@@ -1,16 +1,21 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
HeaderGenerationError,
|
|
||||||
cfg::Config,
|
cfg::Config,
|
||||||
cli::{ByteType, Constness, ContainerType, Language},
|
cli::{ByteType, Constness, ContainerType, Language},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn with_header(
|
#[derive(Error, Debug)]
|
||||||
config: &Config,
|
pub enum HeaderGenerationError {
|
||||||
in_data: &[u8],
|
#[error("input file doesn't contain valid UTF-8: \"{0}\"")]
|
||||||
buf: &mut String,
|
InvalidUtf8String(String),
|
||||||
) -> Result<(), HeaderGenerationError> {
|
}
|
||||||
|
|
||||||
|
pub fn header(config: &Config, in_data: &[u8]) -> Result<String, HeaderGenerationError> {
|
||||||
|
let mut buf = String::with_capacity(in_data.len() * 6);
|
||||||
|
|
||||||
// Program banner at the top of the header file
|
// Program banner at the top of the header file
|
||||||
write!(
|
write!(
|
||||||
buf,
|
buf,
|
||||||
@@ -22,24 +27,30 @@ pub fn with_header(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// 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.guard_name, config.line_ending
|
config.header_guard, config.line_ending
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
write!(buf, "#define {}{}", config.header_guard, config.line_ending).unwrap();
|
||||||
} else {
|
} else {
|
||||||
write!(buf, "#ifndef {}{}", config.guard_name, config.line_ending).unwrap();
|
// C style
|
||||||
|
write!(buf, "#ifdef {}{}", config.impl_macro, config.line_ending).unwrap();
|
||||||
}
|
}
|
||||||
write!(buf, "#define {}{}", config.guard_name, config.line_ending).unwrap();
|
|
||||||
|
|
||||||
with_includes(config, buf);
|
// Includes & main content
|
||||||
with_contents(config, in_data, buf)?;
|
|
||||||
|
with_contents(config, in_data, &mut buf)?;
|
||||||
|
|
||||||
|
// End header guard
|
||||||
|
|
||||||
write!(buf, "#endif{}", config.line_ending).unwrap();
|
write!(buf, "#endif{}", config.line_ending).unwrap();
|
||||||
|
|
||||||
Ok(())
|
Ok(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_contents(
|
pub fn with_contents(
|
||||||
@@ -47,6 +58,21 @@ 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
|
||||||
|
|
||||||
@@ -61,9 +87,18 @@ 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);
|
||||||
with_declaration(config, in_data, buf);
|
write!(
|
||||||
|
buf,
|
||||||
|
"{}{}",
|
||||||
|
length_define(config, length),
|
||||||
|
config.line_ending
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
with_declaration(config, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -78,15 +113,8 @@ pub fn with_includes(config: &Config, buf: &mut String) {
|
|||||||
"#include <stdint.h>"
|
"#include <stdint.h>"
|
||||||
};
|
};
|
||||||
|
|
||||||
let stddef_h = if config.language == Language::Cpp {
|
|
||||||
"#include <cstddef>"
|
|
||||||
} else {
|
|
||||||
"#include <stddef.h>"
|
|
||||||
};
|
|
||||||
|
|
||||||
match config.container_type {
|
match config.container_type {
|
||||||
ContainerType::CArray(byte_type) => {
|
ContainerType::CArray(byte_type) => {
|
||||||
write!(buf, "{}{}", stddef_h, config.line_ending).unwrap();
|
|
||||||
match byte_type {
|
match byte_type {
|
||||||
ByteType::U8 => write!(buf, "{}{}", stdint_h, config.line_ending).unwrap(),
|
ByteType::U8 => write!(buf, "{}{}", stdint_h, config.line_ending).unwrap(),
|
||||||
ByteType::I8 => write!(buf, "{}{}", stdint_h, config.line_ending).unwrap(),
|
ByteType::I8 => write!(buf, "{}{}", stdint_h, config.line_ending).unwrap(),
|
||||||
@@ -102,15 +130,13 @@ pub fn with_includes(config: &Config, buf: &mut String) {
|
|||||||
|
|
||||||
write!(buf, "#include <array>{}", config.line_ending).unwrap();
|
write!(buf, "#include <array>{}", config.line_ending).unwrap();
|
||||||
}
|
}
|
||||||
ContainerType::CString => {
|
|
||||||
write!(buf, "{}{}", stddef_h, config.line_ending).unwrap();
|
|
||||||
}
|
|
||||||
ContainerType::StdString => {
|
ContainerType::StdString => {
|
||||||
write!(buf, "#include <string>{}", config.line_ending).unwrap();
|
write!(buf, "#include <string>{}", config.line_ending).unwrap();
|
||||||
}
|
}
|
||||||
ContainerType::StdStringView => {
|
ContainerType::StdStringView => {
|
||||||
write!(buf, "#include <string_view>{}", config.line_ending).unwrap();
|
write!(buf, "#include <string_view>{}", config.line_ending).unwrap();
|
||||||
}
|
}
|
||||||
|
_ => (),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,47 +177,16 @@ 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,
|
|
||||||
_ => in_data.len(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let modifiers = definition_modifiers(config);
|
let modifiers = definition_modifiers(config);
|
||||||
|
|
||||||
|
write!(
|
||||||
|
buf,
|
||||||
|
"{}{} = {};{}",
|
||||||
|
modifiers,
|
||||||
|
data_type_and_symbol(config),
|
||||||
match config.container_type {
|
match config.container_type {
|
||||||
ContainerType::CArray(_) => {
|
ContainerType::CArray(_) => with_array_initialiser(config, in_data),
|
||||||
write!(
|
ContainerType::StdArray(_) => with_array_initialiser(config, in_data),
|
||||||
buf,
|
|
||||||
"{}{} = {};{}",
|
|
||||||
modifiers,
|
|
||||||
length_data_type_symbol(config),
|
|
||||||
length,
|
|
||||||
config.line_ending
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
ContainerType::CString => {
|
|
||||||
write!(
|
|
||||||
buf,
|
|
||||||
"{}{} = {};{}",
|
|
||||||
modifiers,
|
|
||||||
length_data_type_symbol(config),
|
|
||||||
length,
|
|
||||||
config.line_ending
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
|
|
||||||
write!(
|
|
||||||
buf,
|
|
||||||
"{}{} = {};{}",
|
|
||||||
modifiers,
|
|
||||||
data_type_and_symbol(config, in_data),
|
|
||||||
match config.container_type {
|
|
||||||
ContainerType::CArray(_) => with_array_initialiser(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
|
||||||
@@ -201,42 +196,47 @@ pub fn with_definition(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_declaration(config: &Config, in_data: &[u8], buf: &mut String) {
|
pub fn with_declaration(config: &Config, buf: &mut String) {
|
||||||
let modifiers = declaration_modifiers(config);
|
let modifiers = declaration_modifiers(config);
|
||||||
|
|
||||||
match config.container_type {
|
|
||||||
ContainerType::CArray(_) => {
|
|
||||||
write!(buf, "{}{};{}", modifiers, in_data.len(), config.line_ending).unwrap();
|
|
||||||
}
|
|
||||||
ContainerType::CString => {
|
|
||||||
write!(
|
write!(
|
||||||
buf,
|
buf,
|
||||||
"{}{};{}",
|
"{}{};{}",
|
||||||
modifiers,
|
modifiers,
|
||||||
in_data.len() + 1,
|
data_type_and_symbol(config),
|
||||||
config.line_ending
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
};
|
|
||||||
|
|
||||||
write!(
|
|
||||||
buf,
|
|
||||||
"{}{};{}",
|
|
||||||
modifiers,
|
|
||||||
data_type_and_symbol(config, in_data),
|
|
||||||
config.line_ending
|
config.line_ending
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_array_initialiser(in_data: &[u8]) -> String {
|
pub fn with_array_initialiser(config: &Config, 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();
|
||||||
|
|
||||||
|
if signed_literals {
|
||||||
for b in in_data {
|
for b in in_data {
|
||||||
write!(out_str, "{:#x},", b).unwrap();
|
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();
|
||||||
@@ -264,15 +264,10 @@ pub fn with_string_initialiser(
|
|||||||
Ok(out_str)
|
Ok(out_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn length_data_type_symbol(config: &Config) -> String {
|
pub fn length_define(config: &Config, length: usize) -> String {
|
||||||
let mut out_str = String::with_capacity(36 + config.symbol_name.len());
|
let mut out_str = String::with_capacity(36 + config.symbol_name.len());
|
||||||
|
|
||||||
let size_type = match config.language {
|
write!(out_str, "#define {} {}", length_symbol(config), length).unwrap();
|
||||||
Language::C => "size_t",
|
|
||||||
Language::Cpp => "std::size_t",
|
|
||||||
};
|
|
||||||
|
|
||||||
write!(out_str, "{} {}", size_type, length_symbol(config)).unwrap();
|
|
||||||
|
|
||||||
out_str
|
out_str
|
||||||
}
|
}
|
||||||
@@ -280,60 +275,54 @@ pub fn length_data_type_symbol(config: &Config) -> String {
|
|||||||
pub fn length_symbol(config: &Config) -> String {
|
pub fn length_symbol(config: &Config) -> String {
|
||||||
let mut out_str = String::with_capacity(config.symbol_name.len() + 5);
|
let mut out_str = String::with_capacity(config.symbol_name.len() + 5);
|
||||||
|
|
||||||
write!(
|
write!(out_str, "{}_LEN", config.symbol_name.to_uppercase(),).unwrap();
|
||||||
out_str,
|
|
||||||
"{}{}",
|
|
||||||
config.symbol_name,
|
|
||||||
if config
|
|
||||||
.symbol_name
|
|
||||||
.chars()
|
|
||||||
.all(|c| c.is_uppercase() || c == '_')
|
|
||||||
{
|
|
||||||
"_LEN"
|
|
||||||
} else {
|
|
||||||
"_len"
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
out_str
|
out_str
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data_type_and_symbol(config: &Config, in_data: &[u8]) -> String {
|
pub fn data_type_and_symbol(config: &Config) -> String {
|
||||||
let u8_type = match config.language {
|
let mut out_str = String::with_capacity(config.symbol_name.len() + 20);
|
||||||
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) => match byte_type {
|
ContainerType::CArray(byte_type) => write!(
|
||||||
ByteType::UnsignedChar => {
|
out_str,
|
||||||
format!("unsigned char {}[{}]", config.symbol_name, length_sym)
|
"{} {}[{}]",
|
||||||
}
|
if config.language == Language::Cpp {
|
||||||
ByteType::Char => format!("char {}[{}]", config.symbol_name, length_sym),
|
byte_type.to_cpp_type()
|
||||||
_ => format!("{} {}[{}]", u8_type, config.symbol_name, length_sym),
|
} else {
|
||||||
|
byte_type.to_c_type()
|
||||||
},
|
},
|
||||||
ContainerType::StdArray(byte_type) => {
|
config.symbol_name,
|
||||||
format!(
|
length_sym
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
ContainerType::StdArray(byte_type) => write!(
|
||||||
|
out_str,
|
||||||
"std::array<{},{}> {}",
|
"std::array<{},{}> {}",
|
||||||
match byte_type {
|
if config.language == Language::Cpp {
|
||||||
ByteType::UnsignedChar => "unsigned char",
|
byte_type.to_cpp_type()
|
||||||
ByteType::Char => "char",
|
} else {
|
||||||
ByteType::U8 => u8_type,
|
byte_type.to_c_type()
|
||||||
ByteType::I8 => i8_type,
|
|
||||||
},
|
},
|
||||||
in_data.len(),
|
length_sym,
|
||||||
config.symbol_name
|
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),
|
|
||||||
ContainerType::StdStringView => format!("std::string_view {}", config.symbol_name),
|
out_str
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
73
src/main.rs
73
src/main.rs
@@ -1,10 +1,7 @@
|
|||||||
use std::io::{BufReader, BufWriter, Read, Write};
|
use std::io::{self, BufReader, BufWriter, Read, Write};
|
||||||
|
use std::path::Path;
|
||||||
use std::{env, fs::OpenOptions, process::ExitCode};
|
use std::{env, fs::OpenOptions, process::ExitCode};
|
||||||
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
use crate::cfg::Config;
|
|
||||||
|
|
||||||
mod cfg;
|
mod cfg;
|
||||||
mod cli;
|
mod cli;
|
||||||
mod constants;
|
mod constants;
|
||||||
@@ -43,10 +40,25 @@ fn main() -> ExitCode {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match write_header(&cfg) {
|
let in_data = match read_input_file(&cfg.input_file_path) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to read input file: {}", e);
|
||||||
|
return ExitCode::FAILURE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let header_src = match generate::header(&cfg, &in_data) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to generate header source file: {}", e);
|
||||||
|
return ExitCode::FAILURE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match write_output_file(header_src.as_bytes(), cfg.overwrite, &cfg.output_file_path) {
|
||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{}", e);
|
eprintln!("Failed to write output file: {}", e);
|
||||||
return ExitCode::FAILURE;
|
return ExitCode::FAILURE;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -54,48 +66,33 @@ fn main() -> ExitCode {
|
|||||||
return ExitCode::SUCCESS;
|
return ExitCode::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
fn read_input_file(input_path: &Path) -> Result<Vec<u8>, io::Error> {
|
||||||
pub enum HeaderGenerationError {
|
|
||||||
#[error("I/O error: \"{0}\"")]
|
|
||||||
IOError(String),
|
|
||||||
#[error("input file doesn't contain valid UTF-8: \"{0}\"")]
|
|
||||||
InvalidUtf8String(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_header(config: &Config) -> Result<(), HeaderGenerationError> {
|
|
||||||
let in_file = OpenOptions::new()
|
let in_file = OpenOptions::new()
|
||||||
.read(true)
|
.read(true)
|
||||||
.write(false)
|
.write(false)
|
||||||
.open(config.input_file_path.clone())
|
.open(input_path)?;
|
||||||
.expect("failed to open input file");
|
|
||||||
|
|
||||||
let mut buf = String::with_capacity(if let Ok(meta) = in_file.metadata() {
|
|
||||||
meta.len() as usize
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
});
|
|
||||||
|
|
||||||
|
let file_size = in_file.metadata()?.len();
|
||||||
|
let mut in_data: Vec<u8> = Vec::with_capacity(file_size as usize);
|
||||||
let mut reader = BufReader::new(in_file);
|
let mut reader = BufReader::new(in_file);
|
||||||
let mut in_data: Vec<u8> = Vec::new();
|
|
||||||
let n_bytes_read = match reader.read_to_end(&mut in_data) {
|
|
||||||
Ok(n) => n,
|
|
||||||
Err(e) => return Err(HeaderGenerationError::IOError(e.to_string())),
|
|
||||||
};
|
|
||||||
|
|
||||||
generate::with_header(config, &in_data[..n_bytes_read], &mut buf)?;
|
let _ = reader.read_to_end(&mut in_data)?;
|
||||||
|
|
||||||
|
Ok(in_data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_output_file(buf: &[u8], overwrite: bool, output_path: &Path) -> Result<(), io::Error> {
|
||||||
|
if overwrite {
|
||||||
|
let _ = std::fs::remove_file(output_path);
|
||||||
|
}
|
||||||
let out_file = OpenOptions::new()
|
let out_file = OpenOptions::new()
|
||||||
.write(true)
|
.write(true)
|
||||||
.truncate(config.overwrite)
|
.create_new(true)
|
||||||
.create(true)
|
.open(output_path)?;
|
||||||
.open(config.output_file_path.clone())
|
|
||||||
.expect("failed to open input file");
|
|
||||||
let mut writer = BufWriter::new(out_file);
|
let mut writer = BufWriter::new(out_file);
|
||||||
|
|
||||||
match writer.write_all(buf.as_bytes()) {
|
writer.write_all(buf)?;
|
||||||
Ok(_) => (),
|
|
||||||
Err(e) => return Err(HeaderGenerationError::IOError(e.to_string())),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user