mirror of
https://github.com/exoticorn/upkr.git
synced 2026-01-20 11:36:42 +01:00
add basic example for compiling upkr to a c library
This commit is contained in:
2
c_library/.gitignore
vendored
Normal file
2
c_library/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
/target/
|
||||
/upkr
|
||||
127
c_library/Cargo.lock
generated
Normal file
127
c_library/Cargo.lock
generated
Normal file
@@ -0,0 +1,127 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[package]]
|
||||
name = "cdivsufsort"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edefce019197609da416762da75bb000bbd2224b2d89a7e722c2296cbff79b8c"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"sacabase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lexopt"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "478ee9e62aaeaf5b140bd4138753d1f109765488581444218d3ddda43234f3e8"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sacabase"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9883fc3d6ce3d78bb54d908602f8bc1f7b5f983afe601dabe083009d86267a84"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "upkr"
|
||||
version = "0.2.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cdivsufsort",
|
||||
"lexopt",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "upkr_c"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"upkr",
|
||||
]
|
||||
17
c_library/Cargo.toml
Normal file
17
c_library/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "upkr_c"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "upkr"
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[profile.release]
|
||||
opt-level = "s"
|
||||
strip = "debuginfo"
|
||||
lto = true
|
||||
panic = "abort"
|
||||
|
||||
[dependencies]
|
||||
upkr = { path="..", default-features=false }
|
||||
8
c_library/Makefile
Normal file
8
c_library/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
upkr: upkr.c upkr.h target/release/libupkr.a
|
||||
gcc -O2 -Ltarget/release -o upkr upkr.c -lupkr -lm
|
||||
strip upkr
|
||||
|
||||
target/release/libupkr.a: cargo
|
||||
cargo build --release
|
||||
|
||||
.PHONY: cargo
|
||||
11
c_library/Readme.md
Normal file
11
c_library/Readme.md
Normal file
@@ -0,0 +1,11 @@
|
||||
This is a simple example of compiling upkr to a library that can be linked in a
|
||||
c program. It consists of a small rust crate which implements the c api and
|
||||
compiles to a static library and a matching c header file. As is, the rust
|
||||
crate offers two simple functions to compress/uncompress data with the default
|
||||
upkr config.
|
||||
|
||||
The provided makefile will only work on linux. Building the example upkr.c on
|
||||
other platforms is left as an exercise for the reader ;)
|
||||
|
||||
On Windows you might have to make sure to install and use the correct rust
|
||||
toolchain version (mingw vs. msvc) to match your c compiler.
|
||||
42
c_library/src/lib.rs
Normal file
42
c_library/src/lib.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use std::ffi::c_int;
|
||||
|
||||
// the upkr config to use, this can be modified to use other configs
|
||||
fn config() -> upkr::Config {
|
||||
upkr::Config::default()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn upkr_compress(
|
||||
output_buffer: *mut u8,
|
||||
output_buffer_size: usize,
|
||||
input_buffer: *const u8,
|
||||
input_size: usize,
|
||||
compression_level: c_int,
|
||||
) -> usize {
|
||||
let output_buffer = unsafe { std::slice::from_raw_parts_mut(output_buffer, output_buffer_size) };
|
||||
let input_buffer = unsafe { std::slice::from_raw_parts(input_buffer, input_size) };
|
||||
|
||||
let packed_data = upkr::pack(input_buffer, compression_level.max(0).min(9) as u8, &config(), None);
|
||||
let copy_size = packed_data.len().min(output_buffer.len());
|
||||
output_buffer[..copy_size].copy_from_slice(&packed_data[..copy_size]);
|
||||
|
||||
packed_data.len()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn upkr_uncompress(output_buffer: *mut u8, output_buffer_size: usize, input_buffer: *const u8, input_size: usize) -> isize {
|
||||
let output_buffer = unsafe { std::slice::from_raw_parts_mut(output_buffer, output_buffer_size)};
|
||||
let input_buffer = unsafe { std::slice::from_raw_parts(input_buffer, input_size)};
|
||||
|
||||
match upkr::unpack(input_buffer, &config(), output_buffer.len()) {
|
||||
Ok(unpacked_data) => {
|
||||
output_buffer[..unpacked_data.len()].copy_from_slice(&unpacked_data);
|
||||
unpacked_data.len() as isize
|
||||
}
|
||||
Err(upkr::UnpackError::OverSize { size, .. }) => size as isize,
|
||||
Err(other) => {
|
||||
eprintln!("[upkr] compressed data corrupt: {}", other);
|
||||
-1
|
||||
}
|
||||
}
|
||||
}
|
||||
99
c_library/upkr.c
Normal file
99
c_library/upkr.c
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "upkr.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if(argc < 2) {
|
||||
fprintf(stdout, "Usage:\n upkr [compress] [-0 .. -9] <file> [<out-file>]\n upkr [uncompress] <file> [<out-file>]\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int argi = 1;
|
||||
int uncompress = 0;
|
||||
int compression_level = 4;
|
||||
if(strcmp(argv[argi], "compress") == 0) {
|
||||
++argi;
|
||||
} else if(strcmp(argv[argi], "uncompress") == 0) {
|
||||
uncompress = 1;
|
||||
++argi;
|
||||
}
|
||||
|
||||
if(argi < argc && argv[argi][0] == '-') {
|
||||
compression_level = atoi(argv[argi] + 1);
|
||||
++argi;
|
||||
}
|
||||
|
||||
if(argi == argc) {
|
||||
fprintf(stdout, "intput filename missing\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const char* input_name = argv[argi++];
|
||||
char* output_name;
|
||||
if(argi < argc) {
|
||||
output_name = argv[argi];
|
||||
} else {
|
||||
output_name = malloc(strlen(input_name) + 5);
|
||||
strcpy(output_name, input_name);
|
||||
strcat(output_name, uncompress ? ".unp" : ".upk");
|
||||
}
|
||||
|
||||
FILE* file = fopen(input_name, "rb");
|
||||
if(file == 0) {
|
||||
fprintf(stdout, "failed to open input file '%s'\n", file);
|
||||
return 1;
|
||||
}
|
||||
fseek(file, 0, SEEK_END);
|
||||
long input_size = ftell(file);
|
||||
rewind(file);
|
||||
|
||||
char* input_buffer = (char*)malloc(input_size);
|
||||
long offset = 0;
|
||||
while(offset < input_size) {
|
||||
long read_size = fread(input_buffer + offset, 1, input_size - offset, file);
|
||||
if(read_size <= 0) {
|
||||
fprintf(stdout, "error reading input file\n");
|
||||
return 1;
|
||||
}
|
||||
offset += read_size;
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
long output_buffer_size = input_size * 8;
|
||||
long output_size;
|
||||
char* output_buffer = (char*)malloc(output_buffer_size);
|
||||
for(;;) {
|
||||
if(uncompress) {
|
||||
output_size = upkr_uncompress(output_buffer, output_buffer_size, input_buffer, input_size);
|
||||
} else {
|
||||
output_size = upkr_compress(output_buffer, output_buffer_size, input_buffer, input_size, compression_level);
|
||||
}
|
||||
if(output_size < 0) {
|
||||
return 1;
|
||||
}
|
||||
if(output_size <= output_buffer_size) {
|
||||
break;
|
||||
}
|
||||
output_buffer = (char*)realloc(output_buffer, output_size);
|
||||
output_buffer_size = output_size;
|
||||
}
|
||||
|
||||
file = fopen(output_name, "wb");
|
||||
if(file == 0) {
|
||||
fprintf(stdout, "failed to open output file '%s'\n", output_name);
|
||||
return 1;
|
||||
}
|
||||
offset = 0;
|
||||
while(offset < output_size) {
|
||||
long written_size = fwrite(output_buffer + offset, 1, output_size - offset, file);
|
||||
if(written_size <= 0) {
|
||||
fprintf(stdout, "error writing output file\n");
|
||||
return 1;
|
||||
}
|
||||
offset += written_size;
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
return 0;
|
||||
}
|
||||
25
c_library/upkr.h
Normal file
25
c_library/upkr.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef UPKR_H_INCLUDED
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// input_buffer/input_size: input data to compress
|
||||
// output_buffer/output_buffer_size: buffer to compress into
|
||||
// compression_level: 0-9
|
||||
// returns the size of the compressed data, even if it didn't fit into the output buffer
|
||||
size_t upkr_compress(void* output_buffer, size_t output_buffer_size, void* input_buffer, size_t input_size, int compression_level);
|
||||
|
||||
// input_buffer/input_size: compressed data
|
||||
// output_buffer/output_buffer_size: buffer to uncompress into
|
||||
// return value:
|
||||
// >= 0 : size of uncompressed data, even if it didn't fit into the output buffer
|
||||
// < 0 : input data corrupt, unable to decompress
|
||||
ptrdiff_t upkr_uncompress(void* output_buffer, size_t output_buffer_size, void* input_buffer, size_t input_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
Reference in New Issue
Block a user