Skip to content

Commit 4d70834

Browse files
committed
UPD: Updated C bindings to stable working.
1 parent 5d1b00f commit 4d70834

22 files changed

+703
-728
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ __pycache__/
77

88
# C extensions
99
*.so
10+
*.bin
1011

1112
# Distribution / packaging
1213
.Python

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
members = [
33
"rust",
44
"python",
5-
"mbn_c",
5+
"c",
66
]
77
resolver = "2"
88

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,14 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET "13.3")
1111

1212

1313
enable_testing()
14-
15-
# Find Google Test
1614
find_package(GTest CONFIG REQUIRED)
1715

1816
# Path to the cbindgen-generated header
1917
set(CBINDGEN_HEADER_DIR "${CMAKE_SOURCE_DIR}/../target")
2018

2119
# Rust binary
2220
set(RUST_PROJECT_DIR "${CMAKE_SOURCE_DIR}/..")
23-
set(RUST_LIB_DIR "${RUST_PROJECT_DIR}/target/debug") # or target/release for release builds
21+
set(RUST_LIB_DIR "${RUST_PROJECT_DIR}/target/debug")
2422
set(RUST_LIB "${RUST_LIB_DIR}/libmbn_c.a")
2523

2624
# Add a custom command to build Rust library using Cargo
@@ -38,8 +36,9 @@ add_custom_target(build_rust ALL DEPENDS "${RUST_LIB}")
3836

3937
# Test executable
4038
add_executable(MbnTests
41-
tests/test_decode_c.cpp # Add your test files here
39+
tests/test_decode_c.cpp
4240
tests/test_encode_c.cpp
41+
tests/test_records.cpp
4342
)
4443

4544
# Include the cbindgen header directory

mbn_c/Cargo.toml renamed to c/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ crate-type = ["staticlib"] # for .a static lib
88

99

1010
[dependencies]
11-
mbn = {path = "../rust"}
11+
mbinary = {path = "../rust"}
1212
libc ="0.2.168"
1313
cbindgen = { version = "0.27.0" }
1414

mbn_c/build.rs renamed to c/build.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ fn main() {
99
.with_config(config)
1010
.generate()
1111
.expect("Unable to generate bindings")
12-
.write_to_file("../target/mbn_c.h");
12+
.write_to_file("../target/mbinary.h");
1313
}
File renamed without changes.
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,23 @@ pragma_once = true
33
line_length = 100
44
tab_width = 4
55
sys_includes = ["stdio.h"]
6+
67
# Affects enum typedefs
78
cpp_compat = true
89

910
[export]
10-
include =["RecordHeader","RType", "Mbp1Msg", "OhlcvMsg", "TradesMsg", "BboMsg", "TbboMsg", "BidAskPair"]
11+
include =["RecordHeader","RType", "Mbp1Msg", "OhlcvMsg", "TradesMsg", "BboMsg", "TbboMsg", "BidAskPair", "RecordData","CRecordEnum" , "Side", "Action"]
1112

1213
[export.rename]
1314
"FILE" = "FILE"
1415

1516
[enum]
16-
prefix_with_name = true
17+
prefix_with_name = false
1718

1819
[parse]
1920
parse_deps = true
20-
include = ["mbn"]
21-
extra_bindings = ["mbn"]
21+
include = ["mbinary"]
22+
extra_bindings = ["mbinary"]
2223

2324
# [defines]
2425
# cbindgen = true
Lines changed: 33 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use mbn::{decode::RecordDecoder, record_ref::RecordRef, records::RecordHeader};
1+
use mbinary::decode::RecordDecoder;
22
use std::ffi::CStr;
33
use std::fs::File;
44
use std::io::{BufReader, Cursor, Read};
55
use std::ptr;
66
use std::slice;
77

8-
use crate::records::RecordData;
8+
use crate::records::CRecordEnum;
99

1010
/// C-compatible wrapper around RecordDecoder
1111
pub struct CRecordDecoder {
@@ -19,77 +19,72 @@ pub extern "C" fn create_buffer_decoder(
1919
source_size: usize,
2020
) -> *mut CRecordDecoder {
2121
if source.is_null() || source_size == 0 {
22-
return ptr::null_mut(); // Return null pointer for invalid input
22+
return ptr::null_mut();
2323
}
2424

25-
// Safety: Convert the raw pointer and size to a Vec<u8>
25+
// Convert the raw pointer and size to a Vec<u8>
2626
let source_slice = unsafe { slice::from_raw_parts(source, source_size) };
2727
let source_buffer = Cursor::new(source_slice.to_vec());
2828

2929
let decoder = CRecordDecoder {
3030
decoder: RecordDecoder::new(Box::new(source_buffer)),
3131
};
3232

33-
Box::into_raw(Box::new(decoder)) // Return a raw pointer for FFI
33+
Box::into_raw(Box::new(decoder))
3434
}
3535

3636
/// Create a new `CRecordDecoder` with a file as the source.
3737
#[no_mangle]
3838
pub extern "C" fn create_file_decoder(file_path: *const libc::c_char) -> *mut CRecordDecoder {
3939
if file_path.is_null() {
40-
return ptr::null_mut(); // Return null pointer for invalid input
40+
return ptr::null_mut();
4141
}
4242

4343
// Convert C string to Rust Path
4444
let c_str = unsafe { CStr::from_ptr(file_path) };
4545
let path = match c_str.to_str() {
4646
Ok(path) => path,
47-
Err(_) => return ptr::null_mut(), // Invalid UTF-8
47+
Err(_) => return ptr::null_mut(),
4848
};
4949

5050
let decoder = match create_decoder_from_file(path) {
5151
Ok(f) => f,
52-
Err(_) => return ptr::null_mut(), // Failed to open file
52+
Err(_) => return ptr::null_mut(),
5353
};
5454

5555
let c_decoder = CRecordDecoder { decoder };
5656

57-
Box::into_raw(Box::new(c_decoder)) // Return a raw pointer for FFI
57+
Box::into_raw(Box::new(c_decoder))
5858
}
5959

60+
// Helper function, not exposed to C directly.
61+
fn create_decoder_from_file(
62+
file_path: &str,
63+
) -> Result<RecordDecoder<Box<dyn Read>>, std::io::Error> {
64+
let file = File::open(file_path)?;
65+
let buf_reader = BufReader::new(file);
66+
let boxed_reader: Box<dyn Read> = Box::new(buf_reader);
67+
Ok(RecordDecoder::new(boxed_reader))
68+
}
69+
70+
/// Iteratively decodes records, returning false when all records are decoded.
6071
#[no_mangle]
61-
pub extern "C" fn decode_records(
62-
decoder: *mut CRecordDecoder,
63-
output_size: *mut usize,
64-
) -> *mut RecordData {
65-
if decoder.is_null() || output_size.is_null() {
66-
return std::ptr::null_mut(); // Invalid pointers
72+
pub extern "C" fn decoder_iter(decoder: *mut CRecordDecoder, output: *mut CRecordEnum) -> bool {
73+
if decoder.is_null() || output.is_null() {
74+
return false;
6775
}
6876

6977
let decoder = unsafe { &mut *decoder };
70-
71-
// Decode records into a Vec<TestRecord>
72-
let records = match decoder.decoder.decode_to_owned() {
73-
Ok(records) => records,
74-
Err(_) => return std::ptr::null_mut(), // Decoding error
75-
};
76-
77-
// Map RecordEnum to RecordData
78-
let records_data: Vec<RecordData> = records
79-
.into_iter()
80-
.map(|record_enum| record_enum.into())
81-
.collect();
82-
83-
// println!("{:?}", &records);
84-
85-
// Return the number of records
86-
unsafe {
87-
*output_size = records_data.len();
78+
let mut iterator = decoder.decoder.decode_iterator();
79+
80+
match iterator.next() {
81+
Some(Ok(record_enum)) => {
82+
let c_record: CRecordEnum = record_enum.into();
83+
unsafe { ptr::write(output, c_record) };
84+
true
85+
}
86+
_ => false,
8887
}
89-
90-
// Allocate memory for the records on the heap and pass ownership to the caller
91-
let records_box = records_data.into_boxed_slice();
92-
Box::into_raw(records_box) as *mut RecordData
9388
}
9489

9590
/// Destroy the `CRecordDecoder`
@@ -100,15 +95,6 @@ pub extern "C" fn destroy_record_decoder(decoder: *mut CRecordDecoder) {
10095
}
10196

10297
unsafe {
103-
let _ = Box::from_raw(decoder); // Drop the decoder
98+
let _ = Box::from_raw(decoder);
10499
}
105100
}
106-
107-
fn create_decoder_from_file(
108-
file_path: &str,
109-
) -> Result<RecordDecoder<Box<dyn Read>>, std::io::Error> {
110-
let file = File::open(file_path)?;
111-
let buf_reader = BufReader::new(file);
112-
let boxed_reader: Box<dyn Read> = Box::new(buf_reader);
113-
Ok(RecordDecoder::new(boxed_reader))
114-
}

c/src/encode.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
use mbinary::encode::RecordEncoder;
2+
use std::ffi::CStr;
3+
use std::os::raw::c_char;
4+
use std::path::Path;
5+
6+
use crate::records::RecordData;
7+
8+
/// C-compatible wrapper around RecordEncoder
9+
pub struct CRecordEncoder {
10+
buffer: Vec<u8>,
11+
}
12+
13+
/// Create a new 'CRecordEncoder', encoding to a buffer.
14+
#[no_mangle]
15+
pub extern "C" fn create_record_encoder() -> *mut CRecordEncoder {
16+
let c_encoder = CRecordEncoder { buffer: Vec::new() };
17+
Box::into_raw(Box::new(c_encoder))
18+
}
19+
20+
#[no_mangle]
21+
pub extern "C" fn encode_records(
22+
encoder: *mut CRecordEncoder,
23+
records: *const RecordData,
24+
record_count: usize,
25+
) -> i32 {
26+
if encoder.is_null() || records.is_null() {
27+
return -1;
28+
}
29+
30+
let encoder = unsafe { &mut *encoder };
31+
encoder.buffer.clear(); // Clear previous data
32+
let mut record_encoder = RecordEncoder::new(&mut encoder.buffer);
33+
34+
for i in 0..record_count {
35+
// records is pointer to first RecordData, this iterated the pointer to i-th RecordData
36+
let record = unsafe { &*records.add(i) };
37+
let record_ref = record.to_record_ref();
38+
39+
// Attempt to encode the record
40+
if let Err(_) = record_encoder.encode_record(&record_ref) {
41+
return -2;
42+
}
43+
}
44+
45+
0
46+
}
47+
48+
#[no_mangle]
49+
pub extern "C" fn get_encoded_data(
50+
encoder: *const CRecordEncoder,
51+
output: *mut u8,
52+
output_size: *mut usize,
53+
) -> i32 {
54+
if encoder.is_null() || output_size.is_null() {
55+
return -1;
56+
}
57+
58+
let encoder = unsafe { &*encoder };
59+
let buffer = &encoder.buffer;
60+
61+
if !output.is_null() {
62+
let output_slice = unsafe { std::slice::from_raw_parts_mut(output, buffer.len()) };
63+
output_slice.copy_from_slice(buffer);
64+
}
65+
66+
// Return the size of the buffer
67+
unsafe {
68+
*output_size = buffer.len();
69+
}
70+
71+
0
72+
}
73+
74+
#[no_mangle]
75+
pub extern "C" fn write_buffer_to_file(
76+
encoder: *mut CRecordEncoder,
77+
file_path: *const c_char,
78+
append: bool,
79+
) -> i32 {
80+
if encoder.is_null() || file_path.is_null() {
81+
return -1;
82+
}
83+
84+
// Convert the C string file path to a Rust Path
85+
let c_str = unsafe { CStr::from_ptr(file_path) };
86+
let path = match c_str.to_str() {
87+
Ok(s) => Path::new(s),
88+
Err(_) => return -2,
89+
};
90+
91+
let encoder = unsafe { &mut *encoder };
92+
let record_encoder = RecordEncoder::new(&mut encoder.buffer);
93+
94+
// Write the buffer to the file
95+
if let Err(_) = record_encoder.write_to_file(path, append) {
96+
return -4;
97+
}
98+
99+
0
100+
}
101+
102+
/// Destroy the `CRecordEncoder`
103+
#[no_mangle]
104+
pub extern "C" fn destroy_record_encoder(encoder: *mut CRecordEncoder) {
105+
if encoder.is_null() {
106+
return;
107+
}
108+
109+
// Convert the raw pointer back to a Box and drop it
110+
unsafe {
111+
let _ = Box::from_raw(encoder);
112+
}
113+
}
File renamed without changes.

0 commit comments

Comments
 (0)