Expand description
HEDL FFI Bindings
Provides C-compatible interface for HEDL operations. All functions use C-style error handling with return codes.
§Memory Management
IMPORTANT: Memory ownership follows strict rules:
- Strings returned by
hedl_*functions MUST be freed withhedl_free_string - Byte arrays returned by
hedl_to_parquetMUST be freed withhedl_free_bytes - Documents MUST be freed with
hedl_free_document - Diagnostics MUST be freed with
hedl_free_diagnostics
WARNING - Memory Safety Requirements:
The hedl_free_* functions ONLY accept pointers that were allocated by HEDL
functions. Passing any of the following will cause undefined behavior:
- Pointers from
malloc/calloc/realloc(wrong allocator) - Stack-allocated variables
- Already-freed pointers (double free)
- Pointers from other libraries
- NULL is safe and will be ignored
§Thread Safety
§Error Handling Thread Safety
Error messages are stored in thread-local storage, providing lock-free, wait-free error handling for multi-threaded applications.
Key Guarantees:
- Each thread maintains its own independent error state
hedl_get_last_error()/hedl_get_last_error_threadsafe()returns the error for the CALLING thread only- Errors from one thread will NEVER appear in or overwrite errors in another thread
- No mutexes, locks, or other synchronization primitives are required
- Zero contention between threads accessing error messages
- You MUST call error functions from the same thread that received the error code
Thread-Safe Functions:
hedl_get_last_error()- Get error for current threadhedl_get_last_error_threadsafe()- Explicit thread-safe aliashedl_clear_error_threadsafe()- Clear error for current thread
Example (Multi-threaded C with pthreads):
void* worker(void* arg) {
const char* input = (const char*)arg;
HedlDocument* doc = NULL;
if (hedl_parse(input, -1, 0, &doc) != HEDL_OK) {
// Get error for THIS thread - independent of other threads
const char* err = hedl_get_last_error_threadsafe();
fprintf(stderr, "Parse error: %s\n", err);
return NULL;
}
// Process document...
hedl_free_document(doc);
return (void*)1;
}
int main() {
pthread_t threads[8];
const char* inputs[8] = { ... };
// Launch threads - each with independent error state
for (int i = 0; i < 8; i++) {
pthread_create(&threads[i], NULL, worker, (void*)inputs[i]);
}
for (int i = 0; i < 8; i++) {
pthread_join(threads[i], NULL);
}
}§Document Handle Thread Safety
Document handles (HedlDocument*) are NOT thread-safe by design for
performance reasons. Do not share document handles between threads without
external synchronization (mutexes, etc.).
Safe Pattern:
- Each thread creates its own document handles
- Each thread frees its own document handles
- No sharing of document pointers across threads
Unsafe Pattern:
- Passing a
HedlDocument*to multiple threads (data race) - Accessing the same document from multiple threads (undefined behavior)
§Error Handling
- All functions return error codes (
HEDL_OKon success) - Use
hedl_get_last_errorto get the error message for the current thread
§Security
§Poison Pointers
To detect double-free and use-after-free bugs, this library uses poison pointers:
- After freeing a document or diagnostics, the internal pointer is checked against a poison value
- All accessor functions validate that pointers are not poisoned before use
- This provides defense-in-depth against memory safety bugs
Note: Since C passes pointers by value, we cannot modify the caller’s pointer after freeing. However, we can detect if a freed pointer is passed back to us by checking for the poison value in accessor functions.
§Audit Logging
This library provides comprehensive audit logging for all FFI function calls
using the tracing crate. The logging system captures:
- Function entry/exit with timing information
- Sanitized parameters (pointer addresses are masked for security)
- Success/failure outcomes with error details
- Performance metrics (call duration)
- Thread context for correlation
§Configuring Logging
To enable logging, initialize a tracing subscriber in your application:
use tracing_subscriber::{fmt, EnvFilter};
// Initialize the tracing subscriber
tracing_subscriber::fmt()
.with_env_filter(
EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new("info"))
)
.with_target(true)
.with_thread_ids(true)
.with_line_number(true)
.init();
// Now all FFI calls will be logged§Log Levels
ERROR: Function failures with error detailsWARN: Recoverable errors or unusual conditionsINFO: Function call entry/exit with basic metricsDEBUG: Detailed parameter information (sanitized)
§Environment Variables
Control logging via the RUST_LOG environment variable:
# Log all INFO and above
export RUST_LOG=info
# Log only FFI audit events
export RUST_LOG=hedl_ffi::audit=debug
# Log everything at DEBUG level
export RUST_LOG=debug§Example Output
2025-01-05T10:30:45.123Z INFO hedl_ffi::audit: FFI call started function="hedl_parse" thread_id=ThreadId(1) depth=0
2025-01-05T10:30:45.125Z DEBUG hedl_ffi::audit: FFI call parameters function="hedl_parse" params=[("input_len", "1024")]
2025-01-05T10:30:45.130Z INFO hedl_ffi::audit: FFI call completed function="hedl_parse" duration_ms=7.2 status="success"See the audit module for more details on the logging implementation.
Re-exports§
pub use async_ops::hedl_async_cancel;pub use async_ops::hedl_async_free;pub use async_ops::hedl_canonicalize_async;pub use async_ops::hedl_lint_async;pub use async_ops::hedl_parse_async;pub use async_ops::HedlAsyncOp;pub use async_ops::HedlCompletionCallback;pub use async_ops::HedlCompletionCallbackFn;pub use async_ops::hedl_to_json_async;pub use async_ops::hedl_to_yaml_async;pub use async_ops::hedl_to_xml_async;pub use async_ops::hedl_to_csv_async;pub use async_ops::hedl_to_neo4j_cypher_async;pub use async_ops::hedl_to_toon_async;
Modules§
- async_
ops - Async FFI operations. Asynchronous operation support for FFI.
- audit
- FFI audit logging. Audit logging for FFI function calls.
- reentrancy
- Reentrancy guard. Reentrancy detection for FFI callback functions.
Macros§
- audit_
ffi_ call - Helper macro to wrap an FFI function with audit logging.
Structs§
- Hedl
Diagnostics - Opaque handle to lint diagnostics
- Hedl
Document - Opaque handle to a HEDL document
Constants§
- HEDL_
ERR_ ALLOC - Memory allocation error.
- HEDL_
ERR_ CANCELLED - Operation was cancelled.
- HEDL_
ERR_ CANONICALIZE - Canonicalization error.
- HEDL_
ERR_ CSV - CSV conversion error.
- HEDL_
ERR_ INVALID_ HANDLE - Invalid handle provided.
- HEDL_
ERR_ INVALID_ UTF8 - Invalid UTF-8 encoding error.
- HEDL_
ERR_ JSON - JSON conversion error.
- HEDL_
ERR_ LINT - Lint validation error.
- HEDL_
ERR_ NEO4J - Neo4j export error.
- HEDL_
ERR_ NULL_ PTR - Null pointer argument error.
- HEDL_
ERR_ PARQUET - Parquet conversion error.
- HEDL_
ERR_ PARSE - HEDL parsing error.
- HEDL_
ERR_ QUEUE_ FULL - Task queue is full.
- HEDL_
ERR_ REENTRANT_ CALL - Reentrant call detected (thread safety violation).
- HEDL_
ERR_ TOON - TOON conversion error.
- HEDL_
ERR_ XML - XML conversion error.
- HEDL_
ERR_ YAML - YAML conversion error.
- HEDL_OK
- Success return code.
Functions§
- hedl_
alias_ ⚠count - Get the number of aliases in a document.
- hedl_
canonicalize ⚠ - Canonicalize a HEDL document.
- hedl_
canonicalize_ ⚠callback - Canonicalize a HEDL document using zero-copy callback pattern.
- hedl_
clear_ error_ threadsafe - Clear the last error for the current thread.
- hedl_
diagnostics_ ⚠count - Get the number of diagnostics.
- hedl_
diagnostics_ ⚠get - Get a diagnostic message.
- hedl_
diagnostics_ ⚠severity - Get a diagnostic severity (0=Hint, 1=Warning, 2=Error).
- hedl_
free_ ⚠bytes - Free byte array allocated by HEDL functions (e.g.,
hedl_to_parquet). - hedl_
free_ ⚠diagnostics - Free a diagnostics handle.
- hedl_
free_ ⚠document - Free a document handle.
- hedl_
free_ ⚠string - Free a string allocated by HEDL functions.
- hedl_
from_ ⚠json - Parse JSON into a HEDL document.
- hedl_
from_ ⚠parquet - Parse Parquet bytes into a HEDL document.
- hedl_
from_ ⚠toon - Parse TOON into a HEDL document.
- hedl_
from_ ⚠xml - Parse XML into a HEDL document.
- hedl_
from_ ⚠yaml - Parse YAML into a HEDL document.
- hedl_
get_ last_ error - Get the last error message for the current thread.
- hedl_
get_ last_ error_ threadsafe - Get the last error message for the current thread (thread-safe variant).
- hedl_
get_ ⚠version - Get the HEDL version of a parsed document.
- hedl_
lint ⚠ - Lint a HEDL document.
- hedl_
parse ⚠ - Parse a HEDL document from a string.
- hedl_
root_ ⚠item_ count - Get the number of root items in a document.
- hedl_
schema_ ⚠count - Get the number of struct definitions in a document.
- hedl_
to_ ⚠csv - Convert a HEDL document to CSV.
- hedl_
to_ ⚠csv_ callback - Convert a HEDL document to CSV using zero-copy callback pattern.
- hedl_
to_ ⚠json - Convert a HEDL document to JSON.
- hedl_
to_ ⚠json_ callback - Convert a HEDL document to JSON using zero-copy callback pattern.
- hedl_
to_ ⚠neo4j_ cypher - Convert a HEDL document to Cypher queries for Neo4j.
- hedl_
to_ ⚠neo4j_ cypher_ callback - Convert a HEDL document to Cypher queries using zero-copy callback pattern.
- hedl_
to_ ⚠parquet - Convert a HEDL document to Parquet bytes.
- hedl_
to_ ⚠toon - Convert a HEDL document to TOON format.
- hedl_
to_ ⚠xml - Convert a HEDL document to XML.
- hedl_
to_ ⚠xml_ callback - Convert a HEDL document to XML using zero-copy callback pattern.
- hedl_
to_ ⚠yaml - Convert a HEDL document to YAML.
- hedl_
to_ ⚠yaml_ callback - Convert a HEDL document to YAML using zero-copy callback pattern.
- hedl_
validate ⚠ - Validate a HEDL document string.
Type Aliases§
- Hedl
Output Callback - Output callback function type for zero-copy string return.