Skip to content

Commit f209ae7

Browse files
committed
add README for vortex-ffi
1 parent 8d2e85c commit f209ae7

File tree

2 files changed

+51
-0
lines changed

2 files changed

+51
-0
lines changed

vortex-ffi/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
[package]
22
name = "vortex-ffi"
33
description = "Native C FFI bindings for Vortex"
4+
readme = "README.md"
5+
# This crate is not meant to be consumed by other Rust code but rather produces a static binary
6+
# that can be linked to by other languages.
47
publish = false
58
version.workspace = true
69
homepage.workspace = true

vortex-ffi/README.md

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Foreign Function Interface
2+
3+
Vortex is a file format that can be used by any execution engine. Nearly every programming language supports
4+
the C ABI (Application Binary Interface), so by providing an FFI interface to work with Vortex objects we can
5+
make it easy to support a variety of languages.
6+
7+
## Design
8+
9+
The FFI is designed to be very simple and follows a very object-oriented approach:
10+
11+
- **Constructors** are simple C functions that return opaque pointers
12+
- **Methods** are functions that receive an opaque pointer as the first argument, followed by subsequent arguments.
13+
Methods may return a value or void.
14+
- **Destructors** free native resources (allocations, file handles, network sockets) and must be explicitly called by
15+
the foreign language to avoid leaking resources.
16+
17+
Constructors will generally allocate rust memory, and destructors free that memory.
18+
19+
For example:
20+
21+
```c
22+
// DType is an opaque pointer to the Rust DType enum.
23+
// typedef void* DType;
24+
25+
26+
// Pointer to memory allocated by Rust (via `Box::new()`)
27+
DType d_i32 = DType_new(DTYPE_PRIMITIVE_I32, false);
28+
29+
printf("nullable = %d\n", DType_nullable(d_i32));
30+
// "nullable = 1"
31+
32+
// Rust memory is freed, d_i32 is now a dangling pointer.
33+
DType_free(d_i32);
34+
```
35+
36+
## C Strings
37+
38+
C strings are null-terminated, while Rust's are not. This means that unfortunately, we cannot simply return a pointer
39+
to a `&str` or `&String` but instead need to copy the data into a new allocation. Instead, methods that return a string
40+
should instead receive two arguments:
41+
a `*mut c_void` which is a pointer to the start of a buffer that is large enough to hold the largest string, and a
42+
`*mut c_int` to store the length of the buffer after writing.
43+
44+
This means that we can actually request a pointer instead
45+
46+
Because C and Rust have different string representations, functions that return Strings must instead receive
47+
a pointer to a buffer, and a pointer to an integer. Any `str` or `String` from Rust will be copied into the output
48+
buffer,

0 commit comments

Comments
 (0)