Skip to content

Commit 8d75810

Browse files
authored
Basic CPP bindings (#19)
Nothing fancy yet, but it compiles and links to the c++ code, and allows two functions to be called. Note that this requires a change to C++ code - fast-pack/FastPFOR#125 (we could have worked around it, but this is much simpler)
1 parent 70e2855 commit 8d75810

10 files changed

Lines changed: 467 additions & 3 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ jobs:
1616
- uses: taiki-e/install-action@v2
1717
with: { tool: just }
1818
- uses: actions/checkout@v4
19+
with: { submodules: recursive }
1920
#
2021
# Uncomment this and below once the crate has been published to crates.io
2122
#
@@ -39,6 +40,7 @@ jobs:
3940
- uses: taiki-e/install-action@v2
4041
with: { tool: just }
4142
- uses: actions/checkout@v4
43+
with: { submodules: recursive }
4244
- uses: Swatinem/rust-cache@v2
4345
if: github.event_name != 'release' && github.event_name != 'workflow_dispatch'
4446
- name: Read crate metadata

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "cpp"]
2+
path = cpp
3+
url = https://github.com/nyurik/FastPFor-cpp.git

Cargo.toml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,20 @@ keywords = ["fastpfor", "compression"]
1010
categories = ["compression"]
1111
rust-version = "1.83.0"
1212

13+
[features]
14+
# We probably want to build without the C++ bindings by default.
15+
# Keeping it on for now to simplify development.
16+
default = ["cpp"]
17+
cpp = ["dep:cmake", "dep:cxx", "dep:cxx-build"]
18+
1319
[dependencies]
1420
rand = "0.8.5"
1521
thiserror = "2.0.7"
22+
cxx = { version = "1.0.136", optional = true }
23+
24+
[build-dependencies]
25+
cmake = { version = "0.1.52", optional = true }
26+
cxx-build = { version = "1.0.136", optional = true }
1627

1728
[lints.rust]
1829
unused_assignments = "allow"
@@ -21,4 +32,4 @@ dead_code = "allow"
2132
[lints.clippy]
2233
unnecessary_cast = "allow"
2334
ptr_arg = "allow"
24-
needless_range_loop="allow"
35+
needless_range_loop = "allow"

build.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
use std::path::Path;
2+
3+
use cmake::Config;
4+
5+
fn main() {
6+
if !Path::new("cpp/CMakeLists.txt").exists() {
7+
panic!("FastPFOR submodule not initialized. Run `git submodule update --init`.");
8+
}
9+
10+
// Compile FastPFOR using CMake
11+
let lib_path = Config::new("cpp").build().join("lib");
12+
let lib_path = lib_path.to_str().unwrap();
13+
println!("cargo:rerun-if-changed=cpp");
14+
15+
// Compile the bridge
16+
cxx_build::bridge("src/ffi_wrapper.rs")
17+
.include("cpp/headers")
18+
.include("src/bridge")
19+
.std("c++14")
20+
.compile("fastpfor_bridge");
21+
println!("cargo:rerun-if-changed=src/ffi.rs");
22+
println!("cargo:rerun-if-changed=src/bridge/fastpfor_bridge.cc");
23+
println!("cargo:rerun-if-changed=src/bridge/fastpfor_bridge.h");
24+
25+
// Link the FastPFOR library - must be done after the bridge is compiled
26+
println!("cargo:rustc-link-search=native={lib_path}");
27+
println!("cargo:rustc-link-lib=static=FastPFOR");
28+
}

cpp

Submodule cpp added at aebeb29

justfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ check:
3434
cargo check --all-targets --workspace
3535

3636
# Default build
37-
build:
38-
cargo build --all-targets --workspace
37+
build *ARGS:
38+
cargo build --all-targets --workspace {{ARGS}}
3939

4040
# Run all tests
4141
test *ARGS: build

src/bridge/fastpfor_bridge.h

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#pragma once
2+
3+
#include <memory>
4+
#include <rust/cxx.h>
5+
#include "codecfactory.h"
6+
#include "codecs.h"
7+
8+
// Adding things to the same namespace as lib to keep things simpler
9+
namespace FastPForLib {
10+
11+
// Instantiate Coder Factory
12+
inline std::unique_ptr<CODECFactory> new_codec_factory() {
13+
return std::make_unique<CODECFactory>();
14+
}
15+
16+
// Get a list of codec names
17+
inline std::unique_ptr<std::vector<std::string>> codec_factory_all_names(
18+
const CODECFactory& factory
19+
) {
20+
return std::make_unique<std::vector<std::string>>(factory.allNames());
21+
}
22+
23+
// Get codec by name from a factory
24+
inline std::shared_ptr<IntegerCODEC> codec_factory_get_from_name(
25+
const CODECFactory& factory,
26+
rust::Str name
27+
) {
28+
return factory.getFromName(std::string(name));
29+
}
30+
31+
// Encode helpers
32+
inline size_t codec_encode32(
33+
const std::shared_ptr<IntegerCODEC>& codec,
34+
const rust::Slice<const uint32_t> in,
35+
rust::Slice<uint32_t> out
36+
) {
37+
size_t outSize = out.size();
38+
codec->encodeArray(in.data(), in.size(), out.data(), outSize);
39+
return outSize;
40+
}
41+
42+
inline size_t codec_encode64(
43+
const std::shared_ptr<IntegerCODEC>& codec,
44+
const rust::Slice<const uint64_t> in,
45+
rust::Slice<uint32_t> out
46+
) {
47+
size_t outSize = out.size();
48+
codec->encodeArray(in.data(), in.size(), out.data(), outSize);
49+
return outSize;
50+
}
51+
52+
// Decode helper: returns consumed input elements
53+
inline size_t codec_decode32(
54+
const std::shared_ptr<IntegerCODEC>& codec,
55+
const rust::Slice<const uint32_t> in,
56+
rust::Slice<uint32_t> out
57+
) {
58+
size_t outSize = out.size();
59+
codec->decodeArray(in.data(), in.size(), out.data(), outSize);
60+
return outSize;
61+
}
62+
63+
inline size_t codec_decode64(
64+
const std::shared_ptr<IntegerCODEC>& codec,
65+
const rust::Slice<const uint32_t> in,
66+
rust::Slice<uint64_t> out
67+
) {
68+
size_t outSize = out.size();
69+
codec->decodeArray(in.data(), in.size(), out.data(), outSize);
70+
return outSize;
71+
}
72+
73+
} // namespace FastPForLib

0 commit comments

Comments
 (0)